security: pass all 4 checklist items — gateway not public, pairing required, filesystem scoped, tunnel access
Security checklist from @anshnanda / @ledger_eth:
✅ Gateway not public — default bind 127.0.0.1, refuses 0.0.0.0 without
tunnel or explicit allow_public_bind=true in config
✅ Pairing required — one-time 6-digit code printed on startup, exchanged
for bearer token via POST /pair, enforced on all /webhook requests
✅ Filesystem scoped (no /) — workspace_only=true by default, null byte
injection blocked, 14 system dirs + 4 sensitive dotfiles in forbidden
list, is_resolved_path_allowed() for symlink escape prevention
✅ Access via Tailscale/SSH tunnel — tunnel system integrated, gateway
refuses public bind without active tunnel
New files:
src/security/pairing.rs — PairingGuard with OTP generation, constant-time
code comparison, bearer token issuance, token persistence
Changed files:
src/config/schema.rs — GatewayConfig (require_pairing, allow_public_bind,
paired_tokens), expanded AutonomyConfig forbidden_paths
src/config/mod.rs — export GatewayConfig
src/gateway/mod.rs — public bind guard, pairing enforcement on /webhook,
/pair endpoint, /health no longer leaks version/memory info
src/security/policy.rs — null byte blocking, is_resolved_path_allowed(),
expanded forbidden_paths (14 system dirs + 4 dotfiles)
src/security/mod.rs — export pairing module
src/onboard/wizard.rs — wire gateway config
935 tests passing (up from 905), 0 clippy warnings, cargo fmt clean