Skip to content

id: poc: bridge sync session — native store ↔ DO-shaped SQLite store over WebSocket, fork-proof exchange + withholding detection (from #2058) #2276

Closed

Description

@dezren39

From the bridge-semantics investigation on #2058 (doc PR linked there): prove the hosted↔native seam with the smallest thing that exercises the real rules, reusing the native store machinery unchanged.

What to build (PoC only)

A bridge sync session between a native pkgs/id store and a DO-shaped SQLite store (same table shape as event_store/: events by (author, seq), chains, forks, proof_ids, proof_headers) over a WebSocket, reusing ingest_batch + resolve with no semantic changes: no bridge-assigned ordering, no re-signing, no authoritative snapshot.

Acceptance criteria

  • Push and pull across the session converge to identical applied sets and identical live tag claims on both stores
  • A fork authored on each side while disconnected is detected by the proof-carrying exchange (not LogSync height comparison) and both sides end holding the same fork proof
  • A withholding bridge (drops one author's events) is detected from chain-head / after-frontier evidence, and the detection is demonstrated in a test
  • Replaying the same journal into an empty hosted-shaped store and an empty native store yields identical stores (tables compared)
  • Findings (incl. measured fork-proof exchange cost in rows/CPU terms) recorded on this issue for the id: design: ID serverless edition v1 (Cloudflare Workers) #2046 sync-spec decision (Q1 on id: investigate: bridge semantics — hosted (HTTPS/WS) edition syncing with native Iroh nodes #2058)

Not in scope

Permanent feature work on top of this PoC (program rule: an unreviewed PoC making its own decisions is not a foundation), custodial keys, SSO binding (#2258), native multi-hop relay (#2249).

Activity

  1. added
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. added
    source/humanFiled directly by a human
    priority/normalOrdinary priority (the default)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Intake review — fleet monitor, 2026-10-29

  5. added
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 30, 2026
  6. 32 remaining items

  7. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done (PoC delivered; PR awaiting CI)

    Findings for the #2046 Q1 decision (fork-proof cadence)

    Measured on the PoC (laptop run, debug build; all 5 PoC tests pass, 10/10 with the shared test-common unit tests):

    • Fork set (2 authors' worth of state: 3 events, one fork at equal heights): proof-carrying range exchange = 4 flights, 163 reconciliation bytes — and it detected the fork that a height comparison provably misses (pre-session chains had identical next_seq, different heads).
    • Bootstrap set (100 events / 4 authors, one side empty): 4 flights, 3,235 reconciliation bytes ≈ 32.4 bytes/event — about half the naive bound of mailing every event ID both ways (6,400 B), and the flight count did not grow between the 3-event and 100-event sets at this fan-out. Session wall time 5.3 s in a debug build is dominated by per-event signature verification in ingest_batch, not by the exchange.
    • Cadence read: at wave-1 scale the exchange itself is cheap (bytes scale ~linearly at ~32 B/event with fingerprint flights, flights stayed at 4); the cost a cadence decision actually budgets is the verification of transferred events, which is paid only for the symmetric difference. Nothing in the measurements argues against running the proof exchange on every bridge session at this scale; the open part of Q1 remains the DO rows/CPU accounting on the Workers side, which this PoC does not measure (no Workers runtime here).

    What the PoC proved, per acceptance

    • Convergence: push+pull in one session → identical applied sets (store canonical state) and identical live tag claims on both stores.
    • Forks: a fork authored on each side while disconnected is detected by the proof-carrying exchange (≥1 Ingest::Fork outcome per side), and both sides end holding the identical fork proof (same seq, same first/second IDs).
    • Withholding: a relay that drops one author's events is detected from the announced chain head (seq 4 / head hash) plus the after-frontier reference inside a signed event that did arrive (the receiver holds that event waiting on exactly the withheld hash). Detection names the author, the announced head, and the undelivered event IDs; the honest-session control produces no evidence, and an author whose events arrived but waits on the withheld dependency is not itself flagged.
    • Replay identity: the same journal replayed into a fresh native store and a fresh hosted (SQLite) store — in opposite arrival orders — produces identical tables (events / chains / forks / proof_ids / proof_headers compared row-for-row).

    Limitations (stated in the PR as well)

    • The sync set is enumerated via the store's public read API, which exposes the lowest fork proof per author; the internal sync set also carries higher fork points' proof headers.
    • The hello's chain heads are unsigned in the PoC; production evidence for the head half of withholding detection arrives inside signed events (backlinks / after), which is the half the frontier test exercises with real signatures.
    • The hosted store's SQLite tables are mirrored from the same ingest outcomes in the same step (one batch = one transaction, the DO write shape); a rusqlite-native ingest port is separate work, not needed for the semantics questions this PoC answers.
  8. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  9. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Oct 30, 2026
  10. added 2 commits that reference this issue on Oct 30, 2026
  11. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  12. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workkind/enhancementmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/humanFiled directly by a human

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions