Skip to main content
Each exercise below provokes one of the threats in the threat model on purpose and checks what happens: what is refused, what reaches the chain, what the sponsor pays, and what the service reports. The exercises are tests in the repository. Most run against a private Stellar network with 1-second ledgers and the testnet passphrase, where an RPC proxy can hold, drop or inject transactions at a chosen moment; the public RPC offers no such hook. Where a conformance harness exercises the same behaviour against the public testnet, its recorded outcome is shown next to the local one.

Running the exercises

From a source checkout with the local services of docker-compose.yml running:
ops.ts runs every exercise’s test files with Vitest, joins each exercise’s results with what its tests recorded (transaction hashes, fees charged, refusal codes), and prints one evidence document with a case per exercise. --only balance-race,crash-k0 runs a subset. --write records the run in tools/conformance/evidence/ops-local-stellar-testnet.json, only from a clean checkout of main, and pnpm report renders the sections below from it. A run in which any exercise could not run (a local service down, or no Docker for the short-TTL network) exits non-zero and records nothing; --allow-skips records it anyway, with each skipped exercise and its reason, for a rehearsal.

Recorded run

From tools/conformance/evidence/ops-local-stellar-testnet.json: 45 of 45 exercises passed.

Exercises

replay-sequential A settled payment sent to /settle again

Threats R1 · Private Stellar network (1 s ledgers) and Postgres Procedure. Settle an exact payment, then send the same payload and requirements again, with the in-memory and the Postgres settlement ledger. Expected. The repeat is answered with the recorded response and a repeat marker; one transfer on chain; nothing is submitted again. Tests Observed. Passed (20,366 ms).
  • answers a sequential repeat with the recorded response, marked as a repeat: 2 of 2 passed in 14.4 s
  • answers a repeated settle with the original result and moves funds once: 1 of 1 passed in 5.9 s
On the public testnet
  • live-matrix case replay-idempotent: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • live-matrix case replay-late: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

replay-concurrent Concurrent copies of one payment

Threats R1, S6 · Private Stellar network (1 s ledgers) and Postgres Procedure. Send five copies of one payment to /settle at the same moment. Expected. One transaction, one transfer; every caller gets the same body; four answers are marked as repeats. Tests Observed. Passed (20,889 ms).
  • settles 5 concurrent repeats once: one transfer, one original, identical bodies: 2 of 2 passed in 14.2 s
  • settles concurrent duplicates exactly once, all callers getting the same result: 1 of 1 passed in 6.7 s
On the public testnet
  • live-matrix case replay-concurrent: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

replay-new-envelope A settled authorization in a new envelope

Threats R2 · Private Stellar network (1 s ledgers) and Postgres Procedure. Re-wrap a settled authorization entry in a transaction with different bytes and settle it. Expected. Refused with the idempotency conflict naming the recorded transaction; nothing is submitted. Tests Observed. Passed (22,675 ms).
  • refuses the same authorization in another envelope, naming the recorded transaction: 2 of 2 passed in 16.5 s
  • refuses a settled authorization in a different envelope, and verify names the replay: 1 of 1 passed in 6.2 s
On the public testnet
  • live-matrix case replay-rejected: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

replay-upfront One payment signature sent twice to a seller

Threats R4 · Private Stellar network (1 s ledgers) Procedure. Pay a seller built with the seller helpers, then send the same PAYMENT-SIGNATURE again, in the authorization and the upfront flow. Expected. The resource is served once; the second request is refused; the facilitator’s repeat marker is honoured. Tests Observed. Passed (6,596 ms).
  • does not serve a replayed payment signature again: 1 of 1 passed in 0.0 s
  • serves an upfront settlement once when the facilitator answers its repeat: 1 of 1 passed in 6.6 s

front-run A copied exact authorization lands first

