refactor(compose)!: make seo-tools a standalone application - #8
Merged
Merged
Conversation
…yment tree PR #7 made app/test/typecheck work in a clean clone. The mcp service still could not start there: it attaches to the external agentic-ops network (start fails with "Could not attach to network … not found"), pins NODE_ENV=production against an empty SEO_MCP_TOKEN from the template (the server refuses to boot), and bind-mounts a sibling Hugo checkout that Docker then auto-creates as a root-owned directory next to the clone. Compose merges overlays additively, so a volume or network in the base file cannot be removed by one — only parameterised. The frontend mount source therefore becomes ${SEO_FRONTEND_DIR:-…}, defaulting to the production path so a deployment whose .env never heard of the variable is unaffected. `docker compose config mcp` on the base file alone is byte-identical before and after this commit. The overlay is named docker-compose.standalone.yml and must be passed explicitly, rather than following the docker-compose.override.yml convention used by sibling projects. That filename is auto-loaded by any bare `docker compose up`, so once such a file existed in this repo, a deploy that forgot to exclude it would silently switch NODE_ENV to development — which turns the MCP auth guard off and publishes an unauthenticated endpoint. Requiring both -f flags makes that impossible by accident. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Review of #8: seo-tools is a standalone application and a public repository, so its compose file should describe the app and nothing about where it runs. The previous shape had it backwards — the lkv deployment was the base configuration and docker-compose.standalone.yml was an escape hatch from it, including a frontend mount defaulting to a sibling website checkout. Inverting it is also the only layering Compose supports cleanly: overlays merge additively, so a base file's network or volume can never be subtracted by one, only added to. Base as the generic app and deployment as the overlay composes naturally; the reverse needed the ${VAR:-…} parameterisation this removes. Dropped from the mcp service: ../.env.shared, the VIRTUAL_*/LETSENCRYPT_HOST/ HTTPS_METHOD proxy labels, and the external agentic-ops network. The frontend mount now defaults to ./storage/frontend inside the repo. Comments that narrated one particular VPS were rewritten to state the constraint rather than the host — "only a trusted reverse proxy may set X-Forwarded-For" survives, the topology does not. docker-compose.standalone.yml is deleted; with the base standalone it has nothing left to override. NODE_ENV=production and the mandatory SEO_MCP_TOKEN stay. Requiring a token is the right posture for anything listening on a socket, standalone included; a tokenless default is how an open endpoint happens. BREAKING CHANGE: deployments that need a reverse proxy, an external network or extra mounts must now supply their own compose overlay and pass both -f flags. Verified that base + such an overlay reproduces the previous production config byte-identically. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
siva01c
added a commit
that referenced
this pull request
Sep 1, 2026
chore: land PR #8 into main (stacked merge landed on the wrong base)
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.
Stacked on #7 — merge that first, and GitHub will retarget this to
main.seo-tools is a standalone application and this is a public repository, so
docker-compose.ymlshould describe the app and nothing about where it runs. It didn't: themcpservice carried one particular deployment's shared token file, reverse-proxy vhost labels, external network and a bind mount into a sibling website checkout. A clean clone could not start it at all.Why base-as-app is the only layering that works
Compose merges overlays additively. A network or volume in the base file can never be subtracted by an overlay — only added to. So "deployment as base, standalone as overlay" needs
${VAR:-…}parameterisation to fake subtraction, while "app as base, deployment as overlay" composes naturally. The rejected approach was fighting the tool.What the base file loses
env_file: ../.env.sharedVIRTUAL_HOST,LETSENCRYPT_HOST,VIRTUAL_PORT,VIRTUAL_PROTO,VIRTUAL_NETWORK,HTTPS_METHODnetworks: [agentic-ops]+ the top-levelnetworks:block../ludekkvapil/public/seomount${SEO_FRONTEND_DIR:-./storage/frontend}docker-compose.standalone.ymlComments that narrated one particular VPS were rewritten to state the constraint rather than the host — "only a trusted reverse proxy may set
X-Forwarded-For" survives, the topology doesn't. Same formcp-server.tsanddocs/security.md.NODE_ENV=productionand the mandatorySEO_MCP_TOKENstay. Requiring a token is the right posture for anything listening on a socket, standalone included — a tokenless default is how an open endpoint happens..env.examplealready documentsopenssl rand -hex 24.BREAKING CHANGE
Deployments needing a proxy, an external network or extra mounts must supply their own overlay and pass both
-fflags:Relative paths inside the overlay resolve against the base file's directory, not the overlay's.
Verification
docker compose config mcpon the base alone: no external network, every volume source inside the repocheck-no-internal-refs.shpattern; that guard still passestsc --noEmitclean, 275 tests passEnd-to-end in a fresh clone with no sibling files, on branch #7's code: crawled 25 pages of a real site in 46s with 0 errors, generated all reports, ran the MCP server with auth enabled, and completed a full MCP handshake —
initialize,tools/list(crawl,get_report,list_reports),prompts/list(seo-consultant-marek), andprompts/getreturning the persona with the audit report injected. No external network, nothing attached toagentic-ops.🤖 Generated with Claude Code