Skip to content

Feature: Sync Relay (Cloudflare Worker)

Runa the Artificer — Backend Engineer

By the end of this chapter you’ll understand:

  • How D1 (metadata) and R2 (snapshot blobs) split responsibilities in the relay’s storage architecture
  • What the zero-knowledge property means concretely and how the static ZK audit script enforces it in CI
  • How the 52-test suite (ZK canary probes, E2E smoke, concurrent burst load) validates correctness and scale

Feature: Sync Relay (Cloudflare Worker)

Status: Delivered
CAS: CAS-748
Delivered: 2026-04-26
Parent epic: CAS-171 Multi-Device Sync with E2E Encryption


What shipped

A Cloudflare Worker edge service that relays encrypted changesets between Casaconomy desktop clients. The relay is zero-knowledge by design: it stores and forwards opaque ciphertext blobs and cannot read payload content. All decryption happens on-device.

Architecture

Client A ──(push)──► Worker (edge, Cloudflare network)
D1 SQLite (weur) ── R2 (weur)
Client B ──(pull)──► Worker

Storage:

  • D1changesets, devices, vaults, snapshots metadata tables
  • R2 — full snapshot blobs (large uploads, content-addressed)

Entry point: workers/sync-relay/src/index.ts

Zero-knowledge property

The relay enforces ZK at the server layer:

  • ciphertext fields are stored and forwarded verbatim — no decode, parse, or inspect
  • D1 text columns contain only opaque bytes (verified by ZK audit test suite)
  • Nonces are treated as opaque identifiers, never inspected
  • Static audit script (scripts/check-zk-static.sh, npm run lint:zk) fails the build if ciphertext appears in console calls, decode paths, conditionals, or error strings

API

MethodPathPurpose
POST/vaults/:id/changesetsPush a changeset
GET/vaults/:id/changesetsPull changesets (since cursor)
POST/devicesRegister a device keypair
GET/devices/:idFetch device public key
POST/vaults/:id/snapshotsUpload a full snapshot to R2
GET/vaults/:id/snapshots/latestFetch latest snapshot metadata

Rate limiting is enforced per vault per IP. Devices authenticate via Ed25519 keypairs registered at pairing time.

Test suite (sub-task S6 / CAS-754)

  • ZK audit tests: 4 ciphertext-canary probes (push response, D1 columns, pull response cross-field, nonce opacity) — prove ciphertext never escapes the relay
  • E2E smoke: two-device cross-vault push/pull with sequence integrity and round-trip ciphertext fidelity
  • Concurrent burst load: 20 parallel pushes + 20 parallel pulls across distinct vaults — all must return 2xx, no 5xx
  • Static ZK grep audit: integrated into npm run lint:zk

52 tests across 6 files pass. ZK static audit: PASSED.

Tauri client side

transport_http.rs was updated to align the PushRequest struct with the relay API: added id, origin_device, origin_user, timestamp, operation_type, entity, entity_id, schema_version, key_id, and nonce fields. Renamed entity_typeentity to match the relay schema.


Sub-tasks delivered

IdentifierTitle
CAS-749S1 — D1 schema + wrangler config
CAS-750S2 — changeset push/pull handlers
CAS-751S3 — device registration + auth
CAS-752S4 — snapshot upload/fetch (R2)
CAS-753S5 — rate limiting + retention
CAS-754S6 — ZK audit test + E2E smoke + README

Recap

  • The relay stores changesets in D1 (metadata + ordered log) and snapshot blobs in R2; ciphertext fields are verbatim opaque bytes — the relay never decodes, parses, or inspects them.
  • The static ZK audit script (scripts/check-zk-static.sh, npm run lint:zk) fails the build if ciphertext appears in console calls, decode paths, conditionals, or error strings — making the zero-knowledge property a compile-time constraint.
  • 52 tests across 6 files validate the relay: 4 ZK canary probes, an E2E two-device round-trip, and a 20+20 concurrent burst load run — all passing, ZK audit clean.

What changed {#what-changed}

See: CHANGELOG → 2026-04-26