Threats F1, F2 · Private Stellar network (1 s ledgers) Procedure. The RPC proxy runs an attacker right before Rail402’s sendTransaction is forwarded: the attacker copies the payer’s authorization entry into its own transaction and lands it. Expected. Rail402’s transaction fails on chain; the answer is a success with the attacker’s hash; the payer is debited once; exactly one wasted fee equal to the sponsor’s balance change. Tests Observed. Passed (7,380 ms).
  • answers success with the attacker’s hash; the payment moves once and our fee is wasted: 1 of 1 passed in 7.3 s
  • refuses an authorization consumed before the request was claimed: 1 of 1 passed in 0.0 s
  • refuses when the consuming transaction carried a different invocation for the same nonce: 1 of 1 passed in 0.0 s
  • Recorded: attackerTransaction aaf96c55cb951ffc68fab61c0338497613eaa06213c1f20994aacea197fde281, ourFailedTransaction bd8e15bf82001bc667f07088a912a94466d78ad572bac4fea601b27bb913108d, answer success with the attacker's hash, wastedFeeStroops 76214, payerDebitedOnce 1000000

front-run-upto Front-running an upto settlement

Threats F3, F4 · Private Stellar network (1 s ledgers) Procedure. An attacker without the settlement authority copies an upto settlement and submits it first; then a transaction signed by the authority itself lands first. Expected. The attacker’s copy fails and Rail402’s settlement proceeds; a landing by the authority for the same amount is answered with its hash. Tests Observed. Passed (20,704 ms).
  • cannot be landed by an attacker without the settlement authority; Rail402’s settlement proceeds: 1 of 1 passed in 8.5 s
  • answers with the authority’s own earlier transaction for the same amount; our fee is wasted: 1 of 1 passed in 12.2 s

smart-account-signer A smart-account payment signed by a key the account does not accept

Threats A1 · Private Stellar network (1 s ledgers) and Postgres Procedure. Sign an exact payment from a custom account (C…) with a random key, and an upto payment from a custom account with another key, and send them to /verify and /settle. Expected. Both are refused before anything is submitted, named as the account’s own rejection (smart_account_rejected or invalid_signature); nothing moves. Tests Observed. Passed (81 ms).
  • rejects a smart-account payment signed by the wrong key: 1 of 1 passed in 0.1 s
  • refuses a signature by another key as the account’s own rejection: 1 of 1 passed in 0.0 s
On the public testnet
  • live-matrix case c-payer-wrong-key: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • rejection-audit case upto-c-payer-wrong-key: Passed (tools/conformance/evidence/rejection-audit-hosted-stellar-testnet.json)

smart-account-budget An OpenZeppelin smart account paying with an agent key under a spending limit

Threats A4, A5, A6 · Private Stellar network (1 s ledgers) and Postgres Procedure. Deploy an OpenZeppelin smart account whose agent key may only pay through a rule with the spending-limit policy, pay upto within the budget, then over it, then try to use the owner’s policy-free default rule with the agent key. Expected. Payments within the budget settle and reserve their full cap; a payment over the limit and a cap that would exceed the remaining budget are refused with nothing moved; the agent key cannot use the default rule. Tests Observed. Passed (1,383 ms).
  • refuses a payment over its own spending limit, and nothing moves: 1 of 1 passed in 0.2 s
  • has reserved the full cap of each payment against its budget: 1 of 1 passed in 0.0 s
  • does not let the agent key use the owner’s policy-free default rule: 1 of 1 passed in 0.1 s
  • pays up to its budget and refuses the cap that would exceed it: 1 of 1 passed in 1.1 s
On the public testnet
  • live-matrix case upto-c-over-limit: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

smart-account-apply-failure A smart account whose __check_auth passes simulation and fails at inclusion

