nx_asset_access.nx source
↩ module page · 70 lines · 3936 B
1// nx_asset_access.nx -- UNIVERSAL ORGANIZATION TOOLING arc, R6: ACCESS CLASSIFY (the asset PDP).
2//
3// The operator's professional+private split + the image-stack "operator-only" law (search-by-image
4// is operator-only, deny-by-default, fail-closed): an asset is RELEASED to a requester ONLY IF the
5// requester's clearance level >= the level the record's `classification` field demands. Anything
6// else -- a missing classification, an unrecognized classification, or insufficient clearance -- is a
7// DENY. DENY-BY-DEFAULT + FAIL-CLOSED (Rule 12), exactly like nx_access_wall's PDP, but FOCUSED on
8// the one signal an asset record already carries (org_research.tsv private-vs-pro-acl).
9//
10// ZERO new auth/crypto -- pure COMPOSITION (Rule 15): it DECODES the unified content-addressed record
11// via nx_asset_record (ar_decode / ar_get) and gates on its own classification field. The classification
12// travels INSIDE the canonical record bytes, so it is bound to the record's CID (change the class -> a
13// different CID): the access policy cannot be silently separated from the asset. This verdict is exactly
14// the L2/L5 identity-policy decision a daemon would feed into nx_access_wall's aw_decide (this organ is
15// the asset-aware policy; aw_decide stays the network/device/rate composition point).
16//
17// No hardware/persistent-firmware writes (Rule 26). license_tier: ORIGINAL
18import "nx_syscalls.nx"
19import "nx_uxf_decode.nx"
20import "nx_asset_record.nx"
21
22// clearance levels (data-driven, named -- law #11; higher = more access).
23func AA_ANON() -> i64 { return 0 } // public internet / unauthenticated
24func AA_FAMILY() -> i64 { return 1 } // a family member
25func AA_PRO() -> i64 { return 2 } // professional / staff
26func AA_OWNER() -> i64 { return 3 } // the operator (lvl-3)
27
28// decision codes (negative = deny; the SPECIFIC reason so an audit point can log WHY).
29const AA_ALLOW: i64 = 1
30const AA_DENY_CLEARANCE: i64 = 0 - 1 // requester level below the record's required level
31const AA_DENY_UNKNOWN: i64 = 0 - 2 // classification missing / unrecognized -> fail-closed
32
33func aa_streq(a: *u8, b: *u8) -> i64 {
34 var i: i64 = 0
35 while 1 == 1 {
36 if a[i] != b[i] { return 0 }
37 if a[i] == (0 as u8) { return 1 }
38 i = i + 1
39 }
40 return 1
41}
42
43// required clearance level for a classification string. Unrecognized / null -> -1 (fail-closed).
44// "public" is the ONLY class an anon requester may read; everything sensitive needs real clearance.
45func aa_required_level(classification: *u8) -> i64 {
46 if (classification as i64) == 0 { return 0 - 1 }
47 if aa_streq(classification, "public\x00" as *u8) == 1 { return AA_ANON() }
48 if aa_streq(classification, "professional\x00" as *u8) == 1 { return AA_PRO() }
49 if aa_streq(classification, "private\x00" as *u8) == 1 { return AA_OWNER() }
50 if aa_streq(classification, "operator-only\x00" as *u8) == 1 { return AA_OWNER() }
51 return 0 - 1
52}
53
54// THE DECISION over a record's canonical bytes. Decode -> read the classification field -> compare to
55// the requester's clearance. Returns AA_ALLOW / AA_DENY_*. DENY-BY-DEFAULT: every path that is not a
56// full ALLOW is a DENY. A malformed record (canon_decode < 0) or a missing/unknown class fails CLOSED.
57func aa_decide(record_bytes: *u8, n: i64, requester_level: i64) -> i64 {
58 let dk: *i64 = sys_mmap(8 * 32) as *i64
59 let dv: *i64 = sys_mmap(8 * 32) as *i64
60 let nf: i64 = canon_decode(record_bytes, n, dk, dv, 32)
61 if nf < 0 { return AA_DENY_UNKNOWN }
62 let cls: *u8 = ar_get(dk, dv, nf, "classification\x00" as *u8)
63 let req: i64 = aa_required_level(cls)
64 if req < 0 { return AA_DENY_UNKNOWN }
65 if requester_level >= req { return AA_ALLOW }
66 return AA_DENY_CLEARANCE
67}
68
69// boolean convenience for an enforcement point: 1 = released, 0 = withheld.
70func aa_allowed(verdict: i64) -> i64 { if verdict == AA_ALLOW { return 1 } return 0 }