From c09cdd11e37d553e91b3c6136abf0cdaf8fd08e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 08:03:11 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs(a2ui):=20C1.4=20correction=20=E2=80=94?= =?UTF-8?q?=20projection=20security=20requires=20WIDE=20role=20masks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex P2 on #204, verified in-repo before amending: the merged charter's "RBAC by projection" claim silently breaks for surfaces with >64 fields — ClassRbac::field_mask returns a NARROW FieldMask (u64, keystone spec §4) while render_class_with_methods_wide exists precisely because surfaces exceed FieldMask::MAX_FIELDS, and WideFieldMask::full_for ("emit everything") is exactly the unsafe sentinel fallback codex predicted. Three requirements added to C1.4, mandatory for W1+: (a) zero-extension fail-closed rule — a narrow role mask extends past bit 63 as ZEROS, never as full; absent wide role data, unauthorized by default; (b) additive seam — ClassRbac::field_mask_wide(role, class) -> WideFieldMask, so wide grants are expressible at all (narrow field_mask stays for <=64 surfaces); (c) sentinel ban on the RBAC path — full_for is a render convenience, never a role-mask fallback; a missing role mask is a refusal. Interim alternative: page the surface into <=64-field panes. Matching correction line appended to the D-A2UI-SCREEN-ADDRESSING ledger entry (append-only, cites the codex pass). Co-Authored-By: Claude --- docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md | 23 +++++++++++++++++++++++ docs/DISCOVERY-MAP.md | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md b/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md index c0aa73f..f4de5d3 100644 --- a/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md +++ b/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md @@ -62,6 +62,29 @@ screen — same mechanism, different renderer. brick from the SoC/dedup pipeline). A field the role may not see is not *hidden on the client* — it is **absent from the wire**. Pixel streams cannot make that guarantee; address streams get it for free. + **CORRECTION (codex P2 on #204, verified in-repo):** as merged, this + claim silently breaks for surfaces with >64 fields — the RBAC seam's + `ClassRbac::field_mask` returns a NARROW `FieldMask` (u64; + `CLASSID-RBAC-KEYSTONE-SPEC.md` §4), while the wide renderer exists + precisely because surfaces exceed `FieldMask::MAX_FIELDS` + (`ogar-render-askama::render_class_with_methods_wide`, whose + `WideFieldMask::full_for` "emit everything" sentinel is exactly the + unsafe fallback). Three requirements make the claim hold, all MANDATORY + for W1+: + (a) **Zero-extension rule (fail-closed):** a narrow role mask extends + past bit 63 as ZEROS, never as full — `wide-surface ∩ + narrow-role-mask` therefore drops every field ≥ 64 unless a wide grant + exists. Absent wide role data, unauthorized-by-default. + (b) **Wide role projection (additive seam):** `ClassRbac` gains + `field_mask_wide(role, class) -> WideFieldMask` (additive trait method; + narrow `field_mask` stays for ≤64 surfaces) so wide grants are + expressible at all. + (c) **Sentinel ban on the RBAC path:** `WideFieldMask::full_for` is a + RENDER convenience ("unmasked, emit everything") and must never be the + fallback where a role mask is required — a missing role mask is a + refusal, not a full grant. Alternative where (b) is not yet wired: + PAGE the surface into ≤64-field panes and intersect per-pane with the + narrow mask. 5. **rdp-2graph security = reuse, no new crypto:** session KDF via `ogar-auth` (Argon2 — the MedCare Tier-1 password/totp home), transport via `ogar-encryption` (the single generic encryption surface). The diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index b537853..f0b3afb 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -1397,3 +1397,10 @@ isolation. The map's job is to keep them visible. Charter: `docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md`. Depends: D-DOC-IR-SECOND-RETINA (A3 template ruling), D-OGAR-DOC-LAYER (DocRenderer), the Klickwege/ActionDef harvests (MedCare-rs). + **Correction 2026-07-14 (codex P2 on #204, verified in-repo):** the + RBAC-by-projection claim requires WIDE role masks — `ClassRbac::field_mask` + is narrow (u64) while wide surfaces exceed 64 fields; fix mandated in the + charter's C1.4 CORRECTION: zero-extension fail-closed rule (narrow role + mask extends past bit 63 as ZEROS, never full), additive + `field_mask_wide`, and a sentinel ban (`WideFieldMask::full_for` is a + render convenience, never an RBAC fallback; missing role mask = refusal). From 8cf0900eebb112ca28dc0d03366c36774077d3ae Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 08:08:24 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(a2ui):=20operator=20correction=20?= =?UTF-8?q?=E2=80=94=20retype=20field=5Fmask=20to=20WideFieldMask,=20no=20?= =?UTF-8?q?new=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "We already have WideFieldMask" — the C1.4 fix collapses: NO parallel field_mask_wide (a second name for the same projection would be the T1 two-vocabularies anti-pattern applied to the seam itself). Instead ClassRbac::field_mask retypes to WideFieldMask IN PLACE — verified cheap against source: the entire production surface is the trait default (FieldMask::FULL, rbac.rs:176) + one test override (rbac.rs:455); no production impl overrides it, so the retype touches two bodies and the compiler surfaces the rest. WideFieldMask is already universal (Small repr <=64, self-promoting past 64). The one genuine W1 decision named: the permit-all identity — WideFieldMask has EMPTY + full_for(count) but no count-free ALL, and the axis-4 default is deliberately opt-in ("FULL unless configured"). Either WideFieldMask gains ALL (the intersection identity) or the default derives full_for(field_count) from the ClassView. Kept: zero-extension fail-closed (now scoped to legacy narrow-mask interop sites) + the sentinel ban (full_for = render convenience, never an RBAC fallback). Ledger correction line updated to match, citing both passes. Co-Authored-By: Claude --- docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md | 36 +++++++++++++++---------- docs/DISCOVERY-MAP.md | 14 ++++++---- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md b/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md index f4de5d3..41aa475 100644 --- a/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md +++ b/docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md @@ -71,20 +71,28 @@ screen — same mechanism, different renderer. `WideFieldMask::full_for` "emit everything" sentinel is exactly the unsafe fallback). Three requirements make the claim hold, all MANDATORY for W1+: - (a) **Zero-extension rule (fail-closed):** a narrow role mask extends - past bit 63 as ZEROS, never as full — `wide-surface ∩ - narrow-role-mask` therefore drops every field ≥ 64 unless a wide grant - exists. Absent wide role data, unauthorized-by-default. - (b) **Wide role projection (additive seam):** `ClassRbac` gains - `field_mask_wide(role, class) -> WideFieldMask` (additive trait method; - narrow `field_mask` stays for ≤64 surfaces) so wide grants are - expressible at all. - (c) **Sentinel ban on the RBAC path:** `WideFieldMask::full_for` is a - RENDER convenience ("unmasked, emit everything") and must never be the - fallback where a role mask is required — a missing role mask is a - refusal, not a full grant. Alternative where (b) is not yet wired: - PAGE the surface into ≤64-field panes and intersect per-pane with the - narrow mask. + (a) **Retype in place — `WideFieldMask` already exists (operator + correction, 2026-07-14):** NO parallel `field_mask_wide` method. + `ClassRbac::field_mask` changes its return type to [`WideFieldMask`] + directly — verified cheap: the entire production surface is the trait + DEFAULT (`FieldMask::FULL`, rbac.rs:176) + one test override + (rbac.rs:455); no production impl overrides it, so the retype touches + two bodies and the compiler surfaces everything else. `WideFieldMask` + is already the universal mask (`Small` repr ≤64, self-promoting past + 64) — a second method name for the same projection would be the + two-vocabularies anti-pattern (T1) applied to the seam itself. + (b) **The permit-all identity is the ONE W1 decision:** `WideFieldMask` + has `EMPTY` + `full_for(count)` but no count-free permit-all, and the + axis-4 default is deliberately "FULL unless configured" (RBAC opt-in). + Either `WideFieldMask` gains `ALL` (the intersection identity) or the + default derives `full_for(field_count)` from the ClassView. W1 decides; + the opt-in default semantics survive either way. + (c) **Zero-extension rule (fail-closed) for legacy narrow interop:** + wherever an existing narrow `FieldMask` (SoC/dedup bricks) meets a wide + surface, the narrow mask extends past bit 63 as ZEROS, never as full. + And the sentinel ban: `full_for` as a RENDER convenience is fine; as an + RBAC fallback it is forbidden — a missing role mask is a refusal, not a + full grant. 5. **rdp-2graph security = reuse, no new crypto:** session KDF via `ogar-auth` (Argon2 — the MedCare Tier-1 password/totp home), transport via `ogar-encryption` (the single generic encryption surface). The diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index f0b3afb..0e7e394 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -1397,10 +1397,14 @@ isolation. The map's job is to keep them visible. Charter: `docs/A2UI-SCREEN-ADDRESSING-PROPOSAL.md`. Depends: D-DOC-IR-SECOND-RETINA (A3 template ruling), D-OGAR-DOC-LAYER (DocRenderer), the Klickwege/ActionDef harvests (MedCare-rs). - **Correction 2026-07-14 (codex P2 on #204, verified in-repo):** the + **Correction 2026-07-14 (codex P2 on #204, verified in-repo; sharpened by + operator same day — "we already have WideFieldMask"):** the RBAC-by-projection claim requires WIDE role masks — `ClassRbac::field_mask` - is narrow (u64) while wide surfaces exceed 64 fields; fix mandated in the - charter's C1.4 CORRECTION: zero-extension fail-closed rule (narrow role - mask extends past bit 63 as ZEROS, never full), additive - `field_mask_wide`, and a sentinel ban (`WideFieldMask::full_for` is a + is narrow (u64) while wide surfaces exceed 64 fields. Fix per the charter's + C1.4 CORRECTION: **retype `field_mask` → `WideFieldMask` in place** (NOT an + additive `field_mask_wide` — the type already exists and self-promotes; + verified cheap: production surface = the trait default + one test + override); the one W1 decision is the permit-all identity (`ALL` on + `WideFieldMask`, or default `full_for(field_count)`); zero-extension + fail-closed for legacy narrow-mask interop; sentinel ban (`full_for` is a render convenience, never an RBAC fallback; missing role mask = refusal).