From e3b1325a2b0ec426bfb87a6cef68101a47edaf9a Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN Date: Tue, 18 Aug 2026 13:11:50 -0400 Subject: [PATCH] fix(vault): entry ids are never reused -- allocate from a persisted watermark An entry id doubles as the BIP-85 derivation index for managed_account and seed entries, which makes it a permanent cryptographic coordinate: "Alice is account #184" is a recovery claim, and #184 must mean Alice forever. But both implementations allocated ids as max(live ids)+1, and sync deletion removes entries via tombstones -- so deleting #184 on one device let the next creation reissue #184 to a NEW entry, which then re-derived the departed identity's exact child seed and npub. Tombstone retention (capped at 2048) made even the evidence expire. Allocation now comes from a high-watermark persisted in _sync_meta.next_index: never-decreasing, bumped on every insert/save (explicit-id inserts included), merged as max(both sides' watermarks, one past any merged entry or tombstone id), and identical in both implementations -- the merge computation must stay byte-for-byte the same in sync_conflict.py and sync/merge.ts. _sync_meta was chosen because both sides already round-trip it (it carries the tombstones), so old builds preserve the field and no schema version bump is needed. Legacy vaults heal on their next allocation: the floor folds in tombstone ids still within retention, and the first save persists the watermark. Python-side key parsing mirrors the TS regex exactly (ASCII digits, no leading zeros, JS safe-integer range): str.isdigit() alone accepts Unicode digits that Number() rejects, which would have made the two implementations compute different watermarks from the same payload. Fixtures regenerated from Python; the byte-for-byte entry-creation parity test now passes with the watermark present, proving TS's insert-time bump and Python's save-time bump converge on identical indexes. Verified: the recycle regression tests fail on both pre-fix implementations; 1180 Python, 224 core, 149 CLI tests and all 36 cross-impl checks pass with the fix. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Tkj4rzKBA3uhfgk7kqjsb8 --- js/packages/core/src/sync/merge.ts | 26 ++++++++ js/packages/core/src/vault/entryOps.ts | 54 ++++++++++++++- js/packages/core/test/entryOps.test.ts | 65 +++++++++++++++++++ .../test-vectors/fixtures/delta_replay.json | 1 + .../test-vectors/fixtures/entries_index.json | 3 + .../test-vectors/fixtures/entry_mods.json | 3 + .../test-vectors/fixtures/manifest.json | 2 +- .../test-vectors/fixtures/nostr_events.json | 12 ++-- .../test-vectors/fixtures/nostr_snapshot.json | 32 ++++----- .../fixtures/portable_backup.json | 13 ++-- .../test-vectors/fixtures/sync_merge.json | 8 +++ .../fixtures/vault_v3_payload.json | 4 +- src/seedpass/core/entry_management.py | 64 ++++++++++++++++-- src/seedpass/core/sync_conflict.py | 34 ++++++++++ src/tests/test_delta_merge.py | 49 ++++++++++++++ 15 files changed, 334 insertions(+), 36 deletions(-) diff --git a/js/packages/core/src/sync/merge.ts b/js/packages/core/src/sync/merge.ts index 542a8fc0..3271240e 100644 --- a/js/packages/core/src/sync/merge.ts +++ b/js/packages/core/src/sync/merge.ts @@ -142,6 +142,17 @@ function maxEntryTs(entries: Dict): number { return max; } +/** Highest numeric key in a record, or -1 when none. Non-numeric keys are skipped. */ +function maxNumericKey(record: Dict): number { + let max = -1; + for (const key of Object.keys(record)) { + if (!/^(0|[1-9][0-9]*)$/.test(key)) continue; + const id = Number(key); + if (Number.isSafeInteger(id) && id > max) max = id; + } + return max; +} + function maxTombstoneTs(tombstones: Record): number { let max = 0; for (const record of Object.values(tombstones)) { @@ -411,9 +422,24 @@ export function mergeIndexPayloads( maxTombstoneTs(tombstones), ); + // Allocation watermark: never below either side's watermark, nor below one + // past any merged entry or tombstone id. Without this, merging a deletion + // lets max(live)+1 reissue the deleted id — and an entry id is a permanent + // BIP-85 derivation coordinate, so reissuing #184 hands a NEW entry the + // departed identity's exact derived secrets. Must stay byte-identical to + // the Python computation in sync_conflict.py. + const nextIndexWatermark = Math.max( + safeInt(meta["next_index"] ?? 0, 0), + safeInt(incMeta["next_index"] ?? 0, 0), + maxNumericKey(curEntries) + 1, + maxNumericKey(tombstones) + 1, + 0, + ); + Object.assign(meta, { strategy: MERGE_STRATEGY, last_merge_ts: lastMergeTs, + next_index: nextIndexWatermark, source_count: sources.length, sources: sources.slice(-32), tombstones, diff --git a/js/packages/core/src/vault/entryOps.ts b/js/packages/core/src/vault/entryOps.ts index cc57e99b..67f542eb 100644 --- a/js/packages/core/src/vault/entryOps.ts +++ b/js/packages/core/src/vault/entryOps.ts @@ -62,7 +62,56 @@ export function nextIndex(index: VaultIndex): number { } ids.push(id); } - return ids.length > 0 ? Math.max(...ids) + 1 : 0; + const liveFloor = ids.length > 0 ? Math.max(...ids) + 1 : 0; + // Never allocate below the high-watermark. Scanning live entries alone is + // not enough: an entry id doubles as the BIP-85 derivation index for + // managed_account (and seed) entries, and sync deletion removes entries via + // tombstones — so max(live)+1 could reissue a deleted #184 to a NEW entry + // that then re-derives the departed identity's exact child seed and npub. + // Tombstone ids are folded in so vaults from before the watermark existed + // heal on their next allocation (for tombstones still within retention). + return Math.max(liveFloor, storedNextIndex(index), tombstoneFloor(index)); +} + +/** The persisted allocation watermark, 0 when absent (pre-watermark vault). */ +function storedNextIndex(index: VaultIndex): number { + const meta = (index as unknown as Dict)["_sync_meta"]; + if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return 0; + const raw = Number((meta as Dict)["next_index"]); + return Number.isSafeInteger(raw) && raw > 0 ? raw : 0; +} + +/** One past the highest tombstoned id. Non-numeric tombstone keys are historical junk and skipped. */ +function tombstoneFloor(index: VaultIndex): number { + const meta = (index as unknown as Dict)["_sync_meta"]; + if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return 0; + const tombstones = (meta as Dict)["tombstones"]; + if (typeof tombstones !== "object" || tombstones === null || Array.isArray(tombstones)) return 0; + let floor = 0; + for (const key of Object.keys(tombstones)) { + if (!/^(0|[1-9][0-9]*)$/.test(key)) continue; + const id = Number(key); + if (Number.isSafeInteger(id) && id + 1 > floor) floor = id + 1; + } + return floor; +} + +/** + * Record that `id` has been allocated: the watermark becomes at least id+1 + * and never decreases. Kept in _sync_meta because both implementations + * already round-trip that block (it carries the tombstones), so old builds + * preserve it without a schema bump. + */ +function bumpNextIndex(index: VaultIndex, id: number): void { + const container = index as unknown as Dict; + const meta = + typeof container["_sync_meta"] === "object" && + container["_sync_meta"] !== null && + !Array.isArray(container["_sync_meta"]) + ? (container["_sync_meta"] as Dict) + : {}; + meta["next_index"] = Math.max(storedNextIndex(index), id + 1); + container["_sync_meta"] = meta; } /** Next TOTP derivation index: max over totp entries' index field + 1. */ @@ -85,6 +134,9 @@ function insert(index: VaultIndex, id: number, entry: Dict): string { throw new Error(`refusing to overwrite existing entry ${key}`); } (index.entries as unknown as Dict)[key] = entry; + // Covers explicit-id inserts too: an entry created at #500 pushes the + // watermark past 500 even though nextIndex never saw it. + bumpNextIndex(index, id); return key; } diff --git a/js/packages/core/test/entryOps.test.ts b/js/packages/core/test/entryOps.test.ts index 587db075..989d52bf 100644 --- a/js/packages/core/test/entryOps.test.ts +++ b/js/packages/core/test/entryOps.test.ts @@ -83,3 +83,68 @@ describe("entry creation parity", () => { ); }); }); + +import { mergeIndexPayloads, Bip85 } from "@seedpass/core"; + +describe("index allocation watermark", () => { + function freshIndex(): VaultIndex { + return { schema_version: 4, entries: {} } as VaultIndex; + } + + it("does not recycle an id deleted through a merge", () => { + // An entry id is a permanent BIP-85 derivation coordinate: reissuing a + // deleted #2 would hand a NEW entry the departed identity's exact child + // seed and npub. + const index = freshIndex(); + addPasswordEntry(index, "web-a", 20, { clock }); + addPasswordEntry(index, "web-b", 20, { clock }); + const managedId = addManagedAccountEntry(index, "alice", MNEMONIC, { clock }); + expect(managedId).toBe("2"); + const departedSeed = Bip85.fromMnemonic(MNEMONIC).deriveMnemonic(2, 12); + + const incoming = { + schema_version: 4, + entries: { + "2": { kind: "managed_account", label: "alice", modified_ts: FIXED_UNIX + 10, _deleted: true }, + }, + }; + const merged = parseVaultIndex( + mergeIndexPayloads(index as unknown as Record, incoming, "remote"), + ); + expect(merged.entries["2"]).toBeUndefined(); + + const newId = addManagedAccountEntry(merged, "bob", MNEMONIC, { clock }); + expect(newId).toBe("3"); // NOT the tombstoned 2 + const bobSeed = Bip85.fromMnemonic(MNEMONIC).deriveMnemonic(3, 12); + expect(bobSeed).not.toBe(departedSeed); + }); + + it("merge carries the higher watermark even with no entries or tombstones behind it", () => { + const current = { + schema_version: 4, + entries: { "0": { kind: "password", label: "a", modified_ts: 50 } }, + _sync_meta: { next_index: 500 }, + }; + const merged = mergeIndexPayloads(current, { schema_version: 4, entries: {} }, "x"); + expect((merged["_sync_meta"] as Record)["next_index"]).toBe(500); + }); + + it("an explicit high index pushes the watermark past it", () => { + const index = freshIndex(); + addManagedAccountEntry(index, "pinned", MNEMONIC, { clock, index: 40 }); + const nextId = addPasswordEntry(index, "after", 20, { clock }); + expect(nextId).toBe("41"); + }); + + it("a legacy vault heals from tombstones still in retention", () => { + // Pre-watermark vault: no _sync_meta.next_index, but a tombstone for #7. + const legacy = freshIndex(); + addPasswordEntry(legacy, "a", 20, { clock }); // id 0 + const container = legacy as unknown as Record; + container["_sync_meta"] = { + tombstones: { "7": { deleted_ts: 60, entry_hash: "", event_hash: "" } }, + }; + const id = addPasswordEntry(legacy, "fresh", 20, { clock }); + expect(id).toBe("8"); // past the tombstone, not max(live)+1 = 1 + }); +}); diff --git a/js/packages/test-vectors/fixtures/delta_replay.json b/js/packages/test-vectors/fixtures/delta_replay.json index 2a16005e..cb2e3a9e 100644 --- a/js/packages/test-vectors/fixtures/delta_replay.json +++ b/js/packages/test-vectors/fixtures/delta_replay.json @@ -50,6 +50,7 @@ "final_state": { "_sync_meta": { "last_merge_ts": 1700000020, + "next_index": 2, "source_count": 2, "sources": [ "815ddfa9958c34a9", diff --git a/js/packages/test-vectors/fixtures/entries_index.json b/js/packages/test-vectors/fixtures/entries_index.json index 4c5bf91b..04bfa1f2 100644 --- a/js/packages/test-vectors/fixtures/entries_index.json +++ b/js/packages/test-vectors/fixtures/entries_index.json @@ -1,6 +1,9 @@ { "description": "Decrypted entries index (one entry of each kind), _system stripped, timestamps pinned to FIXED_UNIX", "entries": { + "_sync_meta": { + "next_index": 10 + }, "entries": { "0": { "archived": false, diff --git a/js/packages/test-vectors/fixtures/entry_mods.json b/js/packages/test-vectors/fixtures/entry_mods.json index 84ad079f..eda8b8ee 100644 --- a/js/packages/test-vectors/fixtures/entry_mods.json +++ b/js/packages/test-vectors/fixtures/entry_mods.json @@ -1,6 +1,9 @@ { "description": "Entries index after a modification sequence (modify_entry, archive/restore, add_link/remove_link) applied by the real EntryManager at MOD_UNIX. TS replays the same ops from the creation state and must match.", "entries": { + "_sync_meta": { + "next_index": 10 + }, "entries": { "0": { "archived": false, diff --git a/js/packages/test-vectors/fixtures/manifest.json b/js/packages/test-vectors/fixtures/manifest.json index 361fe739..eee5c97e 100644 --- a/js/packages/test-vectors/fixtures/manifest.json +++ b/js/packages/test-vectors/fixtures/manifest.json @@ -28,6 +28,6 @@ ], "fixture_version": 1, "generator": "scripts/generate_ts_port_fixtures.py", - "python_commit": "5dcd779a6f9ce93e0065a974b896e7e2c2b170a0", + "python_commit": "f634435681af4913bc5d487b2b7aa763ca504229", "redaction_policy": "All fixtures derive from public BIP-39 test mnemonics (abandon.../legal.../zoo...). Secret-shaped outputs are generated test material, never from a live profile." } diff --git a/js/packages/test-vectors/fixtures/nostr_events.json b/js/packages/test-vectors/fixtures/nostr_events.json index 58a825b3..1a69e30b 100644 --- a/js/packages/test-vectors/fixtures/nostr_events.json +++ b/js/packages/test-vectors/fixtures/nostr_events.json @@ -3,12 +3,12 @@ "events": [ { "event": { - "content": "H4sIAAAAAAACAwHjDxzwVjN8yABUBOsovylao1dfY22Y+jyucdrP9HwxZksfJP00vjARSES7X9CGz/sCp7ZcIm3Ok3+n478l25p90hmLwZTTO02lv3C3leTqTt680X7TtqbHdEB1RAxkan+TkA18n4UCOw7z3C17AT0qoV3Drvgz5qLhyvTrrMOPxqa32mIUgkVpYAu75KBhS4PMR0FNE/KdEpTvR/De1B+Xk3gb66IfpEsqq5TArksXd7955thFWJVvm1m1amJyUEwk95JSThwVq5SENnISGG7RH7s3z/Ah4hoQCzM/IrN1+tt2Sqg5BHSpemsg2vMSwYlobqggf8P95yYfiIBZ6mkWtA8OfMtm7dYkQDvxOpciy4XDX1BtV9rkFgLnSmptePwMhuHoCVsnpkq0PKttBHFA0eieup6wvI9AODZ60Vi/Saol1oyjNQwmvqiM8JqPwkbr2bEZLfOIMddPAVtktQJV0BtBRAdmU4UwkfufUOSHYYK8fxvJefJU/sq5H/AN2iUsUXaSnJaYnj+Dr5pV/ouYeq0tUCt2p/Shrf3+YZQlovnTkGCLGcLaBqXTPW2uAbf+G5HAgpr1MZwEFhIOWjt8lGQPIHFqYCtC6IcFiOn95oZYnqofGrUpvERLXjLO+8KFd902Kl2ymC6MWmTAR68ab5WR1TzrhFBBp1lOc1sOuTLiXb9wnS0T/JquX1QRplZLnxHP2gzDZsRMVzTY5WgSNvjye5c3qQWvftGs7iIjjTdUt20zSwonFErRCl7AXVkJmiUmTwt4por69NYBLDFMUZAFv82n+jC1Mk5Ixh23SYMXn9ptXM7PdHKDr1r7UNrnRBM33Zzh752A3QzfAlYIorD4usWFgz+lG8Xw97Ef/QVCaRsge3TUlat/MARldvbCCWSB7RiFmWbzyQVIP8MHXoTX0fSdvtSRBRbQUFv2ImotG3E8vgPObiv65LtmPJf6HkhosGX0VIKLgGJj4Mzi63BN0Fu/+/FEHIomX+tlQAuU/HNsWmGIogvqylKT/Bd7AyYaUw9TDg6hlbnoZDpALziNL4k=", + "content": "H4sIAAAAAAACAwEPEPDvVjN8yABUBOsovylao1dfY22Y+jyUbNfT/kYvIQVefc005RoRSlS5R56Yva997vgYZzeN2zz+usNgn8Vr+F/Kj4LYYzXs+iO1j+SzJ4vvhTGc2eKUMVcbFw5+agTunmYu3M1LbUu1mnYvAgMntlyKwPop5qDz2uS79IHKhPrc6DlD1BApeAOrlM19e5PcRU1nE/DZU8CqOvXbxB+s3C1foPZG8xwloIXPrU9xdqBy5sZOXvF2mVO+aWhiTG0054hCXh4bxdXQcw1fVyjfHLwN3bdpsVFNVSM2L7Bo3cpiProrHmfxKTd00/kC2ck5P/ddMJG55So1isccpBZA8xEcd8Fk7dYkUHKDe9pyh8DNHlRpG5LMDALlGis+KecGmuK+TXM9pluiMIFvSDAClKSe7Nf8soQDb2ZN6VrWBucn2qajN0Fp+uHI9ZqF+kbw2bEZLfKUC8dfEUt0pQwzmVUKFwV8UbACyZTMUv6HY9L/YQfaf+ZZu8CJH+YNzA89RmaCjIaK2m7U8LJP/vCyeq0vHmQi4qeh4Li+M/928a6cwiT2W6eOQ6ffF22sVe6uXpGOw9/2ctVXOndcHjlwvmQPIHM9JWsVkMsHkun/rq8A5PkFFbps5AdSVy7O95vKOI97Uw7l13zIWGq/FOpIIdTckmujyFAa6xJFYg106ijtUqck9mBDsN+iDRlGpAFLnxHP2gzDZtBNUS7f8mATNvjyP5c3swPmVvus7iIj0Dk69jl2NElyUhWVU27AX0sZiDYpH0g2/9fYo4AZJyIQAsxd6tGX6iC3PmRIxFn2HcZqmt99XPWAITbI+wOsB9XsVRw02frg8JaAwwfZZk8KqLv7sNWZoi+1AdXg9b9xvFEHFlZvPXnXiKtpf1crPbeGUjSO5gnc2ifC1z1SLtAdTJTeha3A55DACybQRlfcImovXzRo+RjNYyDrtfUvb5nKHhw65SD4fMnCziZh+s6k9nhQ1Aqxy/FSEKAmX+kpAUnT+T8yHzvP4Eq3ljTW8UM0V3YaFGwdSgy7lbu+ZydeZmelNYk=", "created_at": 1700000000, - "id": "28e4ad167400ee19929818b50d2d72cce084613b08e5761e6af1d0e1c3af95aa", + "id": "40e5d66e8165fa02af911469351a6b41d67d0f4f1f0f59f2bf25de73be84b5f2", "kind": 30071, "pubkey": "257f22c3049cf52100bdf17d6bce85b7c9d648850e4e3c1965733dd176b646e0", - "sig": "58c7b0a53c12f5912a879289fb4244d72956522e037d3e31820fa2aecc8e64e9992f05653e4cc30f59a491b26b553ba82ff82443d1c2d5531a7ee77ce29f5447", + "sig": "0be5c33ae156154b70bc3d3990b8bc4e5089514fc69bf98772ea367c6ce1436886b9f9bd4d8823a3879d986fb0faa8da0ea68e869409ad3814bb230fe81c3cb6", "tags": [ [ "d", @@ -20,12 +20,12 @@ }, { "event": { - "content": "{\"ver\": 1, \"algo\": \"gzip\", \"chunks\": [{\"id\": \"seedpass-chunk-0000\", \"size\": 800, \"hash\": \"c57f7c84461a7db48c478ce8f62a4a19be933a592436f2bedeb85bd54ca3ed48\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0001\", \"size\": 800, \"hash\": \"a8dbca0d027ec5af9f517bb543ce3e7f2e511b76ca8fd6656d0054fde796815b\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0002\", \"size\": 800, \"hash\": \"7672fd2923ba8ba868282176385d45400d4787fae28a9430df3c3df9253b24ef\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0003\", \"size\": 800, \"hash\": \"9fdc9b622219d3e2874e4eee96ba134d682a7f720478ba770462c5ade273510c\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0004\", \"size\": 800, \"hash\": \"dbf3068bf08dec2be41992e3163a5ded6bd7b10dd7f6dbc6ffd9fd0bc992f70a\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0005\", \"size\": 90, \"hash\": \"76fd3739a3e002e1921a47e3c0e3b947c2c3f45304deda86745279a5ef24d6c2\", \"event_id\": null}], \"delta_since\": 1700000000, \"nonce\": \"PAqvfNIUFpEog1t93a3M4A==\", \"index0\": null}", + "content": "{\"ver\": 1, \"algo\": \"gzip\", \"chunks\": [{\"id\": \"seedpass-chunk-0000\", \"size\": 800, \"hash\": \"11325bc9b81def0f846a3e13ef1bf26fba08cc37828f3b3b1eac17f685ef2dce\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0001\", \"size\": 800, \"hash\": \"412b97ba88ab3c579d25d677181928a7a1925a2d401a2380e56fb0065b376cbd\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0002\", \"size\": 800, \"hash\": \"01728a50deedd31079ac2677e513285c5d6eaac59ac030eaa7dac7e3e85180dd\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0003\", \"size\": 800, \"hash\": \"0ecbf450f3e44d9db7024243e18786107e7c37a17146d046f29d054ad13ab26d\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0004\", \"size\": 800, \"hash\": \"0927efbc911129ea2b4fa7b9491f0fa62da933cc8d883ac7b659f111b9f0f49b\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0005\", \"size\": 134, \"hash\": \"3f19fbd0b5b1e132044b996030394b13f1cb3b2a3ddcc925b010d96fcab15826\", \"event_id\": null}], \"delta_since\": 1700000000, \"nonce\": \"PAqvfNIUFpEog1t93a3M4A==\", \"index0\": null}", "created_at": 1700000000, - "id": "f6b1362c95101cf8ec58bce9af8c7ae67f41ebf29a52a8c5239953f0b550cf78", + "id": "b9e5972d775f045350a221f9f4717ef83fae8d66c125bce33cdc671e0bf6e7d1", "kind": 30070, "pubkey": "257f22c3049cf52100bdf17d6bce85b7c9d648850e4e3c1965733dd176b646e0", - "sig": "ee8def6729fbd1619f19091fea9d653c5b7539f9a7c2662a9678b8c66903ab0c1ed9af6ba3593858162aa24f8db25ab78118159ab113ac1f183ecbe85d7e10e0", + "sig": "6d7057e826f618faf252581be75872bbb2f168a28e1af319382fd119cfe51c2be4478659f82ea44081f1d2b0769eb12dfeac2faf28b4f3ee39830b864a9112bb", "tags": [ [ "d", diff --git a/js/packages/test-vectors/fixtures/nostr_snapshot.json b/js/packages/test-vectors/fixtures/nostr_snapshot.json index b4e41d8e..673b66e0 100644 --- a/js/packages/test-vectors/fixtures/nostr_snapshot.json +++ b/js/packages/test-vectors/fixtures/nostr_snapshot.json @@ -3,52 +3,52 @@ "chunk_metas": [ { "event_id": null, - "hash": "c57f7c84461a7db48c478ce8f62a4a19be933a592436f2bedeb85bd54ca3ed48", + "hash": "11325bc9b81def0f846a3e13ef1bf26fba08cc37828f3b3b1eac17f685ef2dce", "id": "seedpass-chunk-0000", "size": 800 }, { "event_id": null, - "hash": "a8dbca0d027ec5af9f517bb543ce3e7f2e511b76ca8fd6656d0054fde796815b", + "hash": "412b97ba88ab3c579d25d677181928a7a1925a2d401a2380e56fb0065b376cbd", "id": "seedpass-chunk-0001", "size": 800 }, { "event_id": null, - "hash": "7672fd2923ba8ba868282176385d45400d4787fae28a9430df3c3df9253b24ef", + "hash": "01728a50deedd31079ac2677e513285c5d6eaac59ac030eaa7dac7e3e85180dd", "id": "seedpass-chunk-0002", "size": 800 }, { "event_id": null, - "hash": "9fdc9b622219d3e2874e4eee96ba134d682a7f720478ba770462c5ade273510c", + "hash": "0ecbf450f3e44d9db7024243e18786107e7c37a17146d046f29d054ad13ab26d", "id": "seedpass-chunk-0003", "size": 800 }, { "event_id": null, - "hash": "dbf3068bf08dec2be41992e3163a5ded6bd7b10dd7f6dbc6ffd9fd0bc992f70a", + "hash": "0927efbc911129ea2b4fa7b9491f0fa62da933cc8d883ac7b659f111b9f0f49b", "id": "seedpass-chunk-0004", "size": 800 }, { "event_id": null, - "hash": "76fd3739a3e002e1921a47e3c0e3b947c2c3f45304deda86745279a5ef24d6c2", + "hash": "3f19fbd0b5b1e132044b996030394b13f1cb3b2a3ddcc925b010d96fcab15826", "id": "seedpass-chunk-0005", - "size": 90 + "size": 134 } ], "chunks_b64": [ - "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" ], - "compressed_b64": "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", + "compressed_b64": "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", "description": "Nostr snapshot model: gzip(encrypted index) split into <=limit chunks; chunk id 'seedpass-chunk-%04d', sha256 hash; manifest kind 30070 (d-tag = manifest id), chunks kind 30071 (d-tag = chunk id, content = b64), deltas kind 30072. Manifest id = HMAC-SHA256(key_index, b'manifest|' + nonce) hex, key_index = HKDF chain master->'seedpass:v1:index'. gzip mtime pinned to 0 for the fixture.", - "encrypted_b64": "VjN8yABUBOsovylao1dfY22Y+jyucdrP9HwxZksfJP00vjARSES7X9CGz/sCp7ZcIm3Ok3+n478l25p90hmLwZTTO02lv3C3leTqTt680X7TtqbHdEB1RAxkan+TkA18n4UCOw7z3C17AT0qoV3Drvgz5qLhyvTrrMOPxqa32mIUgkVpYAu75KBhS4PMR0FNE/KdEpTvR/De1B+Xk3gb66IfpEsqq5TArksXd7955thFWJVvm1m1amJyUEwk95JSThwVq5SENnISGG7RH7s3z/Ah4hoQCzM/IrN1+tt2Sqg5BHSpemsg2vMSwYlobqggf8P95yYfiIBZ6mkWtA8OfMtm7dYkQDvxOpciy4XDX1BtV9rkFgLnSmptePwMhuHoCVsnpkq0PKttBHFA0eieup6wvI9AODZ60Vi/Saol1oyjNQwmvqiM8JqPwkbr2bEZLfOIMddPAVtktQJV0BtBRAdmU4UwkfufUOSHYYK8fxvJefJU/sq5H/AN2iUsUXaSnJaYnj+Dr5pV/ouYeq0tUCt2p/Shrf3+YZQlovnTkGCLGcLaBqXTPW2uAbf+G5HAgpr1MZwEFhIOWjt8lGQPIHFqYCtC6IcFiOn95oZYnqofGrUpvERLXjLO+8KFd902Kl2ymC6MWmTAR68ab5WR1TzrhFBBp1lOc1sOuTLiXb9wnS0T/JquX1QRplZLnxHP2gzDZsRMVzTY5WgSNvjye5c3qQWvftGs7iIjjTdUt20zSwonFErRCl7AXVkJmiUmTwt4por69NYBLDFMUZAFv82n+jC1Mk5Ixh23SYMXn9ptXM7PdHKDr1r7UNrnRBM33Zzh752A3QzfAlYIorD4usWFgz+lG8Xw97Ef/QVCaRsge3TUlat/MARldvbCCWSB7RiFmWbzyQVIP8MHXoTX0fSdvtSRBRbQUFv2ImotG3E8vgPObiv65LtmPJf6HkhosGX0VIKLgGJj4Mzi63BN0Fu/+/FEHIomX+tlQAuU/HNsWmGIogvqylKT/Bd7AyYaUw9TDg6hlbnoZDpALziNL4m3m74b5867RBPqLfpR8nh9rsPEBWMqgw4x7y7x21q3VPcNn/IXprDPbaAXsxPYG8SJDbXej0cY3MnAfKigVVSSiSgeCjuTmb7uc1JO6pR9/QQYMqFD4uPJKsD7xAn//6P399EttsAKLAEn4KYNfafIsXnZaGvCiXO2Y5sqrR1Sb+OwZmi69ZnxXd86fu06BfqxzAyppwSe8wyCdA9BUs6AOsoVGr7eh3i4of1k6h7YcRGwXnKUaA+I4ofG3aMKvjluaTlPK7b5NZ0Tay4Bi2oGLTZDeUYQrG2LRa7RhsCkDLFsXXV6W+R02GANX+PWtKNvQPMfjbAlGOwrz/PHjxI4N6831Vv+UPN1KiJVmOLA7/xPRMSC+jEe6+TNmk5dQXg7J8B6cXNec1Q8c5GTXJTqjHSVnD6J1vGGBWD+OdanxrgAN+lMCGVEPhhcvm64UkVxaKQLGwpBxcyAIQvdj7vOZuDM7mVfQ69l0FqrQVebx2BQgyDDUTNctA2OrX0J3RhPQ0X88DNvdieZ6uu+r10U4x0H7E9XUOGLmf3TOKQtIkb23oyVA03pZlA1M5fTGM55wBmtspHFRz+hzuv+bjw1oF+tzww3SvWBFFHYayzcGGj+mjaZ/+uuln/qW+TpWYGhtFmuFIl046ZdGpwA8k7XIyWhcz+pWaFR8dJ9JGWf2qDik3BOl0MvXY0+/9FFq/NY4wqopIPCG8CnsKhv3oE7IOEWls44xAClHV2tMD24SApQ638LCWTvyT9IUdDpKsueg4V1oP73TE5FIAA3al5Hpmb/mBVUjE+7EiywmdY/1OSkg99sQ+KZRgDHicmwpupxWG0cDE/rK052giUqdIe71iR89ijZm/ywsSVvDarNtWuPdTfrXtpXlyEfosPaTJ+ToRr3yOr6R3w4p7AxaXvibRvoUIeQCHfVVXZ32209EA7BKKiuIQugi8dPO3cVsC7xqTjm0WC2B7tCcD03OwHQ9465p3KM1SvwVofcvqxQn1XuhUsoRAIw5NkLBvHTdSiibqct2N63wuAOohvxlIljcWKSkxWNi60mT/sZlws/jcmHQLxhvm7yaS1yOwj46LiE/Zqov4DbEKcZ/UKH0vx8sJ8yfKGAfuRDuZ10LjKuUPfOo6QwtV+qDGwKaYq2C1p8T/QlAQLKy2YxaQLdc9j9r1uzMuvxwDAb5VshJYGUvDG2E2h34tSo3I+KEHsz6prpClCONSAStCFQnEJbXG6M9Nl/KptbIvkPPsb0kxv8hLP66u1e2HmyHGlmMl72t62sScgGRs7dsxgOda9N0ZMas50MsUiG6APGt9VAg1zkt29G+izPrDB6Ca1GzxImifYAKlOjj+CZ6VCo9NjM/3oGXetM7QqZGtDSjUaSeNgvp9m/WMUuwfXZTOd8Y8scRXtVBQ80jr6R9HBA6jcyusNGQDVsAaGWJOVl/+gKk33qjm7lAM4zuPjPQoYSGr14GqQ6dygT4z5qmeCDarY8lLZabbREoez7NO3FAxAi45x6PII/UzpvluEUgdRLv6WDYwyITaida+P9s9uF3AQodQ2z95rdlmpjIh1hls3F/Phl6VKdNNjY4elo4i2MVgaeI6uKb8iK9VnGoQwWIEV9u+8XEKgKg3mOsTF6f83MVB+F8AwdQAPlmvD/cU1cI5ww3sBlocJkhZvoYpChjMFMgZWb5U46ML6lJbw3ODpsjatNM9v4ZxuWLV7ffJ+aHCRFVRn/I+AVbKB0gZ5X5qtK+5VomvQL7y1Zjr5ehYWQf6VFHIVY7HOv874ghGi6G00Scb2316xIImT0hxAzzwryZTBAwkiuQGfpJyAvmsotcVobg2Rd52EH3Okc0eZXIZ45B5r3pKhLxYisCsUNujGDNC2bQAmkyXEJW5QJ2+VHPOaJMUhkb28OvVcqVzUwMHkrWWZw3zBM71YaOezdHzZSu4urdazodtEgvFP32TSx2UhhFUInwGYnXkHZ75gSwekWMaBcKgzC8lJehTWtIlpKp1miZhEw2w91fIFwuZ7a/H0Tfe8hK2ionMFCNlRSorKKeD33Z8aDR0tWogJ1qZ8mgx0PwbdAtWhWpVBfN0rgGTb2IQDndW7kAe2bM8N8O4Sqx48J14HHjsodpD60b9rCiZGkI7TucMq1G6uJMCTKx+5VC0VuKypbkNf8KfFFGEI7ns47wo+5e554b8glcU3We6DB6h79SBorGUjxNsD1/5dOKcuuELpVIXi+SbEwgdgZFmfg0IQusASBxRcXGEIq4mlfHGW9y6rNp2v0COlaM+/Ls4Uf5aYZULiG4GEJ5k8wBsUtCh7GaGrlmXZG7E5oLUWjv7Gj4s+nuSz2/E9vsXpUKfME8TNYrdX2n6x9Ko4LvQECzPF2wWEtA1x5OiWiLAo5y4vifZyL+nFRArW5AwML8RMHdTeKBUdZO24HN9VWeVgVyXKwwo6P0KpEwr2zt75SSDd5l1Wvu3FuyheEdkoAJRzpchl6QIN4EFztpzJYrvITVzkQZIsnZnfrp/3twAYagluvQGHQqYE4sxLslB4O+aVNBTA6UaPQGsWlf2g2iarM+IyClnbrvCc0w6GtHqWarb68/28jPMgi5QwmI+ukLNuvNWL4vkrCwwpA+r2vJu0ywW60TBsvALzgubgiFBjaZ8KrI9qNfoGUamCwdBKJhzZd84O8k8k7VUGnt/cdoNYAjZo5n/zjDRU6pBnHt8Ow5GSFZkVp03UYyWuCXP4KK95U/xmeyIv9Vp5Kd8A/lMoDX2XiaggmVIzz20KAqWDIJBxGgYr6waBgvDoUBE0n/lfvgxAcdDUu/cUV4Ol8wEcC7yVTitai+9Hi0zay4MKIclLPI3eE1kCpnEOeBNoBMTVxHb4ee2sP3QOBWFGzhXSEunXkW3Y/FbBhZTzHWAYzO6mW/pEMAmhkNIEwjLGcq1gx5ymV7EJ4w09iLXRWKSDl9Wqbvsmf32C9rA0vkO4JOQGHrV7zyf1FdaYxqcYB4IN+JOY7hRzr14EVjMzIpwX8FPEQyNSreqhBo2UrHNV1hbLvLn3Xkjyxq9fb83cc9l8lXJkmcdBChhezE3rhFemwmkPLI0ftCf26sLWB9ieHP1dnGpak8PBeEi69tfDXbsX+r+hTS46O4NNraTdh0ojYso/ZtLot3pGMkjDdTBmChwmg945sJl8oSJLA70oXVDDoRVQOlqqukfc5WuDuniwIyv86Y6lZ6EQ7AyLDrHAfUV27cpyar3wdO1IM5M3Cg1snpsJ3k/saeplkBkPz1yRWfHtFsGQiRMU5AzfD4f5XOeg/xtZsWsIHylVUBizBO2LLy9UQ5e5xYbCXqptbCGJUXeUeP6D1LkQzQdmmL6gBB+ipvENccIuJ62QqCfKvA5Chh+EaccaDJPmcZuVTz0UPubrnHDV4DMdYZY49lHNdAG2UtXty8d3/TztRJ3pLDV7MX9+Xs3PEzPoJ8pv0XmaXe2zI4wV9DD9pSMAUQPhKwwZpyKAQXfrUl8ztAKNdwzR83DiEcsqSYlOwZWJr4HnF8VOvYfjbo4jxE0akHUx+ESCPiDIcYLNHmxwoxrTqqWxDIuU0cdQwLyJXJPHiWi2ofvllgCygQo6lSGD1jjowS5wRE8O/Jfn3HPe4FJcrq4DGe43fzSgCNGfCamL2JXw/DJ7f59KchtoATFC2K6MalOhHJNP82yLtWrzvjd4Xrk6lmI3ri9xOEvBHM2NiDoYU8hgVm1hjuOOQT4I2sSLWL1f96fmn70e98pMWvG9ABSeklp6Z05uP46lvEFL2vd+3PaoQZvdRXFMj1jMOS0eBSEObRDmvqfFslL2ReVSpevTNVOL/kK6kNl5SHjN1MOjPabSg/FHOtvZ1IYx6uGSwPh1uTnTbnprEXlRoAp474ZBMy2WCm8yNNe/vMxKG+OFPOnOl0xBkxNgLLh2pv7XCFeIf4pMjgOs9heOiTOloXyTDfMUv9PcRuU7WhH6C6iOFRi4DlZ6pYK0nE776netMiAtWSIfys+wlI2BtoMv6p/3IDM47k5oSopNuz6dwHtEO3Zsa4OiT1Nwy2ToPYGIvR7rf/g9F1fdqqJnc7x3ZFWJGiLoh4Yb+cZJCsMb9KLk8JRnClcwxaj0Zug1KKvJ8iRDb1CKsZjLPNLtRWiE655JZXJUb2tWNCu8KFaj64qPFkUsjI5mXiEDYcPxxQS1dcWrnH+yNfSz6SK7Upt8L0C5ui/rO8qVRetcv3zxA+269xTLG7mGU/4xspMOEsdZzCulTokfQhtsNKoNq0XY62keF89SSkcEYdb/0gy+GeJUlMXZpMHB4Ep3ckgXxKqdCFzfhw2i17g2ZZI57TVZeJFYfp5wXQt7aaFYFPIRCCrkhHLEfsVdhp/1mspxfM1Orq3o=", + "encrypted_b64": "VjN8yABUBOsovylao1dfY22Y+jyUbNfT/kYvIQVefc005RoRSlS5R56Yva997vgYZzeN2zz+usNgn8Vr+F/Kj4LYYzXs+iO1j+SzJ4vvhTGc2eKUMVcbFw5+agTunmYu3M1LbUu1mnYvAgMntlyKwPop5qDz2uS79IHKhPrc6DlD1BApeAOrlM19e5PcRU1nE/DZU8CqOvXbxB+s3C1foPZG8xwloIXPrU9xdqBy5sZOXvF2mVO+aWhiTG0054hCXh4bxdXQcw1fVyjfHLwN3bdpsVFNVSM2L7Bo3cpiProrHmfxKTd00/kC2ck5P/ddMJG55So1isccpBZA8xEcd8Fk7dYkUHKDe9pyh8DNHlRpG5LMDALlGis+KecGmuK+TXM9pluiMIFvSDAClKSe7Nf8soQDb2ZN6VrWBucn2qajN0Fp+uHI9ZqF+kbw2bEZLfKUC8dfEUt0pQwzmVUKFwV8UbACyZTMUv6HY9L/YQfaf+ZZu8CJH+YNzA89RmaCjIaK2m7U8LJP/vCyeq0vHmQi4qeh4Li+M/928a6cwiT2W6eOQ6ffF22sVe6uXpGOw9/2ctVXOndcHjlwvmQPIHM9JWsVkMsHkun/rq8A5PkFFbps5AdSVy7O95vKOI97Uw7l13zIWGq/FOpIIdTckmujyFAa6xJFYg106ijtUqck9mBDsN+iDRlGpAFLnxHP2gzDZtBNUS7f8mATNvjyP5c3swPmVvus7iIj0Dk69jl2NElyUhWVU27AX0sZiDYpH0g2/9fYo4AZJyIQAsxd6tGX6iC3PmRIxFn2HcZqmt99XPWAITbI+wOsB9XsVRw02frg8JaAwwfZZk8KqLv7sNWZoi+1AdXg9b9xvFEHFlZvPXnXiKtpf1crPbeGUjSO5gnc2ifC1z1SLtAdTJTeha3A55DACybQRlfcImovXzRo+RjNYyDrtfUvb5nKHhw65SD4fMnCziZh+s6k9nhQ1Aqxy/FSEKAmX+kpAUnT+T8yHzvP4Eq3ljTW8UM0V3YaFGwdSgy7lbu+ZydeZmelNYnM5rIx5YL6BlamLa0etHRjpsvQNnJ01Vtlrjvt4UqnROcdj/xx7/6EPqINsSbqQ6vaD6/ejUUWu4aENe7pEBK9mClVX3+AlK7tc050+oRt8S4YMPUCpbDJfo/Uq1r95aP19d0HtI9efAMN4vZIL+6HiHfpaHjShVm2Yc9z/VhSIaL1YSX0peDxXd86frA2L/qzg1j7tyyE81eodA88XuSAOItHWfaVhWSs5uV+rF3AbQDsdliUaA+Iv4monPdPwXo4L2YLcob5N48DeT0O2ylIdGthLhAIp37XFvKJ09yUHKFuUV96WaA1jCVwWubGtJggFbdU2elyT+Mg3vzEi3Q5KKQ3y1D4NOp3IClWkvLczuxfXtSS+D9wqrCI5QMSB3U4OsBsPiAQOBV4KMGcV4Wh3CyyyyC5x+KcF3D1K4L0keFWaMFWCH1IFBhe+ivsFxV3aKQGSkQIhJrVdR2ez8KLasrM7mVdD+4l0V/uEgPKjDcA1H69FHdR4ELa/zRMuVxNWUX+pH53b2vek+mkryZp7zVLrQ0SHOOT1LDePLIrNVbMztLDVhmoc0wPI4fDCN5pzn/k/NqWRSWj+9mmAW83ul+vzQJQBbHIUhidLQPNGSOr3iWU7++rikX6S/Tlc4Gj5xztRs5urugYS/R4gTusPGvRFlfZMrIj2ectYQDb2Lrih2VA6QJoDo8k/6hr4poKpl6qvoPCJfuEgtNmuvMKB8Uth5RAtAKpalGHMD24SAoXqCJYUFTvskJEe9Dra5ndy853vOqwVFQDYxgrewJvjGb/mBUJgibuQXj/1rp7h6Gz7YxuWeLiOw6s24r477w0HitHWEzVJll3y0soboe5xDRspnCb3r7s2hc0WvyY9XOHZUeGQupHhyMTiMPYCN7H5Gfyzfr6fDNt4/tlMCy1YhD5X4SUbnbKXnZp0GtZCQzLI6ukMReBm9dVK2cXvkCw/X2ZnC/wB7NPcCE3OwHB6bSrtH+dxCbjCbqA6LRbjE2vxgg6WDIg9NsHLPHROWngK+lmjJrw37pV71ix+4QwIiqQnz3GwuNiTeEb2REijpiJcLwaw2LYaS8+ekq9pvfRudGrroTSP78P8gvUi+lgio8ibLGQbuol8NM/fTC0UsL8+8tjt0WqDj9bbMW8DUhwCLxwVVHI0WYgfEaMJJvvpUvYQ83bwDAb5VsjaZqZqSe2E2h34tSo3uvFRD4xu7zDClCONSIE4noXlGpBXBXx+PN9a8kYarBbL9umzBmgxf36/Kl28nmyHGk7PDC34+jTCphAGYqEgxgMZ79fwpxK8NNV7GrRvhvNpIkT3wSxq19W6i7DhjB4TewSim8jjOYAERz2y6vNsAf/+9Pd8HkCO+pT5gqHEda2lESYc9slt8WeSNU00eXbQok9N45jCDQTBQc5jqKR9HBW9A0gqc5XUTh/XpzKcv1u7PBWzCWuzHb5Osw/kvjPQoReW/10GOJlLyhMu2sr46zGZ/hzx+BDJvYqo/b7NqOKUgg5r9sDPpg/KEdjvq1Vw5EHvb3OLg2ZSqyIY9mkrpbMiEc9aTej54rNhnptRFQv3Z7H5vpQ2wryZ9rC4etq7ErDEk/Yau7MRN2epEHcsGB7PH9tq/8HAKQi1yDe9DNgfYHNTwmCoBQ3QgHpsK3zW01cI55mioUso4NO/ubkSJChjoAewt+G6ls7ML6lYbM0OCsypYFNM9v4OhX/eA2LM9DwWHcAQnesIfoVF9166swUruIcvtMuwaAI0SBOj/cwh5+QfbdVDNUArjbtr9US3z/sTg0Kea3HurB4MnT2izozzU6zMXU9x02+QFymcmRkzpN6JlUQkmte4wcGw+Icz+1RRYc7DZH0rrhX5Ji8ENUduD/tdXneP0Trj3MKS9NX2f0Tcb/cbVllVH4DrENeRSVhaCR1SWx7zWsTpjlOdIqIWjRekYupPumxdMls/0X93nPl20YKRQsqlClsGUSSrfYQ2+kUeuUHGRPN9VQZnTn8X1Zgp1miZF1xmwd2OtI28prOyiQUML5kdH20ptFSJkRCsrzsMXO8NMSZRX5k+m0mq4UmgR8BpvgE/C4f4BZ0Il6xASznTTr3ZX70Ef2LP+s9a82o7Y8Lmc6Ty+QTlD62bdboiZPweuSrcIT0XrPOc1vnrKIATkdiASpbkpasYPE5WQ5u28whv4HSOd1pfdsnWRmPK+We/DT/A19yZAiyYJXr15tkKcuuELgUcW23TLIwx8ADFGDj38Z5ryyrxRcXGB8kiyYRSCDxiarNpzKWTaUWfO+N+JwZ87wVBqqNsDdNogJWVYAhIB7GaimwyiBK7n96IU67+fih+oeZiGyT/Akm6S4BefJF8jl1oNWw1sJ/MI4JrxESnKk0hCNxaG4ibXP3bBIx2/uPYayb6nNdKLW7R0JftG4CcCeKPggMfyVTboIBdlMExnG0pI+Q26paybvXrrxYQzRzh0mOq2F02geGeCRBcVmWP1Y8T4x9EGe75GIfvvgBRmBZIYQ6WAP5tef80x5D2x77UmHAu8d72keh0VBa+6shTHx/LveJSMzmJy143+iY+MXw1zu78GI5haWnUu2yt76+uyBga8ku5RN3LdukV6ajH2L68guAhkQPr/nkJfw2yEGsWhRxT+71pYIyBAjKd9KlRZPDNdKWcGKFRkrm1DRH84G+na50EQjh/rJbi8MU3IIjjpCOES8qtAnXp8+YsD3VI0dz0TkT0m2EE6FEfd5y1RmeyNbxfJweNodugdIZJEPEQAgmVIzz2QGGs3jEcEMCgdS1xLRhqjFMLGcn/lfv3h5yNWFrgoZGprY4mXcC7TdDmMWtq5KsimuQt5SQeUGTcCvcg1yZjFOcCPABM3EwSftjfm4f3TjODRX40S3T7XrvSnk8EdZgejfHRg01X7CU9JoPCHh4FZEglqGMqVZfpn3Qkw83hU9qIHRKKSDl4HShrNqSznGwv1ISzLgRMhKf7Au9ne9TT7YzpewB4IEyZaR8gFC1kttSzo2S+2O5GaJVjZCrPcsP52cxHNckjL7lZyL/iDzK1tvx8TtdtBppXM5pN9xcjh+nIGu/RLb1z1bXGVf9Ge2qoLvnv2nMbFV9GKOWqJ8NEDS9t/LZCYq65q4aDsil9cc6cS1wvuXEiJ/JpKo90rnYy2CYTgOAyxWx95lsMHUqSp7q70oVA3+6A38Mnqzi3/VYJ/7QtCwIyv9nbdcAuAE5GzrD5TUBP1+3WJyar35caRMT4snDuBpy88pi3bJfZKdOBkPz13lYFS4W5CttI4FqRiCtsvxNOZNCyL0+GYpOnBASQHeVOFzG3NRZi+xrYbKFuosLUCARH7l1DfuieBFzWdG2X8UdN/i5vk92cInNqjBvdPeqE5CayLReOpLac66TbfRczEFpuKXsHCtzCqNBZ4Q2l3lNHEyEpWFi4d/xIXoFYgUGQhjDUNiVsiOOnrFVtov9U3XiFWTEhXELAjofQcBiXvE72xpTyqw6XfrUlYWjRqBMj2k5lmT8O4TGYEmwZU1BtyXVjTe5ONyG8KmhTljnUiUxRG7bij40Kf0D3kQq3viz5wMPIP80c5FqJSZJLLb1F26rfvBhg2voF8/mCy+gwGx+DvhaQMGlJYKIXJz6UdspsYDGc5rayT4HNC7wf3+1eDlqGYLl98KMlsoQQjb/ZehJlvJFEeGktHHvQLztj9Bw4Qrs3sSuzfdbBqFfKXIOY5ou4ggFi0hvkLfJH8c0qyCaLUL2+7z4qzr+vfpZ6SEUByuMwt/egJmV4YVdTjyJ/pDic/4QKK4QC3s51jFDVwXqDwbfO3r16KQ5gcOTdX6pevTPFbC+j6igN2UTS2Z9JaaGLKmY1lHOtvYoL+I77CHPfUAoETCCrprGTER6EZFrot4VlkfVzdSGJrO8b0rT5NFfKnGp+RBmgJlfa2CsuqXCLq1Kpth32bxqiuizQ+psOSXcd8Ux//F1oEzcj32I+j+kVj4ZhY6rbsNmR/uF0KQKiANbSJvys+w+PVp/s8brtvDbU/9w/tRa4cx+xaxiS48M2IwH0+qf/twy2ThGLiQhVvaB/AxJ/act+JvQxR+SUDs53OE95cahPZId9NatZelod1mtmJx2Oj8VkkYDZLZ+kxKVzSu3Nzz/NMAsVgs65d4YHtBVlYDJQewbEaHV+rXK2wxiNoWtmFDIYOxhT0sUPyG0HfaPSB6iJ/3WvN8J0iAJxL6HtOwUPPw6y21Y4X/RqC78/nGE75xgjJfd4ZNxEOsfvVTF19U9KoFo3Vw62BLWtobvjMRdPL3sxR2jIOp3dBYsaDE3FojJ2USkZ6c4UGfjzx+f7g3EaKR5GAdINWETrt9yFJnOckceJ9QdauhTACK9FyPiURc7E/Wt0o5Mb+gqgjWH3VDRNpuynMgIdE5G1agT4LGtI7DDRiOYC2wJP0bXlaGXtaAYbLWiQA==", "event_kinds": { "delta": 30072, "manifest": 30070, @@ -57,7 +57,7 @@ "fixture_version": 1, "key_index_hex": "96cef1333dc7ea0ea01ca46e5495e530c14313173c93506382d3c444b5a62743", "manifest_id": "168836eb089b4d0b57f92b3b14eaeb9e875ee9dcd8f59c80f6d027e08f273c12", - "manifest_json": "{\"ver\": 1, \"algo\": \"gzip\", \"chunks\": [{\"id\": \"seedpass-chunk-0000\", \"size\": 800, \"hash\": \"c57f7c84461a7db48c478ce8f62a4a19be933a592436f2bedeb85bd54ca3ed48\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0001\", \"size\": 800, \"hash\": \"a8dbca0d027ec5af9f517bb543ce3e7f2e511b76ca8fd6656d0054fde796815b\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0002\", \"size\": 800, \"hash\": \"7672fd2923ba8ba868282176385d45400d4787fae28a9430df3c3df9253b24ef\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0003\", \"size\": 800, \"hash\": \"9fdc9b622219d3e2874e4eee96ba134d682a7f720478ba770462c5ade273510c\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0004\", \"size\": 800, \"hash\": \"dbf3068bf08dec2be41992e3163a5ded6bd7b10dd7f6dbc6ffd9fd0bc992f70a\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0005\", \"size\": 90, \"hash\": \"76fd3739a3e002e1921a47e3c0e3b947c2c3f45304deda86745279a5ef24d6c2\", \"event_id\": null}], \"delta_since\": 1700000000, \"nonce\": \"PAqvfNIUFpEog1t93a3M4A==\", \"index0\": null}", + "manifest_json": "{\"ver\": 1, \"algo\": \"gzip\", \"chunks\": [{\"id\": \"seedpass-chunk-0000\", \"size\": 800, \"hash\": \"11325bc9b81def0f846a3e13ef1bf26fba08cc37828f3b3b1eac17f685ef2dce\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0001\", \"size\": 800, \"hash\": \"412b97ba88ab3c579d25d677181928a7a1925a2d401a2380e56fb0065b376cbd\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0002\", \"size\": 800, \"hash\": \"01728a50deedd31079ac2677e513285c5d6eaac59ac030eaa7dac7e3e85180dd\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0003\", \"size\": 800, \"hash\": \"0ecbf450f3e44d9db7024243e18786107e7c37a17146d046f29d054ad13ab26d\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0004\", \"size\": 800, \"hash\": \"0927efbc911129ea2b4fa7b9491f0fa62da933cc8d883ac7b659f111b9f0f49b\", \"event_id\": null}, {\"id\": \"seedpass-chunk-0005\", \"size\": 134, \"hash\": \"3f19fbd0b5b1e132044b996030394b13f1cb3b2a3ddcc925b010d96fcab15826\", \"event_id\": null}], \"delta_since\": 1700000000, \"nonce\": \"PAqvfNIUFpEog1t93a3M4A==\", \"index0\": null}", "manifest_nonce_b64": "PAqvfNIUFpEog1t93a3M4A==", "mnemonic_id": "abandon12", "unpinned_chunk_count": 6 diff --git a/js/packages/test-vectors/fixtures/portable_backup.json b/js/packages/test-vectors/fixtures/portable_backup.json index b479a324..bb7615cb 100644 --- a/js/packages/test-vectors/fixtures/portable_backup.json +++ b/js/packages/test-vectors/fixtures/portable_backup.json @@ -1,17 +1,20 @@ { - "canonical_json_sha256": "97a10f8aa357ffc08d086944cb8da2e25fdf779125894556b4ae8c7f483ede1e", + "canonical_json_sha256": "301acbaa8fc1f708ed52530a45c4c29bf9d5ac679046cba36b4482a94e531352", "description": "Portable backup wrapper (portable_backup.py, format_version 1). payload = V3 AES-GCM over canonical JSON of the index, key = index key of the parent seed; checksum = sha256(canonical_json(index)). Nonce/created_at pinned (fixture-only).", "encrypted_wrapper": { - "checksum": "97a10f8aa357ffc08d086944cb8da2e25fdf779125894556b4ae8c7f483ede1e", + "checksum": "301acbaa8fc1f708ed52530a45c4c29bf9d5ac679046cba36b4482a94e531352", "cipher": "aes-gcm", "created_at": 1700000000, "encryption_mode": "seed-only", "fingerprint": "C557EEC878DFD852", "format_version": 1, - "payload": "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" + "payload": "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" }, "fixture_version": 1, "index": { + "_sync_meta": { + "next_index": 10 + }, "entries": { "0": { "archived": false, @@ -175,12 +178,12 @@ }, "mnemonic_id": "abandon12", "plaintext_wrapper": { - "checksum": "97a10f8aa357ffc08d086944cb8da2e25fdf779125894556b4ae8c7f483ede1e", + "checksum": "301acbaa8fc1f708ed52530a45c4c29bf9d5ac679046cba36b4482a94e531352", "cipher": "none", "created_at": 1700000000, "encryption_mode": "none", "fingerprint": "C557EEC878DFD852", "format_version": 1, - "payload": "eyJlbnRyaWVzIjp7IjAiOnsiYXJjaGl2ZWQiOmZhbHNlLCJjdXN0b21fZmllbGRzIjpbXSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImdlbl92ZXJzaW9uIjoyLCJraW5kIjoicGFzc3dvcmQiLCJsYWJlbCI6ImV4YW1wbGUuY29tIiwibGVuZ3RoIjoxNiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiJwYXNzd29yZCBub3RlIiwidGFncyI6WyJ3ZWIiXSwidHlwZSI6InBhc3N3b3JkIiwidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInVzZXJuYW1lIjoiYWxpY2UifSwiMSI6eyJhcmNoaXZlZCI6ZmFsc2UsImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkZXRlcm1pbmlzdGljIjp0cnVlLCJkaWdpdHMiOjYsImluZGV4IjowLCJraW5kIjoidG90cCIsImxhYmVsIjoiZXhhbXBsZS10b3RwIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJwZXJpb2QiOjMwLCJ0YWdzIjpbIm90cCJdLCJ0eXBlIjoidG90cCJ9LCIyIjp7ImFyY2hpdmVkIjpmYWxzZSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRldGVybWluaXN0aWMiOmZhbHNlLCJkaWdpdHMiOjgsImtpbmQiOiJ0b3RwIiwibGFiZWwiOiJpbXBvcnRlZC10b3RwIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJwZXJpb2QiOjQ1LCJzZWNyZXQiOiJKQlNXWTNEUEVIUEszUFhQIiwidGFncyI6W10sInR5cGUiOiJ0b3RwIn0sIjMiOnsiYXJjaGl2ZWQiOmZhbHNlLCJjdXN0b21fZmllbGRzIjpbXSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImluZGV4IjozLCJraW5kIjoic3NoIiwibGFiZWwiOiJleGFtcGxlLXNzaCIsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoic3NoIG5vdGUiLCJ0YWdzIjpbXSwidHlwZSI6InNzaCJ9LCI0Ijp7ImFyY2hpdmVkIjpmYWxzZSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImluZGV4Ijo0LCJraW5kIjoibm9zdHIiLCJsYWJlbCI6ImV4YW1wbGUtbm9zdHIiLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6IiIsInRhZ3MiOltdLCJ0eXBlIjoibm9zdHIifSwiNSI6eyJhcmNoaXZlZCI6ZmFsc2UsImN1c3RvbV9maWVsZHMiOltdLCJkYXRlX2FkZGVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRhdGVfbW9kaWZpZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwia2V5IjoidG9rZW4iLCJraW5kIjoia2V5X3ZhbHVlIiwibGFiZWwiOiJhcGktdG9rZW4iLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6IiIsInRhZ3MiOlsiYXBpIl0sInR5cGUiOiJrZXlfdmFsdWUiLCJ2YWx1ZSI6ImFiYzEyMyJ9LCI2Ijp7ImFyY2hpdmVkIjpmYWxzZSwiY29udGVudCI6ImhlbGxvIGZpeHR1cmUgd29ybGQiLCJjdXN0b21fZmllbGRzIjpbXSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImZpbGVfdHlwZSI6InR4dCIsImtpbmQiOiJkb2N1bWVudCIsImxhYmVsIjoiZXhhbXBsZS1kb2MiLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6IiIsInRhZ3MiOltdLCJ0eXBlIjoiZG9jdW1lbnQifSwiNyI6eyJhcmNoaXZlZCI6ZmFsc2UsImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJpbmRleCI6Nywia2luZCI6InNlZWQiLCJsYWJlbCI6ImV4YW1wbGUtc2VlZCIsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoiIiwidGFncyI6W10sInR5cGUiOiJzZWVkIiwid29yZF9jb3VudCI6MjR9LCI4Ijp7ImFyY2hpdmVkIjpmYWxzZSwiY3VzdG9tX2ZpZWxkcyI6W10sImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJmaW5nZXJwcmludCI6IkRDOTgyRkQ0NEUzMkY1OUEiLCJpbmRleCI6OCwia2luZCI6Im1hbmFnZWRfYWNjb3VudCIsImxhYmVsIjoiZXhhbXBsZS1tYW5hZ2VkIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJ0YWdzIjpbXSwidHlwZSI6Im1hbmFnZWRfYWNjb3VudCIsIndvcmRfY291bnQiOjEyfSwiOSI6eyJhcmNoaXZlZCI6ZmFsc2UsImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJpbmRleCI6OSwia2V5X3R5cGUiOiJlZDI1NTE5Iiwia2luZCI6InBncCIsImxhYmVsIjoiZXhhbXBsZS1wZ3AiLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6IiIsInRhZ3MiOltdLCJ0eXBlIjoicGdwIiwidXNlcl9pZCI6ImZpeHR1cmVAZXhhbXBsZS5jb20ifX0sInNjaGVtYV92ZXJzaW9uIjo0fQ==" + "payload": "eyJfc3luY19tZXRhIjp7Im5leHRfaW5kZXgiOjEwfSwiZW50cmllcyI6eyIwIjp7ImFyY2hpdmVkIjpmYWxzZSwiY3VzdG9tX2ZpZWxkcyI6W10sImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJnZW5fdmVyc2lvbiI6Miwia2luZCI6InBhc3N3b3JkIiwibGFiZWwiOiJleGFtcGxlLmNvbSIsImxlbmd0aCI6MTYsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoicGFzc3dvcmQgbm90ZSIsInRhZ3MiOlsid2ViIl0sInR5cGUiOiJwYXNzd29yZCIsInVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6ImFsaWNlIn0sIjEiOnsiYXJjaGl2ZWQiOmZhbHNlLCJkYXRlX2FkZGVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRhdGVfbW9kaWZpZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGV0ZXJtaW5pc3RpYyI6dHJ1ZSwiZGlnaXRzIjo2LCJpbmRleCI6MCwia2luZCI6InRvdHAiLCJsYWJlbCI6ImV4YW1wbGUtdG90cCIsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoiIiwicGVyaW9kIjozMCwidGFncyI6WyJvdHAiXSwidHlwZSI6InRvdHAifSwiMiI6eyJhcmNoaXZlZCI6ZmFsc2UsImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkZXRlcm1pbmlzdGljIjpmYWxzZSwiZGlnaXRzIjo4LCJraW5kIjoidG90cCIsImxhYmVsIjoiaW1wb3J0ZWQtdG90cCIsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoiIiwicGVyaW9kIjo0NSwic2VjcmV0IjoiSkJTV1kzRFBFSFBLM1BYUCIsInRhZ3MiOltdLCJ0eXBlIjoidG90cCJ9LCIzIjp7ImFyY2hpdmVkIjpmYWxzZSwiY3VzdG9tX2ZpZWxkcyI6W10sImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJpbmRleCI6Mywia2luZCI6InNzaCIsImxhYmVsIjoiZXhhbXBsZS1zc2giLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6InNzaCBub3RlIiwidGFncyI6W10sInR5cGUiOiJzc2gifSwiNCI6eyJhcmNoaXZlZCI6ZmFsc2UsImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJpbmRleCI6NCwia2luZCI6Im5vc3RyIiwibGFiZWwiOiJleGFtcGxlLW5vc3RyIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJ0YWdzIjpbXSwidHlwZSI6Im5vc3RyIn0sIjUiOnsiYXJjaGl2ZWQiOmZhbHNlLCJjdXN0b21fZmllbGRzIjpbXSwiZGF0ZV9hZGRlZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJkYXRlX21vZGlmaWVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImtleSI6InRva2VuIiwia2luZCI6ImtleV92YWx1ZSIsImxhYmVsIjoiYXBpLXRva2VuIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJ0YWdzIjpbImFwaSJdLCJ0eXBlIjoia2V5X3ZhbHVlIiwidmFsdWUiOiJhYmMxMjMifSwiNiI6eyJhcmNoaXZlZCI6ZmFsc2UsImNvbnRlbnQiOiJoZWxsbyBmaXh0dXJlIHdvcmxkIiwiY3VzdG9tX2ZpZWxkcyI6W10sImRhdGVfYWRkZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZGF0ZV9tb2RpZmllZCI6IjIwMjMtMTEtMTRUMjI6MTM6MjArMDA6MDAiLCJmaWxlX3R5cGUiOiJ0eHQiLCJraW5kIjoiZG9jdW1lbnQiLCJsYWJlbCI6ImV4YW1wbGUtZG9jIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJ0YWdzIjpbXSwidHlwZSI6ImRvY3VtZW50In0sIjciOnsiYXJjaGl2ZWQiOmZhbHNlLCJkYXRlX2FkZGVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRhdGVfbW9kaWZpZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiaW5kZXgiOjcsImtpbmQiOiJzZWVkIiwibGFiZWwiOiJleGFtcGxlLXNlZWQiLCJsaW5rcyI6W10sIm1vZGlmaWVkX3RzIjoxNzAwMDAwMDAwLCJub3RlcyI6IiIsInRhZ3MiOltdLCJ0eXBlIjoic2VlZCIsIndvcmRfY291bnQiOjI0fSwiOCI6eyJhcmNoaXZlZCI6ZmFsc2UsImN1c3RvbV9maWVsZHMiOltdLCJkYXRlX2FkZGVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRhdGVfbW9kaWZpZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiZmluZ2VycHJpbnQiOiJEQzk4MkZENDRFMzJGNTlBIiwiaW5kZXgiOjgsImtpbmQiOiJtYW5hZ2VkX2FjY291bnQiLCJsYWJlbCI6ImV4YW1wbGUtbWFuYWdlZCIsImxpbmtzIjpbXSwibW9kaWZpZWRfdHMiOjE3MDAwMDAwMDAsIm5vdGVzIjoiIiwidGFncyI6W10sInR5cGUiOiJtYW5hZ2VkX2FjY291bnQiLCJ3b3JkX2NvdW50IjoxMn0sIjkiOnsiYXJjaGl2ZWQiOmZhbHNlLCJkYXRlX2FkZGVkIjoiMjAyMy0xMS0xNFQyMjoxMzoyMCswMDowMCIsImRhdGVfbW9kaWZpZWQiOiIyMDIzLTExLTE0VDIyOjEzOjIwKzAwOjAwIiwiaW5kZXgiOjksImtleV90eXBlIjoiZWQyNTUxOSIsImtpbmQiOiJwZ3AiLCJsYWJlbCI6ImV4YW1wbGUtcGdwIiwibGlua3MiOltdLCJtb2RpZmllZF90cyI6MTcwMDAwMDAwMCwibm90ZXMiOiIiLCJ0YWdzIjpbXSwidHlwZSI6InBncCIsInVzZXJfaWQiOiJmaXh0dXJlQGV4YW1wbGUuY29tIn19LCJzY2hlbWFfdmVyc2lvbiI6NH0=" } } diff --git a/js/packages/test-vectors/fixtures/sync_merge.json b/js/packages/test-vectors/fixtures/sync_merge.json index c1dd94ca..1937e6ad 100644 --- a/js/packages/test-vectors/fixtures/sync_merge.json +++ b/js/packages/test-vectors/fixtures/sync_merge.json @@ -34,6 +34,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000010, + "next_index": 1, "source_count": 1, "sources": [ "tag-newer" @@ -114,6 +115,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000010, + "next_index": 1, "source_count": 1, "sources": [ "tag-older" @@ -202,6 +204,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000000, + "next_index": 1, "source_count": 1, "sources": [ "tag-equal" @@ -300,6 +303,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000005, + "next_index": 2, "source_count": 1, "sources": [ "tag-delete" @@ -386,6 +390,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000020, + "next_index": 1, "source_count": 1, "sources": [ "tag-revive" @@ -465,6 +470,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000020, + "next_index": 1, "source_count": 1, "sources": [ "tag-stale" @@ -539,6 +545,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000009, + "next_index": 1, "source_count": 1, "sources": [ "tag-tt" @@ -601,6 +608,7 @@ "merged": { "_sync_meta": { "last_merge_ts": 1700000000, + "next_index": 4, "source_count": 1, "sources": [ "tag-adopt" diff --git a/js/packages/test-vectors/fixtures/vault_v3_payload.json b/js/packages/test-vectors/fixtures/vault_v3_payload.json index 97000361..9bc99907 100644 --- a/js/packages/test-vectors/fixtures/vault_v3_payload.json +++ b/js/packages/test-vectors/fixtures/vault_v3_payload.json @@ -3,7 +3,7 @@ "fixture_version": 1, "mnemonic_id": "abandon12", "nonce_hex": "c8005404eb28bf295aa3575f", - "payload_b64": "VjN8yABUBOsovylao1dfY22Y+jyucdrP9HwxZksfJP00vjARSES7X9CGz/sCp7ZcIm3Ok3+n478l25p90hmLwZTTO02lv3C3leTqTt680X7TtqbHdEB1RAxkan+TkA18n4UCOw7z3C17AT0qoV3Drvgz5qLhyvTrrMOPxqa32mIUgkVpYAu75KBhS4PMR0FNE/KdEpTvR/De1B+Xk3gb66IfpEsqq5TArksXd7955thFWJVvm1m1amJyUEwk95JSThwVq5SENnISGG7RH7s3z/Ah4hoQCzM/IrN1+tt2Sqg5BHSpemsg2vMSwYlobqggf8P95yYfiIBZ6mkWtA8OfMtm7dYkQDvxOpciy4XDX1BtV9rkFgLnSmptePwMhuHoCVsnpkq0PKttBHFA0eieup6wvI9AODZ60Vi/Saol1oyjNQwmvqiM8JqPwkbr2bEZLfOIMddPAVtktQJV0BtBRAdmU4UwkfufUOSHYYK8fxvJefJU/sq5H/AN2iUsUXaSnJaYnj+Dr5pV/ouYeq0tUCt2p/Shrf3+YZQlovnTkGCLGcLaBqXTPW2uAbf+G5HAgpr1MZwEFhIOWjt8lGQPIHFqYCtC6IcFiOn95oZYnqofGrUpvERLXjLO+8KFd902Kl2ymC6MWmTAR68ab5WR1TzrhFBBp1lOc1sOuTLiXb9wnS0T/JquX1QRplZLnxHP2gzDZsRMVzTY5WgSNvjye5c3qQWvftGs7iIjjTdUt20zSwonFErRCl7AXVkJmiUmTwt4por69NYBLDFMUZAFv82n+jC1Mk5Ixh23SYMXn9ptXM7PdHKDr1r7UNrnRBM33Zzh752A3QzfAlYIorD4usWFgz+lG8Xw97Ef/QVCaRsge3TUlat/MARldvbCCWSB7RiFmWbzyQVIP8MHXoTX0fSdvtSRBRbQUFv2ImotG3E8vgPObiv65LtmPJf6HkhosGX0VIKLgGJj4Mzi63BN0Fu/+/FEHIomX+tlQAuU/HNsWmGIogvqylKT/Bd7AyYaUw9TDg6hlbnoZDpALziNL4m3m74b5867RBPqLfpR8nh9rsPEBWMqgw4x7y7x21q3VPcNn/IXprDPbaAXsxPYG8SJDbXej0cY3MnAfKigVVSSiSgeCjuTmb7uc1JO6pR9/QQYMqFD4uPJKsD7xAn//6P399EttsAKLAEn4KYNfafIsXnZaGvCiXO2Y5sqrR1Sb+OwZmi69ZnxXd86fu06BfqxzAyppwSe8wyCdA9BUs6AOsoVGr7eh3i4of1k6h7YcRGwXnKUaA+I4ofG3aMKvjluaTlPK7b5NZ0Tay4Bi2oGLTZDeUYQrG2LRa7RhsCkDLFsXXV6W+R02GANX+PWtKNvQPMfjbAlGOwrz/PHjxI4N6831Vv+UPN1KiJVmOLA7/xPRMSC+jEe6+TNmk5dQXg7J8B6cXNec1Q8c5GTXJTqjHSVnD6J1vGGBWD+OdanxrgAN+lMCGVEPhhcvm64UkVxaKQLGwpBxcyAIQvdj7vOZuDM7mVfQ69l0FqrQVebx2BQgyDDUTNctA2OrX0J3RhPQ0X88DNvdieZ6uu+r10U4x0H7E9XUOGLmf3TOKQtIkb23oyVA03pZlA1M5fTGM55wBmtspHFRz+hzuv+bjw1oF+tzww3SvWBFFHYayzcGGj+mjaZ/+uuln/qW+TpWYGhtFmuFIl046ZdGpwA8k7XIyWhcz+pWaFR8dJ9JGWf2qDik3BOl0MvXY0+/9FFq/NY4wqopIPCG8CnsKhv3oE7IOEWls44xAClHV2tMD24SApQ638LCWTvyT9IUdDpKsueg4V1oP73TE5FIAA3al5Hpmb/mBVUjE+7EiywmdY/1OSkg99sQ+KZRgDHicmwpupxWG0cDE/rK052giUqdIe71iR89ijZm/ywsSVvDarNtWuPdTfrXtpXlyEfosPaTJ+ToRr3yOr6R3w4p7AxaXvibRvoUIeQCHfVVXZ32209EA7BKKiuIQugi8dPO3cVsC7xqTjm0WC2B7tCcD03OwHQ9465p3KM1SvwVofcvqxQn1XuhUsoRAIw5NkLBvHTdSiibqct2N63wuAOohvxlIljcWKSkxWNi60mT/sZlws/jcmHQLxhvm7yaS1yOwj46LiE/Zqov4DbEKcZ/UKH0vx8sJ8yfKGAfuRDuZ10LjKuUPfOo6QwtV+qDGwKaYq2C1p8T/QlAQLKy2YxaQLdc9j9r1uzMuvxwDAb5VshJYGUvDG2E2h34tSo3I+KEHsz6prpClCONSAStCFQnEJbXG6M9Nl/KptbIvkPPsb0kxv8hLP66u1e2HmyHGlmMl72t62sScgGRs7dsxgOda9N0ZMas50MsUiG6APGt9VAg1zkt29G+izPrDB6Ca1GzxImifYAKlOjj+CZ6VCo9NjM/3oGXetM7QqZGtDSjUaSeNgvp9m/WMUuwfXZTOd8Y8scRXtVBQ80jr6R9HBA6jcyusNGQDVsAaGWJOVl/+gKk33qjm7lAM4zuPjPQoYSGr14GqQ6dygT4z5qmeCDarY8lLZabbREoez7NO3FAxAi45x6PII/UzpvluEUgdRLv6WDYwyITaida+P9s9uF3AQodQ2z95rdlmpjIh1hls3F/Phl6VKdNNjY4elo4i2MVgaeI6uKb8iK9VnGoQwWIEV9u+8XEKgKg3mOsTF6f83MVB+F8AwdQAPlmvD/cU1cI5ww3sBlocJkhZvoYpChjMFMgZWb5U46ML6lJbw3ODpsjatNM9v4ZxuWLV7ffJ+aHCRFVRn/I+AVbKB0gZ5X5qtK+5VomvQL7y1Zjr5ehYWQf6VFHIVY7HOv874ghGi6G00Scb2316xIImT0hxAzzwryZTBAwkiuQGfpJyAvmsotcVobg2Rd52EH3Okc0eZXIZ45B5r3pKhLxYisCsUNujGDNC2bQAmkyXEJW5QJ2+VHPOaJMUhkb28OvVcqVzUwMHkrWWZw3zBM71YaOezdHzZSu4urdazodtEgvFP32TSx2UhhFUInwGYnXkHZ75gSwekWMaBcKgzC8lJehTWtIlpKp1miZhEw2w91fIFwuZ7a/H0Tfe8hK2ionMFCNlRSorKKeD33Z8aDR0tWogJ1qZ8mgx0PwbdAtWhWpVBfN0rgGTb2IQDndW7kAe2bM8N8O4Sqx48J14HHjsodpD60b9rCiZGkI7TucMq1G6uJMCTKx+5VC0VuKypbkNf8KfFFGEI7ns47wo+5e554b8glcU3We6DB6h79SBorGUjxNsD1/5dOKcuuELpVIXi+SbEwgdgZFmfg0IQusASBxRcXGEIq4mlfHGW9y6rNp2v0COlaM+/Ls4Uf5aYZULiG4GEJ5k8wBsUtCh7GaGrlmXZG7E5oLUWjv7Gj4s+nuSz2/E9vsXpUKfME8TNYrdX2n6x9Ko4LvQECzPF2wWEtA1x5OiWiLAo5y4vifZyL+nFRArW5AwML8RMHdTeKBUdZO24HN9VWeVgVyXKwwo6P0KpEwr2zt75SSDd5l1Wvu3FuyheEdkoAJRzpchl6QIN4EFztpzJYrvITVzkQZIsnZnfrp/3twAYagluvQGHQqYE4sxLslB4O+aVNBTA6UaPQGsWlf2g2iarM+IyClnbrvCc0w6GtHqWarb68/28jPMgi5QwmI+ukLNuvNWL4vkrCwwpA+r2vJu0ywW60TBsvALzgubgiFBjaZ8KrI9qNfoGUamCwdBKJhzZd84O8k8k7VUGnt/cdoNYAjZo5n/zjDRU6pBnHt8Ow5GSFZkVp03UYyWuCXP4KK95U/xmeyIv9Vp5Kd8A/lMoDX2XiaggmVIzz20KAqWDIJBxGgYr6waBgvDoUBE0n/lfvgxAcdDUu/cUV4Ol8wEcC7yVTitai+9Hi0zay4MKIclLPI3eE1kCpnEOeBNoBMTVxHb4ee2sP3QOBWFGzhXSEunXkW3Y/FbBhZTzHWAYzO6mW/pEMAmhkNIEwjLGcq1gx5ymV7EJ4w09iLXRWKSDl9Wqbvsmf32C9rA0vkO4JOQGHrV7zyf1FdaYxqcYB4IN+JOY7hRzr14EVjMzIpwX8FPEQyNSreqhBo2UrHNV1hbLvLn3Xkjyxq9fb83cc9l8lXJkmcdBChhezE3rhFemwmkPLI0ftCf26sLWB9ieHP1dnGpak8PBeEi69tfDXbsX+r+hTS46O4NNraTdh0ojYso/ZtLot3pGMkjDdTBmChwmg945sJl8oSJLA70oXVDDoRVQOlqqukfc5WuDuniwIyv86Y6lZ6EQ7AyLDrHAfUV27cpyar3wdO1IM5M3Cg1snpsJ3k/saeplkBkPz1yRWfHtFsGQiRMU5AzfD4f5XOeg/xtZsWsIHylVUBizBO2LLy9UQ5e5xYbCXqptbCGJUXeUeP6D1LkQzQdmmL6gBB+ipvENccIuJ62QqCfKvA5Chh+EaccaDJPmcZuVTz0UPubrnHDV4DMdYZY49lHNdAG2UtXty8d3/TztRJ3pLDV7MX9+Xs3PEzPoJ8pv0XmaXe2zI4wV9DD9pSMAUQPhKwwZpyKAQXfrUl8ztAKNdwzR83DiEcsqSYlOwZWJr4HnF8VOvYfjbo4jxE0akHUx+ESCPiDIcYLNHmxwoxrTqqWxDIuU0cdQwLyJXJPHiWi2ofvllgCygQo6lSGD1jjowS5wRE8O/Jfn3HPe4FJcrq4DGe43fzSgCNGfCamL2JXw/DJ7f59KchtoATFC2K6MalOhHJNP82yLtWrzvjd4Xrk6lmI3ri9xOEvBHM2NiDoYU8hgVm1hjuOOQT4I2sSLWL1f96fmn70e98pMWvG9ABSeklp6Z05uP46lvEFL2vd+3PaoQZvdRXFMj1jMOS0eBSEObRDmvqfFslL2ReVSpevTNVOL/kK6kNl5SHjN1MOjPabSg/FHOtvZ1IYx6uGSwPh1uTnTbnprEXlRoAp474ZBMy2WCm8yNNe/vMxKG+OFPOnOl0xBkxNgLLh2pv7XCFeIf4pMjgOs9heOiTOloXyTDfMUv9PcRuU7WhH6C6iOFRi4DlZ6pYK0nE776netMiAtWSIfys+wlI2BtoMv6p/3IDM47k5oSopNuz6dwHtEO3Zsa4OiT1Nwy2ToPYGIvR7rf/g9F1fdqqJnc7x3ZFWJGiLoh4Yb+cZJCsMb9KLk8JRnClcwxaj0Zug1KKvJ8iRDb1CKsZjLPNLtRWiE655JZXJUb2tWNCu8KFaj64qPFkUsjI5mXiEDYcPxxQS1dcWrnH+yNfSz6SK7Upt8L0C5ui/rO8qVRetcv3zxA+269xTLG7mGU/4xspMOEsdZzCulTokfQhtsNKoNq0XY62keF89SSkcEYdb/0gy+GeJUlMXZpMHB4Ep3ckgXxKqdCFzfhw2i17g2ZZI57TVZeJFYfp5wXQt7aaFYFPIRCCrkhHLEfsVdhp/1mspxfM1Orq3o=", + "payload_b64": "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", "plaintext_canonical_json": "entries_index.json#entries (indent=2, sort_keys)", - "plaintext_sha256": "041cbba36182e89980e0770f9654df2104600cd37e0518e49ee84a8e4ee2fbb3" + "plaintext_sha256": "ef590217009cee8b16455ee3513dfade88b68105089e433db585b168d8e0f675" } diff --git a/src/seedpass/core/entry_management.py b/src/seedpass/core/entry_management.py index 6984fdef..c59ad73b 100644 --- a/src/seedpass/core/entry_management.py +++ b/src/seedpass/core/entry_management.py @@ -187,6 +187,7 @@ class EntryManager: def _save_index(self, data: Dict[str, Any]) -> None: try: + self._bump_next_index_watermark(data) self.vault.save_index(data) self._index_cache = data logger.debug("Index saved successfully.") @@ -194,6 +195,61 @@ class EntryManager: logger.error(f"Failed to save index: {e}") raise + @staticmethod + def _allocation_floor(data: Dict[str, Any]) -> int: + """One past the highest id ever seen: live entries, tombstones, and the + persisted watermark. Scanning live entries alone is not enough — an + entry id doubles as the BIP-85 derivation index for managed_account + (and seed) entries, and sync deletion removes entries via tombstones, + so ``max(live) + 1`` could reissue a deleted #184 to a NEW entry that + then re-derives the departed identity's exact child seed and npub. + Mirrors nextIndex in the TypeScript core (vault/entryOps.ts).""" + + def _max_numeric_key(record: Any) -> int: + # Same key shape the TS side accepts (`^(0|[1-9][0-9]*)$`, within + # JS safe-integer range): ASCII digits only — str.isdigit() would + # accept Unicode digits that Number() rejects — no leading zeros, + # and nothing above 2**53 - 1, so both implementations compute the + # same floor from the same payload. + best = -1 + if isinstance(record, dict): + for key in record: + text = str(key) + if not (text.isascii() and text.isdigit()): + continue + if len(text) > 1 and text[0] == "0": + continue + value = int(text) + if value > 2**53 - 1: + continue + if value > best: + best = value + return best + + entries = data.get("entries") + meta = data.get("_sync_meta") + meta = meta if isinstance(meta, dict) else {} + try: + stored = int(meta.get("next_index", 0) or 0) + except (TypeError, ValueError): + stored = 0 + return max( + _max_numeric_key(entries) + 1, + _max_numeric_key(meta.get("tombstones")) + 1, + stored if stored > 0 else 0, + 0, + ) + + def _bump_next_index_watermark(self, data: Dict[str, Any]) -> None: + """Persist the allocation watermark so it survives deletion. Kept in + ``_sync_meta`` because both implementations already round-trip that + block (it carries the tombstones), so no schema bump is needed.""" + meta = data.get("_sync_meta") + if not isinstance(meta, dict): + meta = {} + data["_sync_meta"] = meta + meta["next_index"] = self._allocation_floor(data) + def _entry_kind(self, entry: dict[str, Any]) -> str: return str( entry.get("kind", entry.get("type", EntryType.PASSWORD.value)) @@ -239,11 +295,9 @@ class EntryManager: """ try: data = self._load_index() - if "entries" in data and isinstance(data["entries"], dict): - indices = [int(idx) for idx in data["entries"].keys()] - next_index = max(indices) + 1 if indices else 0 - else: - next_index = 0 + # Watermark-aware: never reuse an id that any past entry held, + # even one deleted through sync (see _allocation_floor). + next_index = self._allocation_floor(data) logger.debug(f"Next index determined: {next_index}") return next_index except Exception as e: diff --git a/src/seedpass/core/sync_conflict.py b/src/seedpass/core/sync_conflict.py index 1539cee9..34e0f56b 100644 --- a/src/seedpass/core/sync_conflict.py +++ b/src/seedpass/core/sync_conflict.py @@ -124,6 +124,26 @@ def _max_tombstone_ts(tombstones: dict[str, dict[str, Any]]) -> int: return max_ts +def _max_numeric_key(record: Any) -> int: + """Highest numeric key, or -1 when none. Key shape matches the TypeScript + side exactly (``^(0|[1-9][0-9]*)$`` within JS safe-integer range) so both + implementations compute the same allocation watermark from one payload.""" + best = -1 + if isinstance(record, dict): + for key in record: + text = str(key) + if not (text.isascii() and text.isdigit()): + continue + if len(text) > 1 and text[0] == "0": + continue + value = int(text) + if value > 2**53 - 1: + continue + if value > best: + best = value + return best + + def _prefer_entry(current: dict[str, Any], incoming: dict[str, Any]) -> bool: """Return True when incoming should replace current deterministically. @@ -311,10 +331,24 @@ def merge_index_payloads( _max_entry_ts(cur_entries), _max_tombstone_ts(tombstones), ) + # Allocation watermark: never below either side's watermark, nor below one + # past any merged entry or tombstone id. Without it, merging a deletion + # lets max(live)+1 reissue the deleted id — and an entry id is a permanent + # BIP-85 derivation coordinate, so reissuing #184 hands a NEW entry the + # departed identity's exact derived secrets. Must stay byte-identical to + # the TypeScript computation in sync/merge.ts. + next_index_watermark = max( + _safe_int(meta.get("next_index", 0), default=0), + _safe_int(inc_meta.get("next_index", 0), default=0), + _max_numeric_key(cur_entries) + 1, + _max_numeric_key(tombstones) + 1, + 0, + ) meta.update( { "strategy": "modified_ts_hash_tombstone_v2", "last_merge_ts": last_merge_ts, + "next_index": next_index_watermark, "source_count": len(sources), "sources": sources[-32:], "tombstones": tombstones, diff --git a/src/tests/test_delta_merge.py b/src/tests/test_delta_merge.py index c7493f63..9524ee1e 100644 --- a/src/tests/test_delta_merge.py +++ b/src/tests/test_delta_merge.py @@ -473,3 +473,52 @@ def test_merge_last_merge_ts_order_independent(): merge_index_payloads(base, b, source_tag="b"), a, source_tag="a" ) assert ab["_sync_meta"]["last_merge_ts"] == ba["_sync_meta"]["last_merge_ts"] == 100 + + +def test_merged_deletion_does_not_recycle_entry_index(): + """An entry id is a permanent BIP-85 derivation coordinate: after a + deletion arrives via merge, allocation must NOT reuse the tombstoned id, + or a new entry would re-derive the departed identity's exact secrets.""" + with TemporaryDirectory() as tmpdir: + vault, enc_mgr = create_vault(Path(tmpdir)) + cfg_mgr = ConfigManager(vault, Path(tmpdir)) + backup_mgr = BackupManager(Path(tmpdir), cfg_mgr) + entry_mgr = EntryManager(vault, backup_mgr) + + entry_mgr.add_entry("web-a", 12) # id 0 + entry_mgr.add_entry("web-b", 12) # id 1 + entry_mgr.add_entry("web-c", 12) # id 2 + + # A remote device deleted entry 2. + incoming = { + "schema_version": 4, + "entries": { + "2": {"kind": "password", "label": "web-c", "modified_ts": 10**10, "_deleted": True}, + }, + } + merged = merge_index_payloads( + vault.load_index(), incoming, source_tag="remote" + ) + assert "2" not in merged["entries"] + assert merged["_sync_meta"]["next_index"] == 3 + vault.save_index(merged) + + # Fresh manager (no cache): the deleted id must not come back. + entry_mgr2 = EntryManager(vault, backup_mgr) + assert entry_mgr2.get_next_index() == 3 + new_id = entry_mgr2.add_entry("web-d", 12) + assert int(new_id) == 3 + + +def test_watermark_survives_merge_even_after_tombstones_would_age_out(): + """The watermark, not tombstone retention, is what carries the guarantee: + a vault whose meta says next_index=500 keeps allocating past 500 even when + no entry or tombstone that high survives.""" + current = { + "schema_version": 4, + "entries": {"0": {"kind": "password", "label": "a", "modified_ts": 50}}, + "_sync_meta": {"next_index": 500}, + } + incoming = {"schema_version": 4, "entries": {}} + merged = merge_index_payloads(current, incoming, source_tag="x") + assert merged["_sync_meta"]["next_index"] == 500