# Local vault worker and signed recovery

This guide covers the local Gate C test harness only: chain 31337, mUSDC, fixed local personas and explicitly local unlocked test accounts. It is not a public hosting setup or production key-management design. The normal browser sandbox remains separate.

## Complete local demonstration

```powershell
npm.cmd run relay:demo
```

The fixture creates temporary SQLite storage, a loopback HTTP API/RPC bridge and local mock contracts. Seven worker processes execute payment, dispute and pool refund across API/database restarts. The merchant spends its received tokens before the refund. The fixture and RPC bridge are cleaned up afterward. All monetary values are test-only.

## One bounded worker pass

`scripts/local-vault-worker.js` consumes a trusted local manifest containing `databasePath`, `rpcUrl` and the pinned `config`. The manifest is created by the fixture; it contains public addresses, not signing keys. The worker rejects external RPC hosts and non-local chain IDs. It is intentionally a bounded pass, suitable for repeated invocation by a local test runner rather than a public long-running service.

```powershell
node scripts/local-vault-worker.js <manifest.json> tick
node scripts/local-vault-worker.js <manifest.json> reconcile
```

Payment source delivery acknowledges `QUEUED` work, not chain execution. Jobs then become `SUBMITTED` and, after receipt verification, `APPLIED`. Dispute/refund/denial/expiry requests are inserted in the same SQLite transaction as their domain decision. Their bodies are immutable. Ledger and final job completion commit together. Worker ownership expires and is checked again before writes; duplicate processes cannot create a second authorized transfer.

Dispute review begins after chain opening is confirmed. Final refund/denial waits for the configured appeal policy or independent appeal decision. The native provisional `chargeback.approved` notification alone does not authorize a refund. Expiry requires both the local fixture deadline and the actual vault deadline. Pending chain transactions never produce a local final refund or release.

`GET /v1/payments/{id}` exposes separate payment/dispute/final execution evidence. The owning pool operator's `GET /v1/reconciliation` also observes the vault and reports pending or diverged chain evidence; it does not repair balances. A changing local snapshot is reported as pending and retried.

## Unknown submission, failed receipt or reorg

Keep the worker stopped when the stream is halted. Use the raw journal/audit and canonical chain to locate the transaction for the original operation. A transaction hash is not sufficient by itself: chain, vault, sender, calldata, receipt/event, confirmations and current reservation/pool must all match. A reverted or missing receipt cannot authorize completion.

If the original response was lost but an exact successful receipt exists, map the journal operation key to that hash in an overrides JSON file. For a genuine reorg or failed call, any replacement must first be explicitly signed/submitted by the authorized local account using the same intended calldata. The recovery tool does not send that replacement for you. Incompatible or unresolved state remains halted.

```powershell
node scripts/local-vault-worker.js <manifest.json> prepare-recovery <overrides.json>
```

The returned plan binds the protocol network ID, chain/vault, current database snapshot, receipt observations, a nonce and a short expiry. Save it as `plan.json`. The overrides file is an object whose keys are journal `idempotencyKeyHash` values and whose values are the matching canonical transaction hashes. Only known managed operations may be supplied.

The vault administrator and provider independently inspect the plan and sign the exact digest bytes using Ethereum message signing. The approval format is:

```json
{
  "administratorSignature": "<signature over the plan digest bytes>",
  "providerSignature": "<independent provider signature over the same digest bytes>"
}
```

For trusted local test signers, the exact signing operation is `signer.signMessage(getBytes(plan.digest))`. The worker never generates those approvals automatically. Keep unrelated mutations quiescent while approving; changes invalidate the snapshot and require a fresh plan.

```powershell
node scripts/local-vault-worker.js <manifest.json> approve-recovery <plan.json> <approvals.json>
```

Approval reacquires exclusive worker ownership, re-observes the chain and rechecks the snapshot, role signatures, expiry and replay marker. It updates proof/cursor metadata and records an append-only signed audit; it does not credit, debit, cancel or refund a ledger account. Resume ordinary worker passes afterward so pending confirmed work can be applied through the normal financial gate.

Raw orphan events stay available in `TransactionJournal.events()`. `canonicalEvents()` refuses reads while halted and excludes events classified as orphaned by signed recovery. The cursor rescans from its approved start under a new generation. Do not delete the database, signing identities, audit, orphan evidence or idempotency records as a recovery shortcut.

## Persistence and boundary

Keep the SQLite database and its adjacent `.identities.key` file together using a proper SQLite backup or a stopped-process copy. Keys are plaintext local fixture material excluded from Git; Windows protection depends on directory ACLs. Sessions renew after process restart. Missing trust material fails startup.

The ledger is TEST-USD mirror accounting, not a live ERC-20 wallet balance. Real wallet display/signing, public identities, non-local networks, production custody and operational incident procedures belong to later gates. [Gate C acceptance and evidence](../BACKSTOP-GATE-C-ACCEPTANCE.md).