Threats A2 · Private Stellar network (1 s ledgers) Procedure. Pay exact from an OpenZeppelin smart account with a delegate key under its own context rule, then, through the RPC proxy between the settle-time simulation and the broadcast, have the owner remove that rule. Expected. The transaction fails on chain in the account’s __check_auth, is named smart_account_rejected, moves nothing, and its fee is the only sponsor cost, counted as wasted. Tests Observed. Passed (19,134 ms).
  • names a smart account whose __check_auth passes the settle-time simulation and fails at inclusion: 1 of 1 passed in 19.1 s
  • Recorded: payer CAMV6HX56SB3DFE4HVLBQ7RWHGSE2ZJH7HQSQ6D464AQIM7Y7AUCD5ID, phase between the settle-time simulation and inclusion, transaction 58c5be34c7a935f9270f856f35be054d45c6e2ead9d629657cf8cbd898ba64d6, ledger 3685, settle invalid_exact_stellar_payload_smart_account_rejected, wastedFeeStroops 74145, sponsorDeltaStroops -74145

balance-race The payer spends the balance before settlement lands

Threats S1 · Private Stellar network (1 s ledgers) Procedure. Verify a payment, then move the payer’s whole balance away: once before /settle, and once through the RPC proxy between the settle-time simulation and the broadcast. Expected. Before settle: refused as insufficient_funds with nothing submitted. After simulation: the transaction fails on chain, is named insufficient_funds, and its fee is the only sponsor cost, counted as wasted. Tests Observed. Passed (24,096 ms).
  • refuses at settle, with nothing submitted, a payment whose balance moved after verify: 1 of 1 passed in 11.2 s
  • names a balance spent between the settle-time simulation and inclusion; the fee is spent and counted as wasted: 1 of 1 passed in 12.9 s
  • Recorded: phase between verify and settle, settle insufficient_funds, submissions 0, sponsorDeltaStroops 0
  • Recorded: phase between the settle-time simulation and inclusion, transaction 81cd00f0bf9442d18027eaaa365cff0860fa5228086b965d852f4e683e239f45, ledger 3666, settle insufficient_funds, wastedFeeStroops 76216, sponsorDeltaStroops -76216

expiry-race An authorization that expires while the settlement waits

Threats S2 · Private Stellar network (1 s ledgers) Procedure. Hold an exact settlement at the RPC proxy until its authorization’s expiration ledger has passed; hold an upto settlement past its expiration_ledger. Expected. exact: included and failed, named signature_expired, fee spent. upto: refused by the network before inclusion (ledger bounds), no fee spent. Tests Observed. Passed (27,027 ms).
  • names an authorization that expires between submission and inclusion; the fee is spent: 1 of 1 passed in 8.9 s
  • refuses at the network an upto settlement held past expiration_ledger; no fee is spent: 1 of 1 passed in 9.5 s
  • holds the lower edge at the current ledger plus a one-ledger margin: 2 of 2 passed in 8.6 s
  • Recorded: scheme exact, expirationLedger 3492, transaction a91ff6bb7c7b787672c13b01a3165dcba29a6f1863c87233206e87b5a3ee11c6, settle invalid_exact_stellar_signature_expired, wastedFeeStroops 76216
On the public testnet
  • live-matrix case expiry-window: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • live-matrix case upto-expiry-window: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

ceiling-refusal A settlement above the fee ceiling

Threats S3 · Private Stellar network (1 s ledgers) Procedure. Configure a fee ceiling below the payment’s simulated fee and send it to /verify and /settle. Expected. Both refuse with invalid_exact_stellar_payload_fee_exceeds_maximum; nothing is recorded or submitted; the sponsor’s balance is unchanged. Tests Observed. Passed (6,129 ms).
  • refuses a payment whose settlement would cost more than the fee ceiling, before signing anything: 1 of 1 passed in 6.1 s
  • enforces the fee ceiling: 1 of 1 passed in 0.0 s
  • Recorded: ceilingStroops 20000, verify invalid_exact_stellar_payload_fee_exceeds_maximum, settle invalid_exact_stellar_payload_fee_exceeds_maximum, submissions 0, sponsorDeltaStroops 0

budget-trip The hourly fee budget is reached

