Repository navigation
Make stacktape dev safe on a shared machine and network - #317
Merged
Merged
Conversation
The dev agent listened on every interface without authentication, local databases were published on all interfaces with a fixed password and relayed to the public bore.pub, worktrees of one project shared database containers, and the agent reported ready while a workload had failed. - The agent listens on 127.0.0.1 and requires a per-session token from .stacktape/dev-agent/<port>.token (mode 0600) on every request. It refuses foreign Host headers, any Origin header and non-JSON POSTs. dev:stop, the MCP dev tool and the live canary send the token. - /env masks secret-sourced and secret-named values and passwords in connection strings, using the shared scrubber. - Lambda tunnels run only through a relay named in STACKTAPE_TUNNEL_SERVER; otherwise dev mode warns which functions cannot reach which local databases and points to --remote-resources. - Local databases without a configured password get a generated one, stored next to their data. Data created with the old fixed password keeps it, with a warning; --freshDb replaces both. - Database ports are published on 127.0.0.1 only. Container names include a hash of the working directory. A port-bind retry removes the half-created container, Postgres readiness is checked over TCP, and --freshDb can delete data the database container owns on Linux. - Readiness requires every workload running; failed workloads appear in /status and AGENT_READY, and the daemon parent stops the session and exits non-zero. A hung daemon start kills the whole process tree. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…an agent [skip ci] The token moves from <workingDir>/.stacktape/dev-agent/<port>.token to the user's ~/.stacktape/dev-agent-tokens/<port>.token (directory 0700, file 0600). dev:stop, the MCP dev tool and getRunningAgent read it by port, so `dev:stop --agentPort` works from any directory. A starting agent removes token files whose port nothing listens on. `dev --agent` now returns the ready payload when it finds an agent already running, so the MCP tool gets the port instead of building a session that the agent refuses. The tool no longer falls back to the requested port when the CLI reports none. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dev mode exposed more than the developer's own machine. The agent HTTP API listened on every interface with no authentication, and a web page could send it simple POSTs (SQL with
confirm: true,/stop, rebuilds)./envreturned raw secrets. Local databases were published on all interfaces with the fixed passwordlocaldevpasswordand relayed through the public, unauthenticatedbore.pub. Two worktrees of one project on one stage shared and stopped each other's database containers, and the agent reported ready while a workload had failed.What changed:
~/.stacktape/dev-agent-tokens/<port>.token(directory 0700, file 0600, removed on stop; only the path is printed). Requests with a foreign Host, any Origin header, or a non-JSON POST are refused.dev:stop, the MCPstacktape_devtool, the startup/compact docs and the live dev canary send the token./envmasks values read from$Secret/$SsmParam, values under secret-like names (including container AWS credentials) and passwords in connection strings, using the sharedscrubSensitiveText.STACKTAPE_TUNNEL_SERVERnames the user's own relay. Otherwise dev mode warns which Lambda can't reach which local database and points to--remote-resources. No new flags..stacktape/dev-data/<stage>/<resource>/password. Data created with the old fixed password keeps it, with a warning;--freshDbreplaces data and password. All database ports are published on 127.0.0.1.--freshDbcould not delete Linux data owned by the container user. All three are fixed.readyrequires the session started and every workload running. Failed workloads appear in/statusand the AGENT_READY payload, and the daemon parent stops the session and exits non-zero with the workload errors. A hung daemon start now kills the whole process tree.How it was checked:
/env, LAN unreachability and token-file cleanup. Readiness is checked with failed, starting and running workloads. A Docker test starts two worktrees on one stage through the real local-resource code, plus an upgraded checkout whose old-format container holds legacy data, plus--freshDb. dev:stop is tested with and without the token. The MCP fixture daemons now require the token.local-dev(70),local-dev-containers,local-dev-ministack,clisource tests (160 files), terminal ownership, operation rules, llm-docs/mcp-docs/corpus,generate:check, typecheck, lint, fmt.check:publicfails only onpackaging-harness.spec.ts, which also fails on main.test:toolsnow fails on main's unregistereddependency-installer.spec.tsfrom Reuse the runner's dependency install on Git push deploys [skip ci] #316.local-dev-liveandlocal-dev-lambda-livecanaries (no API key in this worktree). Container-to-database reachability throughhost.docker.internalon Docker Desktop is not verified; native Linux host networking is.Follow-up fixes (4281dc3): The live canary showed that
dev:stop --agentPortfailed when run outside the directory dev mode was started in, because the token lived in that directory. The token now lives in the user's state directory under the agent's port, so dev:stop, the MCP tool and the lock-file health check find it from any directory; a starting agent removes token files whose port nothing listens on. Separately,dev --agentreturned no result when it found an agent already running, so the MCPstacktape_devtool built a session without a token and reported the agent unreachable on every retry. It now returns the same ready payload as a new agent, and the tool no longer guesses the port when the CLI reports none. New tests cover dev:stop from another directory and from another user, the reuse result through the real CLI, the MCP reuse path, the token directory's permissions and stale-file pruning. The livedev-mode-local-loopcanary now passes the dev:stop and leftover checks; three checks in the interactive, terminal-SIGTERM and occupied-ports scenarios fail identically on origin/main on this machine, because native Linux Docker runs the API container with host networking, which publishes no port for the canary to find and cannot move the API off an occupied port.Risks:
--remote-resources. Thelocal-dev-lambda-liverelease lane now needsSTACKTAPE_TUNNEL_SERVERset to a relay we control.