code wiki / (root) / nx_rights_ledger.nx

nx_rights_ledger.nx source

↩ module page · 182 lines · 6752 B

1// nx_rights_ledger.nx -- the rights/consent spine for licensed talent 2// likeness + media (the DMS keystone). THE predicate: 3// 4// nx_rights_is_licensed(talent, use, now) -> 1 allow / 0 deny 5// 6// default-DENY: a use is permitted ONLY when a current, unexpired, unrevoked 7// grant for that EXACT (talent, use_scope) exists. Models a real licensing 8// contract -- a grant carries a term (expires_us) and a use-scope (what was 9// licensed); revocation supersedes a grant; the grant row is NEVER erased 10// (ADDITIVE law #13 -- it stays as the audit trail). Unknown talent, 11// unknown use, expired, and revoked all DENY by construction. 12// 13// Mirrors the proven nx_trace_consent state machine (propose/decide/check -> 14// grant/revoke/is_licensed) with licensing-specific use scopes. 15// 16// RUNG 1 (this file): semantics, in-memory. RUNG 2 (flagged, NOT a silent 17// cap): persist each row through nx_canon_cid -> nx_seg_store for the durable 18// never-lose audit trail on the NAS. 19// license_tier: ORIGINAL 20 21import "nx_syscalls.nx" 22import "nx_tier.nx" 23 24// ===== Sealed enum: NxRightState ================================ 25const NX_RL_NONE: nx_int = 0 // no matching row -> default-deny 26const NX_RL_GRANTED: nx_int = 1 27const NX_RL_EXPIRED: nx_int = 2 28const NX_RL_REVOKED: nx_int = 3 29const NX_RL_N_STATES: nx_int = 4 30 31// ===== Sealed enum: NxUseScope ================================= 32// the distinct things a performer can license; a grant for one use does NOT 33// authorize another (MERCH != WEB_GALLERY != AD != non-explicit AVATAR). 34const NX_USE_MERCH: nx_int = 0 // physical merchandise likeness 35const NX_USE_WEB_GALLERY: nx_int = 1 // display in owned gallery/search 36const NX_USE_AD_CAMPAIGN: nx_int = 2 // marketing / advertisement 37const NX_USE_AVATAR_NONEXPLICIT: nx_int = 3 // non-explicit synthetic avatar 38const NX_USE_N_SCOPES: nx_int = 4 39 40// ===== status codes ============================================ 41const NX_RL_OK: nx_int = 0 42const NX_RL_ERR_BAD_USE: nx_int = 1 43const NX_RL_ERR_FULL: nx_int = 2 44const NX_RL_ERR_NOT_FOUND: nx_int = 3 45const NX_RL_ERR_NOT_GRANTED: nx_int = 4 46 47// ===== struct: NxLicense ======================================= 48// one license row. talent_bk = talent business key (stable id; rung 2 swaps 49// to the content-addressed talent hub key). 50struct NxLicense { 51 grant_id: nx_int, 52 talent_bk: nx_int, 53 use_scope: nx_int, 54 state: nx_int, 55 granted_us: nx_size, 56 expires_us: nx_size, 57 revoked_us: nx_size, 58} 59 60struct NxRightsLedger { 61 rows: *NxLicense, 62 capacity: nx_size, 63 count: nx_size, 64} 65 66const NX_RL_ROW_BYTES: nx_size = 56 // 7 fields * 8 bytes 67 68func nx_use_is_valid(u: nx_int) -> nx_int { 69 if u < 0 { return 0 } 70 if u >= NX_USE_N_SCOPES { return 0 } 71 return 1 72} 73 74func nx_rights_ledger_new(capacity: nx_size) -> *NxRightsLedger { 75 let l: *NxRightsLedger = (sys_mmap(24)) as *NxRightsLedger 76 let bytes: nx_size = capacity * NX_RL_ROW_BYTES 77 l.rows = (sys_mmap(bytes)) as *NxLicense 78 l.capacity = capacity 79 l.count = 0 80 return l 81} 82 83func _rl_at(l: *NxRightsLedger, idx: nx_size) -> *NxLicense { 84 return (l.rows as i64 + (idx as i64) * NX_RL_ROW_BYTES) as *NxLicense 85} 86 87func _rl_find(l: *NxRightsLedger, grant_id: nx_int) -> nx_int { 88 var i: nx_size = 0 89 while i < l.count { 90 let r: *NxLicense = _rl_at(l, i) 91 if r.grant_id == grant_id { return i as i64 } 92 i = i + 1 93 } 94 return -1 95} 96 97// ===== nx_rights_grant ========================================= 98// talent_bk grants use_scope from now_us for term_us (additive append). 99func nx_rights_grant(l: *NxRightsLedger, 100 grant_id: nx_int, 101 talent_bk: nx_int, 102 use_scope: nx_int, 103 now_us: nx_size, 104 term_us: nx_size) -> nx_int { 105 if nx_use_is_valid(use_scope) == 0 { return NX_RL_ERR_BAD_USE } 106 if l.count >= l.capacity { return NX_RL_ERR_FULL } 107 let r: *NxLicense = _rl_at(l, l.count) 108 r.grant_id = grant_id 109 r.talent_bk = talent_bk 110 r.use_scope = use_scope 111 r.state = NX_RL_GRANTED 112 r.granted_us = now_us 113 r.expires_us = now_us + term_us 114 r.revoked_us = 0 115 l.count = l.count + 1 116 return NX_RL_OK 117} 118 119// ===== nx_rights_revoke ======================================== 120// revoke a GRANTED license by grant_id; stamps revoked_us, keeps the row. 121func nx_rights_revoke(l: *NxRightsLedger, grant_id: nx_int, now_us: nx_size) -> nx_int { 122 let idx: nx_int = _rl_find(l, grant_id) 123 if idx < 0 { return NX_RL_ERR_NOT_FOUND } 124 let r: *NxLicense = _rl_at(l, idx as nx_size) 125 if r.state != NX_RL_GRANTED { return NX_RL_ERR_NOT_GRANTED } 126 r.state = NX_RL_REVOKED 127 r.revoked_us = now_us 128 return NX_RL_OK 129} 130 131// ===== nx_rights_state ========================================= 132// detailed current state of (talent, use) at now_us, for audit/UI. 133// GRANTED iff some effective grant exists; else the most relevant deny reason 134// (REVOKED beats EXPIRED beats NONE). Auto-flips lapsed grants to EXPIRED. 135func nx_rights_state(l: *NxRightsLedger, talent_bk: nx_int, use_scope: nx_int, now_us: nx_size) -> nx_int { 136 var saw_revoked: nx_int = 0 137 var saw_expired: nx_int = 0 138 var i: nx_size = 0 139 while i < l.count { 140 let r: *NxLicense = _rl_at(l, i) 141 if r.talent_bk == talent_bk { 142 if r.use_scope == use_scope { 143 if r.state == NX_RL_GRANTED { 144 if now_us > r.expires_us { 145 r.state = NX_RL_EXPIRED 146 saw_expired = 1 147 } else { 148 return NX_RL_GRANTED 149 } 150 } 151 if r.state == NX_RL_REVOKED { saw_revoked = 1 } 152 if r.state == NX_RL_EXPIRED { saw_expired = 1 } 153 } 154 } 155 i = i + 1 156 } 157 if saw_revoked == 1 { return NX_RL_REVOKED } 158 if saw_expired == 1 { return NX_RL_EXPIRED } 159 return NX_RL_NONE 160} 161 162// ===== nx_rights_is_licensed =================================== 163// THE keystone predicate. default-DENY: 1 = allowed, 0 = denied. 164func nx_rights_is_licensed(l: *NxRightsLedger, talent_bk: nx_int, use_scope: nx_int, now_us: nx_size) -> nx_int { 165 if nx_rights_state(l, talent_bk, use_scope, now_us) == NX_RL_GRANTED { return 1 } 166 return 0 167} 168 169func nx_rights_count(l: *NxRightsLedger) -> nx_size { 170 return l.count 171} 172 173func nx_rights_count_by_state(l: *NxRightsLedger, state: nx_int) -> nx_int { 174 var hits: nx_int = 0 175 var i: nx_size = 0 176 while i < l.count { 177 let r: *NxLicense = _rl_at(l, i) 178 if r.state == state { hits = hits + 1 } 179 i = i + 1 180 } 181 return hits 182}