Threats S4 · Private Stellar network (1 s ledgers) Procedure. Set an hourly fee budget that one settlement exhausts; settle one payment, then another. Expected. The first settles; the second is refused with settle_exact_stellar_sponsor_unavailable before any RPC work, and the refusal is counted as a budget refusal. Tests Observed. Passed (9,755 ms).
  • stops settling once the hourly fee budget is committed, before any RPC work: 1 of 1 passed in 9.8 s
  • refuses once the hourly fee budget is committed: 1 of 1 passed in 0.0 s
  • Recorded: budgetStroops 1, firstSettlement e22186df586e5380bfc7ade5437468ac057262300287ff9608236a65701be1e2, refusal settle_exact_stellar_sponsor_unavailable, submissionsAfterTrip 0
Threats S5 · The Rail402 service on the private network Procedure. Run the service with a reserve floor above the sponsor’s balance, and with a readiness floor above it. Expected. Settlements are refused and counted as reserve refusals; /health reports the sponsor below its floor; /ready fails. Tests Observed. Passed (7,337 ms).
  • counts refusals below the reserve and reports the sponsor below its floor: 1 of 1 passed in 5.1 s
  • fails while the sponsor holds less than MIN_SPONSOR_BALANCE_XLM: 1 of 1 passed in 2.2 s

ssrf-origin Origin checks and SEP-1 fetches to private addresses

Threats X1, X5 · Local sockets and a stub resolver Procedure. Catalog resources whose URLs are private IP literals or names that resolve to private addresses; fetch a stellar.toml from a private address. Expected. Refused before any connection is made, unless loopback is explicitly allowed; never with pubnet. Tests Observed. Passed (11 ms).
  • refuses a host whose DNS answers are not public, before connecting: 1 of 1 passed in 0.0 s
  • refuses a private IP literal for origin checks unless loopback is allowed: 1 of 1 passed in 0.0 s
  • never connects to a private address unless loopback is explicitly allowed: 1 of 1 passed in 0.0 s
  • refuses loopback discovery fetches on a service that serves pubnet: 1 of 1 passed in 0.0 s

ssrf-rebinding DNS rebinding

Threats X2 · Local sockets and a stub resolver Procedure. Resolve a name to a public and a private address, and to a private address on the second lookup. Expected. The private address is never dialled. Tests Observed. Passed (5 ms).
  • checks every DNS answer, so a rebinding name cannot reach a private address: 1 of 1 passed in 0.0 s

ssrf-redirect Redirects to private or other destinations

Threats X3 · Local sockets and a stub resolver Procedure. Answer an origin check and a buyer request with redirects to a private address and to another origin. Expected. Origin checks follow no redirect; buyers follow none to a private or other origin, and none after paying. Tests Observed. Passed (6 ms).
  • never follows a redirect towards a private or other destination: 1 of 1 passed in 0.0 s
  • does not follow redirects: 1 of 1 passed in 0.0 s
On the public testnet
  • rejection-audit case mcp-redirect-private: Passed (tools/conformance/evidence/rejection-audit-hosted-stellar-testnet.json)

ssrf-oversized Oversized, compressed and slow answers

Threats X4 · Local sockets and a stub resolver Procedure. Serve a body larger than the cap, a compression bomb, and answers that stall. Expected. Stopped at the decoded size limit and at the header, idle and total timeouts. Tests Observed. Passed (645 ms).
  • stops oversized and compressed-bomb answers at the decoded limit: 1 of 1 passed in 0.0 s
  • times out waiting for headers, on an idle body, and in total: 1 of 1 passed in 0.6 s

catalog-takeover Another payTo pays for a listed resource

Threats P2 · The Rail402 service on the private network Procedure. After a resource is listed, settle a payment for the same URL to a different payTo through a real seller. Expected. The listing is unchanged; the settlement reports bazaar_owner_conflict in EXTENSION-RESPONSES. Tests Observed. Passed (13,359 ms).
  • refuses a different payTo for the same resource, with a coded reason: 1 of 1 passed in 13.0 s
  • never lets a different payTo modify a listing: 1 of 1 passed in 0.1 s
  • transfers ownership only when the origin names the new payTo: 1 of 1 passed in 0.2 s
On the public testnet
  • live-bazaar case owner-conflict: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

