Sequence Diagrams¶
Flows as actually implemented — node names are the files in
infrastructure/graph/nodes/, use cases the modules in application/.
1. Full happy mission (create → settle → verify → reconcile)¶
sequenceDiagram
autonumber
participant B as Browser / demo driver
participant N as nginx (bearer injection)
participant API as FastAPI routers
participant G as LangGraph (thread_id = mission_id)
participant UC as application use cases
participant PG as PostgreSQL
participant PM as providers-mock
participant RM as Rain adapter (mock)
B->>N: POST /api/missions (idempotency key, tokenless)
N->>API: add Authorization bearer
API->>UC: create_mission (idempotency claim w/ lease)
UC->>PG: mandate and pools and funding journal (allocate)
B->>N: POST /missions/id/run
API->>PG: durable admission BEFORE 202 (advisory lock, loser → 409)
API->>G: ainvoke (AsyncPostgresSaver, durability=sync)
G->>UC: compile_mandate → plan_evidence → discover_providers
par Send fan-out per planned audition
G->>UC: run_audition (x402: claim rail_operation → pay PM → grade vs backend answer key)
UC->>PM: 402 → PAYMENT-SIGNATURE retry → result labels
G->>UC: run_audition (trial: LegacyBatch trial endpoint → grade)
end
G->>UC: assess_quorum → select_provider (floor-reject first, term breakdown persisted)
G->>UC: propose_payment → authorize_and_reserve (ONE UoW: lock order → policy → reserve journal and grant reservation)
G->>UC: prepare_rail (claim-then-act: rail_operation committed, card issued via RM)
Note over RM: scope proof: wrong-merchant probe while active → REAL merchant_not_allowed decline (ledgered)
G->>UC: execute_payment (leased submit claim → authorization) → confirm_settlement (reserved→spent journal, receipt)
G->>UC: retire_credential
Note over RM: post-retirement re-auth probe → REAL card_inactive decline (ledgered)
G->>UC: verify_outcome (holdout grading 9640bp) → reconcile (identity holds)
UC->>PG: every step appends hash-chained ledger events (NOTIFY)
PG-->>B: SSE /missions/id/events (projected, Last-Event-ID resume)
2. Two-stage attack (prompt defense, then simulated total compromise)¶
sequenceDiagram
autonumber
participant G as LangGraph
participant BUY as buyer LLM role
participant UC as use cases
participant POL as PolicyEngine (deterministic)
participant PG as PostgreSQL
Note over G: LegacyBatch checkout response embeds the injection text
G->>BUY: parse provider response
BUY-->>G: flags=[provider_upsell_injection], NO proposal
G->>UC: record_injection_flags (ledger BEFORE the gate — interrupt-safe)
G->>G: demo_attack_gate (mission.attack_simulation, durable column)
alt simulate branch (labeled compromise switch)
G->>UC: propose_payment — fixed schema-valid malicious output<br/>$5.00 GiftCardHub via the NORMAL use case (fresh proposal id)
UC->>POL: authorize_and_reserve → evaluate
POL-->>UC: block (security): WithinEnvelope PASS,<br/>merchant_not_allowed and outside_mandate_scope and task_link_missing
UC->>PG: policy_evaluation appended, mission-scoped grant revoked,<br/>ZERO rail calls, no credential, nothing reserved
else skip
G->>UC: reconcile
end
G->>UC: reconcile (blocked amount and reasons in the evidence ledger)
3. x402 paid audition (exact scheme, Monad testnet)¶
sequenceDiagram
autonumber
participant UC as run_audition
participant OPS as rail_operations (PG)
participant PM as provider (x402 resource server)
participant F as facilitator (ladder: molandak → self-hosted → local mock)
participant CH as Monad testnet (eip155:10143)
UC->>OPS: claim (INSERT..ON CONFLICT, owner and lease) and persist EXACT signed payload, COMMIT
UC->>PM: GET audition
PM-->>UC: 402 PAYMENT-REQUIRED (accepts: exact / eip155:10143 / USDC, extra echoed)
UC->>UC: sign EIP-3009 TransferWithAuthorization (payload persisted BEFORE call)
UC->>PM: retry with PAYMENT-SIGNATURE and payment-identifier pay_UUID
PM->>OPS: replay check (same id and hash → cached, mutate → 409)
PM->>F: POST /verify
F->>CH: signature / balance / nonce checks
PM->>PM: run the paid job (grade-free: labels only)
PM->>F: POST /settle → transferWithAuthorization (facilitator pays gas)
F-->>PM: tx hash
PM-->>UC: 200 with PAYMENT-RESPONSE (settlement)
UC->>CH: eth_getTransactionReceipt → explorer URL persisted
UC->>OPS: CAS finalize (succeeded), receipt row, grading vs backend answer key
Note over UC,OPS: retry after crash reuses the byte-identical persisted payload — never re-signs
4. Crash recovery + webhook-driven reconciliation¶
sequenceDiagram
autonumber
participant W as recovery worker (jobs/payment_recovery)
participant OPS as rail_operations
participant RW as /webhooks/rain receiver
participant INBOX as webhook inbox (PG)
participant UC as reconcile_rail_status
participant RAIL as rail (query by idempotency key)
Note over OPS: process died mid-operation (submit / freeze / retire)
W->>OPS: sweep unfinished ops — expired lease → take over (owner fence bump)
W->>RAIL: query-before-act with the SAME operation id
alt remote effect exists
W->>UC: resume from persisted payload → finalize (exactly one effect)
else never executed
W->>UC: re-execute claim's call → finalize
end
RW->>INBOX: event stored (status=received, payload hash, dup id and different hash → 409 incident)
INBOX->>INBOX: business transition and processed mark in ONE transaction
INBOX->>UC: persist reconciliation job (idempotent per attempt)
W->>UC: execute jobs for pending / reconciling / settlement_unknown attempts
UC->>RAIL: query settlement
alt settled
UC->>UC: confirm_settlement FIRST (reserved→spent) → retire
else proven failed
UC->>UC: compensate (release and void)
else still unknown
UC->>UC: hold — reservation kept, case stays open
else mismatch
UC->>UC: security incident — freeze card (real lifecycle call), never auto-release
end
5. Escalation approval (interrupt → typed approval → re-authorize)¶
sequenceDiagram
autonumber
participant G as LangGraph
participant API as approval endpoint
participant H as human approver
participant UC as authorize_and_reserve
G->>G: escalate node → interrupt() (checkpointed, nothing reserved)
H->>API: POST /missions/id/approve {mission_id, proposal_id, proposal_version,<br/>merchant_amount_digest, approver, decided_at}
API->>API: bare {"approved": true} → 422, first terminal decision wins (partial unique)
API->>G: Command(resume=typed payload) on the same thread_id
G->>UC: RE-ENTER authorize_and_reserve — full policy re-check<br/>(budget/grant/price/scope may have changed during the wait)
alt still allow
UC-->>G: reserved → purchase saga proceeds
else changed
UC-->>G: block — approval never bypasses the gate
end