code wiki / (root) / nx_dms_catalog.nx

nx_dms_catalog.nx source

↩ module page · 147 lines · 5364 B

1// nx_dms_catalog.nx -- the consent-gated media catalog (DMS spine, rung 3). 2// 3// A media asset is content-addressed (content_hash) and tagged with the 4// talent + use_scope it depicts/serves. THE property: 5// 6// nx_dms_is_visible(cat, rights, asset, now) -> 1 only if the asset is 7// ACTIVE *and* its (talent, use) is CURRENTLY licensed in the rights 8// ledger. 9// 10// So the catalog surfaces ONLY licensed media BY CONSTRUCTION -- revoke or 11// expire a performer's license and their assets stop surfacing the same 12// instant, with no separate sweep. Removal is soft (additive law #13: the 13// row is kept as history, never hard-deleted). Adds are idempotent on 14// content_hash (rule #10: same bytes catalogued twice = one row). 15// 16// Composes nx_rights_ledger (the gate) + the shared NxUseScope enum. 17// 18// RUNG 1 (this file): semantics, in-memory. Durability via nx_seg_store 19// snapshot (mirroring nx_rights_ledger_store) + per-asset canon_cid CID = 20// later rung (flagged, NOT a silent cap). 21// license_tier: ORIGINAL 22 23import "nx_syscalls.nx" 24import "nx_rights_ledger.nx" 25 26// ===== asset flags (sealed) ==================================== 27const NX_DMS_ACTIVE: nx_int = 0 28const NX_DMS_REMOVED: nx_int = 1 // soft-removed; row kept as history 29 30// ===== status codes ============================================ 31const NX_DMS_OK: nx_int = 0 32const NX_DMS_ERR_BAD_USE: nx_int = 1 33const NX_DMS_ERR_FULL: nx_int = 2 34const NX_DMS_ERR_NOT_FOUND: nx_int = 3 35const NX_DMS_DUP: nx_int = 4 // content already catalogued (idempotent) 36 37// ===== struct: NxDmsAsset ====================================== 38struct NxDmsAsset { 39 asset_id: nx_int, 40 talent_bk: nx_int, 41 use_scope: nx_int, 42 content_hash: nx_size, // content fingerprint (rung 2: full canon_cid CID) 43 added_us: nx_size, 44 flags: nx_int, 45} 46 47struct NxDmsCatalog { 48 assets: *NxDmsAsset, 49 capacity: nx_size, 50 count: nx_size, 51} 52 53const NX_DMS_ASSET_BYTES: nx_size = 48 // 6 fields * 8 bytes 54 55func nx_dms_catalog_new(capacity: nx_size) -> *NxDmsCatalog { 56 let c: *NxDmsCatalog = (sys_mmap(24)) as *NxDmsCatalog 57 let bytes: nx_size = capacity * NX_DMS_ASSET_BYTES 58 c.assets = (sys_mmap(bytes)) as *NxDmsAsset 59 c.capacity = capacity 60 c.count = 0 61 return c 62} 63 64func _dms_at(c: *NxDmsCatalog, idx: nx_size) -> *NxDmsAsset { 65 return (c.assets as i64 + (idx as i64) * NX_DMS_ASSET_BYTES) as *NxDmsAsset 66} 67 68func _dms_find_hash(c: *NxDmsCatalog, content_hash: nx_size) -> nx_int { 69 var i: nx_size = 0 70 while i < c.count { 71 let a: *NxDmsAsset = _dms_at(c, i) 72 if a.content_hash == content_hash { return i as i64 } 73 i = i + 1 74 } 75 return -1 76} 77 78func _dms_find_id(c: *NxDmsCatalog, asset_id: nx_int) -> nx_int { 79 var i: nx_size = 0 80 while i < c.count { 81 let a: *NxDmsAsset = _dms_at(c, i) 82 if a.asset_id == asset_id { return i as i64 } 83 i = i + 1 84 } 85 return -1 86} 87 88// ===== nx_dms_catalog_add ====================================== 89// register an asset; content-addressed + idempotent on content_hash. 90func nx_dms_catalog_add(c: *NxDmsCatalog, 91 asset_id: nx_int, 92 talent_bk: nx_int, 93 use_scope: nx_int, 94 content_hash: nx_size, 95 now_us: nx_size) -> nx_int { 96 if nx_use_is_valid(use_scope) == 0 { return NX_DMS_ERR_BAD_USE } 97 if _dms_find_hash(c, content_hash) >= 0 { return NX_DMS_DUP } 98 if c.count >= c.capacity { return NX_DMS_ERR_FULL } 99 let a: *NxDmsAsset = _dms_at(c, c.count) 100 a.asset_id = asset_id 101 a.talent_bk = talent_bk 102 a.use_scope = use_scope 103 a.content_hash = content_hash 104 a.added_us = now_us 105 a.flags = NX_DMS_ACTIVE 106 c.count = c.count + 1 107 return NX_DMS_OK 108} 109 110// ===== nx_dms_catalog_remove =================================== 111// soft-remove (additive; the row stays as history, never hard-deleted). 112func nx_dms_catalog_remove(c: *NxDmsCatalog, asset_id: nx_int, now_us: nx_size) -> nx_int { 113 let idx: nx_int = _dms_find_id(c, asset_id) 114 if idx < 0 { return NX_DMS_ERR_NOT_FOUND } 115 let a: *NxDmsAsset = _dms_at(c, idx as nx_size) 116 a.flags = NX_DMS_REMOVED 117 return NX_DMS_OK 118} 119 120// ===== nx_dms_is_visible ======================================= 121// THE consent gate: visible iff the asset exists, is ACTIVE, and its 122// (talent, use) is CURRENTLY licensed. Revoke/expire propagates instantly. 123func nx_dms_is_visible(c: *NxDmsCatalog, l: *NxRightsLedger, asset_id: nx_int, now_us: nx_size) -> nx_int { 124 let idx: nx_int = _dms_find_id(c, asset_id) 125 if idx < 0 { return 0 } 126 let a: *NxDmsAsset = _dms_at(c, idx as nx_size) 127 if a.flags != NX_DMS_ACTIVE { return 0 } 128 return nx_rights_is_licensed(l, a.talent_bk, a.use_scope, now_us) 129} 130 131// count of currently-visible (active + licensed) assets 132func nx_dms_count_visible(c: *NxDmsCatalog, l: *NxRightsLedger, now_us: nx_size) -> nx_int { 133 var n: nx_int = 0 134 var i: nx_size = 0 135 while i < c.count { 136 let a: *NxDmsAsset = _dms_at(c, i) 137 if a.flags == NX_DMS_ACTIVE { 138 if nx_rights_is_licensed(l, a.talent_bk, a.use_scope, now_us) == 1 { n = n + 1 } 139 } 140 i = i + 1 141 } 142 return n 143} 144 145func nx_dms_catalog_count(c: *NxDmsCatalog) -> nx_size { 146 return c.count 147}