* harden: non-root container with tini, healthcheck, and a robust entrypoint
entrypoint.sh: escape sed metacharacters (\, &, #) so any future DocSearch value substitutes literally instead of crashing the pass or corrupting the bundle; rewrite only files that still contain the placeholder, making restarts no-ops; on substitution failure, log a warning and start the server anyway rather than crash-looping under restart: unless-stopped.
Dockerfile: run as the built-in non-root node user (artifacts chowned so the runtime sed keeps write access); add tini as PID 1 so SIGTERM actually reaches node and docker stop takes ~1s instead of the 10s kill grace; add a HEALTHCHECK so a dead server shows as unhealthy instead of silently Up.
Container-tested: boots 202ms, all routes/assets/redirects OK, metacharacter-laden values injected literally, uid 1000, health reaches healthy, restart idempotent (no rewrites, value intact), docker stop 0s.
* harden: substitute DocSearch placeholders independently; strip CR/LF from values
Review findings: the three substitutions were &&-chained, so one failing value skipped the remaining valid ones; and a raw newline in a value breaks the single-line sed command. Run each substitution independently, accumulating failures into one warning, and strip CR/LF in escape() (no legitimate DocSearch token contains them).
Container-tested: newline-laden appId is sanitized and all three placeholders still apply with no warning; forced full failure logs all three sed errors plus one warning and the server still starts healthy.
---------
Co-authored-by: Brandon Hopkins <brandon@techhut.tv>
Enable Next `output: 'standalone'` and rewrite the Dockerfile to package only the traced standalone server + .next/static + public on node:20-slim (glibc, matching the runner), running `node server.js`. No in-image npm ci, no full source or dependency tree. Cuts the image ~52%, so docker push and the server pull get much faster.
Also fixes a latent entrypoint bug (${VAR:"none"} -> ${VAR:-"none"}) that busybox sh tolerated but Debian dash (node:20-slim /bin/sh) rejects, which would otherwise crash the container on boot.
Smoke-tested via isolated build + docker run: boots in 37ms, all routes 200, redirects still bundled (/slack-url -> 307), static + public assets served, DocSearch env-injection intact.