code wiki / _hdl_build / nx_share_plane.nx
nx_share_plane.nx
buildroot/runtime/_hdl_build/nx_share_plane.nx
about
⚠⚠ SUPERSEDED 2026-07-25 -- DO NOT BUILD ON THIS. Use `nx_suite_share.nx`.
This organ was a SECOND implementation of a rule the estate already had. `runtime/nx_rebac.nx` is the one
authorization plane -- a sovereign Zanzibar/ReBAC tuple model, deny-by-default, additive tombstones with
latest-wins -- and its own header says it exists because the ecosystem once carried three incompatible
authz philosophies. This file was quietly becoming the fourth.
It was retired the way the estate requires: by PROOF, not assertion.
* `nx_share_convergence_gate` ran the same nine scenarios through both planes: 7 AGREE, 2 declared
divergences, 0 silent ones -- so the grant/revoke/check half was measurably redundant.
* `nx_suite_share` now speaks the suite's vocabulary over nx_rebac, and `nx_suite_share_gate` re-aims
all fifteen of the attacks below at it: 15/15 GREEN. Nothing this file protected went unprotected.
* The two divergences became work rather than losses: claim-before-share is kept as a facade-level
tightening, and delegated re-sharing moved onto the shared plane as a rewrite-table ROW (the new
`album` object type with its `curator` relation) instead of a capability bit only this file knew about.
KEPT, not deleted, because the convergence gate imports it -- the evidence for the retirement has to keep
compiling, or the retirement becomes a claim again. That gate is its only remaining consumer.
---- original header, for the record ----
nx_share_plane.nx -- THE SHARE PLANE. One identity-keyed answer to "who may see what", so that shared
gallery, shared contact directory and digital gifts are three thin surfaces over one rule instead of
three copies of it.
WHY THIS IS RUNG 1 AND NOT A SHARED-GALLERY PAGE. The ruler's gap_queue put D2 (shared gallery), E3
(shared contact info) and F1 (digital gifts) in its top four, all at rank 9. Three of the top four are
the SAME primitive wearing different clothes. Building them one page at a time would produce three
who-may-see-what implementations that drift apart, and the one that drifts is a privacy bug. So the
measurement chose the rung; the composition is just rule 15 applied to what the measurement found.
SHAPE: a caller-supplied arena, no syscalls, no main -> the live daemon composes it per request AND a
gate imports it standalone, exactly like nx_connect_ui. The log is APPEND-ONLY (rule 13): revoking
appends a revocation, it never erases the grant that existed. History is what makes "who could see this
last March" answerable, and a sharing plane that cannot answer that is not auditable.
THE INVARIANTS (each one is a gate tooth, not a promise in a comment):
* DENY BY DEFAULT -- no matching live grant means no.
* ONLY THE OWNER GRANTS -- a subject cannot grant themselves, and holding VIEW never lets you pass
VIEW to someone else. No transitive escalation without an explicit ADMIN bit.
* REVOCATION IS TOTAL AND IMMEDIATE -- after a revoke every capability answers no, on the next call.
* SELF-GRANT IS REFUSED -- an owner already has their own resource; letting a self-grant exist would
dependencies 0 imports · 2 importers
imports: none
imported by: nx_share_convergence_gate.nxnx_share_plane_gate.nx
structs
| none |
consts
| 49 | const SP_KIND: i64 = 0 |
| 50 | const SP_OWNER: i64 = 1 |
| 51 | const SP_SUBJ: i64 = 2 |
| 52 | const SP_CLASS: i64 = 3 |
| 53 | const SP_RID: i64 = 4 |
| 54 | const SP_CAP: i64 = 5 |
| 55 | const SP_STATE: i64 = 6 |
| 56 | const SP_SEQ: i64 = 7 |
| 57 | const SP_WIDTH: i64 = 8 |
| 59 | const SP_H_N: i64 = 0 |
| 60 | const SP_H_CAP: i64 = 1 |
| 61 | const SP_H_SEQ: i64 = 2 |
| 62 | const SP_HDR: i64 = 4 |
| 64 | const SP_K_GRANT: i64 = 1 |
| 65 | const SP_K_OWN: i64 = 2 |
| 67 | const SP_ST_LIVE: i64 = 1 |
| 68 | const SP_ST_REVOKED: i64 = 0 |
| 71 | const SP_VIEW: i64 = 1 |
| 72 | const SP_ADD: i64 = 2 |
| 73 | const SP_ADMIN: i64 = 4 |
| 74 | const SP_CAPMAX: i64 = 7 |
| 77 | const SP_C_ALBUM: i64 = 1 |
| 78 | const SP_C_CONTACT: i64 = 2 |
| 79 | const SP_C_GIFT: i64 = 3 |
| 80 | const SP_C_CALENDAR: i64 = 4 |
| 83 | const SP_E_FULL: i64 = -2 |
| 84 | const SP_E_NOTOWNER: i64 = -1 |
| 85 | const SP_E_SELF: i64 = -3 |
| 86 | const SP_E_BADCAP: i64 = -4 |
| 87 | const SP_E_NOOWNER: i64 = -5 |
| 88 | const SP_E_BADARG: i64 = -6 |
functions
| 90 | func sp_init(a: *i64, cap: i64) -> i64 |
| 97 | func sp_count(a: *i64) -> i64 { return a[SP_H_N] } called by 1: main |
| 98 | func sp_slots_needed(cap: i64) -> i64 { return SP_HDR + cap*SP_WIDTH } |
| 99 | func sp_at(a: *i64, i: i64, field: i64) -> i64 { return a[SP_HDR + i*SP_WIDTH + field] } |
| 101 | func sp_append(a: *i64, kind: i64, owner: i64, subj: i64, rclass: i64, rid: i64, cap: i64, state: i64) -> i64 |
| 120 | func sp_owner_of(a: *i64, rclass: i64, rid: i64) -> i64 |
| 134 | func sp_claim(a: *i64, owner: i64, rclass: i64, rid: i64) -> i64 |
| 145 | func sp_live_cap(a: *i64, subj: i64, rclass: i64, rid: i64) -> i64 |
| 167 | func sp_may_grant(a: *i64, actor: i64, rclass: i64, rid: i64) -> i64 |
| 176 | func sp_grant(a: *i64, actor: i64, subj: i64, rclass: i64, rid: i64, capbits: i64) -> i64 |
| 187 | func sp_revoke(a: *i64, actor: i64, subj: i64, rclass: i64, rid: i64) -> i64 |
| 199 | func sp_may(a: *i64, actor: i64, rclass: i64, rid: i64, want: i64) -> i64 |
| 212 | func sp_gift(a: *i64, giver: i64, receiver: i64, rclass: i64, rid: i64) -> i64 |
| 221 | func sp_provenance_len(a: *i64, rclass: i64, rid: i64) -> i64 |
| 235 | func sp_provenance_at(a: *i64, rclass: i64, rid: i64, idx: i64) -> i64 |
| 254 | func sp_viewers(a: *i64, rclass: i64, rid: i64, out: *i64, outcap: i64) -> i64 |