Record oficina.works as the primary domain
Supersedes ruling 11 (passkey RP on a provisional oncarlos.com host). The RP is id.oficina.works; a Sheets instance is <team>.sheets.oficina.works, one team to one hostname. Hosting stays on Carloku, with oficina.works as a custom domain in front of it. The rename drill in S9.7 did not have to run: no app in the suite has a passkey.Config yet, so no credential was ever scoped to an oncarlos.com host. The window documented there is sized for the next move, not this one. The full-host RP ID constraint survives with a new reason -- on a zone we own, a suffix RP ID would be valid at every host in it, so the home's credential would be presentable straight to a Sheets instance, which is the separation ruling 15 keeps. Spec only. No code, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJ4p3qTNQqScxS6Z6wp8Pw
1 file changed,
+35
−20
docs/superpowers/specs/2026-08-30-sheets-design.md+35 −20
CI passed — run details
Intent
Attributed to this commit: 78.0 g CO₂e (8017787 tokens). This is an attribution, not a measurement — usage counted from this commit's own marker forward to the next one on domain/oficina-works, never backward from it. A turn that produced more than one commit books all of its tokens to the last one, and a concurrent session on this branch can add to the same figure. Some of it was priced using the shared default rate, because no model-specific factor row exists yet for at least one model involved. The Carbon view has the repo total, which is never derived from this attribution.
diff --git a/docs/superpowers/specs/2026-08-30-sheets-design.md b/docs/superpowers/specs/2026-08-30-sheets-design.md| index c0142e6..6602356 100644 |
| --- a/docs/superpowers/specs/2026-08-30-sheets-design.md |
| +++ b/docs/superpowers/specs/2026-08-30-sheets-design.md |
| @@ -42,13 +42,14 @@ value reaching the replicated SQLite file or any object storage we write. |
| | 8 | **Cell fill stored as OKLCH intent; imported hex retained** | Intent only (lossy round-trip); absolute hex (breaks in dark mode) | Paul, 2026-08-30 | |
| | 9 | **Provisioning: the home queues, an operator drains** | A standing console token in a web-facing app | Paul, 2026-08-30 | |
| | 10 | **The home is its own repo and its own spec** | Keeping it here; Docs drafting it | Paul, 2026-08-30 | |
| -| 11 | **Passkey RP is an `oncarlos.com` host, provisionally** — §9.7 | A real suite domain now; no passkeys | Paul, 2026-08-30 | |
| +| 11 | ~~Passkey RP is an `oncarlos.com` host, provisionally~~ — superseded by 18 | A real suite domain now; no passkeys | Paul, 2026-08-30 | |
| | 12 | **Link sharing is viewer-or-commenter, never editor** | Anonymous link editing | Paul, 2026-08-30 | |
| | 13 | **Passkey 2FA optional per person, owner-enforceable per team** | Mandatory; none | Paul, 2026-08-30 | |
| | 14 | **Light/dark is a per-person preference, switchable in the editor** | Per-document; per-team; follow-OS-only | Paul, 2026-08-30 | |
| | 15 | **One RP: the home's credential origin. Instances never enroll passkeys** — §8.1 | Per-instance passkeys (three teams = three credentials) | Proposed by this session, **confirmed by Paul, 2026-08-30** | |
| | 16 | **Version history replays inputs and recomputes; derived values are never logged** — §4.3 | Logging derived values (instant restore, log grows with fan-out) | Paul, 2026-08-30 | |
| | 17 | **Slip order written down: charts first, conditional formatting second. Version history's baseline is restore-a-cell-or-range** — §13 | Holding all eleven at full scope; cutting charts outright today | Paul, 2026-08-30 | |
| +| 18 | **`oficina.works` is the primary domain; the RP is `id.oficina.works`, instances are `<team>.sheets.oficina.works`** — §9.7 | Waiting until passkeys shipped (the move only stays free until then) | Paul, 2026-09-03 | |
| --- |
| @@ -664,25 +665,39 @@ requires a **re-announce sweep**, not merely outbox retry — retry |
| redelivers *failed* upserts and does not re-enumerate every existing grant. |
| That sweep is a named operator verb, not an emergent behaviour. |
| -### 9.7 The RP rename drill — smaller than the first draft thought |
| - |
| -Ruling 11 puts the RP on a platform-default host provisionally, which |
| -**schedules** the rename rather than avoiding it. |
| - |
| -**The concrete hosts, corrected 2026-08-30.** The account is `oficina`, |
| -sqid **`bds`** (`console.carloku.com/a/bds/`). Default addresses are |
| -`<app>.<sqid>.<apps-domain>` and the hosted apps-domain is **`oncarlos.com`** |
| -— *not* `carloku.com`, which is the console brand and appears nowhere in |
| -`platform.md`. So the credential origin is an `oncarlos.com` host under |
| -`bds`, and earlier drafts of this spec naming carloku.com were simply wrong. |
| -Worth catching here, because the RP hostname is the single most expensive |
| -string in the document to get wrong. |
| - |
| -**Never widen the RP ID to a registrable suffix on a shared platform |
| -domain.** WebAuthn permits an RP ID that is a suffix of the origin's |
| -hostname, so `oncarlos.com` would be *accepted* — and it is shared by every |
| -Carloku tenant, which would scope our credentials across other people's |
| -apps. The RP ID is the full host, always. |
| +### 9.7 The RP rename drill — scheduled, then made unnecessary |
| + |
| +Ruling 11 put the RP on a platform-default host provisionally, which |
| +**scheduled** this rename rather than avoiding it. Ruling 18 then ran it |
| +early, for free. |
| + |
| +**The hostnames, settled 2026-09-03.** The suite's primary domain is |
| +**`oficina.works`**, registered and ours. The credential origin is |
| +**`id.oficina.works`** — that is the RP — and a Sheets instance is |
| +**`<team>.sheets.oficina.works`**, one team to one hostname. Hosting stays |
| +on Carloku, with `oficina.works` as a custom domain in front of it. |
| + |
| +This replaces the provisional `<app>.<sqid>.<apps-domain>` on the hosted |
| +apps-domain `oncarlos.com` under account sqid **`bds`** |
| +(`console.carloku.com/a/bds/`). `bds` was a hosting artifact and does not |
| +survive the move. Earlier drafts naming `carloku.com` — the console brand — |
| +were wrong about the old domain too; none of it matters now except as the |
| +`LegacyRPID` value, if it is ever needed. |
| + |
| +**The drill did not have to run.** At the time of the move no `passkey.Config` |
| +existed in any app in the suite — sign-in was magic-link throughout — so no |
| +credential had ever been scoped to an `oncarlos.com` host. The window below |
| +is sized for the *next* move, not this one, and its cost is the argument for |
| +having moved before passkeys shipped rather than after. |
| + |
| +**Never widen the RP ID to a registrable suffix.** WebAuthn permits an RP ID |
| +that is a suffix of the origin's hostname, so `oficina.works` would be |
| +*accepted* for `id.oficina.works`. The objection used to be other Carloku |
| +tenants on a shared platform domain; on a zone we own it is that such an RP |
| +ID is valid at every host in the zone — this instance included — so the |
| +credential the home holds would be presentable straight to a Sheets |
| +instance, which is the separation ruling 15 exists to keep. The RP ID is the |
| +full host, always. |
| **But the mechanism already exists.** `passkey.Config.LegacyRPID` — |
| verified at source — is *"a relying party this instance used to be, accepted |