Phase 9.5: reconfirmed no canonical commit exists (broader content search across full history — still zero hits outside this session's own commits); Chase confirmed the real document lives on PC #2, unpushed. Not duplicated here per Chase's explicit instruction — he owns bringing it to canonical Gitea. Infisical: Phase 10's 'project does not exist' finding was incomplete, not wrong about what it checked — a proper org-scoped API enumeration (8 projects) still shows no RootNode Academy project visible to the claude-code-migration machine identity, even though Chase confirms it exists. Root cause narrowed to a missing per-project access grant (Infisical scopes identity visibility per-project, separate from org membership) rather than the project not existing. Corrected docs/SECRETS_AND_INFISICAL.md and docs/PHASE10_CANONICAL_STATE_AUDIT.md accordingly, with the exact one-step fix (grant the identity access via Project Settings -> Access) and the identity id needed to do it. No duplicate project was created. Website pricing: a read-only fetch of rootnodeacademy.com found stale founding-price RANGES that don't match the exact fixed V1 prices Chase gave directly this phase; Chase confirmed the fixed prices in this repo's docs are correct and current. Noted as a real discrepancy for his own awareness, not something this repo's scope covers fixing. Updated the Phase 10 launch matrix with a Category column (TECHNICAL READY / OWNER DECISION REQUIRED / EXTERNAL BLOCKER / HARDWARE BLOCKER) distinguishing who needs to act on each remaining gate. No application code changed — audit/documentation reconciliation only.
8.9 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").
Update, Phase 10.1: Chase confirmed a "RootNode Academy" project already exists on this
instance. Re-investigated with a proper API call rather than trusting the CLI's interactive
picker alone — GET /api/v2/organizations/{orgId}/workspaces (the same authenticated identity,
a direct, complete enumeration, not a scrollable TUI) returns 8 projects visible to this
identity: Core Infrastructure, Kubernetes, Media Platform, Monitoring, Network Service,
Privileged Access Manager, Storage, Synthos Systems. (Two more than Phase 10's original 6-project
CLI capture, and with different membership — the CLI capture was evidently incomplete, not just
under-scrolled; "Hermes Fleet," which appeared in the CLI list, doesn't appear in this
authoritative API list at all.) Still no "RootNode Academy" entry, under this identity.
Root cause, narrowed: an identity's own workspace list in Infisical reflects only the
projects it has been explicitly granted access to (a per-project ACL, separate from org
membership) — an org owner's dashboard view would show every project in the org regardless of
any one identity's grants. The most likely explanation is that the project exists, created by
Chase, but the claude-code-migration machine identity was never added to its access list —
not that the project doesn't exist. This session cannot tell the two apart without either
org-owner API scope or the web dashboard, neither available here. No duplicate project was
created, per Phase 10.1's explicit instruction.
Owner action required — the fix, precisely: in the Infisical dashboard
(https://infisical.cedshomelab.com), open the RootNode Academy project → Project Settings →
Access → Machine Identities, and add claude-code-migration (identity id
f3b42de1-11b6-4aa1-8f2e-7b85accec5fb) with read/write access to whichever environments should
be automatable. Once granted, a future session can list/read/write its secrets directly — no
further CLI/API workaround needed. Until then, every secret continues to live only in local,
gitignored .env files (development) — see docs/PHASE10_PRODUCTION_DEPLOYMENT.md for
staging/production status.
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
- In the existing "RootNode Academy" project's Project Settings → Access → Machine
Identities, add
claude-code-migration(idf3b42de1-11b6-4aa1-8f2e-7b85accec5fb) with read/write access to whichever environments should be automatable. Do not create a second project — this one-line access grant is the fix, per Phase 10.1's investigation. - Confirm the project's environment names (Phase 10.1 could not see them without access) — reuse whatever exists rather than renaming to force a Development/Staging/Production match, per this repo's own "existing structure wins" instruction.
- Once access is granted, a future session 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.