Lock alpha scope, tracker auth, TOPLOC fraud verification, and deferred multi-tracker money-path work; supersede legacy fraud issues with ADR-0018. Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
3.7 KiB
Markdown
67 lines
3.7 KiB
Markdown
# ADR-0019: Money-path consistency (multi-tracker)
|
||
|
||
## Status: Accepted (design); implementation deferred post-alpha
|
||
|
||
## Context
|
||
|
||
ADR-0015 assumes a **single settlement tracker** with Raft-replicated ledger events. The hive already gossips billing, accounts, and stats between trackers. When multiple trackers participate, several money-path races appear:
|
||
|
||
- Settlement retries may double-pay if idempotency keys diverge from on-chain reality.
|
||
- Any tracker follower can apply `forfeit` / `charge` events — fine under one trusted operator, dangerous with adversarial peers.
|
||
- Raft term/vote state is in-memory — restart can duplicate leadership epochs.
|
||
- `forfeit` events are not commutative with `charge`/`payout` ordering under concurrent replication.
|
||
|
||
Alpha ships with **one settlement-capable tracker** (ADR-0016). This ADR records the accepted multi-tracker design so Bucket 2 issues do not block alpha while preventing silent architectural drift.
|
||
|
||
## Decisions
|
||
|
||
### 1. Settlement idempotency (C2)
|
||
|
||
- Every on-chain payout batch carries a **stable `settlement_id`** already debiting pending before broadcast (`server.py` ~3353–3356, `_send_settlement` ~3358).
|
||
- **Required hardening:** `solana_adapter.send_payouts` (~186–213) and the settlement loop must treat the Solana transaction signature as the idempotency anchor — retries reuse the same `settlement_id`; confirmed signatures must never spawn a second debit.
|
||
- Deposit watcher already uses `deposit-<sig>` event ids — extend the same pattern to payouts.
|
||
|
||
### 2. Consensus-gated money mutations (C3/C4)
|
||
|
||
Money-affecting events (`charge`, `payout`, `forfeit`, `credit`, `settlement`, `bind`) must commit through **Raft** (or successor consensus), not best-effort gossip alone:
|
||
|
||
- Only the Raft leader appends money commands to the replicated log.
|
||
- Followers apply committed entries in order; gossip becomes a transport, not the source of truth.
|
||
- Settlement signing remains **leader-only** and **treasury-key-bearing** (ADR-0015).
|
||
|
||
Shard-assignment commands already use Raft (`raft.py`); extend the log command set.
|
||
|
||
### 3. Durable Raft metadata (A3)
|
||
|
||
Persist `currentTerm`, `votedFor`, and log index to disk (`raft.py` ~26 — today term lives only in memory). Without this, restart can elect split leaders and duplicate settlement epochs.
|
||
|
||
### 4. Commutative forfeit semantics (H1)
|
||
|
||
Define explicit ordering rules when replicating `forfeit` vs `charge`/`payout`:
|
||
|
||
- Forfeit amount is snapshotted at event creation time (`billing.py` ~287).
|
||
- Apply forfeit **after** pending balance reflects all prior committed charges in the same epoch, or use signed pending snapshots in the event payload.
|
||
- `setdefault` on `_pending_since` (~324) and wallet bindings (~351) must not resurrect stale state on out-of-order apply — use last-writer-wins with `(term, index)` tie-break.
|
||
|
||
### 5. Alpha posture
|
||
|
||
| Item | Alpha | Post-alpha |
|
||
|---|---|---|
|
||
| Single treasury holder | Required | Optional multi-sig |
|
||
| Gossip-authenticated billing | Required (ADR-0017) | Superseded by Raft commit for money |
|
||
| On-chain idempotency | Best-effort resend | Hard guarantee (C2) |
|
||
| Consensus-gated settlement | Leader check only (~3331) | Full Raft gate (C3/C4) |
|
||
|
||
## Consequences
|
||
|
||
- Bucket 2 issues in `.scratch/alpha-hardening/` are **tracked but not alpha-blocking**.
|
||
- Implementing C3/C4 before alpha would delay release without security benefit while only one operator runs settlement.
|
||
- When enabling third-party settlement trackers, **all four** hardening items must ship together — partial deployment is worse than single-tracker alpha.
|
||
|
||
## Related
|
||
|
||
- ADR-0015 (settlement loop, pending collateral)
|
||
- ADR-0016 (single settlement tracker for alpha)
|
||
- ADR-0017 (gossip auth prerequisite)
|
||
- Issues: `12-c2-on-chain-idempotency.md` through `15-h1-commutative-forfeit.md`
|