forged-metadata Echoed metadata that the origin does not confirm

Threats P1 · Postgres Procedure. Settle payments whose resource block carries a changed description and price. Expected. The listing changes only when the resource’s own 402 confirms the new metadata. Tests
  • packages/bazaar/src/testing.ts: does not let echoed metadata change a listing until the origin confirms it (run by packages/store-postgres/test/stores.integration.test.ts)
  • packages/bazaar/src/testing.ts: never lists an HTTP resource whose own response is not a 402 (run by packages/store-postgres/test/stores.integration.test.ts)
Observed. Passed (254 ms).
  • does not let echoed metadata change a listing until the origin confirms it: 1 of 1 passed in 0.1 s
  • never lists an HTTP resource whose own response is not a 402: 1 of 1 passed in 0.1 s
On the public testnet
  • live-bazaar case forged-metadata: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

mcp-tool-planting A payer plants content under a seller’s MCP tool

Threats P9 · Postgres Procedure. Pay a seller’s payTo one base unit, exact and upto under a huge cap, echoing the seller’s MCP endpoint and tool with a forged description and price; then settle the tool’s real price. Expected. The planted listing stays pending and is quarantined with origin_payment_mismatch; the real settlement publishes the tool’s own content, and a planted settlement never changes or withdraws a confirmed tool. Tests Observed. Passed (465 ms).
  • never publishes a tool description or price planted with: 2 of 2 passed in 0.3 s
  • never lets a planted settlement withdraw or change a tool its origin confirmed: 1 of 1 passed in 0.1 s
  • reads a tool’s PaymentRequired from its unpaid call, sending the example arguments: 1 of 1 passed in 0.1 s
On the public testnet
  • live-bazaar case mcp-tool: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

schema-redos Hostile JSON Schema

Threats P3 · In-process, with the RPC or store simulated Procedure. Validate metadata against schemas with catastrophic patterns, combinatorial checks, deep nesting and external references. Expected. Each stops within its budget or is refused before compiling; the sandbox keeps serving. Tests Observed. Passed (359 ms).
  • stops a catastrophic-backtracking pattern within its budget and keeps serving: 1 of 1 passed in 0.3 s
  • stops a combinatorial uniqueItems check within its budget: 1 of 1 passed in 0.0 s
  • finds external references in every schema position, and only there: 1 of 1 passed in 0.0 s
On the public testnet
  • live-bazaar case schema-rejected: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

route-traversal Encoded traversal in route templates and parameters

Threats P4 · Postgres Procedure. Catalog templates that decode to NUL, CR, LF, a backslash or an empty segment; fill parameters with encoded dot segments. Expected. Unsafe templates are not used; unsafe parameter values are refused before any request. Tests Observed. Passed (164 ms).
  • refuses route templates that decode to NUL, CR, LF, a backslash or an empty segment: 1 of 1 passed in 0.0 s
  • uses a routeTemplate only when it is safe and matches the paid path: 1 of 1 passed in 0.2 s
  • fills route parameters and refuses traversal: 1 of 1 passed in 0.0 s
On the public testnet
  • live-bazaar case route-template: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

crash-k0 Killed after the network accepted the transaction

Threats O1 · Rail402 service processes over Postgres on the private network, killed with SIGKILL Procedure. SIGKILL the service right after sendTransaction returned; restart it. Expected. Recovery finds the accepted transaction; no replacement is submitted; one transfer. Tests Observed. Passed (14,456 ms).
  • K0: finds the transaction the network accepted before the process died: 2 of 2 passed in 14.5 s
On the public testnet
  • fault-recovery case k0-exact: Passed (tools/conformance/evidence/fault-recovery-stellar-testnet.json)
  • fault-recovery case k0-upto: Passed (tools/conformance/evidence/fault-recovery-stellar-testnet.json)

crash-k1 Killed before the broadcast left

