code wiki / _hdl_build / nx_suite_share.nx

nx_suite_share.nx source

↩ module page · 131 lines · 6785 B

1// nx_suite_share.nx -- the family suite's sharing vocabulary (albums, contact cards, gifts) expressed 2// ENTIRELY as tuples on nx_rebac, the estate's one authorization plane. This REPLACES nx_share_plane, 3// which was a second implementation of a rule that already existed. 4// 5// WHAT THE CONVERGENCE PROOF DECIDED (nx_share_convergence_gate: 7 agree, 2 declared divergences, 0 silent): 6// * the grant / revoke / check half was REDUNDANT -- both planes gave identical verdicts on every 7// scenario that mattered, so it is retired here rather than maintained twice; 8// * DIVERGENCE 1 (kept, deliberately): rebac lets anyone bootstrap the first grant on an OWNERLESS 9// object. For family media that is too loose -- an album nobody has claimed should not be shareable by 10// a passer-by -- so sg_grant REFUSES until someone has claimed it. This is a facade-level tightening 11// of a plane rule, which is allowed; loosening one would not be. 12// * DIVERGENCE 2 (migrated, not dropped): delegated re-sharing now rides the `curator` relation on the 13// new `album` object type in rb_expand -- a rewrite-table ROW, i.e. data on the shared plane, instead 14// of a capability bit in a private one. 15// * the GIFT half was the only genuinely new capability, and it is here: a gift MOVES ownership (the 16// giver stops being the owner) and the chain of holders stays recoverable from the append-only log via 17// rb_list_subjects_history. No second history is kept, because keeping one is how the duplicate began. 18// license_tier: ORIGINAL 19import "nx_rebac.nx" 20 21const SG_VIEW: *u8 = "view\x00" 22const SG_ADD: *u8 = "add\x00" 23const SG_MANAGE: *u8 = "manage\x00" 24const SG_R_VIEWER: *u8 = "viewer\x00" 25const SG_R_CURATOR: *u8 = "curator\x00" 26const SG_R_OWNER: *u8 = "owner\x00" 27const SG_SYSTEM: *u8 = "system\x00" 28 29const SG_E_NOTOWNER: i64 = -1 30const SG_E_UNCLAIMED: i64 = -2 31const SG_E_SELF: i64 = -3 32const SG_E_BADARG: i64 = -6 33const SG_MAXCHAIN: i64 = 64 34 35// "album:7001" / "contact:42" / "gift:900" -- one namespace per resource class, so the same numeric id in 36// two classes can never be the same object (the isolation nx_share_plane's T14 pinned, now free from the 37// object naming itself rather than from a class field nobody outside the plane could see). 38func sg_obj(kind: *u8, id: i64, out: *u8) -> i64 { 39 var o: i64 = 0 40 o = rb_cat(out, o, kind) 41 o = rb_cat(out, o, ":" as *u8) 42 o = rb_catn(out, o, id) 43 out[o] = 0 as u8 44 return o 45} 46func sg_sub(uid: i64, out: *u8) -> i64 { 47 var o: i64 = 0 48 o = rb_cat(out, o, "user:" as *u8) 49 o = rb_catn(out, o, uid) 50 out[o] = 0 as u8 51 return o 52} 53func sg_owner_of(prefix: *u8, kind: *u8, id: i64) -> i64 { 54 let obj: *u8 = sys_mmap(256) 55 sg_obj(kind, id, obj) 56 let out: *i64 = sys_mmap(8*SG_MAXCHAIN) as *i64 57 let n: i64 = rb_list_subjects(prefix, obj, SG_R_OWNER, out, SG_MAXCHAIN) 58 if n <= 0 { return 0 } 59 return out[n-1] as i64 as i64 // pointer to the current owner subject string 60} 61func sg_is_claimed(prefix: *u8, kind: *u8, id: i64) -> i64 { 62 let obj: *u8 = sys_mmap(256) 63 sg_obj(kind, id, obj) 64 return rb_obj_has_owner(prefix, obj) 65} 66// claim: idempotent for the current owner, refused for anyone else (no silent ownership theft) 67func sg_claim(prefix: *u8, owner: i64, kind: *u8, id: i64) -> i64 { 68 if owner <= 0 { return SG_E_BADARG } 69 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 70 let sub: *u8 = sys_mmap(64); sg_sub(owner, sub) 71 if rb_obj_has_owner(prefix, obj) == 1 { 72 if rb_tuple_current(prefix, obj, SG_R_OWNER, sub) == 1 { return 0 } // already mine 73 return SG_E_NOTOWNER 74 } 75 rb_put(prefix, obj, SG_R_OWNER, sub, SG_SYSTEM, 1) 76 return 1 77} 78// grant. STRICTER THAN THE PLANE BENEATH IT, on purpose: rebac would bootstrap a first grant on an 79// ownerless object; a family album must be claimed before anyone can be added to it. 80func sg_grant(prefix: *u8, actor: i64, subj: i64, kind: *u8, id: i64, rel: *u8) -> i64 { 81 if subj <= 0 { return SG_E_BADARG } 82 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 83 if rb_obj_has_owner(prefix, obj) == 0 { return SG_E_UNCLAIMED } 84 let asub: *u8 = sys_mmap(64); sg_sub(actor, asub) 85 let ssub: *u8 = sys_mmap(64); sg_sub(subj, ssub) 86 if rb_seq(asub, ssub) == 1 { return SG_E_SELF } 87 if rb_may_grant(prefix, asub, obj) == 0 { return SG_E_NOTOWNER } 88 rb_put(prefix, obj, rel, ssub, asub, 1) 89 return 1 90} 91// revoke: a tombstone, never an erasure -- the grant that existed stays in the record 92func sg_revoke(prefix: *u8, actor: i64, subj: i64, kind: *u8, id: i64, rel: *u8) -> i64 { 93 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 94 if rb_obj_has_owner(prefix, obj) == 0 { return SG_E_UNCLAIMED } 95 let asub: *u8 = sys_mmap(64); sg_sub(actor, asub) 96 let ssub: *u8 = sys_mmap(64); sg_sub(subj, ssub) 97 if rb_may_grant(prefix, asub, obj) == 0 { return SG_E_NOTOWNER } 98 rb_put(prefix, obj, rel, ssub, asub, 0) 99 return 1 100} 101// the read path: one question, answered by the one plane 102func sg_may(prefix: *u8, actor: i64, kind: *u8, id: i64, perm: *u8) -> i64 { 103 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 104 let asub: *u8 = sys_mmap(64); sg_sub(actor, asub) 105 return rb_check(prefix, asub, perm, obj, 0) 106} 107// A GIFT MOVES OWNERSHIP. Tombstone the giver's owner tuple, then establish the receiver's -- so the giver 108// genuinely stops being the owner rather than merely sharing. Both writes are appends; the chain survives. 109func sg_gift(prefix: *u8, giver: i64, receiver: i64, kind: *u8, id: i64) -> i64 { 110 if receiver <= 0 { return SG_E_BADARG } 111 if giver == receiver { return SG_E_SELF } 112 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 113 if rb_obj_has_owner(prefix, obj) == 0 { return SG_E_UNCLAIMED } 114 let gsub: *u8 = sys_mmap(64); sg_sub(giver, gsub) 115 let rsub: *u8 = sys_mmap(64); sg_sub(receiver, rsub) 116 if rb_tuple_current(prefix, obj, SG_R_OWNER, gsub) == 0 { return SG_E_NOTOWNER } 117 rb_put(prefix, obj, SG_R_OWNER, gsub, gsub, 0) 118 rb_put(prefix, obj, SG_R_OWNER, rsub, gsub, 1) 119 return 1 120} 121// whose hands has this passed through? read straight out of the append-only log -- no second ledger. 122func sg_provenance(prefix: *u8, kind: *u8, id: i64, out: *i64, cap: i64) -> i64 { 123 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 124 return rb_list_subjects_history(prefix, obj, SG_R_OWNER, out, cap) 125} 126// everyone who can currently see this, for the "who can see this" panel: a sharing rule nobody can inspect 127// is one nobody can trust. 128func sg_viewers(prefix: *u8, kind: *u8, id: i64, out: *i64, cap: i64) -> i64 { 129 let obj: *u8 = sys_mmap(256); sg_obj(kind, id, obj) 130 return rb_list_subjects(prefix, obj, SG_R_VIEWER, out, cap) 131}