Files
neuron-tai/docs/adr/0019-money-path-consistency-multi-tracker.md
D.Popov 68e057209c Add alpha-hardening ADRs and issue plan from pre-release audit.
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>
2026-07-04 23:12:09 +03:00

3.7 KiB
Raw Blame History

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 ~33533356, _send_settlement ~3358).
  • Required hardening: solana_adapter.send_payouts (~186213) 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.
  • 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