Skip to content

Commit 6cddca9

Browse files
committed
docs(adr): ADR-0126 wall 3 holds for the access class; ADR-0131 D5 records the single-posture re-slug rule
ADR-0126 §6 wall 3 gains a dated note: the boot report (#25311, 54df640) and the marketplace refusal (#25355, 448374d) 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, 09e07c2). It names the removed move and the stranding it leaves. Notes only; no decision text changes. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbVJUgUf5HuWXPQvAo4sFg
1 parent 448374d commit 6cddca9

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

‎docs/adr/0126-packaged-metadata-customization-model.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,49 @@ read at each runtime's own consult point; an empty ledger changes nothing anywhe
276276
they never write the ledger — the ledger records the customer's **choices**, and no upgrade
277277
un-makes a choice. The converse holds too: disabling never blocks or edits the base's upgrade
278278
stream. The two never meet: the base's version history and the ledger share no columns.
279+
280+
> **Note (2026-10-23, ruling of record `6259332195` on #24443, items 1–2, 4–5) — wall 3 is true
281+
> in practice for the access class.** Two stages of #24446 make this wall hold for sharing rules,
282+
> permission sets, positions and capabilities. They add no spec key and no rename spelling.
283+
>
284+
> - **The boot report** (U1, #25311, `54df640b38`). At `kernel:bootstrapped`, plugin-security
285+
> compares the customer's access state with the registry the boot holds. That state is the
286+
> activation-ledger rows, the environment-authored permission sets and positions (clones
287+
> included) and the `sys_sharing_rule` rows. The report lists orphans, suspected re-enablements,
288+
> sharing-rule collisions, broken references and field denies a customer set lacks. It writes and
289+
> refuses nothing.
290+
> - **The marketplace refusal** (U2, #25355, `448374d3f8`). On a re-install over an installed
291+
> package, before its first write, `POST /api/v1/marketplace/install-local` runs the same
292+
> comparison through the `security.accessUpgradeCheck` service. It runs over the registry and
293+
> over a projection with the incoming manifest. Only the delta refuses: 409
294+
> `ACCESS_UPGRADE_CONFIRMATION_REQUIRED`, naming each finding, and nothing is written. A finding
295+
> that predates the upgrade does not block it; the boot report keeps raising it. A check that
296+
> cannot read its sources answers 503 and writes nothing.
297+
> - **The confirm token.** The refusal carries a `findingSet` token. The same call with
298+
> `confirmAccessUpgrade` set to that token installs. The install's own entry in the marketplace
299+
> install ledger, not the activation ledger, records the confirmation (`findingSet`,
300+
> `confirmedBy`, `confirmedAt`). The token confirms exactly the findings it names: if they
301+
> change, the call is refused again with the fresh set, and a bare `true` confirms nothing. The
302+
> CLI sends it as `os package install … --confirm-access-upgrade`.
303+
> - **Who is told.** Under `single`, the administrator. The boot report notifies the Default
304+
> Organization's `admin_full_access` holders and any `OS_PLATFORM_OWNER_EMAIL` address, once per
305+
> finding set. The refusal answers the administrator who installs (`manage_metadata`). Under a
306+
> wall (`group`, `isolated`), the operator. The boot report is one `error` line in the operator's
307+
> log and writes into no organization (seat ruling `6353787407`, option A). Only the operator
308+
> reaches the refusal: ADR-0131 D12 answers a tenant administrator 403 first, with no finding.
309+
> A tenant administrator is never the fallback.
310+
> - **Two accepted limits**, both following from §9 (a clone records no base and no link) and from
311+
> the bare `(metadata_type, name)` ledger key. A re-enablement is only *suspected*: nothing
312+
> records which name replaced which, so a package with several unswitched names yields several
313+
> candidates. The field-deny check reads "grants the same object", not "copied from that set",
314+
> so it can name an unrelated customer set and is a review item.
315+
> - **Two doors are still open**, each with its card. `POST /api/v1/packages` with `overwrite`
316+
> replaces an installed package without this check (#25382). A cloud-fetched marketplace
317+
> install of a package shipping a catalog name an environment-authored item holds writes its
318+
> install entry, and the next cold boot refuses; it is to be refused before the first write, not
319+
> confirmed (#25366).
320+
> - **The other classes follow after v18** (ruling item 4), as does the retirement of
321+
> `package-upgrade.zod.ts`. Until then this wall holds in practice for the access class only.
279322
4. **The paper protocol is not the model.** The three-layer
280323
`metadata-customization.zod.ts` surface (#12057 — exported, documented, zero consumers) is
281324
**superseded by this ADR as a matter of record**: nothing may build against it, and its

‎docs/adr/0131-total-organization-ownership-no-null-organization-id.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,27 @@ The `single` anchor's own disposition is #11979's (Choice 4B), which this record
455455
> outside `os serve` over a database built before 18.x, it let the deployment's only platform
456456
> administrator be banned. That anchor is retired.
457457
458+
> **Note (2026-10-22, #25286, `09e07c259a`; landed record `6353078154` on #25271) — under `single`,
459+
> no slug change makes another organization the Default Organization.** Under `single`, a
460+
> `sys_organization` slug changed to `default` is admitted only on the organization that is already
461+
> the Default Organization. Every other such write is refused 403 `PERMISSION_DENIED` by the
462+
> last-admin guard's `sys_organization` `beforeUpdate` hook, for every context: better-auth's
463+
> `organization/update`, seeds and plugins alike, `isSystem` included. A read the guard cannot
464+
> complete refuses too. Before this hook, such a re-slug made that organization the Default
465+
> Organization, and every holder of a grant it owned a platform administrator. Walled postures are
466+
> unchanged, because no grant anchors there.
467+
>
468+
> - **Removed, with no in-place replacement:** moving the Default Organization among several
469+
> organizations. Its second write, another organization re-slugged to `default`, is exactly the
470+
> refused capture. To move the anchor, move the grant, not the slug: a grant re-owned by the
471+
> Default Organization anchors.
472+
> - **The stranding it leaves.** Re-slugging the Default Organization away while other
473+
> organizations exist is still admitted when an administrator stays standing (ADR-0135 D5.2,
474+
> unchanged). After it, no organization is the Default Organization and none can take `default`,
475+
> the previous holder included, until the store holds one organization again or declares a wall.
476+
> The tenancy census already reports that shape at error level
477+
> (`SINGLE_POSTURE_MANY_ORGANIZATIONS`), with those two ways out.
478+
458479
### D6 — Managed definitions are sealed; customization is environment-level state beside them, never an edit and never per organization
459480

460481
**Two install modes.** The package **declares** the modes it permits (`installModes`, default

0 commit comments

Comments
 (0)