nx_journal_log.nx
buildroot/runtime/nx_journal_log.nx
about
nx_journal_log.nx -- hash-chained append-only event log (L1 of
the storage substrate).
ST-3 milestone of NISHI_STORAGE_SUBSTRATE_ROADMAP.md. Composes
L0 nx_blob_store + nx_sha256 to ship the substrate-native
replacement for bash lib_fact_emit.sh.
Each appended event:
1. Payload bytes get stored as an L0 blob -> blob_hash
2. Entry struct = (seq_no, ts_us, schema_id, prev_entry_hash,
blob_hash) is canonicalized to bytes
3. entry_hash = SHA-256(canonical_entry_bytes)
4. The new entry's prev_entry_hash field points to the PRIOR
entry's entry_hash (chain link)
5. Entry-0's prev_entry_hash is zeroed (chain root)
To detect tamper anywhere in history:
- Recompute entry_hash(N-1) from the live entry struct
- Compare against entry_N.prev_entry_hash
- First mismatch returns its seq_no; -1 if chain is intact
Tampering ANY field in entry K (prev_hash, blob_hash, ts, schema)
changes entry_hash(K) -> next entry's prev pointer mismatches ->
verify_chain catches it at K+1.
Per Cardinal 13 (additive-only): the log NEVER deletes; updates
to data write new events with new payloads; "removal" semantics
at higher layers go via new is_current=0 events, not mutation.
V1 scope:
- In-memory entries (file-backed deferred to ST-3.5)
- Max NX_JOURNAL_LOG_CAPACITY = 256 entries
- Canary-bracketed for tamper-detect on the LOG itself (in
addition to chain verification on its CONTENTS)
- schema_id is operator-supplied (caller picks the meaning)
Deferred per roadmap:
- File-backed persistence (ST-3.5)
- Merkle Mountain Range layout for O(log n) inclusion proofs
(ST-3.5 or later)
- Multi-process append (ST-4)
dependencies 3 imports · 8 importers
imports: nx_syscalls.nxnx_sha256.nxnx_blob_store.nx
imported by: nx_journal_log_test.nxnx_optimizer_milestone_attest.nxnx_self_build.nxnx_spore_up_lifecycle_test.nxnx_spore_up_network_test.nxnx_spore_up_test.nxnx_win_ledger.nxprobe_blob_journal.nx
structs
| 95 | struct NxJournalEntry |
| 110 | struct NxJournalLog |
consts
| 74 | const NX_MAGIC_1000000: i64 = 1000000 |
| 77 | const NX_JOURNAL_LOG_CAPACITY: i64 = 256 |
| 80 | const NX_JOURNAL_OK: i64 = 0 |
| 81 | const NX_JOURNAL_BAD_INPUT: i64 = 1 |
| 82 | const NX_JOURNAL_FULL: i64 = 2 |
| 83 | const NX_JOURNAL_NOT_FOUND: i64 = 3 |
| 84 | const NX_JOURNAL_TAMPER: i64 = 4 |
| 85 | const NX_JOURNAL_CHAIN_BROKEN: i64 = 5 |
| 88 | const NX_JOURNAL_LOG_CANARY_PRE: i64 = 0x4E584A4F555250 // "NXJOURP" |
| 89 | const NX_JOURNAL_LOG_CANARY_POST: i64 = 0x4E584A4F55524550 // "NXJOUREP" |
functions
| 122 | func _journal_store_i64_le(buf: *u8, off: i64, v: i64) -> i64 called by 1: _journal_canonicalize |
| 135 | func _journal_load_i64_le(buf: *u8, off: i64) -> i64 called by 1: _journal_entry_hash |
| 155 | func _journal_canonicalize(e: *NxJournalEntry, buf: *u8) -> i64 |
| 173 | func _journal_entry_hash(e: *NxJournalEntry, |
| 188 | func nx_journal_log_new(blob_store: *NxBlobStore) -> *NxJournalLog |
| 206 | func nx_journal_log_is_valid(j: *NxJournalLog) -> i64 |
| 220 | func nx_journal_log_append( |
| 289 | func nx_journal_log_read( |
| 318 | func nx_journal_log_verify_chain(j: *NxJournalLog) -> i64 |
| 341 | func nx_journal_log_count(j: *NxJournalLog) -> i64 |
| 348 | func nx_journal_log_latest_hash(j: *NxJournalLog, |