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.
88 lines
7.5 KiB
Markdown
88 lines
7.5 KiB
Markdown
# 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_SECRET` must be **TEST** values
|
|
only (`sk_test_...`/a `stripe listen`-issued or TEST-mode dashboard `whsec_...`). This is what
|
|
Phase 9.1 validated end to end.
|
|
- **Production**: `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` must 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). See `docs/
|
|
PHASE10_LAUNCH_READINESS.md` Gate 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 `.env` files 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
|
|
|
|
1. Create the "RootNode Academy" project in the Infisical dashboard
|
|
(`https://infisical.cedshomelab.com`) with Development/Staging/Production environments.
|
|
2. Share the project id (or grant the existing machine identity access to it) so secrets can
|
|
actually be pushed.
|
|
3. Once that exists, this session (or the next one) can populate every "Secret" row above from
|
|
the current local `platform/.env`/`platform/provisioner/.env` values 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.
|