You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Keep server secrets out of the webapp and admin-dashboard containers #403
The production compose file gives the webapp and the admin dashboard the whole .env.production through env_file. Neither app uses the server secrets in it. So both containers hold SUPABASE_SERVICE_ROLE_KEY (bypasses all RLS), DATABASE_URL and the storage secret for no reason. Any env leak in a Next app then becomes a full database compromise.
Severity: Medium
Area: infra (docker-compose.prod.yml)
Source: security review of 2026-10-06, finding M5. Confirmed by reading every process.env use in both apps.
webapp:NEXT_PUBLIC_*, NODE_ENV, NEXT_PHASE, SERVER_RESTAPI_URL, SUPABASE_URL. SENTRY_* is read only at build time in apps/webapp/next.config.js. The only server route is apps/webapp/src/pages/api/health.ts.
admin-dashboard: its NEXT_PUBLIC_* keys and NODE_ENV.
Re-check with grep -rn "process.env\." apps/webapp/src apps/webapp/next.config.js apps/admin-dashboard/src before you change anything.
Fix plan
Remove env_file: .env.production from the webapp and admin-dashboard services.
List the needed keys under each service's environment: block.
Add SUPABASE_URL to the webapp block. Today it arrives only through env_file (apps/webapp/src/utils/supabase/url.ts:3-4). Without it, server-side rendering falls back to the public URL.
Rename NEXT_PUBLIC_SPACES_SECRET. The NEXT_PUBLIC_ prefix makes Next inline it into the client bundle the day any client code reads it. Update every reader in the same change.
Keep build-time args (NEXT_PUBLIC_* used at build) as they are in the Dockerfile.
Acceptance criteria
docker exec <webapp> env shows no SUPABASE_SERVICE_ROLE_KEY, DATABASE_URL or storage secret.
Same for the admin-dashboard container.
Webapp SSR, sign-in, documents and chat work in staging.
Admin dashboard sign-in and pages work in staging.
Note
Deploy to staging first (.github/workflows/stage.docs.plus.yml). A missing key shows only at runtime.
Fixed in 1db1bd3. Both services already listed every runtime key they read, so the change is only: remove env_file, and add SUPABASE_URL to the webapp. NEXT_PUBLIC_SPACES_SECRET has no reader anywhere, so its line was removed from .env.example instead of being renamed.
Before closing: after the staging deploy, check webapp SSR, sign-in, documents and chat, and the admin pages. A missing key shows only at runtime.
Related, not changed here: NEXT_PUBLIC_ELASTIC_APM_SECRET_TOKEN in .env.example carries the same NEXT_PUBLIC_ prefix risk.
changed the title [-][Security] Webapp and admin-dashboard containers receive the full production env, including the service-role key[/-][+]Keep server secrets out of the webapp and admin-dashboard containers[/+]on Oct 6, 2026
Reopened: the board automation closed this issue before the push. The code is on main through merge 083f37d84. It stays open until the staging check in the acceptance criteria passes: docker exec env, SSR, sign-in, documents, chat and the admin pages.
The fix (1db1bd3, merged as 083f37d84) has been live since the 2026-10-09 deploy. The latest green deploy is run 38107083947.
docker-compose.prod.yml: neither webapp nor admin-dashboard has an env_file, and neither environment: block names a secret.
A read-only check printed only set or unset, never a value. In docsplus-webapp-57, docsplus-webapp-58 and docsplus-admin-dashboard-29, SUPABASE_SERVICE_ROLE_KEY and DATABASE_URL are unset. The webapp has SUPABASE_URL.
Prod has served SSR, sign-in, documents, chat and the admin pages for three days on this config.
The staging step was replaced by prod running cleanly for three days. Closing.
Activity
fix(docker): keep server secrets out of the webapp and admin containers
HMarzban commentedon Oct 6, 2026
Fixed in
1db1bd3. Both services already listed every runtime key they read, so the change is only: removeenv_file, and addSUPABASE_URLto the webapp.NEXT_PUBLIC_SPACES_SECREThas no reader anywhere, so its line was removed from.env.exampleinstead of being renamed.Before closing: after the staging deploy, check webapp SSR, sign-in, documents and chat, and the admin pages. A missing key shows only at runtime.
Related, not changed here:
NEXT_PUBLIC_ELASTIC_APM_SECRET_TOKENin.env.examplecarries the sameNEXT_PUBLIC_prefix risk.Generated by Claude Code
docs: record the security and push fixes in changelogs and agent docs
[-][Security] Webapp and admin-dashboard containers receive the full production env, including the service-role key[/-][+]Keep server secrets out of the webapp and admin-dashboard containers[/+]chore(merge): land the security and push fixes branch
HMarzban commentedon Oct 9, 2026
Reopened: the board automation closed this issue before the push. The code is on
mainthrough merge083f37d84. It stays open until the staging check in the acceptance criteria passes:docker execenv, SSR, sign-in, documents, chat and the admin pages.HMarzban commentedon Oct 12, 2026
Verified on production, 2026-10-12
The fix (
1db1bd3, merged as083f37d84) has been live since the 2026-10-09 deploy. The latest green deploy is run 38107083947.docker-compose.prod.yml: neitherwebappnoradmin-dashboardhas anenv_file, and neitherenvironment:block names a secret.setorunset, never a value. Indocsplus-webapp-57,docsplus-webapp-58anddocsplus-admin-dashboard-29,SUPABASE_SERVICE_ROLE_KEYandDATABASE_URLare unset. The webapp hasSUPABASE_URL.The staging step was replaced by prod running cleanly for three days. Closing.