Skip to content

docs(agents): fund-safety, crash-consistency and distributed-transaction invariants - #493

Merged
Amperstrand merged 1 commit into
mainfrom
agents-fund-safety-invariants
Sep 22, 2026
Merged

Amperstrand merged 1 commit into
mainfrom
agents-fund-safety-invariants

Conversation

@Amperstrand

Copy link
Copy Markdown
Collaborator

Adds the Fund safety, crash consistency, and distributed transaction invariants section to AGENTS.md, placed directly after Orientation so every agent reads it before touching payment code, plus an implementation-specific subsection (gonuts fork maintenance, bbolt persistence, counter ownership, WalletPort/sidecar, pure-Go/OpenWrt constraint).

Why: cross-implementation audit of Go vs Rust/CDK found the same invariant classes behind #257, #266, #375, #403, #417, #480 recurring because the knowledge lived in issue threads, not in agent guidance. Encoded rules: derivation outputs never re-exposed; counters monotonic, reserved before the exposing call; canonical mint identity at every persistence boundary; validate-before-receive; irreversible-op + fallible-work means durable forward recovery or compensation; ambiguous outcomes reconciled not blind-retried; idempotent retries; partial successes never discarded; process-memory state not authoritative; migrations preserve value per-item; research-first rule for protocol-sensitive changes (NUTs, CDK/gonuts source, issue history).

Docs-only change; verified against upstream/main.

…ion invariants

Teach agents the invariants behind #257/#266/#375/#403/#417/#480:
derivation-output non-reuse, monotonic counters, canonical mint
identity, irreversible-op ordering, ambiguous-outcome reconciliation,
process-memory non-authority, and the research-first protocol rule.
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

Verification: PR #493 (fund-safety invariants section in AGENTS.md)

Tested as-is at head d4cf8d7df081ac287ff8b7dbd0746e4a937fb5d1 (MERGEABLE, docs-only, +118 lines to AGENTS.md). I checked every descriptive claim about the code as merged today — including the gonuts-tollgate fork at the version main now pins (v0.12.1 via #506, merged today) — and I cross-checked each invariant against what the merged tree actually provides, since this doc is the reference the P1 audit series points at. The rules are stated as obligations (not as claims of current behavior), and the two biggest ones are indeed still open audit gaps — worth saying explicitly here so nobody reads the doc as a status report.

Descriptive claims — all verified

  • SaveKeyset's monotonic guard and mergeMintURLAliases in gonuts-tollgate: both exist in the pinned v0.12.1 — the guard is wallet/storage/bolt.go:507-518 (a freshly-fetched record with Counter 0 can never rewind a higher persisted counter; the comment even names the restart-path AddMint as the tollwallet: service restart permanently breaks swaps — 'Duplicate outputs' against any mint with prior swap history #480 trigger), and mergeMintURLAliases is in the same file, matching the "counter is keyset state inside the keyset record / mint-URL-named buckets / no cross-op bbolt transaction" description.
  • Counter reserved before the exposing call: provided by chore(deps): bump gonuts-tollgate v0.11.2 → v0.12.0 (melt/swap-retry counter reservation + #480 landing) #506 (v0.12.0/v0.12.1 — swap-retry range reserved before re-exposure, melt blank-output range reserved before PostMeltBolt11), with wallet_swap_retry_counter_test.go / wallet_melt_counter_test.go / wallet_writer_audit_test.go in the fork pinning it.
  • Validate-before-Receive: clientRegisteredForGate is literally commented "the pre-Receive pre-flight of issue Payment consumed with no session when gate-open fails after successful Receive (no refund path) #403" and runs before m.tollwallet.Receive (line 519); NUT-00 verification quote sits above Receive; swap-fee classification (isBelowSwapFeeError) matches merchant_swap_fee_test.go.
  • normalizeMintURL at every identity layer: registeredMints is keyed through it in tollwallet.go, accepted-mint comparison likewise.
  • WalletPort/sidecar: port.go is the seam, cdk_wallet.go sits behind the cdk_wallet build tag, sidecar.go speaks AF_UNIX, and ErrSidecarAmbiguous exists with exactly the documented reconcile-don't-retry contract. quote_store.go (merchant) persists Lightning quotes. All as stated.
  • Cloud-lab lanes: fees (test_swap_fees.py), keyset rotation (run-keyset-rotation.sh, test_keyset_rotation.py), mint failure (test_mint_failure.py) all exist. The "restart loses session metering" honesty note matches the merged session manager (memory-only).

Invariants vs merged code — the audit gaps (consistent with the doc's framing, stating them for the record)

Both gaps are exactly what the audit series tracks, so the doc is honest as written; flagging so reviewers don't mistake the rules for implemented behavior.

Factual nits to fix while you're in there

Docs-only: no battery impact (branch is based on pre-#506 main, pins v0.11.2, but that's irrelevant for a docs merge). Consider a one-line Changed / Internal CHANGELOG bullet for the new agent guidance (repo rule says doc-only one-liners may be batched but not skipped; precedent is mixed).

Disposition: land-with-followups — fix the "replace directive" wording (and optionally the patch count) in a quick amend; the invariant content itself is accurate against the merged tree, with the two open gaps correctly tracked by #507/#511.

@Amperstrand
Amperstrand merged commit 466c36d into main Sep 22, 2026
@Amperstrand
Amperstrand deleted the agents-fund-safety-invariants branch October 11, 2026 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant