design: constrain disposable canary session bootstrap and teardown
1 file changed,
+56
−0
docs/superpowers/specs/2026-09-07-canary-fixture-bootstrap.md+56 −0
CI failed — run details
diff --git a/docs/superpowers/specs/2026-09-07-canary-fixture-bootstrap.md b/docs/superpowers/specs/2026-09-07-canary-fixture-bootstrap.md| new file mode 100644 |
| index 0000000..bdb2e40 |
| --- /dev/null |
| +++ b/docs/superpowers/specs/2026-09-07-canary-fixture-bootstrap.md |
| @@ -0,0 +1,56 @@ |
| +# Isolated Sheets canary session fixture |
| + |
| +Status: proposed fallback for Fable review; do not deploy yet. Normal SMTP-based sign-in remains preferred and root has asked the user about a controlled mailbox. This design is only for testing unchanged session validation and editor behavior if that mailbox is unavailable. |
| + |
| +## Target and boundary |
| + |
| +The only permitted target is app sheets, account bds, instance overnight-sheets.bds.oncarlos.com, channel canary/overnight-batch, with its separately bound named environment. Root has verified current platform CLI supports instance environment binding. Never change the app's default environment, production origin or production channel. The fixture receives fresh canary SHEETS_DEK and SHEETS_INSTANCE_KEY and an owner address sheets-review@example.test. It does not read or copy production secrets, sessions, databases or content. |
| + |
| +Use a temporary reviewed bootstrap binary on this isolated instance, then replace it with the exact normal candidate binary. The bootstrap binary contains no login endpoint, alternate auth middleware, universal cookie, content endpoint or API backdoor. It initializes existing schema/session rows during startup only, then serves only platform health/version responses while awaiting replacement. It never serves the editor, so no browser behavior can accidentally be certified against bootstrap code. |
| + |
| +## Host and environment guards |
| + |
| +- Compile the single expected HTTPS origin and canary hostname into the bootstrap. Require SHEETS_ORIGIN to equal that origin exactly (scheme/host, no wildcard or suffix match). |
| +- Require a one-use fixture run ID in a dedicated canary environment variable, and require it to equal the run ID compiled into this bootstrap build. It is a scope guard, not an authentication mechanism. |
| +- Require SHEETS_OWNERS to equal exactly the synthetic owner address, with no additional addresses. Validate the key formats normally and require root's fresh-key provisioning record; do not compare against or obtain production key values. |
| +- Require the resolved state directory/database path to belong to the expected canary instance. Platform's systemd template uses STATE_DIRECTORY=/var/lib/carlos-app/<host>; require its final host component to equal the exact canary host and require the resolved DB path to remain inside that directory after clean/absolute resolution. Refuse an absent or different STATE_DIRECTORY. If this instance uses an exec-child activation shape instead, stop and review that actual path contract before changing the guard. |
| +- Before deployment, root verifies the control-plane route has this exact host, named environment and canary-only channel. Runtime path/origin guards complement that check; neither a release name nor an HTTP Host header alone establishes isolation. |
| +- Refuse bootstrap if any app content or member/grant/session row already exists. This is a fresh-fixture tool, not a repair or reset tool. Existing migration ledger rows alone are harmless. Never clear tables to make the precondition pass. |
| + |
| +## Session material and initialization |
| + |
| +Generate a random 32-byte session token locally using the framework's token format (base64 raw URL encoding), and compute SHA-256 hex with sessions.HashToken. Keep the raw token only in a chmod-0600 local Playwright storage-state file. Send only the hash, synthetic subject and bounded expiry through the dedicated canary environment. No token, key, cookie or sign-in link goes into stdout, logs, discussions, screenshots, commits or release metadata. |
| + |
| +Run sheets.BootSchema through the normal migrate.Apply path. Seed only one row in the existing sessions table in one transaction after rechecking the empty-state precondition. The pinned sessions schema contains token_hash, subject, method, auth_time, created_at and expires_at. Use: |
| + |
| +- token_hash: supplied 64-character lowercase hex hash, validated strictly; |
| +- subject: exactly sheets-review@example.test; |
| +- method: fixture (do not claim the mailbox was verified); |
| +- auth_time: empty; |
| +- created_at: current UTC RFC3339; |
| +- expires_at: a supplied absolute UTC time no later than two hours after creation, validated before insertion. |
| + |
| +No custom table or migration is added. A repeated bootstrap start may accept exactly the same single hash/subject/expiry row as an idempotent success but must not extend its expiry or mint another session. Any other session or app data causes refusal. Once the candidate browser creates app content, bootstrap is intentionally unable to run again on that database. |
| + |
| +The normal candidate's actor() resolves the session subject and memberFor admits/promotes only the synthetic owner listed in SHEETS_OWNERS. Do not seed a broader role or grant independently. Local tests must prove the seeded row is accepted by the real sessions.New lookup/adoption path, while a random token and an expired fixture row are rejected. |
| + |
| +Playwright state sets the cookie __Host-rastrillo_session for the exact HTTPS canary origin: Secure=true, HttpOnly=true, SameSite=Lax, path=/, no Domain attribute. The raw value never reaches bootstrap. The cookie is a fixture for the existing session store, not a signature forged using an application key. |
| + |
| +## Deployment sequence and assertions |
| + |
| +1. Record production process version and route/channel/environment bindings without reading secrets. Verify isolated canary has fresh keys and the expected empty database. |
| +2. After Fable approves bootstrap design/code and local fixture tests, deploy its uniquely stamped binary only to the explicit canary host/channel with carlos deploy. Verify bootstrap /api/version and that every non-platform route refuses; no app assertions yet. |
| +3. Remove the fixture-specific hash/run/expiry settings from the named environment before replacing bootstrap. They are not used by the normal candidate. |
| +4. Deploy the exact clean candidate built with make release. Poll process-served /api/version until it equals the intended candidate SHA; verify the canary channel no longer names the bootstrap release. Verify production still names its previously recorded release. Never treat an edge version header or a successful promote as process proof. |
| +5. Only now open Playwright with the private cookie state. Verify /sheets accepts this fixture session, unsigned and random-cookie requests cannot write, and then run deployed clipboard/batch, clear/reopen version regression, reload, second-observer and scriptless-table checks. Record candidate SHA alongside outcomes. |
| +6. Do not claim these checks exercised email delivery, pending-cookie magic-link binding, SMTP credentials or mailbox access. They certify real persisted-session validation and candidate editor behavior only. Normal SMTP sign-in remains a separate readiness check. |
| + |
| +## Teardown |
| + |
| +Use the candidate's normal POST /signout while the fixture cookie is present; confirm it deletes/revokes the row and the old cookie no longer accesses /sheets. Close browser contexts and delete the private storage-state/token files. Fixture expiry is a second bound if signout fails. Remove remaining fixture-specific named-environment variables. Keep the canary channel on the normal candidate or remove only this disposable instance through supported CLI, according to root's overnight verification needs. Never leave bootstrap selected by any channel. Remove its channel reference first; an immutable release artifact may remain in storage if the platform has no safe deletion verb, but its origin/path/run guards and missing fixture env must make it inert if mistakenly selected. |
| + |
| +Do not delete production routes, releases, keys, backups or configuration during teardown. Any authentication or cleanup failure is recorded as a failed/limited canary check, not hidden by reseeding. |
| + |
| +## Evidence required before use |
| + |
| +Fable review, wrong-host/wrong-origin/nonempty-database/expired-session/local-session-acceptance tests, supported CLI route/environment inspection, bootstrap replacement proof, and exact candidate process SHA. No bootstrap deployment has occurred as part of writing this proposal. |