Skip to content

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