13728285815b

docs: inline notes for tombstone accumulation, per-entry LWW limitation, flags2 format caution; expand Projects.md §3.8 with full permission list and justifications (v3.6.117)

time
2026-05-28 20:17 UTC · 5 months ago
machine
koh2
parent
3bc26b9c7542
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/chirp-sync-vault-adapter.ts+7 −0src/background/vault.ts+6 −0src/crypto/peachPayload.ts+7 −0
manifest.firefox.json+1 −1
@@ -2,7 +2,7 @@22   "_comment_audio_capture": "Firefox does not honor the Chrome 'audioCapture' optional_permission — getUserMedia in WebExtensions is gated solely by the runtime permission prompt. Microphone access for chirp pairing works from the popup under the existing '<all_urls>' host permission; no manifest entry is required or accepted here.",33   "manifest_version": 3,44   "name": "Peach Password Manager",5-  "version": "3.6.116",5+  "version": "3.6.117",66   "description": "Zero-knowledge password manager",77   "browser_specific_settings": {88     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "3.6.116",4+  "version": "3.6.117",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "3.6.116",3+  "version": "3.6.117",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/background/chirp-sync-vault-adapter.ts+7 −0
@@ -91,6 +91,13 @@9191     },9292 9393     async buildTombstoneManifest() {94+      // KNOWN LIMITATION: tombstones accumulate without bound. Every deleted95+      // entry leaves a {id, deletedAt} record that persists indefinitely and is96+      // sent in every sync session, so a long-lived vault's manifest grows with97+      // total deletions, not live entries. Acceptable for now (records are tiny98+      // and the trash-retention purge eventually splices the entry array).99+      // Future: prune tombstones older than N months once every paired device100+      // has confirmed it holds no entry with that id.94101       requireUnlocked();95102       const manifest: Record<string, number> = {};96103       for (const entry of getDeletedEntries()) {

view file

src/background/vault.ts+6 −0
@@ -1770,6 +1770,12 @@17701770   remoteEntries: VaultEntry[],17711771   remoteTombstones: Record<string, number>,17721772 ): Promise<{ received: number; applied: number; conflicts: number }> {1773+  // DESIGN: last-write-wins is applied PER-ENTRY, not per-field. If two devices1774+  // concurrently edit different fields of the same entry, the one with the1775+  // newer modifiedAt wins the whole entry and the other device's field edits1776+  // are dropped silently. This is the deliberate trade-off for a simple,1777+  // conflict-free merge — per-field CRDT merge is out of scope. Guidance for1778+  // users: edit a given entry on one device at a time.17731779   if (!derivedKey) throw new Error("vault_locked");17741780 17751781   const received = remoteEntries.length + Object.keys(remoteTombstones).length;

view file

src/crypto/peachPayload.ts+7 −0
@@ -112,6 +112,13 @@112112 113113 // v3-only flags2 byte (login entries only). Reserves room for further114114 // post-v3.6.82 fields without another envelope bump.115+//116+// CAUTION: flags2 bits are read positionally within FORMAT_V3. Adding a new117+// boolean flag bit here does NOT require a format bump. But adding a new118+// VARIABLE-LENGTH field gated by a new flag bit DOES — older v3 decoders don't119+// know to skip the new field and the read cursor desyncs for everything after120+// it. If you need a new variable-length field, bump FORMAT_V3 → FORMAT_V4 and121+// gate the decoder on the format byte. Only add single-bit booleans to flags2.115122 const F2_HAS_EMAIL = 1 << 0;116123 117124 const NETWORK_VALUES: CardNetwork[] = ["visa", "mastercard", "amex", "discover", "unknown"];

view file