From 6cddca9f499279d4a9b82c6b794aa046711f1096 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 05:05:49 +0000 Subject: [PATCH] docs(adr): ADR-0126 wall 3 holds for the access class; ADR-0131 D5 records the single-posture re-slug rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0126 §6 wall 3 gains a dated note: the boot report (#25311, 54df640b38) and the marketplace refusal (#25355, 448374d3f8) make it true in practice for sharing rules, permission sets, positions and capabilities. The note names who is told under `single` and under a wall, the findingSet confirm token, the two accepted limits under §9, the two doors still open (#25382, #25366), and that the other classes follow after v18. ADR-0131 D5 gains a dated note beside its 2026-10-22 note: under `single`, a slug changed to `default` is admitted only on the current Default Organization and refused 403 for every context (#25286, 09e07c259a). It names the removed move and the stranding it leaves. Notes only; no decision text changes. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01HbVJUgUf5HuWXPQvAo4sFg --- ...6-packaged-metadata-customization-model.md | 43 +++++++++++++++++++ ...ation-ownership-no-null-organization-id.md | 21 +++++++++ 2 files changed, 64 insertions(+) diff --git a/docs/adr/0126-packaged-metadata-customization-model.md b/docs/adr/0126-packaged-metadata-customization-model.md index f2a1e14e920..e0c1257ba08 100644 --- a/docs/adr/0126-packaged-metadata-customization-model.md +++ b/docs/adr/0126-packaged-metadata-customization-model.md @@ -276,6 +276,49 @@ read at each runtime's own consult point; an empty ledger changes nothing anywhe they never write the ledger — the ledger records the customer's **choices**, and no upgrade un-makes a choice. The converse holds too: disabling never blocks or edits the base's upgrade stream. The two never meet: the base's version history and the ledger share no columns. + + > **Note (2026-10-23, ruling of record `6259332195` on #24443, items 1–2, 4–5) — wall 3 is true + > in practice for the access class.** Two stages of #24446 make this wall hold for sharing rules, + > permission sets, positions and capabilities. They add no spec key and no rename spelling. + > + > - **The boot report** (U1, #25311, `54df640b38`). At `kernel:bootstrapped`, plugin-security + > compares the customer's access state with the registry the boot holds. That state is the + > activation-ledger rows, the environment-authored permission sets and positions (clones + > included) and the `sys_sharing_rule` rows. The report lists orphans, suspected re-enablements, + > sharing-rule collisions, broken references and field denies a customer set lacks. It writes and + > refuses nothing. + > - **The marketplace refusal** (U2, #25355, `448374d3f8`). On a re-install over an installed + > package, before its first write, `POST /api/v1/marketplace/install-local` runs the same + > comparison through the `security.accessUpgradeCheck` service. It runs over the registry and + > over a projection with the incoming manifest. Only the delta refuses: 409 + > `ACCESS_UPGRADE_CONFIRMATION_REQUIRED`, naming each finding, and nothing is written. A finding + > that predates the upgrade does not block it; the boot report keeps raising it. A check that + > cannot read its sources answers 503 and writes nothing. + > - **The confirm token.** The refusal carries a `findingSet` token. The same call with + > `confirmAccessUpgrade` set to that token installs. The install's own entry in the marketplace + > install ledger, not the activation ledger, records the confirmation (`findingSet`, + > `confirmedBy`, `confirmedAt`). The token confirms exactly the findings it names: if they + > change, the call is refused again with the fresh set, and a bare `true` confirms nothing. The + > CLI sends it as `os package install … --confirm-access-upgrade`. + > - **Who is told.** Under `single`, the administrator. The boot report notifies the Default + > Organization's `admin_full_access` holders and any `OS_PLATFORM_OWNER_EMAIL` address, once per + > finding set. The refusal answers the administrator who installs (`manage_metadata`). Under a + > wall (`group`, `isolated`), the operator. The boot report is one `error` line in the operator's + > log and writes into no organization (seat ruling `6353787407`, option A). Only the operator + > reaches the refusal: ADR-0131 D12 answers a tenant administrator 403 first, with no finding. + > A tenant administrator is never the fallback. + > - **Two accepted limits**, both following from §9 (a clone records no base and no link) and from + > the bare `(metadata_type, name)` ledger key. A re-enablement is only *suspected*: nothing + > records which name replaced which, so a package with several unswitched names yields several + > candidates. The field-deny check reads "grants the same object", not "copied from that set", + > so it can name an unrelated customer set and is a review item. + > - **Two doors are still open**, each with its card. `POST /api/v1/packages` with `overwrite` + > replaces an installed package without this check (#25382). A cloud-fetched marketplace + > install of a package shipping a catalog name an environment-authored item holds writes its + > install entry, and the next cold boot refuses; it is to be refused before the first write, not + > confirmed (#25366). + > - **The other classes follow after v18** (ruling item 4), as does the retirement of + > `package-upgrade.zod.ts`. Until then this wall holds in practice for the access class only. 4. **The paper protocol is not the model.** The three-layer `metadata-customization.zod.ts` surface (#12057 — exported, documented, zero consumers) is **superseded by this ADR as a matter of record**: nothing may build against it, and its diff --git a/docs/adr/0131-total-organization-ownership-no-null-organization-id.md b/docs/adr/0131-total-organization-ownership-no-null-organization-id.md index 6b608b91897..4c85375ea48 100644 --- a/docs/adr/0131-total-organization-ownership-no-null-organization-id.md +++ b/docs/adr/0131-total-organization-ownership-no-null-organization-id.md @@ -455,6 +455,27 @@ The `single` anchor's own disposition is #11979's (Choice 4B), which this record > outside `os serve` over a database built before 18.x, it let the deployment's only platform > administrator be banned. That anchor is retired. +> **Note (2026-10-22, #25286, `09e07c259a`; landed record `6353078154` on #25271) — under `single`, +> no slug change makes another organization the Default Organization.** Under `single`, a +> `sys_organization` slug changed to `default` is admitted only on the organization that is already +> the Default Organization. Every other such write is refused 403 `PERMISSION_DENIED` by the +> last-admin guard's `sys_organization` `beforeUpdate` hook, for every context: better-auth's +> `organization/update`, seeds and plugins alike, `isSystem` included. A read the guard cannot +> complete refuses too. Before this hook, such a re-slug made that organization the Default +> Organization, and every holder of a grant it owned a platform administrator. Walled postures are +> unchanged, because no grant anchors there. +> +> - **Removed, with no in-place replacement:** moving the Default Organization among several +> organizations. Its second write, another organization re-slugged to `default`, is exactly the +> refused capture. To move the anchor, move the grant, not the slug: a grant re-owned by the +> Default Organization anchors. +> - **The stranding it leaves.** Re-slugging the Default Organization away while other +> organizations exist is still admitted when an administrator stays standing (ADR-0135 D5.2, +> unchanged). After it, no organization is the Default Organization and none can take `default`, +> the previous holder included, until the store holds one organization again or declares a wall. +> The tenancy census already reports that shape at error level +> (`SINGLE_POSTURE_MANY_ORGANIZATIONS`), with those two ways out. + ### D6 — Managed definitions are sealed; customization is environment-level state beside them, never an edit and never per organization **Two install modes.** The package **declares** the modes it permits (`installModes`, default