Threats O1 · Rail402 service processes over Postgres on the private network, killed with SIGKILL Procedure. Hold the broadcast at the RPC proxy, SIGKILL the service, drop the held request, restart. Expected. Recovery rebroadcasts the identical recorded bytes; one transaction hash; one transfer. Tests Observed. Passed (14,580 ms).
  • K1: rebroadcasts the identical recorded bytes when the broadcast never left: 2 of 2 passed in 14.6 s
On the public testnet
  • fault-recovery case k1-exact: Passed (tools/conformance/evidence/fault-recovery-stellar-testnet.json)
  • fault-recovery case k1-upto: Passed (tools/conformance/evidence/fault-recovery-stellar-testnet.json)

crash-k2 Killed after inclusion, before confirmation

Threats O1 · Rail402 service processes over Postgres on the private network, killed with SIGKILL Procedure. Hide the transaction from getTransaction until the service is killed; restart. Expected. Recovery finds the included transaction and answers its success. Tests Observed. Passed (15,383 ms).
  • K2: finds a transaction that was included before the process saw it confirmed: 2 of 2 passed in 15.4 s

crash-k3 Down until the time bound passed

Threats O1 · Rail402 service processes over Postgres on the private network, killed with SIGKILL Procedure. Drop the broadcast, keep the service down until the transaction’s time bound has passed, restart. Expected. The settlement expires only because retained history proves it absent; nothing moves. Tests Observed. Passed (41,102 ms).
  • K3: expires a transaction whose time bound passed while the service was down, and nothing moves: 1 of 1 passed in 41.1 s
On the public testnet
  • fault-recovery case k3-exact: Passed (tools/conformance/evidence/fault-recovery-stellar-testnet.json)

crash-unresolvable A settlement beyond the RPC’s retention

Threats O4 · Rail402 service processes over Postgres on the private network, killed with SIGKILL Procedure. Restart with a recorded settlement whose ledgers the RPC no longer retains. Expected. It stays unresolved and is reported in /health and the metrics; it is never expired. Tests Observed. Passed (1,820 ms).
  • keeps a settlement the RPC can no longer prove absent unresolved, and reports it: 1 of 1 passed in 1.8 s

concurrency Three batches of twenty concurrent settlements

Threats O2, S6 · Private Stellar network (1 s ledgers) Procedure. Settle 3 batches of 20 payments over 8 channels, exact, upto and mixed, with duplicates injected into every batch. Expected. Every payment settles once; distinct (source, sequence) pairs; no txBadSeq or TRY_AGAIN_LATER. Tests Observed. Passed (101,731 ms).
  • with injected duplicates, without a collision: 3 of 3 passed in 101.7 s
On the public testnet
  • concurrency case distinct-sequences: Passed (tools/conformance/evidence/concurrency-mixed-stellar-testnet.json)

replicas Two replicas on one Postgres

Threats O2, R1 · Two Rail402 service processes over one Postgres on the private network Procedure. Run two service replicas on one Postgres and send every duplicate to the other replica. Expected. One settlement per payment across replicas; no channel is leased twice. Tests Observed. Passed (31,632 ms).
  • with every duplicate sent to the other replica: 1 of 1 passed in 31.6 s

seq-conflict A channel used behind the service’s back

Threats O2 · The Rail402 service on the private network Procedure. Submit a transaction from a channel account outside the service, then settle through it. Expected. The conflict is counted in rail402_sequence_conflicts_total. Tests Observed. Passed (15,773 ms).
  • counts a sequence conflict when a channel’s sequence is used behind its back: 1 of 1 passed in 15.8 s

rpc-outage The RPC stops answering

Threats O3 · The Rail402 service on the private network Procedure. Make the RPC proxy answer errors, then recover. Expected. /ready fails while the RPC is unreachable; transport errors are counted and reported in /health. Tests Observed. Passed (4,535 ms).
  • fails while the RPC is unreachable: 1 of 1 passed in 3.5 s
  • counts transport and request errors while the RPC fails, and reports it in /health: 1 of 1 passed in 1.0 s

store-outage The settlement store fails

