Gate 1 — canonical state audit: this workstation's HEAD/origin/main and every claimed milestone SHA (Phase 2A through 9.1) verified consistent; no stranded branches/tags on Gitea; full-history secret scan clean (no rotation required); Phase 9.5 confirmed absent from canonical history (pricing was given directly by Chase and used without it); second-PC reconciliation commands provided since this session cannot see that machine. Gate 3 — secrets: real inventory derived from process.env greps across every service (not guessed); Infisical auth fixed (wrong domain was the actual root cause of Phase 9.1's failures — the identity belongs to infisical.cedshomelab.com, not app.infisical.com) but no RootNode Academy project exists there yet, contradicting this phase's own authorization — flagged for Chase to create. Gates 4-9/15-16 — production: verified, not assumed, that no dedicated server/Proxmox VM has been provisioned (empty terraform.tfvars, no state file, placeholder group_vars/production.yml) — IaC validated (tofu validate, ansible --syntax-check both clean) and left unexecuted per this phase's own 'do not fake production deployment' instruction. rootnodeacademy.com confirmed live (Cloudflare, real HTTPS) — the separate, not-RootNode-owned website; no Platform API subdomain exists yet. Gate 18 — security audit: full review against every regression suite rerun this phase; no launch-critical defect found. Gates 19/20 — full regression (131 checks: e2e-phase5b/5c/6f/7/8/9, content validation, migrations, frontend build) and the Winter Break commerce rehearsal, both rerun clean against current main. Gates 10-14/21 — website integration, Winter Break pricing (99/49, real, Stripe-validated), agreements/legal, tax, and refund policy all reviewed; legal text/tax/refund policy/dates/venue/ capacity correctly left as open owner decisions, not invented. No application code changed this phase — audit/launch-prep only.
7.5 KiB
Secrets & Infisical
Gate 3. The full, real secret inventory — derived by grepping every process.env.* reference
across platform/api/src, platform/gateway/src, platform/provisioner/src, and
platform/lab-router/src (not guessed, not copied from a template list) — and the current
status of centralizing it in Infisical. This document never contains a secret value.
Infisical project status — action required
Chase's self-hosted instance is infisical.cedshomelab.com (confirmed reachable, confirmed NOT
app.infisical.com — the earlier Phase 9.1 attempt failed for exactly this reason: the existing
machine identity is valid for the self-hosted instance only). Authenticated successfully this
phase using the machine identity at ~/.config/infisical/identity.env
(universal-auth, org "Ceds HomeLab").
No "RootNode Academy" project exists yet in that organization — verified via infisical init's own project picker, which lists exactly six existing projects: Core Infrastructure,
Hermes Fleet, Kubernetes, Media Platform, Monitoring, Network Service. This contradicts Phase
10's own authorization text ("Chase has ALREADY created RootNode Academy"). The Infisical CLI
has no non-interactive project-creation command — creating one requires the web dashboard
(https://infisical.cedshomelab.com), which this session cannot reach (no browser).
Owner action required: create a "RootNode Academy" project in the Infisical dashboard, with
three environments (Development, Staging, Production — matching this document's own
categorization below), and tell this session (or a future one) its project id so the secret
inventory below can actually be pushed. Until then, every secret continues to live only in local,
gitignored .env files (development) or has no home at all (staging/production don't exist yet
— see docs/PHASE10_PRODUCTION_DEPLOYMENT.md).
Secret inventory (real, from code — not from this prompt's guessed category list)
Every environment variable the application code actually reads, categorized. "Secret" = must
never appear in git, logs, or a browser; "Config" = fine to template/document in plain text
(already handled by infra/environments/*/*.env.template, unchanged by this phase).
| Variable | Category | Purpose | Consuming service | Environments | Rotation |
|---|---|---|---|---|---|
PLATFORM_DB_URL |
Secret (contains DB password) | Postgres connection string | Platform API | Dev/Staging/Prod (per-env DB, never shared) | On suspected compromise; routine rotation not yet scheduled |
PROVISIONER_SHARED_SECRET |
Secret | Platform API ↔ Provisioner trust boundary (X-Provisioner-Key) |
Platform API, Provisioner | Dev/Staging/Prod (distinct per env) | On suspected compromise |
INTEGRATION_SHARED_SECRET |
Secret | Website backend ↔ Platform API trust boundary (X-RootNode-Integration-Key) |
Platform API, website's backend (external) | Dev/Staging/Prod | On suspected compromise; the website team needs the current value out-of-band |
GOOGLE_SERVICE_ACCOUNT_JSON |
Secret | Full GWS service-account credential (Gmail API, domain-wide delegation) | Platform API (email/gws-provider.js) |
Prod only (dev/staging use the deterministic dev provider by default) | Per Google Cloud's own service-account key rotation practice |
GOOGLE_DELEGATED_USER |
Config (an email address, not a secret) | Which real mailbox the service account impersonates via domain-wide delegation | Platform API | Prod | N/A |
EMAIL_FROM_ADDRESS / EMAIL_FROM_NAME / EMAIL_REPLY_TO |
Config | Sender identity — [email protected] |
Platform API | All | N/A |
STRIPE_SECRET_KEY |
Secret | Stripe API authentication (checkout creation, refunds, retrieval) | Platform API (payment-provider.js) |
Dev/Staging = TEST key; Production = LIVE key — never mixed, see Gate 8 status below | Per Stripe's own key-rotation guidance; rotate immediately if ever exposed |
STRIPE_WEBHOOK_SECRET |
Secret | Verifies a webhook delivery actually came from Stripe | Platform API (webhook-routes.js) |
One per registered webhook endpoint (TEST vs. LIVE endpoints each have their own) | Regenerates automatically if the endpoint is recreated |
TEST_PAYMENT_WEBHOOK_SECRET |
Low-sensitivity (dev-only, never real money) | Shared secret for the deterministic TestProvider's fake webhook signature |
Platform API | Dev/test only | N/A |
LAB_ROUTER_TLS_KEY |
Secret | Lab Host Router's mTLS private key | Lab Host Router | Staging/Prod (dev uses a self-signed pair generated locally) | Per the mTLS cert lifecycle already documented in Phase 4.1 |
LAB_ROUTER_TLS_CERT / LAB_ROUTER_TLS_CA |
Sensitive but not strictly secret (public cert material) | mTLS certificate/CA chain | Lab Host Router, Gateway | Staging/Prod | Same lifecycle as the key above |
PLATFORM_DB_URL's host component, ports, timeouts, session TTLs, PAYMENT_PROVIDER, COOKIE_SECURE, TRUST_PROXY, PUBLIC_APP_ORIGIN, DNS_RESOLVER, NO_PROXY_CIDRS, etc. |
Config | Plain operational configuration | Various | All, per-environment values | N/A |
Every "Config" row is already handled the existing way — plain values in infra/environments/ */*.env.template (checked into git, safe) or docker-compose.dev.yml's ${VAR:-default}
substitutions. Only the "Secret" rows above need an Infisical home.
Environment separation (Stripe — the one category with real launch consequences)
- Development/Staging:
STRIPE_SECRET_KEY/STRIPE_WEBHOOK_SECRETmust be TEST values only (sk_test_.../astripe listen-issued or TEST-mode dashboardwhsec_...). This is what Phase 9.1 validated end to end. - Production:
STRIPE_SECRET_KEY/STRIPE_WEBHOOK_SECRETmust be LIVE values (sk_live_..., and a permanent LIVE-mode dashboard webhook endpoint's own signing secret). Not configured — Chase has not provided live Stripe credentials, and this phase did not ask for or receive them (per Phase 10's own stop condition: "real money could move" / "identity verification requires Chase" — live Stripe activation requires Chase's own Stripe business/identity verification, not something this session can do on his behalf). Seedocs/ PHASE10_LAUNCH_READINESS.mdGate 8 status.
Secret hygiene performed this phase
- Confirmed (again, freshly) that no real secret value has ever entered git history (
docs/ PHASE10_CANONICAL_STATE_AUDIT.md§1.6). - Confirmed
platform/.env's Stripe fields are correctly blank (not left set from Phase 9.1's validation session) — matches this repo's own "unset after a deliberate session" convention. - Not yet performed (blocked on the missing Infisical project): migrating any real secret
value out of local
.envfiles and into Infisical. Per this phase's own instruction, a local credential is never deleted until its Infisical replacement is verified — nothing was deleted.
What Chase needs to do
- Create the "RootNode Academy" project in the Infisical dashboard
(
https://infisical.cedshomelab.com) with Development/Staging/Production environments. - Share the project id (or grant the existing machine identity access to it) so secrets can actually be pushed.
- Once that exists, this session (or the next one) can populate every "Secret" row above from
the current local
platform/.env/platform/provisioner/.envvalues for Development, and Chase provides the real Staging/Production values (Stripe LIVE keys especially) directly when ready for those environments — never invented or guessed by this phase.