INTEGRATION-CONTRACT.md

15.9 KB · raw

rendered · source

Desktop Helper Pairing — Integration Contract

AUTHORITATIVE. The single document an integrator follows to apply the DESKTOP-PAIRING draft set to the live peach-browser source. It reconciles every individual draft (01–06) into one apply plan: file map, order, message contract, new types, deps, CSS, test count, and the consolidated TODO list.

All file:line citations are against source as read 2026-06-16. Where a draft and this contract disagree, this contract wins (it is the reconciliation).


1. File map — each draft → its destination on apply

Draft file What it describes Destination on apply Action
_GROUND-TRUTH.md Wire contract pin (none — reference only) keep in repo as design record
README.md Orientation (none — reference only) keep as design record
INTEGRATION-CONTRACT.md This document (none — reference only) keep as design record
01-vault-id-in-pair-identity.md Add vault_id to outbound PAIR_IDENTITY edit src/shared/chirp-handshake.ts (Hunks A–D; defer Hunk E) apply patch
02-desktop-device-type.md "desktop" DeviceType NO-OP — already in src/shared/sync-types.ts:11; storage already accepts it optional regression test only
03-desktop-pairing-handler.md Background code-pairing client new file src/background/desktop-pairing.ts + 3 case blocks in src/background/messages.ts + openBinaryWebSocket helper create + wire
04-desktop-pairing-ui.md Popup pairing UI new file src/popup/components/DesktopPairingFlow.tsx + entry-point edits in src/popup/views/ChirpSync.tsx + CSS in src/popup/styles/popup.css create + wire
05-subnet-probe.md Handshake-free port probe new file src/background/desktop-pairing-probe.ts create
06-sync-coordinator-desktop.md Include desktop in sync edit src/background/sync-trigger.ts (one filter line) apply patch
tests/01-pair-identity-vault-id.test.ts Change 1 spec src/shared/chirp-pair-identity-vault-id.test.ts (or merge into chirp-handshake.test.ts) relocate
tests/03-desktop-pairing.test.ts Change 3 spec src/background/desktop-pairing.test.ts relocate, fix ../../→../
tests/04-desktop-pairing-ui.test.tsx Change 4 spec src/popup/components/DesktopPairingFlow.test.tsx relocate (paths already written for that location)
tests/06-sync-coordinator-desktop.test.ts Change 6 spec src/background/sync-trigger.desktop.test.ts (or merge into sync-trigger.test.ts) relocate, fix ../../→../

On apply, the entire src/drafts/DESKTOP-PAIRING/ tree can be deleted (use trash, never rm) once the destinations above exist — it is design-time scaffolding, not shipped code.


2. Integration order (dependency-correct)

