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.
105 lines
8.9 KiB
Markdown
105 lines
8.9 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").
|
|
|
|
**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_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. In the existing "RootNode Academy" project's **Project Settings → Access → Machine
|
|
Identities**, add `claude-code-migration` (id `f3b42de1-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.
|
|
2. 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.
|
|
3. Once access is granted, a future session 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.
|