mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-04 20:02:05 +00:00
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 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tkj4rzKBA3uhfgk7kqjsb8
662 lines
16 KiB
JSON
662 lines
16 KiB
JSON
{
|
|
"cases": [
|
|
{
|
|
"current": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site-renamed",
|
|
"length": 16,
|
|
"modified_ts": 1700000010,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000010,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-newer"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site-renamed",
|
|
"length": 16,
|
|
"modified_ts": 1700000010,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "newer-incoming-wins",
|
|
"source_tag": "tag-newer"
|
|
},
|
|
{
|
|
"current": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000010,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site-old",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000010,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-older"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000010,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "older-incoming-loses",
|
|
"source_tag": "tag-older"
|
|
},
|
|
{
|
|
"current": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [
|
|
"a",
|
|
"c"
|
|
],
|
|
"type": "password",
|
|
"username": ""
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": true,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "from-incoming",
|
|
"tags": [
|
|
"b",
|
|
"a"
|
|
],
|
|
"type": "password",
|
|
"username": "alice"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000000,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-equal"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"0": {
|
|
"archived": true,
|
|
"custom_fields": null,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"links": null,
|
|
"modified_ts": 1700000000,
|
|
"notes": "from-incoming",
|
|
"tags": [
|
|
"a",
|
|
"b",
|
|
"c"
|
|
],
|
|
"type": "password",
|
|
"username": "alice"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "equal-ts-field-union",
|
|
"source_tag": "tag-equal"
|
|
},
|
|
{
|
|
"current": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
},
|
|
"1": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "keep",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"_deleted": true,
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "site",
|
|
"length": 16,
|
|
"modified_ts": 1700000005,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000005,
|
|
"next_index": 2,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-delete"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000005,
|
|
"entry_hash": "",
|
|
"event_hash": "54256f38e047c9c99df60917aa2ad798ac232df7c56247e6b1681875f9ec5e97",
|
|
"source": "tag-delete"
|
|
}
|
|
}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"1": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "keep",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "incoming-delete-creates-tombstone",
|
|
"source_tag": "tag-delete"
|
|
},
|
|
{
|
|
"current": {
|
|
"_sync_meta": {
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000000,
|
|
"entry_hash": "",
|
|
"event_hash": "",
|
|
"source": "x"
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "revived",
|
|
"length": 16,
|
|
"modified_ts": 1700000020,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000020,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-revive"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "revived",
|
|
"length": 16,
|
|
"modified_ts": 1700000020,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "entry-newer-than-tombstone-survives",
|
|
"source_tag": "tag-revive"
|
|
},
|
|
{
|
|
"current": {
|
|
"_sync_meta": {
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000020,
|
|
"entry_hash": "",
|
|
"event_hash": "",
|
|
"source": "x"
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"entries": {
|
|
"0": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "stale",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000020,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-stale"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000020,
|
|
"entry_hash": "",
|
|
"event_hash": "",
|
|
"source": "x"
|
|
}
|
|
}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"name": "tombstone-beats-older-entry",
|
|
"source_tag": "tag-stale"
|
|
},
|
|
{
|
|
"current": {
|
|
"_sync_meta": {
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000001,
|
|
"entry_hash": "aa",
|
|
"event_hash": "",
|
|
"source": "one"
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"incoming": {
|
|
"_sync_meta": {
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000009,
|
|
"entry_hash": "bb",
|
|
"event_hash": "",
|
|
"source": "two"
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000009,
|
|
"next_index": 1,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-tt"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {
|
|
"0": {
|
|
"deleted_ts": 1700000009,
|
|
"entry_hash": "bb",
|
|
"event_hash": "",
|
|
"source": "two"
|
|
}
|
|
}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {},
|
|
"schema_version": 4
|
|
},
|
|
"name": "tombstone-vs-tombstone-newer-wins",
|
|
"source_tag": "tag-tt"
|
|
},
|
|
{
|
|
"current": {},
|
|
"incoming": {
|
|
"entries": {
|
|
"3": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "new",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"merged": {
|
|
"_sync_meta": {
|
|
"last_merge_ts": 1700000000,
|
|
"next_index": 4,
|
|
"source_count": 1,
|
|
"sources": [
|
|
"tag-adopt"
|
|
],
|
|
"strategy": "modified_ts_hash_tombstone_v2",
|
|
"tombstones": {}
|
|
},
|
|
"_system": {
|
|
"index0": {
|
|
"canonical_views": {},
|
|
"checkpoints": {},
|
|
"events": {},
|
|
"heads": {},
|
|
"schema_version": 1,
|
|
"stats": {
|
|
"checkpoint_count": 0,
|
|
"event_count": 0,
|
|
"last_compaction_ts": 0,
|
|
"last_validation_ts": 0,
|
|
"writer_count": 0
|
|
},
|
|
"view_manifest": {
|
|
"builder_versions": {},
|
|
"canonical_view_types": [],
|
|
"local_only_view_types": [],
|
|
"version": 1
|
|
}
|
|
}
|
|
},
|
|
"entries": {
|
|
"3": {
|
|
"archived": false,
|
|
"kind": "password",
|
|
"label": "new",
|
|
"length": 16,
|
|
"modified_ts": 1700000000,
|
|
"notes": "",
|
|
"tags": [],
|
|
"type": "password"
|
|
}
|
|
},
|
|
"schema_version": 4
|
|
},
|
|
"name": "empty-current-adopts-incoming",
|
|
"source_tag": "tag-adopt"
|
|
}
|
|
],
|
|
"description": "merge_index_payloads parity cases. merged output includes the normalized empty _system.index0 skeleton and _sync_meta with strategy modified_ts_hash_tombstone_v2.",
|
|
"fixture_version": 1
|
|
}
|