Apply in this sequence; each step compiles on its own.

  1. Change 1 — vault_id into PAIR_IDENTITY (chirp-handshake.ts, Hunks A–D). Adds LocalIdentity.vaultId + PairingFrame.vault_id. Nothing downstream type-checks against vaultId until this lands.
  2. Change 2 — verify-only. Confirm "desktop" ∈ DeviceType (sync-types.ts:11); optionally add the round-trip regression test. No code.
  3. Change 5 — desktop-pairing-probe.ts (probeSubnetForPort). Standalone; Change 3 imports it.
  4. Change 3 — desktop-pairing.ts (the client + DesktopPairingError, DesktopPairingSocket, message-payload shapes) + the openBinaryWebSocket helper. Depends on Change 1 (vaultId on identity) and Change 5 (findHost).
  5. messages.ts cases — the three PAIR_WITH_DESKTOP / DESKTOP_PAIR_CONFIRM handlers and the two background pushes. Depends on Change 3's module. Gate exactly like CHIRP_GET_LOCAL_IDENTITY.
  6. Change 4 — DesktopPairingFlow.tsx + ChirpSync.tsx entry point + the peach-desktop-* / peach-code-input CSS. Depends on the message contract (step 5) existing.
  7. Change 6 — widen the sync-trigger.ts host filter to include "desktop". Independent of the UI; can land any time after pairing persists a desktop device, but logically last (it's the "now actually sync it" step).

3. The reconciled popup ↔ background message contract (CANONICAL)

Reconciled across 03 (defined four messages) and 04 (uses all four; pinned { confirmed } not the brief's { accept }). 03 and 04 AGREE — 04 adopts 03's DESKTOP_PAIR_RESULT terminal push and 03's { confirmed: boolean } payload. The brief's three-message / {accept:true} shape is superseded. Final canonical shapes:

// ── popup → background ─────────────────────────────────────────────────────

/** Start pairing with the typed 6-digit code. */
{ type: "PAIR_WITH_DESKTOP", payload: { code: string } }
//   response: { ok: true } | { ok: false, error: DesktopPairingError }
//   (synchronous start failures — e.g. vault_locked — come back HERE;
//    the SAS and the terminal result arrive LATER as pushes.)

/** User accepted/rejected the SAS shown by DESKTOP_PAIR_SAS. */
{ type: "DESKTOP_PAIR_CONFIRM", payload: { confirmed: boolean } }
//   response: { ok: true }

// ── background → popup (pushes via chrome.runtime.sendMessage) ──────────────

/** SAS ready — show the confirm gate. */
{ type: "DESKTOP_PAIR_SAS", payload: { sas: string } }

/** Terminal outcome. */
{ type: "DESKTOP_PAIR_RESULT",
  payload:
    | { status: "complete", device: PairedDevice }
    | { status: "error", error: DesktopPairingError } }

DesktopPairingError (defined in desktop-pairing.ts, Change 3):

type DesktopPairingError =
  | "invalid_code"      // socket closed before PEACH_HELLO_ACK (wrong/expired)
  | "no_host_found"
  | "handshake_failed"
  | "sas_mismatch"
  | "vault_locked";

Bridge: sendMessage (src/shared/bridge.ts:13) for popup→bg; pushes received via chrome.runtime.onMessage.addListener (App.tsx:601 idiom).

Reconciliation flags (resolved)


4. New message types — where the union lives

The three new type strings (PAIR_WITH_DESKTOP, DESKTOP_PAIR_CONFIRM inbound; DESKTOP_PAIR_SAS, DESKTOP_PAIR_RESULT outbound) are handled in the background switch in src/background/messages.ts, alongside the CHIRP_* cases (messages.ts:1318-1384).

Where to add the type literals: the existing CHIRP_* messages are dispatched by string case in that switch; the integrator should follow the same pattern the repo already uses for chirp messages — add the four case labels (two inbound handlers in the switch; the two outbound are chrome.runtime.sendMessage pushes, not switch cases). If the repo maintains a discriminated message-union type for sendMessage payloads, extend it where the CHIRP_* variants are declared (co-located with the chirp definitions — verify the exact union file at integration; the chirp messages are the template). No change to src/shared/types.ts is required — the brief's guess that device/message types live there is wrong (_GROUND-TRUTH.md §0.1); message handling is in messages.ts and sync types are in sync-types.ts.


5. New dependencies — NONE.

Confirmed: no new npm packages. Everything reuses shipped modules:

No package.json change.


6. New CSS hooks (Change 4 → src/popup/styles/popup.css)

The UI reuses existing classes (peach-chirp-pair, peach-chirp-head, peach-chirp-back, peach-chirp-headline, peach-chirp-sub, peach-chirp-action, peach-chirp-meter*, peach-btn*, peach-sr-only, peach-underline-input, peach-unlock-input, peach-label). New additive hooks to add a small block for in popup.css:

New class Purpose
peach-desktop-pair root container modifier on the flow
peach-code-input the 6-digit code field (mono, wide letter-spacing)
peach-desktop-enter / -searching / -confirm / -paired / -error per-phase containers
peach-desktop-sas the big SAS glyph (load-bearing layout is inline for now)
peach-desktop-connect Connect button modifier
peach-chirp-pair-start--desktop the "Desktop Helper" entry button in ChirpSync.tsx

Inline styles in the draft cover the SAS/confirm layout in the interim (mirroring ChirpSync.tsx's inline-styled SAS gate at lines 445-472), so the CSS block is polish, not a blocker.


7. Estimated test count across the four draft test files

Test file it() specs
tests/01-pair-identity-vault-id.test.ts 3
tests/03-desktop-pairing.test.ts 9
tests/04-desktop-pairing-ui.test.tsx 12
tests/06-sync-coordinator-desktop.test.ts 2
Total 26

(Change 2 ships no required test; an optional paired-devices round-trip regression would add 1 if the integrator wants it.)


8. Do existing tests need updating? — NO.

Desktop pairing is additive. Verified against the existing suites:

No existing test file requires edits. All four draft test files are net-new additions.


9. Consolidated remaining-integration TODO checklist

Carried forward from every pass (01–06):


10. Version note

The brief said bump 4.0.1 → 4.0.2. But dev is already at 4.0.2 (manifest.json "version": "4.0.2", verified 2026-06-16). Repeated versions are rejected by the stores (auto-memory: "every code change is a version bump"). Integration must bump 4.0.2 → 4.0.3 across all three manifest targets (manifest.json, manifest.firefox.json, and the mobile manifest) on apply — not 4.0.2. (Drafts touch only src/drafts/, so no version bump happens at draft time; this is an apply-time obligation.)


11. Quality-bar checklist (from the brief), annotated

Quality bar Status in drafts Integrator must verify
SAS KAT 0fbab8cd… → 895245 ✅ pinned: derivePairingSAS reused (not re-implemented); KAT asserted in tests/03 spec "derives the SAS via the real derivePairingSAS"; cross-checked against peach-sync/src/pairing.zig:275-279 (_GROUND-TRUTH.md §5) run the relocated tests/03 spec — must stay green
tsc clean ⚠️ drafts are apply-ready TS but not compiled in place after apply, tsc --noEmit clean — esp. the LocalChirpIdentity vs LocalIdentity reconciliation (TODO 2) and ChirpSyncSession cast in tests/06
All tests green ⚠️ 26 new specs written to repo idiom (vitest, renderView, injected deps) relocate the 4 files, fix import prefixes, run the full suite — 26 new + existing unchanged
Additive only (no existing test edits) ✅ verified §8 — no closed-union assertion, no absence-of-vault_id assertion, no desktop fixture in sync-trigger.test.ts confirm the suite passes with zero edits to existing test files
vault_id backwards-compatible ✅ optional both ends; JSON.stringify drops undefined; peach-sync ?[]const u8 = null + ignore_unknown_fields (pairing.zig:54,57) confirm a chirp (mobile) pairing still works after Change 1
MITM defence = user-confirmed SAS ✅ pairWithDesktopHelper gates persistence on confirm(sas) → true; rejects → sas_mismatch, persists nothing confirm the messages.ts round-trip actually blocks on the popup confirm
Correct ports / handshakes ✅ pairing 49142 / Noise NK / browser initiator; sync 8765 / Noise XX / browser initiator (06-...md §"which handshake for which phase") confirm wsPort default 8765 matches helper announce
No new deps ✅ §5 — none confirm package.json untouched