Threats O3 · Private Stellar network (1 s ledgers) and Postgres Procedure. Fail the envelope write before broadcast; fail the ledger read at verify; close Postgres’ idle connections. Expected. Nothing is broadcast without a record and the payment stays retryable; verify stays available; the pool reconnects. Tests Observed. Passed (20,143 ms).
  • broadcasts nothing when the envelope cannot be recorded first, leaving the payment retryable: 1 of 1 passed in 6.0 s
  • keeps verify available when the ledger cannot be read: 2 of 2 passed in 14.0 s
  • survives the server closing its idle connections, as in a restart or failover: 1 of 1 passed in 0.1 s

channel-exhaustion Every channel busy

Threats O5 · Private Stellar network (1 s ledgers) Procedure. Hold every channel and settle; send more settlements than channels to a service. Expected. A retryable channel_unavailable refusal with no record left; lease waits are measured. Tests Observed. Passed (23,504 ms).
  • refuses when no channel frees up in time, leaving the payment retryable: 1 of 1 passed in 7.0 s
  • measures the wait for a channel when settlements outnumber channels: 1 of 1 passed in 16.5 s

ttl-extend Extending the upto contract’s TTL

Threats U7 · The Rail402 service on the private network Procedure. Let the keeper and the operator command extend the settlement contract’s instance and code entries. Expected. Both entries’ liveUntilLedgerSeq move; /health and the TTL gauge report the new values. Tests Observed. Passed (4,779 ms).
  • extends the contract from the keeper and the operator command, and reports its TTL: 1 of 1 passed in 4.8 s

ttl-restore An archived upto contract

Threats U7, S7 · A dedicated Stellar network with short TTLs, and the service Procedure. Let the settlement contract’s entries expire, pay while archived, then restore by the operator command and by the keeper. Expected. Payments are refused with invalid_upto_stellar_settlement_contract_archived and nothing is submitted; after restoration they settle. Tests Observed. Passed (69,868 ms).
  • refuses payments while the contract is archived, and settles once an operator restores it: 1 of 1 passed in 25.9 s
  • is restored by the TTL keeper where automatic restoration is allowed: 1 of 1 passed in 44.0 s

degraded-indexing The embedder fails

Threats O7 · In-process, with the RPC or store simulated Procedure. Make the embedding model throw or time out during search and index builds. Expected. Search answers lexical-only with partialResults: true; index builds are counted as degraded. Tests Observed. Passed (22 ms).
  • answers lexical-only, as partial results, when the embedder fails or runs out of time: 1 of 1 passed in 0.0 s
  • builds a lexical-only snapshot when listings cannot be embedded: 1 of 1 passed in 0.0 s

mcp-cap Paid calls above the agent’s caps

Threats M1 · Private Stellar network (1 s ledgers) Procedure. Ask rail402-mcp and the guarded signer to pay above the per-call cap; run concurrent payments against one session budget. Expected. Refused before signing; the session budget is never exceeded; balances unchanged. Tests Observed. Passed (96 ms).
  • refuses a price above the caller’s cap before signing: 1 of 1 passed in 0.0 s
  • refuses to sign a payment above the per-call cap: 1 of 1 passed in 0.0 s
  • never lets 20 concurrent payments exceed the session budget: 1 of 1 passed in 0.1 s
On the public testnet
  • rejection-audit case mcp-per-call-cap: Passed (tools/conformance/evidence/rejection-audit-hosted-stellar-testnet.json)

mcp-ssrf Paid calls to private destinations

Threats X6 · Local sockets and a stub resolver Procedure. Ask the buyer to pay URLs on this machine in every IP spelling and a name that resolves to a private address. Expected. Refused before anything is sent, without naming the address. Tests Observed. Passed (3 ms).
  • refuses IP-literal URLs to this machine in every spelling, before connecting: 1 of 1 passed in 0.0 s
  • refuses private destinations before sending anything: 1 of 1 passed in 0.0 s
On the public testnet
  • rejection-audit case mcp-private-address: Passed (tools/conformance/evidence/rejection-audit-hosted-stellar-testnet.json)

