Skip to content

feat(up): add --auto-forward dynamic user-space port forwarding#188

Open
pofallon wants to merge 2 commits into
mainfrom
015-auto-forward-ports
Open

feat(up): add --auto-forward dynamic user-space port forwarding#188
pofallon wants to merge 2 commits into
mainfrom
015-auto-forward-ports

Conversation

@pofallon

@pofallon pofallon commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What

Adds deacon up --auto-forward — a deacon consumer extension (modeled on VS Code Dev Containers) that starts a detached, host-side forwarder for the container, polls its TCP LISTEN sockets (/proc/net/tcp{,6} via docker exec, ~1s), and relays each detected or declared port to a 127.0.0.1:<host-port> listener over docker exec -i. Because docker exec shares the container netns, this reaches 127.0.0.1-bound servers that static -p publishing cannot. up returns to the shell immediately.

Implements feature spec specs/015-auto-forward-ports/ (US1–US5); all active tasks T001–T053 done, T054–T060 tracked as intentional deferrals.

Highlights

  • New core module crates/core/src/port_forward/ (detect, registry, relay, daemon):
    • Pure /proc/net/tcp{,6} LISTEN parser (little-endian v4/v6 decode + dedup).
    • Host-global forwarded_ports.json registry: fs2 advisory lock + atomic temp-file/rename writes; collision-free allocation (same-number preference, privileged→≥1024 remap), stale-pid pruning, release.
    • Per-connection byte relay with socat/nc/bash-/dev/tcp fallback and fail-fast when none.
    • Supervisor loop: eager declared + lazy auto-detected forwarding, withdrawal, portsAttributes.onAutoForward honoring (ignore/silent/notify), compose "service:port" dialing, PORT_EVENT: emission, pid marker, SIGTERM/self-exit cleanup. Detach/signaling via safe nix wrappers (setsid/dup2/kill) — production stays unsafe-free.
  • CLI wiring: --auto-forward flag, hidden __forward-daemon re-exec entrypoint, spawn/adopt-or-reuse + static -p suppression in the up container & compose paths, reap hooks in down and up --remove-existing-container.
  • Docs/examples: up/SPEC.md §2.1, SECURITY.md surface, README.md section, and the self-verifying examples/up/auto-forward/ canary (registered in the aggregator).

Behavior contract

  • Loopback-only (127.0.0.1, never 0.0.0.0/LAN), TCP-only, Unix-only in v1 (non-Unix warns, up unaffected).
  • Declared ports (forwardPorts/appPort/--forward-port) forward eagerly and are suppressed from static -p; undeclared ports forward lazily and are withdrawn when they stop.
  • Best-effort: a forwarding failure warns loudly but never fails up (exit 0).
  • Per-port mappings → stderr (forwarder log); the up result JSON on stdout is unchanged.

Tests

  • Hermetic unit tests: parser, registry (allocation/collision/release/prune), attribute resolution, marker adopt-or-reuse.
  • Docker integration suite (integration_auto_forward, docker-exclusive — it binds real host ports): loopback reach, dynamic detect/withdraw via exec, multi-container collision-free + release, down/replace reaping, ignore/silent/notify, compose "service:port".
  • Full gate green: cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, make test-nextest (2784 passed, 0 failed).

Notes

  • integration_auto_forward is classified docker-exclusive in .config/nextest.toml (per spec task T033) because it binds real host ports and must not race other port-publishing docker tests.
  • Surfaced a pre-existing, unrelated bug while writing tests: exec --workspace-folder can't resolve up's container due to a configHash mismatch between the two paths — filed as exec/run-user-commands can't resolve up's container: configHash mismatch between up and exec #187 (the US2 test works around it via exec --container-id).

🤖 Generated with Claude Code

Adds a `deacon up --auto-forward` flag (deacon consumer extension, modeled on
VS Code Dev Containers) that starts a detached, host-side forwarder for the
container. The forwarder polls the container's TCP LISTEN sockets
(/proc/net/tcp{,6} via `docker exec`, ~1s) and relays each detected or declared
port to a 127.0.0.1:<host-port> listener over `docker exec -i`, reaching even
127.0.0.1-bound servers that static `-p` cannot.

- New core module `crates/core/src/port_forward/` (detect, registry, relay,
  daemon): pure /proc/net/tcp parser; host-global forwarded_ports.json registry
  with fs2 advisory lock + atomic writes and collision-free allocation
  (same-number preference, privileged->>=1024 remap, stale-pid pruning);
  per-connection relay with socat/nc/dev-tcp fallback (fail-fast); supervisor
  loop with eager declared + lazy auto-detected forwarding, withdrawal,
  onAutoForward honoring, compose service:port dialing, PORT_EVENT emission,
  pid marker, SIGTERM/self-exit cleanup. Detach/signaling via safe nix wrappers
  (setsid/dup2/kill); production stays unsafe-free; Unix-only in v1.
- CLI: `--auto-forward` on `up`, hidden `__forward-daemon` re-exec entrypoint,
  spawn/adopt-or-reuse + static `-p` suppression in the up container & compose
  paths, reap hooks in `down` and `up --remove-existing-container`.
- Tests: hermetic unit tests (parser, registry, attributes, marker) + Docker
  integration suite (loopback reach, dynamic detect/withdraw, multi-container
  collision-free, down/replace reaping, ignore/silent/notify, compose
  service:port). integration_auto_forward is docker-exclusive (binds real host
  ports).
- Docs/examples: up SPEC.md section, SECURITY.md surface, README section, and
  the self-verifying examples/up/auto-forward/ canary.

Best-effort: forwarding failures warn loudly but never fail `up`. Loopback-only,
TCP-only, Unix-only in v1; remaining scope tracked as deferrals (T054-T060).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Annotate the Deferred Work section in tasks.md with the GitHub issues that
capture each deferral's hidden gotchas + recommended path forward
(T054→#189, T055→#190, T056→folded into #194, T057→#191, T058→#192,
T059→#193, T060→#194).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Build system changes deps Dependency updates docs Documentation changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant