9189870090c5

bump the pairing invitation prefix to peach-pair-v2 with the pinned static now mandatory (PB-26-103, v5.0.47)

time
2026-09-02 06:30 UTC · 1 month ago
machine
koh2
agent
hy4-preview
session
assessment remediation corrections
parent
056807d6a42e
8 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/relay-adoption-bg.test.ts+2 −2src/popup/views/AdoptVaultView.test.tsx+2 −2src/popup/views/PairDevice.test.tsx+2 −2src/shared/pairing-invitation.test.ts+2 −2src/shared/pairing-invitation.ts+8 −1
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "5.0.46",4+  "version": "5.0.47",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -2,7 +2,7 @@22   "manifest_version": 3,33   "minimum_chrome_version": "116",44   "name": "Peach Password Manager",5-  "version": "5.0.46",5+  "version": "5.0.47",66   "description": "Zero-knowledge password manager",77   "action": {88     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "5.0.46",3+  "version": "5.0.47",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

src/background/relay-adoption-bg.test.ts+2 −2
@@ -258,7 +258,7 @@258258     void resolveSourceSasConfirmation; // imported for the post-approval call below259259 260260     const started = await startRelayAdoptionSource();261-    expect(started.invitation).toMatch(/^peach-pair-v1:/);261+    expect(started.invitation).toMatch(/^peach-pair-v2:/);262262     await waitForPush("ADOPTION_SOURCE_SAS");263263     // The adoption must NOT be complete yet — the popup's SAS decision is the264264     // prerequisite for forwarding the root key.@@ -535,7 +535,7 @@535535   it("opens a relay socket and returns an empty invitation", async () => {536536     const { startRelayAdoptionReceiver } = await import("./relay-adoption-bg");537537     const started = await startRelayAdoptionReceiver("receiver-password");538-    expect(started.invitation).toMatch(/^peach-pair-v1:/);538+    expect(started.invitation).toMatch(/^peach-pair-v2:/);539539     // The QR carries vault:"empty" so the phone knows to act as source.540540     const decoded = decodeInvitation(started.invitation);541541     expect(decoded?.vault).toBe("empty");

view file

src/popup/views/AdoptVaultView.test.tsx+2 −2
@@ -196,7 +196,7 @@196196             callback: (response: unknown) => void,197197           ) {198198             if (message.type === "START_ADOPTION_RECEIVER") {199-              callback({ ok: true, data: { invitation: "peach-pair-v1:test" } });199+              callback({ ok: true, data: { invitation: "peach-pair-v2:test" } });200200             } else {201201               callback({ ok: true });202202             }@@ -259,7 +259,7 @@259259             callback: (response: unknown) => void,260260           ) {261261             if (message.type === "START_ADOPTION_RECEIVER") {262-              callback({ ok: true, data: { invitation: "peach-pair-v1:test" } });262+              callback({ ok: true, data: { invitation: "peach-pair-v2:test" } });263263             } else {264264               callback({ ok: true });265265             }

view file

src/popup/views/PairDevice.test.tsx+2 −2
@@ -16,7 +16,7 @@1616 vi.mock("../../shared/bridge", () => ({ sendMessage: mockSendMessage }));1717 1818 const SOURCE_INVITATION =19-  "peach-pair-v1:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyJ9";19+  "peach-pair-v2:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyJ9";2020 2121 import { PairDeviceView } from "./PairDevice";2222 import { renderView } from "./view-test-utils";@@ -87,7 +87,7 @@8787     expect(container.querySelector("svg.peach-pair-qr")).toBeTruthy();8888     expect(container.querySelector("#pairing-invitation-manual")).toBeNull();8989     expect(container.textContent).not.toContain("Show a code instead");90-    expect(container.textContent).not.toContain("peach-pair-v1:");90+    expect(container.textContent).not.toContain("peach-pair-v2:");9191   });9292 9393   // Moment 1 (docs/sync-relay-surfacing.md): present, but never a step.

view file

src/shared/pairing-invitation.test.ts+2 −2
@@ -13,11 +13,11 @@1313 // Cross-client compact vector (pinned in the changelog). Adding a top-level key1414 // would change the byte length, so this is a separate vector from the v1 one.1515 const KAT_POPULATED =16-  "peach-pair-v1:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyIsInZhdWx0IjoicG9wdWxhdGVkIn0";16+  "peach-pair-v2:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyIsInZhdWx0IjoicG9wdWxhdGVkIn0";1717 // A pre-vaultStatus invitation as it would have circulated before the field1818 // existed. Older phones parse this and fall back to "populated".1919 const KAT_LEGACY =20-  "peach-pair-v1:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyJ9";20+  "peach-pair-v2:eyJyZWxheSI6IndzczovL3JlbGF5LnBlYWNocGFzc3dvcmRzLmNvbSIsInRva2VuIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EdyJ9";2121 2222 describe("relay pairing invitation", () => {2323   it("pins the cross-client compact JSON/base64url vector", () => {

view file

src/shared/pairing-invitation.ts+8 −1
@@ -4,7 +4,7 @@44   normalizeRelayUrl,55 } from "./rendezvous";66 7-export const PAIRING_INVITATION_PREFIX = "peach-pair-v1:";7+export const PAIRING_INVITATION_PREFIX = "peach-pair-v2:";88 99 export type VaultStatus = "empty" | "populated";1010 @@ -29,6 +29,13 @@2929  * QR/manual first-pair payload. Reachability plus a credential-free relay URL,3030  * a fresh 128-bit bearer token, and the issuer's vault state — no identity or3131  * vault material is present before Noise and the two-person SAS gate.32+ *33+ * PB-26-103: `static` pins the issuer's Noise static public key so the scanning34+ * device can authenticate the cleartext PEACH_HELLO_ACK instead of trusting it.35+ * Both adoption directions on Android now refuse an invitation without it,36+ * which is a behaviour change rather than a format change — hence the v237+ * prefix, so a mismatched pair fails at the scan instead of mid-handshake.38+ * There is no interop with v1; both clients move together.3239  */3340 export function createPairingInvitation(3441   relayUrl: string,

view file