mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-10-27 16:02:05 +00:00
index0 is the per-vault activity ledger under `_system.index0`: an append-only event stream per writer, daily checkpoints rolling those events up, and derived canonical views (children_of, counts_by_kind, recent_activity). It was the last piece carried through verbatim rather than computed — which was safe for round-tripping but meant a TypeScript client never UPDATED it. A vault written by this port went stale the moment it was touched, and a Python client reading it afterwards saw an activity log missing everything the TS client had done. Worse, `nostr restore` kept the local block verbatim, so a restore silently discarded the remote's history. Hash parity is the entire contract here: event ids, integrity hashes, checkpoint summaries and view hashes are SHA-256 over Python's canonical JSON, and the merge picks winners by comparing those hashes — one byte of encoding disagreement means the two implementations permanently disagree about which record wins. Everything routes through the existing canonicalJson (Python's ensure_ascii escaping, code-point key ordering, CPython number formatting) rather than JSON.stringify, and the tests compare whole structures via canonical JSON rather than toEqual, so they pass only if the bytes that get hashed are identical. Verified against Python fixtures covering the append chain, compaction, manifest metadata, views and a two-replica merge — including non-ASCII labels and tags, a non-numeric entry id, two writers across two days, and a managed-account scope path. Two Python quirks are replicated deliberately rather than "fixed", because diverging would change which events exist rather than merely how they hash: `str(None)` is "None" (so a JSON null in a required field is a non-empty string there), and a stored integrity_hash is preserved even when it disagrees — this normalizes, it does not verify, and silently rewriting a mismatched hash would hide the tampering the hash exists to reveal. Events are emitted by DIFFING the index at the single mutation funnel each surface goes through, rather than from each call site as Python does. That is deliberate: a call-site emitter is something a new code path can forget, and a silently incomplete ledger is worse than an absent one because it reads as "nothing happened". The event vocabulary matches Python's, so a stream from either implementation reads the same. Emission is best-effort — the atlas is derived state, and it must never fail a committed vault write. mergeIndexPayloads now defaults to merging index0 with the same deterministic rules; "preserve-current" and "reject" remain for callers that want them, and `nostr restore` no longer discards the remote's history. Also made gen_high_risk deterministic. Fernet embeds a random IV and the current time, so that fixture regenerated differently every run and would have failed the fixture-drift CI job each time — it now pins both, with a note that production must never do this. Verified end to end: the TS binary performed four mutations, and Python read the resulting atlas, normalized it without dropping a record, and compacted it into checkpoints and views. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN