Skip to content

feat(web): package the sandbox dashboard as a static nginx container - #295

Merged
tiana-code merged 2 commits into
mainfrom
feat/E-08-web-packaging
Jun 19, 2026
Merged

feat(web): package the sandbox dashboard as a static nginx container#295
tiana-code merged 2 commits into
mainfrom
feat/E-08-web-packaging

Conversation

@tiana-code

Copy link
Copy Markdown
Owner

E-08 Sandbox UI #290 (F-08.10) - container packaging for the dashboard.

What

  • web/Dockerfile - multi-stage: node:22-alpine builds the bundle, nginxinc/nginx-unprivileged:1.27-alpine serves it (non-root uid 101, port 8080). SPDX header.
  • web/default.conf - SPA fallback (try_files), server_tokens off, gzip on text assets, Cache-Control no-cache on index.html + immutable on fingerprinted assets.
  • web/.dockerignore - excludes node_modules/dist/.git/.env (keeps package-lock + src).
  • web-ci.yml - new docker job: builds the image (context web, no push), hard-asserts non-root, smoke-runs and curls / + a client route /payments (SPA fallback), Trivy CRITICAL scan with the DB mirror. No deploy.
  • docker-compose.yml - web service (build ./web, 8082:8080, avoids the grafana 3000 mapping).
  • web/README.md - container quickstart.

Scope

Container + compose + CI validation + README. The full Helm chart (deploy/helm/web) is a focused follow-up (#290b), mirroring the #289 split.

Gate chain

  • critic: GO-WITH-CHANGES (port 8082 not 3000 grafana-clash; exact docker build web context; Trivy added; grep -i; single listen - all applied).
  • security-auditor (opus): PASS - non-root by construction, no push/deploy, no workflow injection, server_tokens off, .dockerignore excludes .env/.git, Trivy gate, clean-room clean, no secrets.
  • code-reviewer: 2 HIGH (CI non-root assert + readiness hard-fail with explicit exit + docker logs) + 2 should-fix (gzip + cache headers) - all adopted.
  • evaluator: PASS (0.88).
    Docker is not buildable locally (WSL); the CI docker job is the validation gate.

Closes #290

Serve the SPA from a multi-stage image: a node build stage produces the
bundle and a non-root nginx-unprivileged runtime serves it on 8080 with a
single-page-app fallback so client routes resolve on refresh. The config
disables the version banner, gzips text assets, caches fingerprinted
assets immutably and keeps index.html uncached. A docker-compose web
service serves it alongside the backend, and the web CI workflow builds
the image, asserts it runs non-root, smoke-tests the served page and a
client route, and scans it with Trivy. Nothing is pushed or deployed. The
Helm chart is tracked as a follow-up.

Closes #290
@tiana-code tiana-code added this to the v0.4.0 milestone Jun 19, 2026
The nginx-unprivileged base ships libcrypto3/libssl3 3.3.3-r0, which
Trivy flags for the CVE-2026-31789 heap overflow (fixed in 3.3.7-r0).
Upgrade the two packages in a transient root layer, then drop back to
the non-root uid the image runs as.

Part of #290
@tiana-code
tiana-code merged commit f24cc45 into main Jun 19, 2026
10 checks passed
@tiana-code
tiana-code deleted the feat/E-08-web-packaging branch June 19, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sandbox UI: production build + container/Helm packaging for web

1 participant