code wiki / (root) / nx_browser_profile_store.nx

nx_browser_profile_store.nx source

↩ module page · 72 lines · 3404 B

1// nx_browser_profile_store.nx -- the Nishi browser's PERSISTENT PROFILE STORE (lib, no main). 2// Operator 2026-07-02: contacts/chat/settings "stored locally in nishi browser in a DIFFERENT cache 3// than the history-clearing cache so that this more permanent info" survives; backup = nx_profile_backup. 4// 5// THE SPLIT (the whole point): TWO seg_store families under knowledge/store/ -- 6// PROFILE prefix "knowledge/store/browser_profile-" = permanent (contacts/chat/settings). 7// CACHE prefix "knowledge/store/browser_cache-" = clearable (http cache/history when they land). 8// A history-clear deletes ONLY browser_cache-* files; it never touches browser_profile-*. The gate 9// (nx_browser_profile_gate) proves this split mechanically: clear the cache family -> profile intact. 10// 11// Storage = nx_seg_store (append-only, content-addressed, crash-safe temp->rename, additive law: deletes 12// are TOMBSTONES so history is never destroyed). Key scheme: "contact:<id>", "chat:<room>:<ts_us>", 13// "setting:<name>". ⚠ each bp_put = one ss_commit = one new segment (manifest cap 256) -- BATCH bursty 14// writers (chat) with bp_put2 pairs per commit or run ss_compact periodically. license_tier: ORIGINAL 15import "nx_syscalls.nx" 16import "nx_seg_store.nx" 17 18const BP_PROFILE_PREFIX: *u8 = "knowledge/store/browser_profile-" 19const BP_CACHE_PREFIX: *u8 = "knowledge/store/browser_cache-" 20 21func bp_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 22 23// put one record (last-wins for the same key; history stays readable in older segments). 24func bp_put(prefix: *u8, key: *u8, val: *u8, vlen: i64) -> i64 { 25 let w: *i64 = ss_begin() 26 if ss_add(w, 1, key, val, vlen) != 0 { return 0 - 1 } 27 if ss_commit(prefix, w, sys_now_us()) != 0 { return 0 - 2 } 28 return 0 29} 30 31// put TWO records in ONE commit (one segment) -- the batching seam for bursty writers. 32func bp_put2(prefix: *u8, key1: *u8, val1: *u8, vlen1: i64, key2: *u8, val2: *u8, vlen2: i64) -> i64 { 33 let w: *i64 = ss_begin() 34 if ss_add(w, 1, key1, val1, vlen1) != 0 { return 0 - 1 } 35 if ss_add(w, 1, key2, val2, vlen2) != 0 { return 0 - 1 } 36 if ss_commit(prefix, w, sys_now_us()) != 0 { return 0 - 2 } 37 return 0 38} 39 40// tombstone-delete (additive law: the record's history stays; the CURRENT view misses). 41func bp_del(prefix: *u8, key: *u8) -> i64 { 42 let w: *i64 = ss_begin() 43 let z: *u8 = sys_mmap(1) 44 if ss_add(w, 2, key, z, 0) != 0 { return 0 - 1 } 45 if ss_commit(prefix, w, sys_now_us()) != 0 { return 0 - 2 } 46 return 0 47} 48 49// current value of key -> copied into out (cap bytes); returns length, or -1 on miss/tombstone. 50func bp_get(prefix: *u8, key: *u8, out: *u8, cap: i64) -> i64 { 51 let h: *i64 = ss_open(prefix) 52 if (h as i64) == 0 { return 0 - 1 } 53 let pp: *i64 = sys_mmap(16) as *i64 54 let ll: *i64 = sys_mmap(16) as *i64 55 if ss_hget(h, key, pp, ll) != 1 { return 0 - 1 } 56 let src: *u8 = pp[0] as *u8 57 var n: i64 = ll[0] 58 if n > cap { n = cap } 59 var i: i64 = 0 60 while i < n { out[i] = src[i]; i = i + 1 } 61 return n 62} 63 64// 1 if the key currently exists (not tombstoned), else 0. 65func bp_exists(prefix: *u8, key: *u8) -> i64 { 66 let h: *i64 = ss_open(prefix) 67 if (h as i64) == 0 { return 0 } 68 let pp: *i64 = sys_mmap(16) as *i64 69 let ll: *i64 = sys_mmap(16) as *i64 70 if ss_hget(h, key, pp, ll) != 1 { return 0 } 71 return 1 72}