feat(infra): one-command Docker Compose run, and fix the single-service image - #517
Merged
Merged
Conversation
…ce image Add docker-compose.yml: one container (the existing root Dockerfile) with SQLite and storage in a named volume, APP_ENV=production, port bound to 127.0.0.1, and AUTH_SECRET_KEY / AI_ENCRYPTION_KEY generated on first start into the volume (one file per setting, read via a new SECRETS_DIR setting so `docker compose exec` scripts see the same keys). Bringing it up exposed two bugs in the #339 single-service image that left it unusable in a browser: - The API's deny-all CSP was applied to the SPA shell, so every script and stylesheet was blocked and the page rendered blank. The shell now gets its own policy, with index.html's inline script allowed by a hash computed from the built file, plus Google Fonts and the jsDelivr-hosted Monaco editor. - /repositories, /repositories/:id and /analysis/:id/architecture are both client routes and API paths, so a refresh on those pages returned the API's 401 JSON. Browser page loads (Accept: text/html) now get the SPA shell, except the API paths a browser really loads (docs, OAuth redirects, probes). The image also ships apps/backend/scripts so approve_email.py runs in the container, and creates /data owned by appuser so a fresh volume is writable.
parthrohit22
requested review from
SHAURYAKSHARMA24 and
hardikuppal04
as code owners
October 4, 2026 11:15
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- security_headers: collect inline scripts with html.parser instead of a lowercase-only regex (CodeQL "bad HTML filtering regexp"). Same hash for the real index.html; a test covers upper-case tags, attributes, and external scripts. - brace-expansion 5.0.9 -> 5.0.12 and undici 7.29.0 -> 7.29.1 via the existing overrides (GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p, GHSA-rfgv-xxqx-mfg5, GHSA-w293-vg96-wgc3, all published 2026-09-29). Lockfile edited for just those two entries. - Acknowledge GHSA-vfj7-8cjw-p6xm (braces): no patched release exists, it is a build-time-only transitive of tailwindcss, with a reachability guard and a 2026-12-31 review date.
# Conflicts: # apps/frontend/package-lock.json # apps/frontend/package.json
This branch was successfully deployed
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.
Summary
Adds
docker-compose.ymlso PARTHA runs withdocker compose up --build, ahead of posting about the project (r/selfhosted readers will look for a Compose file first). Getting it to work in a real browser exposed two bugs in the #339 single-service image that made it unusable, both fixed here.Linked issue
Related to #339 (single-service hosting) and #292. #292 withdrew the old multi-service Compose stack. This is a different, much smaller setup built on the existing root
Dockerfile, added at the owner's request.Scope
docker compose up --build, plus a browser walkthrough (register → importpallets/click→ analysis → Architecture → repo page refresh → Monaco preview).What changed
Infra
docker-compose.yml: one service, SQLite and storage in thepartha-datavolume,APP_ENV=production, port bound to127.0.0.1.AUTH_SECRET_KEYandAI_ENCRYPTION_KEYare generated on first start into/data/secrets, one file per setting, mode 600.Dockerfile: creates/dataowned byappuser, so a fresh named volume is writable. Also copiesapps/backend/scriptssoapprove_email.pyruns viadocker compose exec. Render (/var/data) is unaffected.Backend
config.py: new optionalSECRETS_DIR, using pydantic-settings'secrets_dir(Docker-secrets style). Environment variables still take precedence.index.html, so every script and stylesheet was blocked.frontend_content_security_policy()now gives the shell its own policy:unsafe-inlinefor scriptsconnect-src 'self'andframe-ancestors 'none'/repositories,/repositories/:idand/analysis/:id/architectureare both client routes and API paths, so refreshing those pages returned the API's 401 JSON.SpaNavigationMiddlewaresends browser page loads (Accept: text/html) to the shell. The exceptions are the API paths a browser really loads: docs, the OAuth redirects and the probes. API fetches are unchanged.Docs
docs/DEVELOPMENT.md: no longer says there's no Compose file.Acceptance criteria completed
docker compose up --buildon a clean volume reaches/ready(production, database ok, storage ok)docker compose exec partha python scripts/approve_email.py, then 201http://localhostin Chromium)pallets/clickcomplete inside the container, and Architecture renders real modules/repositories/:idrenders the appdocker compose restart, so secrets persistTesting performed
Screenshots
Not applicable: no UI change. The fix makes the existing UI load in the container.
Security and data considerations
127.0.0.1, consistent with "trusted-environment use".style-src 'unsafe-inline', because the graph and editor libraries inject styles. Scripts are hash-only.Acceptincludestext/html. Every protected API route needs a bearer header, which a page load can't send, so no API response a browser could have read is lost.Dependencies and blocked work
None.
Scope changes or remaining work
http://localhost. If refresh logs you out there, an opt-out for the cookie'ssecureflag would be the follow-up.self_hostedegress CIDR depends on the Docker runtime.