# ZeqVM state-channel architecture — offline-first compute that settles on reconnect

**The vision:** the framework never stops. Online with credit, compute runs on
the main state machine (charged, signed, recorded). With **no credit** it drops
to a free tier; with **no internet** it drops to a **local** tier that computes
on a bundled engine. Every off-main computation is queued as a pending state
transition. When the device **reconnects with credit**, the queue **settles** to
the user's machine — replayed as real charged transitions, the local chain
reconciled with the server chain, everything brought up to date. A state channel
for physics: off-main execution, on-main settlement.

This applies to **every surface** — the Chrome extension, the web state machine
on all domains, and (next) the VS Code extension — so the behaviour is identical
everywhere: **local → host → GitHub → all domains**, one consistent client.

---

## The three compute tiers (graceful degradation)

| Tier | When | Path | Result |
|---|---|---|---|
| **Main** | online + credit | `POST /api/zeq/compute` | value + **charged state envelope** (tally, signed receipt, chain row) |
| **Free** | online, **no credit** | `POST /api/zeq/prove` (public) | value + public proof, **no charge, no envelope** |
| **Local** | **offline** (no internet), or free path unreachable | bundled `ZeqLocalEngine` (pure-math SDK solver) | value only, **no proof/envelope**, queued for settlement |

The service worker walks down the tiers automatically. Every degraded result is
flagged (`free_tier` / `local` / `settle_pending`) so the UI is honest about
what the user got.

---

## Phases

### SC.1 — Local compute tier ✅ (this drop)
- Bundled the SDK's pure-math solver into the extension as
  `lib/zeq-local-engine.js` (31 KB, no network, no node deps) via
  `app/packages/sdk/build-local-engine.mjs`.
- Service worker `importScripts` it and falls back to it on a network error
  (offline) or when both metered + free paths are unreachable.
- **Verified:** offline `compute({mass:2, acceleration:3})` → F = 6 N locally;
  KO42 → resonance modulation — real physics with no server.

### SC.2 — Pending ledger + settlement (next)
- A local, encrypted, **hash-linked pending ledger** of every off-main compute
  (`{op, inputs, value, local_hash, prev_hash, zeqond, reason}`) — a mini
  entangled-state on the device.
- A reconnect watcher: when the network returns **and** the machine has credit,
  POST the queue to a new **`/api/zeq/settle`** endpoint that replays each entry
  as a real charged transition, mints the envelopes, and returns settlement
  receipts.
- Client reconciles: marks entries settled, links its local head to the server
  head, dedupes. Last-write-wins / idempotent by `local_hash`.

### SC.3 — Parity on the web state machine (all domains)
- Port the same tiered compute + pending-ledger + settlement into the web app
  served from `apps/zeq-dev/public` (so it propagates to every domain via the
  per-domain mount). The browser tab gets the same offline-first behaviour.

### SC.4 — VS Code extension parity
- Bring the same engine + tiers + settlement into `zeq-vs-code-sdk`, plus the
  Chrome features we ported. One consistent client across IDE, browser, and web.

### SC.5 — Mesh settlement (stretch)
- Devices that meet offline can exchange + co-sign pending entries (the mesh),
  settling as a batch when any of them reconnects.

---

## Honesty / invariants
- **Local results never claim a charged envelope.** No fake proofs, no fake
  tally. A local value is a value; the signed receipt is minted only at
  settlement, by the server, with the user's key.
- **Settlement is idempotent.** Replaying the same `local_hash` twice settles
  once — a dropped connection mid-settle can't double-charge.
- **The pending ledger is encrypted at rest** (same HITE/AES the vault uses) and
  hash-linked, so an offline session is tamper-evident before it ever reaches
  the server.
- **No silent data loss.** If settlement fails, entries stay pending and retry;
  the user can always see what's unsettled.