mcp-secret-scan Canary key scan of the MCP server, SDK and CLI

Threats M3 · Private Stellar network (1 s ledgers) Procedure. Pay through every outcome with a canary signing key, capturing protocol frames, tool results, logs at trace level, stdout, stderr and the budget ledger. Expected. No seed, raw key bytes, signature or payment header appears in any captured channel. Tests Observed. Passed (216 ms).
  • leaks nothing into frames, logs or the ledger: 1 of 1 passed in 0.1 s
  • leaks nothing, and the payment reached only the paid origin: 1 of 1 passed in 0.1 s
  • leaks nothing on stdout, stderr or in the ledger: 1 of 1 passed in 0.1 s

upto-over-cap An upto settlement above the cap

Threats U1 · In-process, with the RPC or store simulated Procedure. Ask the upto facilitator to settle more than the signed cap. Expected. Refused before simulating, with the payer and a reason. Tests Observed. Passed (2 ms).
  • refuses an amount above the cap before simulating: 1 of 1 passed in 0.0 s
On the public testnet
  • live-matrix case upto-over-cap: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • rejection-audit case upto-settle-over-cap: Passed (tools/conformance/evidence/rejection-audit-hosted-stellar-testnet.json)

upto-zero Zero upto settlements

Threats R5, U6 · Private Stellar network (1 s ledgers) Procedure. Settle an upto payment for zero on chain, and off chain where configured. Expected. On chain: the nonce is consumed and no funds move. Off chain: no transaction, the authorization used up in the ledger. Tests Observed. Passed (8,382 ms).
  • settles zero on-chain: the nonce is consumed and no funds move: 1 of 1 passed in 4.7 s
  • settles zero off-chain when configured: no transaction, the authorization used up in the ledger: 1 of 1 passed in 3.7 s
On the public testnet
  • live-matrix case upto-zero: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • live-matrix case upto-c-zero: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • live-bazaar case upto-offchain-zero: Passed (tools/conformance/evidence/live-bazaar-stellar-testnet.json)

upto-concurrent Concurrent upto requests for different amounts

Threats U3, F5 · Private Stellar network (1 s ledgers) and Postgres Procedure. Send five settle requests for one upto authorization with different amounts at once; then settle a copy of a payload for zero before the seller settles it. Expected. One success, four idempotency conflicts, one transfer; the first accepted amount holds the authorization and the seller’s later request is a conflict. Tests Observed. Passed (18,243 ms).
  • settles 5 concurrent requests for different amounts once: one success, four conflicts: 2 of 2 passed in 10.7 s
  • lets the first settle request fix the amount: a payload holder’s zero turns the seller’s settle into a conflict: 1 of 1 passed in 7.5 s
On the public testnet
  • live-matrix case upto-repeat-different-amount: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)
  • live-matrix case upto-concurrent-same-authorization: Passed (tools/conformance/evidence/live-matrix-hosted-stellar-testnet.json)

upto-third-party-amount An upto settlement landed first by another holder of the authority

Threats F4 · Private Stellar network (1 s ledgers) Procedure. Land the same upto authorization from another process holding the authority, for the same and for a different amount. Expected. Same amount: success with that transaction. Different amount: settle_upto_stellar_settled_elsewhere naming both amounts. Tests Observed. Passed (18,501 ms).
  • succeeds with that transaction when it settled the same amount: 1 of 1 passed in 9.7 s
  • answers settled_elsewhere when it settled a different amount: 1 of 1 passed in 8.8 s

rotation-channels The channel steps of a sponsor rotation

Threats O6 · Private Stellar network (1 s ledgers) and Postgres Procedure. Run channels status, provision and retire against the network, with the memory store and with Postgres. Expected. Channels are provisioned and merged back into the sponsor; retire refuses while a channel is leased. Tests Observed. Passed (17,332 ms).
  • reports, provisions and retires channel accounts with STORE=memory: 1 of 1 passed in 9.8 s
  • reports durable state and refuses to retire while a channel is leased: 1 of 1 passed in 7.5 s