Skip to main content
This report records what has been reviewed in Rail402, what each review found, and how every finding was resolved. Each finding is an entry in docs/security/findings.json. A unit test fails when a finding has no severity or status, when a fixed finding has no fix commit on main or no regression test that still exists, when an accepted finding has no written acceptance, or when a critical or high finding is accepted rather than fixed. The release workflow refuses to publish a version tag while a critical or high finding is open or accepted.

Tested revision

The release tag, commit, images and packages above are the revision this report applies to: every fix commit below is an ancestor of that commit. The findings come from internal reviews of the code that shipped in those packages. Several were found and fixed before the affected component was first released; for those, the fix commit is the commit that introduced the component.

Scope and method

Each internal review read the code and the tests of the components in its scope, tried to break the invariants of the threat model with probes against a local network and the public testnet, and recorded every finding with a severity:
  • Critical: funds can be lost or stolen, or the facilitator becomes a source of funds.
  • High: a stated security property does not hold, for example an agent can spend beyond its cap.
  • Medium: a property holds only partly, or a wrong outcome is reported for a payment.
  • Low: a defect with a narrow precondition or a bounded cost.
  • Info: a note without a defect in Rail402.
A finding is closed by a fix with a regression test, or, for a medium, low or info finding, by a written acceptance that names the mitigations and the role that approved it.

Findings

Threat model

The threat model lists 170 threats: 146 mitigated, 23 accepted with a stated residual risk, 1 open. The operational test report shows the exercises that run the threat model’s tests, and the conformance report shows the runs against the testnet.

Accepted risks

Threats the threat model accepts, each with the risk that remains:

External audit

No third-party audit has been completed yet. When one is, its report is linked from the review table above and its findings are added to the register with the same rules.

Reporting a vulnerability

See SECURITY.md for how to report a vulnerability privately and which versions receive fixes.

Finding details

F-2026-001

The buyer budget could release an upto remainder without proof High · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M1 Budget reconciliation released the unspent part of an upto cap as soon as any transfer from the payer to the recipient at or below the cap appeared on chain, without proving that the transfer came from that authorization. A seller could make a session under-count what it spent and so exceed the session cap. Reconciliation now counts a payment only from a transfer made with the payment’s own authorization, proven by the payer’s consumed nonce. Fixed in c00699b. Tests

F-2026-002

Paid calls were cut off after 15 seconds of seller silence High · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M4 The SDK applied its probe’s 15-second idle timeout to the paid request, although paid HTTP and MCP calls are meant to wait up to two minutes. A slow seller settled the payment and the buyer lost the answer. Paid requests now wait for the paid answer under their own timeout. Fixed in c00699b. Tests

F-2026-003

A landed upto settlement could be reported as failed Medium · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats F2, O1 When a rebroadcast of a recorded settlement was refused as too late, the engine finished the settlement as failed with no funds moved, although the first broadcast could already have landed. The engine now keeps such a settlement pending and finishes it as failed only when the payer’s unconsumed nonce proves that nothing landed. Fixed in 3a2ee7c. Tests

F-2026-004

Zero-amount upto settlements were cataloged at the signed cap Medium · bazaar · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats P1, U6 A settlement of zero under an upto authorization created or updated a Bazaar listing at the cap the buyer signed, so a listing could be published for free. Zero settlements are never cataloged. Fixed in cecab3a. Tests

F-2026-005

The sponsor’s hourly fee budget was optional on mainnet Medium · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats S4, U6 Sponsor spend on a network was bounded only by an hourly fee budget the operator could leave unset. A pubnet configuration now refuses to start without an explicit hourly budget, and the budget stops settlements before any RPC work once it is committed. Fixed in cecab3a. Tests

F-2026-006

Any holder of an upto payload can choose the settled amount through /settle Medium · facilitator · Accepted risk · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats F5 Whoever holds a signed upto payload can call /settle with any amount up to the cap before the seller does. The payer’s signature still bounds the recipient, the token and the cap. Tests Accepted by the release approver on 2026-09-30. The first settle request fixes the amount, as in the EVM upto scheme; the payer never loses more than the cap it signed and funds only reach the recipient it named. The documentation states this, and sellers settle as soon as they serve.
  • The first settle request fixes the amount; a later request for another amount is refused as a conflict naming the original transaction.
  • The cap, recipient and token are bound by the payer’s signature.
  • Mainnet facilitators can require API keys on /settle.

F-2026-007

A payment option without extra crashed the buyer flows Medium · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M2 A valid x402 v2 payment option without an extra field, or a seller error with non-string fields, raised a raw TypeError: the MCP tool reported an internal error with an unknown payment state and the CLI exited with an unexpected code. Such an option is now refused as unsponsored before anything is signed. Fixed in c00699b. Tests

F-2026-008

An idempotency key stayed blocked after its authorization expired unused Medium · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M4 Once a call with an idempotency key had no stored result, every retry with the same key was refused as in progress for the whole budget window, even after the chain showed that the authorization expired unused. The retry itself now reconciles its row and frees the key when nothing was paid. Fixed in c00699b. Tests

F-2026-009

Domain verification trusted a one-way claim Medium · bazaar · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats P2 A listing reached domain_verified when the host’s stellar.toml listed the owner account, without the account naming the host as its home domain. An endpoint could claim another seller’s account. Trust is now raised only while the domain and the account name each other. Fixed in b42266d. Tests

F-2026-010

Service logs could include RPC URLs and request bodies Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats O6 Transport errors from the RPC client were logged with the request URL and JSON-RPC body; a provider URL can embed an API key. The logger now writes such errors without their URL, body or headers. Fixed in cecab3a. Tests

F-2026-011

Every upto verification called a public Horizon server Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats O3 The upstream upto verifier read the ledger close time from a public Horizon server on every /verify and /settle, before any static check, which made each request depend on a third party. The facilitator now reads it from its own RPC, in the service and in process. Fixed in 3a2ee7c. Tests

F-2026-012

/health and /ready published raw internal errors Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats O3 Health warnings carried raw error strings that could include internal addresses. Public health output now names the kind of failure only; the details stay in the service log. Fixed in cecab3a. Tests

F-2026-013

IPv4-translated IPv6 addresses passed the destination check Low · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats X6 safe-fetch did not recognise the IPv4-translated range ::ffff:0:0:0/96, so a URL could reach a loopback or metadata address in that spelling. Every IP literal spelling is now classified before any lookup, and such ranges are refused. Fixed in 05294d9. Tests

F-2026-014

An MCP paid call could exceed the request limit after signing Low · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M4 Adding the payment to an MCP request could push it over the 64 KiB request limit only after the payment was signed, and the call was then reported with an unknown payment state. The size is now checked with the payment before anything is sent. Fixed in c00699b. Tests

F-2026-015

A ledger failure after sending was reported as unpaid Low · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M4 When the local budget ledger failed after a paid request had been sent, the SDK reported the payment as not paid and retryable, which invited a second payment. Such a call is now reported with an unknown payment state. Fixed in c00699b. Tests

F-2026-016

Seller structured content was returned unsanitized Low · sdk · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats M3 MCP structured content from a seller was passed to the agent as received, and a __proto__ key could replace the copy’s prototype. Seed redaction also missed a seed glued after other base32 characters. Structured content is now sanitized and bounded, and redaction covers glued seeds. Fixed in c00699b. Tests

F-2026-017

A null accepted.extra caused an unexpected error Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 An upto payload whose accepted.extra was null threw inside the static rules and was answered as an unexpected error instead of a coded refusal. Such a payload is now refused with a reason naming the mismatch. Fixed in 7b10203. Tests

F-2026-018

An expiration margin of zero ledgers was accepted Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats E4 The upto rules require a margin of at least one ledger between the current ledger and the authorization’s expiration, but the setting accepted 0. The margin now must be at least one ledger. Fixed in 7b10203. Tests

F-2026-019

The contract TTL keeper and channel provisioning could use the sponsor at the same time Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats U7 The keeper that extends the upto contract’s TTL spent the sponsor’s sequence number under a lock separate from channel provisioning, so the two could collide. Both now run under one task lock that one replica holds at a time. Fixed in 3a2ee7c. Tests

F-2026-020

Settle-time simulation has no resource margin for smart-account payers Low · facilitator · Accepted risk · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats A2 A contract-account payer whose policy state grows between simulation and inclusion can make a settlement fail on chain, and the sponsor pays the fee. Tests Accepted by the release approver on 2026-09-30. Not reproduced. The fee is bounded by the per-transaction fee ceiling and the hourly budget, and the failure is coded and counted as wasted fee.
  • Per-transaction fee ceilings and the hourly sponsor budget bound the cost.
  • Failures at inclusion are reported with a stable code and counted in the wasted-fee metric.

F-2026-021

Rotating the upto authority breaks upto for running sellers until they restart Low · facilitator · Accepted risk · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats U10 A seller’s resource server reads the facilitator’s settlement authority once; after a rotation, its upto offers name the old authority and are refused until it restarts. Tests Accepted by the release approver on 2026-09-30. Rotation is a rare, planned operator action. The runbook tells operators to ask sellers to restart right after the switch; exact payments are unaffected.
  • The rotation procedure in the operations guide includes the seller restart.
  • Refusals name the mismatch with a stable code.

F-2026-022

Delegated signers were never exercised end to end Low · facilitator · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 · threats A1 signerAuthorizations for smart accounts with delegated signers were specified and shipped but had no end-to-end test. The upto payer suite now settles with a delegate’s entry and refuses a missing or forged one. Fixed in cecab3a. Tests

F-2026-023

The evidence runner passed the sponsor seed on a command line Low · operations · Fixed · found by REV-2026-09-internal-1 on 2026-09-30, resolved 2026-09-30 The conformance runbook passed the sponsor seed to env as a command-line argument, where other local users can read it. It now reads the seed from a file. Fixed in 0ffef31. Tests

F-2026-024

A false settlement claim could hide a real payment from the budget High · sdk · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats M1, M6 A seller could report another payment’s transaction hash as the settlement of a call; the claim blocked the real payment’s own proof, and reconciliation then released it, so the session cap under-counted. Claims are now confirmed from the chain, never overwrite a verified settlement, and a false claim is released only after expiry. Fixed in c00699b. Tests

F-2026-025

An upto settlement through a wrapper contract was released from the budget High · sdk · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats M1 An upto settlement made through a wrapper or batch contract consumed the payer’s nonce and moved the cap, but the proof did not recognise it and reconciliation released the row. Proofs now follow the consumed nonce, and a cap whose settled amount cannot be read is counted whole. Fixed in c00699b. Tests

F-2026-026

Rows the RPC could no longer prove stayed pending forever Medium · sdk · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats M1 Budget rows that the RPC could no longer prove were carried into every renewed window, and the oldest ones starved reconciliation of new rows. Rows are now deferred and bounded per run, and a row is released only when the chain proves its authorization expired unused. Fixed in c00699b. Tests

F-2026-027

A keyed MCP retry never reconciled its own row Medium · mcp · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats M4 A retry with the same idempotency key replayed a stale error until some unrelated call reconciled the row, and MCP agents had no way to trigger it. The retry now reconciles its own row first. Fixed in c00699b. Tests

F-2026-028

An MCP tool listing could be planted under another seller for one base unit Medium · bazaar · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats P9, P2 Paying one base unit to a seller’s payTo was enough to publish and freeze an MCP tool listing with any description and price under that seller. MCP tools at an http(s) endpoint are now listed only after the tool’s own server confirms them with an unpaid call, and a planted settlement can neither withdraw nor change a confirmed tool. Fixed in b42266d. Tests

F-2026-029

The upto reference facilitator could lose a settlement’s hash Medium · facilitator · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats F2 In the reference upto mechanism, a failed send answered every repeat with an unexpected error and no transaction hash, and a pending settlement was forgotten once its authorization expired, so a landed transaction could be reported as expired. Pending settlements are now kept and polled past expiry and answered with what landed. Fixed in 7b10203. Tests

F-2026-030

The landing proof read the wrong ledger and misattributed Rail402’s own settlement Low · facilitator · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats F2 The proof that a recorded settlement landed compared a nonce read with an earlier, different RPC response, and recorded Rail402’s own landed fee bump as a third-party settlement without its fee. The engine now finds its own settlement by the inner-hash lookup and attributes it correctly. Fixed in 3a2ee7c. Tests

F-2026-031

The per-client rate limit keyed on full IPv6 addresses Low · facilitator · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats S4 One IPv6 client could rotate addresses within its /64 and escape the per-client limit that bounds keyless sponsor spend. The limiter now keys an IPv6 address by its /64 and an IPv4-mapped address as the IPv4 address. Fixed in cecab3a. Tests

F-2026-032

An unknown transaction meta version read as nothing happened Low · sdk · Fixed · found by REV-2026-09-internal-2 on 2026-09-30, resolved 2026-10-01 · threats M1 Budget proofs treated a transaction whose meta version the SDK could not decode as showing no transfer, so a future protocol upgrade could release paid rows. Such a transaction is now asked about again later. Fixed in c00699b. Tests

F-2026-033

A seller that echoed the payment header put the signed payload into results Medium · sdk · Fixed · found by REV-2026-09-internal-3 on 2026-10-01, resolved 2026-10-02 · threats M3 A seller that copied the PAYMENT-SIGNATURE header into its paid answer made the SDK return the signed payload in its result and store it in the budget ledger. The call’s own payment material is now removed from seller content before it is returned or stored. Fixed in c00699b. Tests

F-2026-034

MCP listing prices were not bound when paying a listing Low · sdk · Fixed · found by REV-2026-09-internal-3 on 2026-10-01, resolved 2026-10-02 · threats M5 When an agent paid a listed MCP tool, the live price was not compared with the listed one, so a seller could switch to a higher price up to the agent’s cap. A live price above the listed one is now refused before signing. Fixed in c00699b. Tests

F-2026-035

An idempotency key reused for another resource replayed the earlier result Low · sdk · Fixed · found by REV-2026-09-internal-3 on 2026-10-01, resolved 2026-10-02 · threats M4 A repeated idempotency key returned the earlier call’s stored result even when the new call targeted a different resource or tool. Such a reuse is now refused. Fixed in c00699b. Tests

F-2026-036

Exact payments to a muxed payTo could never settle Low · facilitator · Fixed · found by REV-2026-09-internal-3 on 2026-10-01, resolved 2026-10-08 · threats E1 The stock exact verifier compares the transfer event’s recipient with payTo literally, and a muxed M… address never matches the CAP-67 event, so such payments failed after submission. Exact requirements with a muxed payTo are now refused up front with a reason that names it. Fixed in 3a2ee7c. Tests

F-2026-037

A bad upto signature from a G account was reported as expired terms Medium · facilitator · Fixed · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 · threats A1 When a G payer’s upto authorization was signed by a key that is not its signer, or by too few multisig keys, the simulation classifier mapped the contract’s authentication error to signature_expired, telling the buyer to sign again rather than that the signature was wrong. Authentication failures now answer invalid_signature for a G payer and smart_account_rejected for a contract account; a real expiry stays signature_expired. Fixed in 10abb21. Tests

F-2026-038

A conflict was reported as final when the original settlement never reached the network Low · facilitator · Fixed · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 · threats R1 When a request with the same authorization and another amount failed before broadcast, a concurrent request for the right amount was told the authorization was already settled, with no transaction and no retry. A request whose conflicting predecessor failed before anything reached the network is now settled. Fixed in 10abb21. Tests

F-2026-039

The upto fee ceiling counted the fee bump’s inclusion fee once Low · facilitator · Fixed · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 · threats S3 With a fee bump, the envelope pays the inclusion fee of the inner transaction and of the fee bump, but the ceiling check counted it once, so the sponsor could pay up to one inclusion fee above its ceiling. Both are now counted. Fixed in 10abb21. Tests

F-2026-040

The upto reference facilitator could miss a settlement re-wrapped by a third party Low · facilitator · Fixed · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 · threats F2 Confirmation polled only the outer fee-bump hash, so if a third party wrapped the public inner transaction in its own fee bump and it landed, the reference mechanism could report it as expired. It now also looks the settlement up by its inner hash. Fixed in 10abb21. Tests

F-2026-041

Two different maxTimeoutSeconds ranges in refusal messages Info · facilitator · Fixed · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 The service named the allowed maxTimeoutSeconds range differently in two refusals. Both now name one range, raised to one ledger more than the expiration margin. Fixed in 10abb21. Tests

F-2026-042

upto events are only as trustworthy as the token contract Info · contract · Accepted risk · found by REV-2026-10-upto on 2026-10-21, resolved 2026-10-21 · threats U9 A malicious token can make transfer_from do nothing and emit false events while the settlement contract still emits upto_settled, so an indexer that trusts the event alone could count a payment that never happened. The payer is not at risk. Tests Accepted by the release approver on 2026-10-21. Rail402 settles only assets the operator configures per network, verifies their decimals on chain at startup, and proves transfers from the token’s own events.
  • Each network accepts only the assets listed in its configuration.
  • Asset decimals are read from the token at startup and a mismatch stops the service.

F-2026-043

Locked dependencies have published advisories High · operations · Fixed · found by REV-2026-10-dependency-scan on 2026-10-22, resolved 2026-10-23 · threats C5 The first osv-scanner and pnpm audit runs over pnpm-lock.yaml report advisories against locked versions, several rated high. Loaded at runtime by the service or the published packages: axios 1.18.0 and follow-redirects 1.16.0 (through @stellar/stellar-sdk, whose 16.x releases pin axios exactly), @modelcontextprotocol/sdk 1.31.0, @modelcontextprotocol/server and client 2.2.0, hono 4.13.8 and ip-address 10.7.2 (through the MCP SDK’s express-rate-limit). Build and test tools only: vite 8.3.0, @vitest/mocker 5.0.1 and source-map-js 1.2.1. Each has a fixed release. Reachability: axios and follow-redirects run in the facilitator process that holds the sponsor seeds, for every RPC and Horizon call; most of the axios advisories need an attacker-chosen URL or configuration, HTTP/2 or a separate prototype-pollution primitive, and the follow-redirects ones leak credential headers when a provider redirects, which matters when an RPC URL or its headers carry a provider key; the RPC URLs are operator configuration, never request input, so exploitation needs a hostile or compromised provider, but the code is reachable and must be fixed before serving pubnet. The MCP SDK’s memory-exhaustion advisory (GHSA-46jr-4wx8-v453) is reachable through the public MCP endpoint of the demo seller and the MCP server’s HTTP transport, which accept unauthenticated requests up to their body limit. The shared-transport advisory (GHSA-jm63-cq47-j5jp) is not reachable: both create a new transport per request. The MCP client’s redirect advisory applies when the Bazaar confirms a seller’s MCP endpoint, which runs through safe-fetch with redirects refused. hono’s serveStatic advisory and ip-address (express-rate-limit is not used by Rail402’s code) are not reachable. The development tools never run in a deployed service. The fix moves axios to 1.20.0, follow-redirects to 1.16.1, source-map-js to 1.2.2, ip-address to 10.7.4 and hono to 4.13.13 with pnpm overrides (the MCP SDK keeps its own hono otherwise), and the catalog’s MCP SDK to 1.32.1, its server and client to 2.3.1, hono to 4.13.13, vitest and @vitest/coverage-v8 to 5.0.5 and vite to 8.3.5 (the catalog pins vite for the browser builds, so vitest alone does not move it). With all of these, osv-scanner over every lockfile reports no advisory. The dependency scan job in CI fails while any of them is locked. Fixed in ebeea7a. Tests

F-2026-044

Pubnet sponsor key files lacked the network tag the service requires High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats K3 The key ceremony’s tools wrote the pubnet sponsor seed without the `# rail402-key network=stellar:pubnet` tag the service requires on pubnet, so the first pubnet start would fail and invite a hand edit of the key file. The key generator now writes network-tagged key files in the service’s format, with the public key printed apart. Fixed in 32bb685. Tests

F-2026-045

A dump restore left the split database roles broken High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N8, K5 Restoring a logical dump as the superuser left every table owned by it and took the runtime role’s privileges away, so both replicas would fail their role check after an incident restore. restore-dump.sh now restores as the owner role and re-applies the runtime grants. Fixed in 32bb685. Tests

F-2026-046

No alert on WAL archiving, backup age or host disk space High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats K5, O11 Nothing alerted when WAL archiving failed or the newest backup aged, although a failing archive fills the database’s disk. Archiving, backup age and disk space are now exported for node_exporter and page the on-call. Fixed in 32bb685. Tests

F-2026-047

A compromised facilitator could silence alerts High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats O12 The facilitator replicas shared a network with Alertmanager, whose API can create silences without authentication, so code execution in the process that holds the sponsor seed could mute every alert. The monitoring networks are now split: only Prometheus reaches Alertmanager. Fixed in 32bb685. Tests

F-2026-048

Recovery drills passed without checking that recovery worked Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats O11 A drill passed as soon as its alert fired; its recovery checks were recorded as text and never asserted. A drill now fails when any structured recovery check does not hold. Fixed in 8695731. Tests

F-2026-049

Alerts did not follow each network in the two-network setup Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N4, O11 With neither network gating /ready, an RPC outage on one network no longer raised a readiness alert, and the sponsor thresholds were not per network. Readiness is now probed per network and the thresholds are per network. Fixed in 32bb685. Tests

F-2026-050

The deploy gate did not pin the release signer or bind the tag to origin Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats C2, C3 The gate accepted any signature the operator’s own git trust settings knew, resolved the tag locally, and accepted an attestation from any workflow or ref. Release signers are now pinned in deploy/release-signers, origin must have the same tag object, and the attestation must come from the release workflow run for that tag. Fixed in ebeea7a. Tests

F-2026-051

Alert inhibition and thresholds had the wrong semantics Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats O11 An inhibition rule treated a missing network label as equal, so any page suppressed every unrelated ticket; the budget alert tested a fixed amount instead of the share of the budget; and a replica that disappeared never alerted. Inhibition is limited to alerts with a network label, the budget ratio is used and a missing replica raises a ticket. Fixed in 32bb685. Tests

F-2026-052

The backup documentation claimed more than the restore drill proved Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats K5 The backup section implied encryption and off-host immutability that the drill did not exercise, and pgBackRest’s repository key could reach Postgres through its environment. The drill now proves the backup is encrypted, pgBackRest reads its settings from a file secret, and the documentation states what the drill does not prove. Fixed in 32bb685. Tests

F-2026-053

Forged payloads used up the per-payTo and per-payer limits High · facilitator · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats S4, Q1 Payer and payTo quotas were taken before the payment proved its signatures, so anyone could exhaust a pubnet seller’s or payer’s quota with forged payloads at no cost. Quotas are now taken only after upstream verification and simulation succeeded. Fixed in 9355cd2. Tests

F-2026-054

TTL keeper spend that failed on chain bypassed the fee budgets Medium · facilitator · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats S13 A contract TTL extension that failed on chain was never recorded, so the keeper’s daily cap and the network’s hourly fee budget did not see its fee. The keeper now reserves its fee ceiling before signing and corrects it to the fee charged, keeping the ceiling when the outcome is unknown. Fixed in 3549435. Tests

F-2026-055

Public metrics and health revealed pubnet budget state Medium · facilitator · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N6 The public /metrics and /health still showed when pubnet’s fee budget ran out, which tells an attacker when its spend has taken effect. Pubnet budget warnings and refusal reasons are now kept off the public views. Fixed in 3549435. Tests

F-2026-056

Pubnet settlements still waited on the shared database pool Medium · facilitator · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N3 Pubnet’s own pool covered only the ledger and channel leases; its limiters and metering used the shared pool, so a testnet flood could still stall pubnet settlements, and pool waits had no bound. Pubnet limits and metering now use its pool, and every pool wait is bounded. Fixed in 3549435. Tests

F-2026-057

JSON-RPC batches bypassed the hosted MCP endpoint’s limits High · mcp · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats H3 One HTTP request carrying a JSON-RPC batch ran many tool calls past the per-client rate limit and the in-flight cap. The hosted endpoint now refuses batches (`rpc_batch_unsupported`) and caps tool calls across all clients with an upstream budget. Fixed in b08b49f. Tests

F-2026-058

All hosted MCP users shared one discovery rate-limit bucket Medium · mcp · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats H3 Every hosted MCP user reached the Bazaar from the endpoint’s one address, so one heavy user could use up the discovery limit for all of them. The hosted endpoint now reads a discovery-internal access key from a file and sends it with a per-client identifier, so the Bazaar budgets each remote client apart (the Bazaar side is F-2026-097). Fixed in b08b49f. Tests

F-2026-059

Hosted MCP mode loaded the key, signer and payment modules Medium · mcp · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats H2 Hosted, discovery-only mode imported the modules that hold keys and sign payments, contrary to its documentation and threat entry. It now imports only the discovery code, and a test checks the module graph. Fixed in b08b49f. Tests

F-2026-060

An approved MCP confirmation could be replayed for ten minutes Medium · mcp · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats M9 An approved payment confirmation stayed valid for ten minutes, so it could approve further payments even with `RAIL402_MCP_CONFIRM=every`. Confirmations are now single-use and bound to the tool call. Fixed in b08b49f. Tests

F-2026-061

Origin checks of all networks shared one queue High · bazaar · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N5, P14 The origin-check queue took the oldest checks of any network and ran them one after another, so a testnet backlog of hosts that never answer delayed pubnet listings without bound. Origin checks now run in one bounded lane per network. Fixed in ebeea7a. Tests

F-2026-062

The load test could not measure settlement latency under discovery load Medium · bazaar · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats N1, N2 The load package served no payments, so the isolation claims (pubnet settlement latency under a testnet or search flood) were not measured. A settle probe now settles real payments during a search flood and reports their latency. Fixed in 8695731. Tests

F-2026-063

Indexed events were not checked against the transaction’s result Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I1, I4 The indexer trusted Soroban events without binding them to the transaction’s result, and did not cross-check an upto settlement’s amount against its signed arguments. Events are now bound to the result and upto settle arguments are checked. Fixed in 1530c9d. Tests

F-2026-064

Explorer partitions were not anchored to an independent source Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I4, I6 Partitions started unanchored, links between partitions were never checked and rebuild anchors outside the range were ignored. Each partition’s start is now anchored to an independent ledger hash and stored links are checked. Fixed in 50e7461. Tests

F-2026-065

A bounded indexer run skipped ledgers the source did not hold yet Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I6 When the source answered that a ledger was not available yet, a bounded run skipped the rest of the partition and exited successfully. It now fails at that ledger instead of moving on. Fixed in 50e7461. Tests

F-2026-066

The testnet beta was missing from the attribution registry Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I3 Settlements through the earlier testnet beta’s contracts appeared as unattributed traffic. The beta’s contracts are now registered and their settlements labelled as beta history. Fixed in 1530c9d. Tests

F-2026-067

Stored history was not re-attributed after a registry update Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I3, I8 After a registry version bump the live database kept the old attribution. Stored rows are now re-attributed, or partitions re-indexed when the upto contracts change. Fixed in 50e7461. Tests

F-2026-068

Shared-relayer rows were counted in the headline without a flag Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I3 Rows from a facilitator marked as a shared relayer were folded into the headline totals with nothing on the row saying so. They are now flagged and totalled on their own line. Fixed in 50e7461. Tests

F-2026-069

Open redirect through the Explorer’s network aliases Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats W6 The `/public`, `/pubnet` and `/mainnet` aliases redirected to a path taken from the request, which could send a visitor to another site. The aliases now redirect only within the Explorer. Fixed in 50e7461. Tests

F-2026-070

Indexed transaction pages were cached as immutable Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats W5 Transaction pages were marked immutable for a year although their attribution can change, and the ETag named the served registry rather than the row’s. Rows under older versions are now cached briefly with an ETag of their own versions. Fixed in 50e7461. Tests

F-2026-071

Explorer pool acquisition never timed out and pages were unbounded Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats W3 Waiting for a database connection had no timeout, one account page held several connections and ran uncapped aggregates. Requests beyond the in-flight limit are now answered 503 with Retry-After, and database use is bounded. Fixed in 50e7461. Tests

F-2026-072

Explorer per-client limits were per exact IP and the lookup budget was cheap to exhaust Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats W4 Limits keyed by exact IPv6 address and per process let one client rotate addresses, and the global live-lookup budget could be used up cheaply. IPv6 clients are now grouped by /64 and the lookup budget keeps a reserve and refunds refused lookups. Fixed in 50e7461. Tests

F-2026-073

The Explorer report rescanned everything every few minutes per replica Medium · explorer · Fixed · found by REV-2026-10-internal-tracks on 2026-10-22, resolved 2026-10-23 · threats I9, W3 Each web replica recomputed the full report every five minutes in one long transaction. The indexer now publishes the report and the web role serves it. Fixed in 50e7461. Tests

F-2026-074

Throwaway smart accounts strand funds on pubnet conformance runs High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats M1 Contract-account payer cases funded an account whose owner key was discarded at the end of the run, so its remaining balance could not be swept back to the treasury. Smart accounts are now deployed only through the run’s funder, which derives and keeps their owner keys and returns their USDC before merging the run’s accounts. Fixed in 8695731. Tests

F-2026-075

Third-party suite code runs with the treasury secret path in its environment High · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats C5 The upstream end-to-end suite inherited the treasury and sponsor secret file paths. It now gets only an allow-listed toolchain environment and its own reviewed variables, and no secret-like variable reaches it whatever its prefix. Fixed in 8695731. Tests

F-2026-076

Spend caps do not see real Soroban fees and miss some deployments Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats M1 The conformance spend caps were debited with estimates and some contract deployments were not debited at all. Every fee the treasury pays is now debited before it is sent and counted as charged, and a fee source outside the run is refused. Fixed in 8695731. Tests

F-2026-077

Sweep-back is skipped when a harness throws or is interrupted Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats M1 An exception between funding and sweep-back left funds in derived accounts. A run now sweeps once after its body, also when the body throws, before the error is rethrown. Fixed in 8695731. Tests

F-2026-078

The settlement cap is a static estimate, not checked per settlement Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats M1 The settlement cap was debited once with a hand-counted bound. Every /settle a harness sends is now debited at its scheme’s fee ceiling before it is sent, and none is sent past the cap. Fixed in 8695731. Tests

F-2026-079

Spend caps apply per harness process and one confirmation is reusable Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats M1 Spend caps applied per harness process and the real-funds confirmation was reusable. One run ledger now holds the caps for every harness process of a run, and a confirmed run is claimed once. Fixed in 8695731. Tests

F-2026-080

Pubnet upto runs ignore the configured contract in several harnesses Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats U8 Several harnesses did not pass the pubnet upto contract on, so pubnet upto cases failed after funding accounts. Every upto client now gets the configured contract on pubnet. Fixed in 8695731. Tests

F-2026-081

Harness fee ceilings assume defaults the facilitator does not have Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats S3 The harnesses assumed pubnet fee-ceiling defaults that the facilitator requires explicitly, and ignored malformed overrides. Pubnet runs now read the fee ceilings explicitly and refuse a malformed or zero value. Fixed in 8695731. Tests

F-2026-082

Skip categories are never emitted, so the pending-dependency gate cannot fire Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats O11 Skipped cases rendered as not categorised, so a required check skipped for a missing dependency could still be published. Evidence recorded from the requirement’s date must give every skipped case a category. Fixed in 8695731. Tests

F-2026-083

A keyed facilitator is not detected before a pubnet run spends Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats Q4 A pubnet run against a facilitator that requires an access key funded accounts before every request was refused. The run now checks the facilitator first and refuses before spending when it wants a key the run does not have. Fixed in 8695731. Tests

F-2026-084

A closed upstream tracking issue is recreated every week Medium · operations · Fixed · found by REV-2026-10-internal-tracks on 2026-10-23, resolved 2026-10-23 · threats SW3 The upstream watch recreated a tracking issue a maintainer had closed while the drift was unchanged. A hand-closed issue now stays closed until the drift changes. Fixed in f8c4e1c. Tests

F-2026-085

An exact payment with its own operation source held a channel and fee budget without landing High · facilitator · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats O5, S8, S4 The payer’s authorization does not cover the operation source, and upstream settlement keeps it when it rebuilds the transaction from a channel. A payload with any non-facilitator operation source therefore verified, was recorded, and was then refused by the network for the missing signature. Its record stayed open until the authorization expired, holding its channel and fee reservation; with a handful of such payloads every channel could be kept busy at no cost. Such operation sources are now refused before anything is claimed (invalid_exact_stellar_payload_operation_source_set), a first submission the network refuses for good finishes at once as a submission failure with nothing charged and frees its channel, and finished settlements count the fee actually charged against the budgets. Because a copy of an exact authorization can still land while it is valid, a refused first submission now gives its channel back at once but stays open until the authorization expired unused (then a submission failure with nothing charged) or a copy landed (then that payment is answered); an upto settlement, which only the authority can settle, still finishes at once. Fixed in 9355cd2. Tests

F-2026-086

One network’s reconciliation failure or hang held up the others Medium · facilitator · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats N3, O3 All networks were reconciled in one sequential round without a per-network catch, so a testnet ledger error or a hung testnet RPC call stopped pubnet’s recorded settlements from being finished and their channels from being freed. Each network now reconciles on its own, one round at a time per network, and RPC calls are bounded. Fixed in 9355cd2. Tests

F-2026-087

The per-payTo limit was shared by every caller Low · facilitator · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats S8 MAX_SETTLEMENTS_PER_PAYTO_PER_MINUTE counted every caller in one bucket per payTo, so genuine minimum-amount payments sent without a key could use up the quota of a seller settling under its own key. The count is now kept per access key, with one shared count for keyless callers. Fixed in 9355cd2. Tests

F-2026-088

RPC calls had no timeout, and a slow settlement could outlive its channel lease Low · facilitator · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats O2 The Stellar SDK sets no timeout on RPC calls, so a hung provider could keep a settlement before signing past its channel lease, after which a second settlement could build on the same channel sequence. Every RPC call of the service’s clients now fails after 10 seconds, a settlement that has signed nothing by the end of its 30-second claim window signs nothing afterwards, and a channel referenced by an open settlement is never leased again. Fixed in 9355cd2. Tests

F-2026-089

A pubnet upto settlement could settle less than the asset’s minimum Low · facilitator · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats S8 The asset minimum applied to an upto cap but not to the amount settled, so a non-zero settlement of one base unit cost the sponsor a full fee. On pubnet a non-zero upto settlement below the minimum is now refused with invalid_upto_stellar_requirements_amount_out_of_range; a zero still settles off-chain. Fixed in 9355cd2. Tests

F-2026-090

A revoked access key stayed valid behind a single-file mount Medium · operations · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats Q2 A container mount of the single key file keeps showing the file it was created with, so a key file replaced by rename on the host was never seen and a revoked key kept working until a restart, with no reload error. API_KEYS_DIR now reads keys.json from a mounted directory, reloads compare the inode too, and /health/private shows the digest of the file in force. Fixed in 3549435. Tests

F-2026-091

Every /ready request took a database connection Low · operations · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats N3 The public, unlimited /ready ran a database query per request on the pool that also serves settlements, so a flood of readiness requests could make settlements wait for connections. The database check now runs at most once every 2 seconds, shared by concurrent requests. Fixed in 3549435. Tests

F-2026-092

Public /health showed access-key counts Info · operations · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats N6 The public /health listed active, expired and revoked key counts and the code of a failed key reload. They are now shown in /health/private only. Fixed in 3549435. Tests

F-2026-093

Pubnet secret settings were looser than intended Info · operations · Fixed · found by REV-2026-10-internal on 2026-10-23, resolved 2026-10-23 · threats K1, N8 With pubnet, the service accepted the schema owner’s connection (DATABASE_MIGRATION_URL), database URL and cursor secret files readable by others, and RPC URLs with credentials in the environment. Each is now refused at startup with a coded error. Fixed in 3549435. Tests

F-2026-094

Search snapshots could outgrow the process’s memory High · search · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats D2, N2 Each search index snapshot kept every published listing with its full content, the snapshot size estimate counted only the indexed text, nothing bounded the catalog’s size, and up to eight superseded snapshots stayed alive while their cursors were valid. Free testnet mcp:// listings of maximum size, plus a cursor after each catalog change, could grow the service’s memory until it stopped, together with pubnet settlement in the same process. Snapshots now keep only what ranking and filters read and pages read listings from the catalog; the catalog is read in batches with a statement timeout; each field indexes a bounded number of terms; each network’s listings take a bounded share of a snapshot (unverified listings at most half); the size estimates are calibrated against the measured heap of thousands of maximum-size listings; and each network holds a bounded number of published listings, with unverified and idle listings expiring. Fixed in 2fd3600. Tests

F-2026-095

Unconfirmed listings counted toward the network-wide quota Medium · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats N5, P13, P5 The network-wide hourly quota of new listings counted pending and withdrawn listings, which any payer can create for any payTo. Dust payments to junk URLs could fill pubnet’s quota and refuse every honest seller’s first listing. The network-wide quota now counts only listings published now; unconfirmed listings count toward their payer’s quota only. Fixed in ebeea7a. Tests

F-2026-096

A shared schema validator let previews and testnet starve pubnet cataloging Medium · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats P3, N5 One validator worker with one queue served both networks and the /verify previews, and a settlement refused because the queue was full lost its cataloging for good. Repeated previews with slow schemas could keep pubnet settlements from being cataloged. Each network now validates settlements and previews in sandboxes of its own, a busy preview reports processing, and a busy settlement validation stays queued and is retried. Fixed in 2fd3600. Tests

F-2026-097

Hosted discovery users shared one cost budget Medium · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats Q6, H3 The discovery cost bucket was keyed by the caller’s address only, even for an internal discovery identity, so every user of a hosted front end shared one budget and one user’s searches could refuse everyone else’s. Requests of a discovery-internal key are now charged to their forwarded client’s bucket and to the identity’s own, never to the address. Fixed in 3549435. Tests

F-2026-098

Origin and stellar.toml fetches could be held open by a drip-fed body Medium · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats X4, X5 Origin checks stopped their timer once headers arrived and then drained the body without a deadline; a non-200 stellar.toml was drained the same way. A host dripping bytes could hold one socket per check open for days. Origin checks now close the connection as soon as the headers arrive, and stellar.toml reads keep one deadline through the whole body. Fixed in 2fd3600. Tests

F-2026-099

A pending ownership claim could be replaced by another payTo Low · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats P2, P13 A listing kept one claimant: a later settlement by another payTo replaced the claim and reset the check, and a check asked for by another payTo could quarantine a published listing on one passing error of its origin. Claims are now kept as a set, a claim never resets a check, and a check only claims asked for never withdraws a published listing. Fixed in ebeea7a. Tests

F-2026-100

One facts queue served every network Low · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats N5 Stellar facts and domain trust were refreshed in one queue across networks, so testnet listings whose hosts stall could delay pubnet trust indefinitely. Each network now refreshes in its own lane, and a busy lane is skipped. Fixed in ebeea7a. Tests

F-2026-101

Route-template listings could change owner on every settlement Low · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats P2 A route-template listing was checked at the claimant’s own path, so on a host serving one tenant per path each tenant could take the template over in turn. A template listing now moves only to a payTo that its example path names too. Fixed in ebeea7a. Tests

F-2026-102

Listing detail showed metadata no origin confirmed Info · bazaar · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 · threats P13 The detail and history routes returned the content of pending and withdrawn listings, which can be metadata a payer echoed under the payTo it paid. Funds were never at risk (the SDK pays published listings only). Those routes now withhold that metadata and say so with contentWithheld. Fixed in ebeea7a. Tests

F-2026-103

The SDK read only the first page of a listing’s history Info · sdk · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-23 DiscoveryClient.versions() returned the first page of at most 100 versions; the Bazaar pages longer histories. It now follows pagination.next to the end, with a bound on the pages read. Fixed in b08b49f. Tests

F-2026-104

One pubnet approval covered later payments to any recipient in the window Medium · sdk · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M9, M1 Under the first-per-session confirmation policy (the rail402-mcp pubnet default), approving the first payment of a budget window let every later payment of that window be signed without asking, to any recipient, resource or listing, up to the caps, and the prompt did not say so. An agent misled by seller or listing text could then pay other sellers unasked. An approval now covers only the window, recipient base account, listing (or resource without one), asset, scheme and settlement authority it was shown for; anything else is asked about again. Every prompt states what approving covers, with the per-call cap, the budget left and the window end, and SignIntent carries approvalScope and those session fields. Fixed in b08b49f. Tests

F-2026-105

A pubnet buyer built in code accepted a plain-http or private Bazaar URL Low · sdk · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M11 createBuyer and createBrowserBuyer checked the RPC and seller egress on pubnet but passed bazaarUrl and facilitatorUrl through, while the environment path refused non-https. An on-path attacker could then rewrite listings, which decide whom a pubnet buyer pays. On pubnet both URLs must now be https on a public host name: no credentials, IP literal, localhost, single label or private-network suffix (payment_policy_invalid). Fixed in b08b49f. Tests

F-2026-106

An MCP resource reached confirmation prompts as raw text Low · mcp · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M9 The MCP payment flow used the endpoint string as given, so line breaks or bidirectional overrides that the URL parser drops or encodes could add lines to, or reorder, the MCP and CLI confirmation prompts. Such an endpoint is now refused with egress_url_invalid, the normalized URL is what is confirmed and reserved, and both prompts show untrusted fields on one line without control, line-break or bidirectional characters. Fixed in b08b49f. Tests

F-2026-107

A pubnet upto seller could name its own settlement authority Low · sdk · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M13 With an upto contract pinned on pubnet, any settlement authority other than the payer and the recipient was accepted, so a seller could name a key it controls and settle the full cap; the prompts did not show the authority and the sealed MCP intent did not bind it. A signing pubnet upto buyer now pins its authorities from uptoFacilitators or from its facilitator’s /supported, and refuses to open without either; the authority is shown in both prompts and bound in the sealed MCP intent. Fixed in b08b49f. Tests

F-2026-108

The memory budget store gave pubnet a full budget on every start Info · sdk · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M8 With RAIL402_BUDGET_STORE=memory, every rail402 run and every restart of rail402-mcp opened a new budget window with the full session total. A pubnet configuration with a key now refuses the memory store and keeps its budget in the SQLite ledger. Fixed in b08b49f. Tests

F-2026-109

The CLI confirmation was described as an agent control Low · docs · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M9 The threat model listed the CLI’s terminal confirmation beside MCP elicitation as a pubnet control, but an agent that runs the CLI can pass —yes or answer its own terminal. The CLI guide and the threat model now say the question protects a person at the terminal, point agents to rail402-mcp’s elicitation, and name the controls that hold against an agent: caps, the tagged pubnet key file and the spending policy. Fixed in b08b49f. Tests

F-2026-110

The Playground key download was untagged Info · cli · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats M10 The Playground downloaded its testnet key as a bare seed, which rail402 keys import could tag for pubnet without warning. The download is now a key file tagged for testnet, and keys import reads tagged files and refuses a tag for another network with key_network_mismatch. Fixed in 9bee50a. Tests

F-2026-111

The Playground relay could reach the facilitator’s other network hosts Low · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats PG1 The relay refused the facilitator and Bazaar host names it was configured with, but one facilitator process also answers for pubnet and the Explorer under sibling host names, so Playground users could drive those through the relay as one shared keyless client. When the facilitator or Bazaar host starts with testnet., mainnet. or pubnet., the relay now also denies the same domain under testnet., mainnet., pubnet. and explorer.; RELAY_DENY_HOSTS stays for any other name. Fixed in 9bee50a. Tests

F-2026-112

One hostile transaction could stop the Explorer indexer Medium · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I5 A settlement through a concluded beta contract took the seller’s muxed id from the transfer event without comparing it with anything, and a CAP-67 to_muxed_id in the text form was kept byte for byte. A token contract chosen by the caller could emit an id containing a NUL character, which Postgres refuses in text, so the commit failed, the partition was marked failed and every restart failed at the same ledger. A to_muxed_id is now taken only as a u64 in every rule (the text and hash memo forms never come from a contract call and count as transfer_event_mismatch), and every row field must have its expected shape and be storable text, or the transaction is counted as a malformed exclusion. Indexing continues past such a transaction. Fixed in 1530c9d. Tests

F-2026-113

Indexers sharing a database collided on the totals tables Medium · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I6 Each commit refreshed the daily and per-account totals by deleting and re-inserting the rows it touched. Two workers committing partitions with a shared seller or day, or a reset racing a worker, both inserted the same key; one commit failed with a unique-key error and its partition was marked failed. The refresh now runs under a transaction-scoped advisory lock per network and epoch, so such transactions take turns and the second recomputes from every committed row, and a commit that still loses a race is retried instead of failing the partition. Fixed in 50e7461. Tests

F-2026-114

Rows between anchors were served before they were anchored, and failed partitions stayed in the report Medium · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I4, I8 The indexer checked an independent Horizon anchor only at a partition’s start and when a partition completed, about 3.7 days of ledgers apart. Rows of a self-consistent fork from the RPC fallback or a tampered data lake object were committed, served and reported in between, and the report kept a partition the indexer had marked failed, or one that did not link to its neighbour, in its coverage. A batch is now committed only after the hash of its last ledger matched the anchor (once per batch in the backfill, every 16 ledgers at the head), the anchors checked are stored and listed in the published manifest, and the report stops before a failed or unlinked partition and is not marked reproducible while one exists. The rebuild command takes a published manifest’s anchors with —anchors-from. Fixed in 50e7461. Tests

F-2026-115

A live lookup did not check that the RPC answered for the requested hash Low · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I4 A transaction page for a hash not indexed yet showed whatever transaction the lookup RPC returned, under the requested hash, and cached it for a minute. A faulty or hostile RPC could show one crafted settlement under any link. The answer is now used only when its envelope’s network hash, or its inner hash for a fee bump, equals the requested hash; otherwise the lookup answers explorer_lookup_unavailable. Fixed in 50e7461. Tests

F-2026-116

Account totals were cut at 200 rows without notice, in an order anyone could game Low · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats W2 Account totals were ordered by side and contract id and cut at 200 rows, and received assets sorted before paid ones, so payments in many throwaway tokens could push an account’s paid totals and verified assets off its page and its API answer without any sign. Each side now lists at most 100 assets, verified Stellar assets first and then by payments, and the page and the API say how many more each side has. Fixed in 50e7461. Tests

F-2026-117

Every Explorer /health request took a database connection Low · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats W3 The Explorer’s /health is exempt from rate limits and the in-flight cap and pinged the database on every request, so a flood of health checks queued pool acquisitions ahead of page queries. All health requests in any two seconds now share one database check. Fixed in 50e7461. Tests

F-2026-118

The concluded testnet beta’s contracts kept counting new calls as beta history Low · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I3 The registry listed the testnet beta’s settlement contracts with open ranges. The contracts cannot be changed and anyone can call them, so every later call was labelled concluded beta history and anyone could inflate it. Each beta contract’s range now ends at testnet ledger 5,326,244, the last ledger closed before the beta’s pubnet account was merged, with archived evidence of both; calls after it are not beta history. Fixed in 1530c9d. Tests

F-2026-119

No launch or rotation step registered the hosted service’s accounts with the Explorer Low · explorer · Fixed · found by REV-2026-10-explorer on 2026-10-23, resolved 2026-10-23 · threats I3 Neither the mainnet key ceremony nor the sponsor rotation said to add the hosted service’s new accounts to the Explorer’s registry, so its own settlements would have shown as unattributed until someone noticed. Both procedures and the runbook now include the registry change, and registry-check —hosted-<network> fails while any signer or settlement authority the service advertises in /supported lacks an open registry entry. Fixed in 50e7461. Tests

F-2026-120

Pre-flight configuration checks refused every release that adds a migration on pubnet High · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats N8 With pubnet, the configuration check a deploy runs before touching the database refused any pending migration, so a release with a migration could not be deployed in the documented order. `CHECK_CONFIG_PENDING_OK=true` now skips only the pending-migration refusals while still checking the database role, and the migrations then run as the schema owner. Fixed in 50e7461. Tests

F-2026-121

The Explorer and hosted MCP images were outside the supply-chain controls Medium · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats C1, C5 The Explorer and hosted MCP Dockerfiles used a base image by tag, were not built in CI and were not released or attested, so the deploy gate could not cover them. Both are now pinned by digest, built in CI and released as attested images, and the gate can require them. Fixed in 643f084. Tests

F-2026-122

Monitoring never scraped the Explorer or the hosted MCP endpoint Medium · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats O11, I6 No scrape job covered the Explorer or the hosted MCP endpoint, so the Explorer lag alert could never fire and a stopped indexer went unnoticed. Scrape jobs and alerts for indexers, the web role and the endpoint were added. Fixed in 32bb685. Tests

F-2026-123

The leak response did not use the cold key to lock out the leaked key Medium · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats K2, O6 The rotation steps had the operator race an attacker for the hot balance and moved funds last, while the cold key can lock the hot key out in one transaction. A cold-key freeze (master weight 0) is now the first step of the leak response. Its source and fee payer is the cold account and only the operation names the sponsor, so the holder of the leaked seed cannot make it fail by using or bumping the sponsor’s sequence (a first version took the sponsor as the source and could be blocked that way); the sweep is a separate cold-signed transaction after it. The testnet rehearsal and the recovery drill bump the sponsor’s sequence with the leaked key before the freeze is submitted, and the freeze still applies. Fixed in ebeea7a. Tests

F-2026-124

The deploy gate accepted an OpenPGP signature the operator’s keyring trusted Low · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats C2 git picks the verifier from the signature block, so an OpenPGP-signed tag was verified against the operator’s keyring, where a key of unknown trust passes. The gate now verifies only SSH signatures, with ssh-keygen and the committed signers file. Fixed in 643f084. Tests

F-2026-125

No alert fired when no backup existed at all Medium · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats K5 The stale-backup alert compared the newest backup’s age and returned nothing when no backup was listed, so a missing base backup or an unreadable repository never paged. It now also pages when archiving has been on for six hours and no completed backup is listed. Fixed in 32bb685. Tests

F-2026-126

The sponsor signer setup needed the hot seed on a networked machine Low · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats K7 The one-time signer setup was signed by a tool that needs the full dependency tree and network access, with the hot seed on that machine. The setup can now be built unsigned and signed offline by the zero-dependency key tool. Fixed in 32bb685. Tests

F-2026-127

The release gate did not name the deploy files kept outside the release Low · operations · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats C3 Files a deployment keeps outside the release, including a pubnet network’s fee and budget settings, were not identified by the gate. It now prints their digest for the host to check and record with the release; reviewing those files remains the operator’s step. Fixed in 643f084. Tests

F-2026-128

A startup RPC failure that threw a non-error stopped the service Low · facilitator · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats N4 With one network, an RPC that answered a startup check with a JSON-RPC error made the process exit with an unreadable message instead of holding that network until the RPC recovers. Any failure that is not a contradiction with the configuration now holds only that network with a code, and every startup failure prints one coded line without URLs or secret seeds. Fixed in 3549435. Tests

F-2026-129

An RPC’s own simulation failure refused a valid payment as invalid Low · facilitator · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats O3 When the RPC failed a simulation without running the host, for example with an incomplete ledger snapshot under load, verify and settle refused the payment with a non-retryable simulation code although nothing about it had been checked, so buyers treated a valid signed payment as refused. Such failures now answer a retryable code saying the payment was not judged; nothing is signed or submitted, and the same payment settles once the RPC recovers. Fixed in 9355cd2. Tests

F-2026-130

Verification could wait minutes for an RPC that stopped answering Low · facilitator · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats O2 The upstream exact verifier builds its own RPC client from the URL alone, without the 10-second call timeout of the service’s own clients, so /verify waited for the platform’s fetch default (minutes) when a provider accepted the connection and never answered; settlement was already bounded by its claim window. Upstream verification is now cut off after the RPC timeout with a retryable unexpected_verify_error, the channel and authority commands use bounded clients, and the threat model no longer claims that every RPC call fails after 10 seconds. Fixed in ebeea7a. Tests

F-2026-131

The rebuild guide claimed an anchored hash no test reproduced Low · explorer · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats I4 The Explorer rebuild guide said that —anchors-from with a published manifest gives the published manifest hash, but no test rebuilt an anchored report. A test now indexes the committed slice with anchors, reads the anchors back from the report’s manifest the way —anchors-from does, and rebuilds to the report’s manifest hash; the parser is shared with the command. Fixed in 50e7461. Tests

F-2026-132

Startup failures that were not RPC failures were reported as an unreachable RPC Low · facilitator · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 · threats N4 Every plain error from a network’s startup checks became the hold reason rpc_unreachable, so a sponsor that does not exist or another replica still provisioning accounts was refused as “its RPC cannot be reached”. A missing sponsor now holds the network as sponsor_missing, a refused channel creation as channels_missing and provisioning contention as the new provisioning_busy; only RPC failures stay rpc_unreachable. Fixed in 3549435. Tests

F-2026-133

The docs pinned a package release without the pubnet buyer guards they describe Info · sdk · Fixed · found by REV-2026-10-internal-pubnet on 2026-10-23, resolved 2026-10-23 Every package was still at 0.4.2 while the agent, seller and in-process pages, the Playground snippets and the buyer example installed @0.4.2 and described pubnet guards (tagged keys, per-recipient approvals, pinned upto authorities, the persistent budget store) that 0.4.2 does not have. Every package and the workspace are now 0.5.0 and those install pins name 0.5.0, and a test keeps every @rail402.dev install pin in the docs at the workspace version. The release is pending: until 0.5.0 is published to npm these pins do not install, so publish before the docs go live; the examples’ package.json files keep the published 0.4.2 so their registry install check passes, and move to 0.5.0 after the publish. Fixed in ebeea7a. Tests

F-2026-134

Discovery beyond capacity made searches fail with 500 and stalled health checks in the shared process Medium · search · Fixed · found by REV-2026-10-discovery on 2026-10-23, resolved 2026-10-24 · threats D5 In the load test, uncached searches at 300 a second over a 5,000-listing catalog (above the 234 a second the service completes) piled up inside the process: 6,245 searches answered 500 after waiting more than DATABASE_ACQUIRE_TIMEOUT_MS for a connection of the pool settlements also use (every result page read its listings from the database), 7,972 timed out at 30 s, and 150 of 600 /health probes timed out. Discovery now sheds load before any limit or database read (at most DISCOVERY_MAX_IN_FLIGHT requests run and DISCOVERY_QUEUE_LENGTH wait, each at most DISCOVERY_QUEUE_TIMEOUT_MS; the rest are refused with 503 discovery_overloaded and Retry-After), reads the catalog and keeps its limits through a database pool of its own, answers a busy or unreachable catalog database with a coded 503 instead of a 500, and reuses a page’s listings read within the one-second revision read at the same revision; /ready answers from its last database probe while a new one runs. Measured again on the release image at 300 a second (tools/loadtest/published/2026-10-26): 33,802 searches answered and 2,198 refused with 503 beside a /health probe (run B-uncached-open-300), 33,956 answered and 2,044 refused beside the settle probe (run B-settle-under-uncached-open-300), with no 500 or timeout; every /health probe and every settlement succeeded. Fixed in 3549435. Tests

F-2026-135

Any page silenced every ticket of its network, hiding an independent failure Medium · operations · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats O11 The Alertmanager inhibition let any page that named a network suppress every ticket of that network. In the channel-exhaustion drill, Rail402SponsorGuardRefusing was paging for testnet, so the Rail402ChannelLeaseTimeouts ticket raised by the exhausted channels was never sent. A page now silences only the tickets of its own cause on its own network: alerts carry a cause label (deploy/monitoring/alerts.ts), the rule matches equal: [network, cause], Rail402ChannelsExhausted covers Rail402ChannelLeaseTimeouts and Rail402RpcStale covers Rail402RpcFailover; every other alert is always sent. Fixed in 32bb685. Tests

F-2026-136

A sequence conflict held its channel until the payment’s authorization expired Medium · facilitator · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats O2 When another transaction took a channel account’s sequence number, the settlement’s txBadSeq refusal was treated as transient (the same bytes might have applied), so the settlement and its channel lease waited for the transaction’s time bound and the payer’s authorization expiry: about 4.5 minutes in the drill, with Rail402SequenceConflict firing correctly. The engine now proves the conflict final, from the channel’s sequence number on the network (at least the transaction’s) and the absence of the transaction’s hash and inner hash at an RPC that has ingested that ledger and retains every ledger since the claim, and then finishes the settlement at once as a submission failure with nothing charged, unless the payer’s nonce shows the authorization was carried elsewhere. The channel is quarantined (SETTLE_CHANNEL_QUARANTINE_MS, doubled per conflict in a row, up to 16 times) instead of being reused at once. The settlement is never re-signed on another channel, so the recorded-bytes idempotency stays as it was. An exact settlement whose bytes the network had accepted before the conflict still waits for the authorization to expire, since another transaction may carry it. For an exact settlement the proven conflict now quarantines and gives back the channel at once, while the record stays open until the payer’s authorization expired unused or a copy of it landed, so a payment carried by another transaction is still answered; upto settlements still finish at once. Fixed in 9355cd2. Tests

F-2026-137

The sponsor balance alerts used fixed thresholds instead of each network’s floor Low · operations · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats S5 Rail402SponsorBalanceLow watched only pubnet, and the testnet sponsor had a separate ticket, both against fixed thresholds in the alert file rather than the configured readiness floors, so a testnet sponsor drill could not fire the sponsor alert and a changed floor silently left the alerts behind. The service now exports each network’s floor (rail402_sponsor_balance_floor_stroops) and one alert fires on every network below twice its own floor: a page for the key custodian, and on testnet, whose XLM has no value, a ticket for the on-call. Fixed in 3549435. Tests

F-2026-138

The Explorer lag alert could not fire without a lag series Low · operations · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats O11, I6 Rail402ExplorerLag compared rail402_explorer_lag_ledgers with a threshold, so an indexer that exported no lag, or whose walked-through ledger stopped moving, never raised it. It now also fires when an indexer answers its scrape without rail402_explorer_last_ledger or when that ledger has not moved for an hour; a stopped indexer stays reported by Rail402ExplorerIndexerDown alone, not twice. Fixed in 32bb685. Tests

F-2026-139

The stale-RPC alert stayed silent on a replica that never read a ledger, or after its polls failed Medium · operations · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats O11 rail402_rpc_ledger_age_seconds was set only by a successful poll: a replica that never read a ledger had no series, and one whose polls started failing kept its last, fresh value, so Rail402RpcStale could not fire for either. The age is now computed at each scrape from the last ledger read, and from when the replica started watching the network while none was read, so both cases are reported (a never-read replica 3 minutes after start). Fixed in 3549435. Tests

F-2026-140

No alert covered discovery load shedding Low · operations · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats D5 Discovery refuses requests beyond its capacity with 503 discovery_overloaded and counts them in rail402_discovery_shed_total, but no alert read the counter, so sustained refusals went unnoticed. Rail402DiscoveryShedding raises a ticket when one reason passes 20 refusals in 10 minutes, with a runbook section. Fixed in ebeea7a. Tests

F-2026-141

The CLI reported retryable facilitator refusals as final, and a pending settlement as not settled Medium · cli · Fixed · found by REV-2026-10-recovery-drills on 2026-10-24, resolved 2026-10-24 · threats M4 A payment the facilitator refused was always printed as payment_rejected with retryable false, even for settle_exact_stellar_channel_unavailable, rate limits or a spent budget, which a new payment can pass; and a refusal with settlement_pending reported paid: not_settled although that transaction may still land, inviting a second payment. The CLI now takes retryable from the facilitator’s code table (exact and upto) for the code the seller passed on, and reports settlement_pending as paid: unknown with exit 5; an unknown code changes nothing. Fixed in b08b49f. Tests

F-2026-142

A sequence conflict first answered TRY_AGAIN_LATER still held its channel until the authorization expired Medium · facilitator · Fixed · found by REV-2026-10-recovery-drills on 2026-10-26, resolved 2026-10-26 · threats O2 The re-run of the sequence-conflict drill on the hosted testnet showed the fix of F-2026-136 did not trigger in the realistic order: the other transaction held the channel’s queue slot, so the scheme’s send and the engine’s immediate resend answered TRY_AGAIN_LATER rather than txBadSeq; the settlement then counted its bytes as possibly public, and the 23 later txBadSeq rebroadcasts all waited for the exact authorization’s expiry (317 s, channel released at expiry, not quarantined; nothing charged). The answer to every send of the recorded bytes is now persisted on the settlement (broadcast_accepted and sends_in_flight, migration 0024): a send is marked in flight before it is made, PENDING or DUPLICATE marks the bytes accepted, ERROR or TRY_AGAIN_LATER ends it as refused, and a lost answer (timeout, transport error, crash) stays in flight and counts as accepted. When the sequence conflict is proven and no send was ever accepted, the settlement finishes at once with nothing charged and its channel quarantined, on whichever replica or reconciler sees the txBadSeq; records written before the migration count as accepted. A declined early finish is logged with its reason. For exact settlements “finishes” now means the channel is quarantined and given back at once, with the record left open until the authorization expired unused or a copy landed; bytes whose channel was given back are never sent again. Fixed in 9355cd2. Tests

F-2026-143

A waiting sequence conflict logged its decline on every rebroadcast Low · facilitator · Fixed · found by REV-2026-10-recovery-drills on 2026-10-26, resolved 2026-10-26 · threats O2 While a settlement whose channel sequence number was taken kept waiting (its bytes may have been public), every rebroadcast logged the same decline, on each replica: about 60 lines per replica in a 5-minute wait, which buried other log lines and gave no count to alert or graph on. A decline is now logged once per settlement and replica, and again only when its reason changes, and each decision is counted in rail402_sequence_conflict_outcomes_total{network, outcome, reason} with a fixed set of reasons (private on pubnet, like the neighbouring sequence-conflict counter). Fixed in 9355cd2. Tests

F-2026-144

The CLI’s retryable flag used the facilitator’s meaning, not the command’s Medium · cli · Fixed · found by REV-2026-10-recovery-drills on 2026-10-26, resolved 2026-10-26 · threats M4 The CLI copied the facilitator’s retryable flag for a refused payment, but the facilitator’s flag means that resending the same signed payment can succeed, while running a CLI command again signs a new payment. So settle_*_settlement_in_progress was reported as retryable with paid: not_settled although another request was still settling the same authorization, which a second run could have paid twice; and settle_*_transaction_submission_failed was reported as not retryable although its reason says to sign a new payment and retry. The CLI now says whether running the command again can succeed: settlement_pending, settlement_in_progress and idempotency_conflict report paid: unknown (exit 5), submission failures and expiries with no funds moved are retryable, and the error-code reference and the CLI page define retryable for client codes this way. Fixed in ebeea7a. Tests

F-2026-145

A quarantined channel returned to rotation while an outside sender still used it Low · facilitator · Fixed · found by REV-2026-10-recovery-drills on 2026-10-26, resolved 2026-10-26 · threats O2 In the hosted re-run the third sequence conflict on one channel came one second after its quarantine ended: the quarantine only waited, so a channel someone kept using was handed back on time and one more payment failed. The account’s sequence number seen at the conflict is now kept with the quarantine (memory and Postgres pools, migration 0025), and the next lease compares it with the account’s current one; when it moved, the channel is quarantined again for twice as long and another channel is leased instead. A failed read leases the channel, where another conflict would quarantine it again. Fixed in ebeea7a. Tests

F-2026-146

The SDK and the MCP server reported a refused payment’s retryability apart from the CLI Medium · sdk · Fixed · found by REV-2026-10-recovery-drills on 2026-10-26, resolved 2026-10-26 · threats M4 Only the CLI read the facilitator’s code a seller passed on with a refused payment. The SDK’s PaymentError kept retryable false and paid: not_settled for every payment_rejected, so an SDK caller saw settlement_pending as nothing paid, and the rail402-mcp tool error took retryable from the code set instead of the error, so an agent never saw a busy channel as retryable. The CLI also called a seller-relayed transaction_submission_failed retryable with nothing moved, although upstream facilitators send that code for a DUPLICATE submission whose bytes can still land. One table in the SDK (refusalOutcomes) now sets retryable and paid on the PaymentError itself, and rail402-mcp and the CLI report the error’s values: settlement_pending, settlement_in_progress, idempotency_conflict, transaction_submission_failed, settle_exact_stellar_transaction_failed and settle_upto_stellar_settled_elsewhere report paid: unknown and are not retryable, and the seller’s code never makes a refusal more retryable than the facilitator’s table. rail402-mcp’s JSON-RPC errors now carry retryable and paid: none as well. Fixed in b08b49f. Tests

F-2026-147

The Explorer’s Bazaar calls shared one rate bucket for every visitor High · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats Q6, W3 Every Bazaar call the Explorer made for its visitors was charged to the Explorer’s own address, so one visitor paging quickly spent the discovery budget of all of them and the pages of everyone else lost their listings and paid-for links. The Explorer now calls with a discovery-internal access key read from a file and sends a per-visitor HMAC identifier (X-Rail402-Client), never the visitor’s address; the facilitator charges each forwarded visitor its own bucket and the key its own. Fixed in 50e7461. Tests

F-2026-148

A payment page was cached as final before its Bazaar link was known Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats W5 A payment page was cached for an hour as soon as the payment was indexed, even when the Bazaar had not linked it yet because cataloging was still running, so its paid-for link stayed missing for an hour. A payment page is now cached as final only once its link is known, or once the Bazaar answered for a payment older than 10 minutes; otherwise for 15 seconds. Fixed in 50e7461. Tests

F-2026-149

Another issuer’s token named USDC was shown and counted as USDC Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats W2 Asset codes were shown by their code alone, so a token any issuer named USDC was labelled and summed as USDC. Only allow-listed contracts are shown by a bare code; any other token shows its code with its issuer and is never counted as USDC. Fixed in 50e7461. Tests

F-2026-150

Pending and withdrawn listings had full resource pages with a way to pay them Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats P9, W2 A listing the Bazaar had not published (pending its origin check, or withdrawn) was rendered like a published one, with a price, a seller and a call snippet, although its metadata may have been echoed by a payer and never confirmed. Such a listing now shows only its state and history: no price, no seller and no way to call it. Fixed in 50e7461. Tests

F-2026-151

A payment’s paid-for link was not checked against the account it paid Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats W2 The Explorer showed the Bazaar’s paid-for link on any payment the Bazaar named, even when the listing’s payTo was not the account the payment paid on chain. A link is now shown only when the listing’s payTo, or the base account of a muxed payTo, is the account the payment paid. Fixed in 50e7461. Tests

F-2026-152

Listing pages paged by what the Explorer kept, skipping listings Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats W3 The services pages computed their next offset from the listings the Explorer kept after filtering, not from what the Bazaar sent, so listings were skipped or repeated across pages, and version history was read in pages too small for long histories. Pages now follow the Bazaar’s own pagination, version history is read 25 at a time, and crawlers are kept off paginated pages and the CSV. Fixed in 50e7461. Tests

F-2026-153

A slow Bazaar held Explorer pages for several sequential timeouts Medium · explorer · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats W3, W4 Each Bazaar call of a page had its own timeout and the calls ran one after another, so a hanging Bazaar held an account page or the CSV for many seconds. All Bazaar calls of one page now share one 3-second deadline; when it runs out the page renders from the ledger alone and says so, and a call cut short is never kept for the next visitor. Fixed in 50e7461. Tests

F-2026-154

A listing with private usage answered as if it did not exist Low · bazaar · Fixed · found by REV-2026-10-explorer-v2 on 2026-10-26, resolved 2026-10-26 · threats P16 The listing usage route answered a listing whose seller keeps its usage private with discovery_listing_not_found, so a client could not tell a private listing from a missing one. It now answers 404 with its own code, discovery_usage_private, documented with the other discovery codes. Fixed in ebeea7a. Tests

F-2026-155

The first payer of an mcp:// tool decided its private-usage opt-out for good Medium · bazaar · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats P15 The private-usage opt-out was read from the payload a buyer echoes. A tool under an mcp:// URL has no origin to confirm it, so the first payer’s echo decided it for good: leaving the key out published every later payer of a seller that opted out, and adding it hid a seller’s usage. Usage is now never published for a listing no origin confirmed (trust settled), applied at read time so the listings cataloged earlier are covered without changing their data, and the opt-out ratchets one way: a settlement that declares it makes the usage private at once and asks the origin to confirm; only the origin’s own 402 can remove it. Fixed in ebeea7a. Tests

F-2026-156

Testnet discovery load could shed pubnet discovery Medium · bazaar · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats N11, D5 One admission gate, one discovery database pool and one limiter served the discovery routes of both networks, ahead of the per-address limit, so enough parallel slow testnet requests made every pubnet discovery request fail with discovery_overloaded. Discovery now admits in two lanes, pubnet-scoped requests and everything else, each with its own gate; with pubnet served next to testnet the pubnet lane reads the catalog and keeps its limits through a discovery pool of its own. Fixed in 3549435. Tests

F-2026-157

The usage routes published each pubnet payer’s purchases by resource Low · bazaar · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats P16 The usage routes linked settlements, and with them payer addresses, to the listings they paid for on every network, and the Explorer labelled each payer’s payments with what they bought. On chain a payment names only payer, payTo and amount. Each network now publishes either settlement links or counts only (<NET>_BAZAAR_USAGE_DETAIL); pubnet defaults to counts, so no route links a pubnet payer or transaction to a listing, and the Explorer labels no pubnet payment. Fixed in ebeea7a. Tests

F-2026-158

A private-usage listing still named its settlements in its history and detail Low · bazaar · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats P16 rail402-private-usage hid usage on the usage routes only: the listing’s version history still named the transaction that created or re-listed it, and its detail showed when it was last paid. While a listing’s usage is private, or on a network that publishes counts only, its versions name no transaction, and a private listing shows no lastSettled. Its settlement count stays public: published clients require it. Fixed in ebeea7a. Tests

F-2026-159

One visitor’s refused Bazaar call blanked Bazaar data for every Explorer visitor Low · explorer · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats W7 Bazaar calls are charged to each visitor’s forwarded budget but their answers are cached for everyone, and any answer but 200 or 404 was cached for 5 seconds as the Bazaar being down. A visitor over its own budget could keep every other visitor’s pages without listings. A 429, or a 503 with Retry-After, is now an answer for that visitor only and is never cached. Fixed in 50e7461. Tests

F-2026-160

The sponsor balance page fired late on pubnet and a floor of 0 switched it off Low · operations · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats O11 Rail402SponsorBalanceLow fired below twice each network’s readiness floor, so pubnet’s default page threshold fell from 100 to 50 XLM, and a pubnet MIN_SPONSOR_BALANCE_XLM of 0 silenced it. The alert now fires below each network’s floor and, on every network but testnet, below 100 XLM whatever the floor; pubnet refuses a floor below 1 XLM at startup (config_pubnet_sponsor_floor_invalid). Fixed in ebeea7a. Tests

F-2026-161

The unused-nonce check of a sequence conflict was not bound to the proof’s ledger Low · facilitator · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats O2 When no send of a settlement’s bytes was ever accepted, a sequence conflict finished the settlement as nothing moved once the payer’s nonce read as unused. That read came from whichever RPC was active, with no check that it had reached the ledger the sequence proof was read at, so a lagging provider or one switched to after a failover could show a nonce another transaction already consumed as unused. The read must now come from an RPC at or past the proof’s ledger; otherwise the settlement keeps waiting (rpc_behind). Fixed in ebeea7a. Tests

F-2026-162

The status page built HTML from its check history without escaping all of it Low · operations · Fixed · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats O15 The status page wrote a day’s downtime from history/summary.json into a title attribute unescaped, and had no Content-Security-Policy of its own, so whoever could write the check history could run script on the page. Every value is now escaped and a day’s downtime is read as a whole number of minutes; the page carries a policy that allows only its one inline script by SHA-256, the same policy the served header builds from that script. Fixed in ebeea7a. Tests

F-2026-163

A quarantine does not check that the lease it clears is still the caller’s Info · facilitator · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats O2 The Postgres channel pool’s quarantine clears any lease on the channel. If the settlement’s own lease expired and another settlement leased the channel between the settlement’s final transition and the quarantine, the newer lease is cleared. Tests Accepted by the release approver on 2026-10-26. A safe guard needs a lease token in the channel row and in every lease call; a check on the lease’s expiry alone would skip the quarantine of a settlement that waited past its lease, which is the case quarantine exists for. The window is one statement long, after the settlement’s final transition.
  • A channel whose recorded settlement is still open is never leased again, released or expired.
  • A cleared lease leaves the channel quarantined, so no other settlement leases it until the quarantine ends.

F-2026-164

A seller’s usage aggregates are heavy for their cost Info · bazaar · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats D5, N11 GET /discovery/sellers/{payTo}/usage costs 2 units and runs up to two count(DISTINCT) aggregates over all of a seller’s settlements, each up to the 5-second statement timeout, on the small discovery pool. Tests Accepted by the release approver on 2026-10-26. The aggregates are bounded by the statement timeout and the per-client cost budget, and they run on the discovery pool, never the settlement pool; with discovery lanes they can no longer shed pubnet discovery from testnet.
  • Per-client cost budget, the seller route charged double.
  • 5-second statement timeout on discovery reads.
  • Admission lanes and a pubnet discovery pool.

F-2026-165

The payee backfill of migration 0026 runs as one update and can name a later owner Info · bazaar · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats P16 Migration 0026 fills the payee of earlier settlement observations in one UPDATE inside the migration’s transaction, and a backfilled settlement another payTo made while claiming a listing is attributed to the listing’s owner of the time. Tests Accepted by the release approver on 2026-10-26. Migrations run in one transaction each, after an ALTER TABLE that already locks the table, so batching inside the migration would hold the same locks; the observation table has one row per cataloged settlement and the migration runs at deploy, before the service serves. Rows observed after it record their payee.
  • The Explorer shows a paid-for link only when the listing’s payTo is the account the payment paid on chain.
  • The migration states the limit of the backfill.

F-2026-166

The Explorer’s live holdings budget is shared by every visitor Info · explorer · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats W4 Live holdings reads are bounded by one budget of 30 calls a minute for all visitors, so one visitor can use it up and the card then shows no live balances for everyone until it refills. Tests Accepted by the release approver on 2026-10-26. The card is labelled live and not audited, renders without data when the budget is spent, and protects a paid external service from being driven by visitors; no indexed figure depends on it.
  • A global call budget per network that fails soft.
  • Indexed pages never depend on live holdings.

F-2026-167

The status page shows sample figures off its own host Info · operations · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 · threats O13 Served from any host but status.rail402.dev, such as a repository preview URL, the status page renders generated sample uptime under a preview banner instead of the recorded history. Tests Accepted by the release approver on 2026-10-26. The sample lets the page be reviewed before data exists; it is shown only off the published host, under a visible preview banner, and the published host always reads the recorded history.
  • The published host never renders the sample.
  • A preview banner is shown whenever the sample is.

F-2026-168

New licences after the audit candidate Info · docs · Accepted risk · found by REV-2026-10-postfreeze on 2026-10-26, resolved 2026-10-26 The changes after the audit candidate add no third-party npm dependency. The Explorer bundles three fonts under the SIL Open Font License 1.1 with their licence texts, and the status checks run on Upptime (MIT). Tests Accepted by the release approver on 2026-10-26. OFL 1.1 is a font licence that permits bundling and is not a copyleft code licence; MIT is permissive. The licence gate checks every dependency on each change.
  • The licence gate refuses copyleft in every dependency path.
  • Font licence texts ship with the fonts.

F-2026-169

A tool listed from a payload without example arguments could never be confirmed again Low · bazaar · Fixed · found by REV-2026-10-rehearsal on 2026-10-27, resolved 2026-10-27 · threats P9 An MCP tool’s origin check called the tool with the example arguments stored on its listing. A listing first cataloged from a payload without an example, by its seller before it declared one or by any payer echoing the tool, was called without arguments; a tool that validates arguments before asking for payment answered with a result, and the listing was quarantined. Every later check, including those its owner’s genuine settlements requested, made the same call, so the seller’s tool stayed withdrawn and its later quarantines recorded nothing. A check the owner’s settlement requests now also tries the call that settlement’s payload declares, and uses only a PaymentRequired to it; a quarantined listing whose check fails for another reason records the new reason, and the store refuses a quarantine version without a reason. Fixed in ebeea7a. Tests

F-2026-170

The RPC failover alert never fired for the first switch after a start Low · operations · Fixed · found by REV-2026-10-local-drills on 2026-10-27, resolved 2026-10-27 · threats O3, O11 rail402_rpc_failovers_total got its series for a pair of providers only when the service first switched between them, so the series appeared at 1. Prometheus sees no increase in a series whose first sample is 1, so Rail402RpcFailover (increase over 15 minutes above 0) stayed silent for the first failover of every replica and for its first fail-back; only a second switch between the same providers in the same process fired it. The service now exports the counter at zero for every ordered pair of a network’s providers when it starts, so the first switch is an increase. Fixed in 3549435. Tests

F-2026-171

The facilitator’s HTTP server had no deadline for writing a response Medium · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats N1, D5 The service’s Node server gave a response no time limit to reach its client. A client that stopped reading kept its socket and the whole response, a full discovery page included, for as long as it liked, after the request had already given up its admission slot, in the one process that settles both networks; pipelining let one connection queue many such answers. Every response, pipelined ones and those the server gives without the app (an invalid Host, an unmet Expect) included, must now be handed to the network within RESPONSE_WRITE_TIMEOUT_MS (30 s) of being ready, or the connection is reset and counted. One connection may have at most MAX_PIPELINED_REQUESTS (4) requests waiting for their answers, and a discovery request keeps its slot until its page is sent, at most DISCOVERY_MAX_IN_FLIGHT_PER_CLIENT per client and lane. At shutdown, idle connections and requests still arriving are dropped, and a client gets at most 1 s more to take an answer. Fixed in ebeea7a. Tests

F-2026-172

Keyless testnet traffic could fail readiness and delay pubnet admission through the shared database pool Medium · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats N3, N4 /ready probed the database through the shared pool, and on the mainnet host every caller’s per-address limit was written through it too. Keyless testnet traffic that held every shared connection therefore failed the aggregate /ready the mainnet host is routed by, and delayed every pubnet /verify and /settle until the pool’s timeout. /ready now probes through one connection of its own, with a timeout. On the pubnet host the key is decided in memory first: a pubnet key holder’s limit is kept through pubnet’s own pool and every other caller’s in the process, so no anonymous request takes a pubnet connection. A client already over a limit is refused from memory until its window ends, /metrics is answered from memory, and at startup the service checks that its database role’s connection limit holds every pool of a process, refusing to serve pubnet below it (config_database_connections_low). Fixed in 3549435. Tests

F-2026-173

Any party could use up a seller’s new-listing quota with listings its own host confirmed for the seller’s payTo Medium · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats P5, P13 A payTo’s hourly quota of new listings counted every confirmed listing owned by that payTo, on any host. Any host can answer a 402 naming any payTo, so a party paying its own server for listings that named a seller, or claims that handed listings over to the seller, could fill the seller’s quota and keep its real listings out of the catalog. The quota is now kept per origin (scheme, host and port), and a listing counts toward it only once its origin confirmed a settlement that paid the payTo its price there (for upto, the whole cap). An ownership transfer clears the charge, and concurrent checks hold the owner’s quota until their transaction ends, so none charges past it. Fixed in 2fd3600. Tests

F-2026-174

safe-fetch read the bodies of answers it never returned without a limit Medium · sdk · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats X4, PG1 safe-fetch drained the body of a redirect it refused or followed, and of a 205 answer, with no byte cap and outside the request’s total timeout. A seller answering the Playground relay with a redirect and an endless body kept the socket, and its slot in the relay’s shared pool, for as long as it kept sending, so a few such requests starved every other relay client. A body that is never handed to the caller is now read only when it arrived whole; otherwise the connection is closed and its slot freed at once. A connection is also closed when an answer ends before the upload was sent, or goes on after a compressed body ends, so no server holds a connection past the request’s limits. Fixed in ebeea7a. Tests

F-2026-175

The pubnet conformance run executed freshly installed npm packages in the process holding the treasury secret Medium · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats CF2 The pubnet live run of the published packages installed them, with every dependency npm resolved, and imported them in the process that read the treasury secret, with the secret file’s path in its environment and install scripts enabled. A compromised dependency could read the treasury seed. The installed packages now run only in a separate process with an allow-listed environment, npm runs with install scripts off, and the payer is a fresh account per run that the treasury process funds on request. On pubnet that process starts in its own user, mount and PID namespaces with the treasury and key paths masked, and the run refuses to start, before anything is spent, where that isolation is unavailable. Fixed in 8695731. Tests

F-2026-176

The demo seller served concurrent copies of one payment Low · sdk · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats R4 In the authorization flow /verify refuses a payment already settled, but copies of one payment that arrive together all pass /verify before any of them settles. The demo seller, the Playground lab sellers and the seller-http example used the facilitator client as it was, so each concurrent copy was served while only one payment moved. Each now wraps its facilitator client in withSettleDedupe, covering the exact routes, the upto /history server and the MCP tools: the first copy is served and the others get seller_settlement_repeat. The seller helpers page and quickstart now wrap the client in both payment flows. Fixed in ebeea7a. Tests

F-2026-177

Anonymous demo seller traffic could use up the facilitator limit its buyers shared Low · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 Every request with a payment header made the demo seller call the facilitator, and those calls all come from the seller’s one address, which the facilitator limits per address. Anyone could send the demo seller payment headers it would never be paid for until that budget was spent and every real buyer’s payment was refused; with default settings the Playground shared the same address and budget. The demo seller now counts, per client and in total, only the payments it actually asks the facilitator about, below what the facilitator allows one address, and refuses the rest with a 402 rate_limited and Retry-After. It keeps at most 10,000 clients in memory, counts IPv6 addresses per /56, and counts IPv4 written as IPv6 as that IPv4. Fixed in 3549435. Tests

F-2026-178

A key file replaced while the service started was marked as loaded but never applied Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats Q2 The access keys were built from the records the configuration had parsed, and the key file was then read a second time for its stat and digest. A keys.json replaced between those two reads, for example to revoke a key, was recorded as seen but never parsed, so the revoked key stayed valid until the file changed again, while /health/private reported the new file’s digest. The records, the file’s stat and its digest now come from one read, at start and on every reload. A file that cannot be read or parsed leaves the previous records in force, reports no digest, and is read again on the next reload. Fixed in 3549435. Tests

F-2026-179

Anonymous traffic from a key holder’s address spent the key holder’s /verify and /settle limit Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats N1, N3 The service-wide limit counted requests per client address. Keyless requests of either network, unknown paths and refused requests from a pubnet key holder’s address or IPv6 /64, such as other clients behind the same NAT, used up the same bucket and got the key holder’s /verify and /settle refused. A /verify or /settle with a usable key is now held to a limit of its own key, sized to what the key may send on all its networks and never below RATE_LIMIT_PER_MINUTE. Every other request is held to its address’s limit, under a prefix no client address can spell as a key’s, and a request for a network that does not admit the key pays its address’s limit as well. Fixed in ebeea7a. Tests

F-2026-180

Any payer could withdraw a seller’s confirmed route-template listing by paying at another path Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P1, P2, P9 The origin check a settlement requested asked the path that settlement paid. For a published route-template listing, any payer could pay the seller at another path of the route that answered without a 402, or with other metadata, and the check withdrew or rewrote the seller’s listing. A published listing is now decided by one origin answer only, at its resource or its route template’s example path, and a published MCP tool by its listed call: a settlement at another path, or a call a payer proposed, can neither change nor withdraw it. While a listing is not yet listed, its check keeps the paths the settlements paying its owner proposed, asks them largest payments first within the host’s budget, and lists the listing from an answer that lists it for its owner. Fixed in 2fd3600. Tests

F-2026-181

One-unit payments for mcp:// tools could use up testnet’s hourly listing quota and published room Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P5, P10 mcp:// tools are listed from the payload alone, since no origin can confirm them, and any payer can list one for any payTo with a one-unit payment. They counted toward the network’s hourly new-listing quota and its room for published listings like listings an origin confirmed, so one payer could fill both and keep every seller with an origin out of the catalog. Listings no origin confirms are now counted apart: on testnet they take at most half of the hourly quota and half of the published room, and never use up those of listings an origin confirmed. Fixed in ebeea7a. Tests

F-2026-182

Hourly new-listing quotas were checked only when a listing was created Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P5, P13 The network’s and a payTo’s hourly quotas of new listings counted listings when they were first cataloged. Listings created at once, or kept pending for hours while their origin did not answer, were all published together once their origin confirmed them, far past the quotas. The quotas are now spent when a listing is first published: a listing confirmed while a quota is spent waits, without spending a retry or asking its origin, until the earliest listing counted leaves the hour (publication_deferred). The network’s count holds a lock until its transaction ends, so concurrent publications stay within the quota. Fixed in 2fd3600. Tests

F-2026-183

A route-template listing’s example path was taken from what its first payer echoed Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P2 A route-template listing’s example (info.input.pathParams) decides which path is asked when someone claims the listing, but it was the pathParams the creating payer echoed, which nothing checked. A payer could set an example that never answers, or answers for the payer, and so block or decide every later transfer of the seller’s listing. The example is now the path the creating settlement paid, and then the path at which the check that listed the listing confirmed it. A payer’s echo no longer decides who may claim a listing no origin has confirmed, and a route template whose paid path does not percent-decode is not used. Fixed in ebeea7a. Tests

F-2026-184

The proxy example logged API keys and payment headers for requests it could not proxy Low · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats Q2 The proxy example deleted X-API-Key and the payment headers from each site’s access log only. Caddy logs a request with its headers outside that log when it cannot proxy it (no ready replica, a failed round trip), when a response is cut off mid-body, for a Host that matches a site only without case, and with debugging on, so those entries kept the credentials in clear. The example’s default logger and a logger for handler errors now delete them too, X-Relay-H-Payment-Signature included, under which the Playground relay forwards a payment, and host names no site claims get a 404. Fixed in ebeea7a. Tests

F-2026-185

A database restore could let an upto authorization settled at zero off-chain be settled again Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats R5 Where upto zeros settle off-chain, the settlement row is the only record that an authorization was settled for zero. Restoring a dump taken before that row lost it, and the seller could then settle the same authorization on chain for up to its signed cap. Every restore is now recorded in settlement_rewinds, and from then on /verify and upto settlements above zero answer settle_upto_stellar_authority_unavailable (retryable) until the chain has passed the longest window rule 7 accepts, about 25 minutes by default. The ledger the hold counts from is recorded only by the service’s network poll, from a provider that answers with the network’s passphrase and whose newest ledger closed within the last minute, counting its age in; requests never move it. Fixed in 3549435. Tests

F-2026-186

A withdrawn listing whose usage is private showed its last settlement Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P15 GET /discovery/resources/{id} built a pending or withdrawn listing’s summary from the copy whose content it withholds, which no longer carried the private-usage extension. The detail of such a listing therefore showed lastSettled although its seller had made its usage private. The summary is now built from the stored listing, so the usage stays private while the content is withheld. Fixed in 3549435. Tests

F-2026-187

rail402 quote and seller check printed a seller’s 402 fields with terminal controls Low · cli · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats P7 The CLI’s human output printed an option’s scheme, raw amount and payTo, and the seller check’s payTo and asset, as the seller’s 402 sent them. Escape sequences in them could rewrite the terminal, fake a line such as the chosen option, plant links or write to the clipboard. Every value from a seller, the Bazaar or the network is now shown without control, bidirectional or invisible format characters, one-line values without line breaks, and a response body without carriage returns; —json output is unchanged. SDK reasons that quote a seller are cleaned the same way, before secrets are redacted from them. Fixed in b08b49f. Tests

F-2026-188

An upto settlement landed through another fee bump could be finished as expired on lagging RPC reads Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats F2, O2, O4 Once its own fee-bump hash read not found past its bounds, a settlement was finished as expired, with no funds moved, on an inner-hash lookup and a nonce read from whichever RPC answered. A lagging provider, a backend behind a balancer or another provider after a failover could answer from before the ledger at which the bounds were seen to pass, so a payment someone else’s fee bump had landed was reported as expired. Reads that would prove a settlement absent, and the search for a copy after a failure on a consumed nonce, now count only from an RPC at or past the ledger their proof holds at, and with the history they need; otherwise the settlement waits. One left undecided for a minute counts in rail402_settlements_unresolvable. Fixed in ebeea7a. Tests

F-2026-189

A pubnet approval shown for one budget window covered the next Low · mcp · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats M9 rail402-mcp’s first-per-session pubnet confirmation recorded the approved call without the budget window it was shown for. When the window renewed, or the session restarted, before the approved call was retried, the approval covered payments in a window the user had never been shown. The confirmation now carries the window and covers only payments in it; otherwise the user is asked again with the new window’s end and budget. Fixed in ebeea7a. Tests

F-2026-190

A keyed retry paid again after the budget window renewed or a new session started Low · sdk · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats M4 Idempotency keys were looked up within the current budget window only. A retry with the same key after the window renewed or a new session started found no earlier call and signed a second payment, although the chain had proven the first one settled. A key now belongs to the payer on its network: every ledger checks it across the payer’s windows inside its atomic reserve, replays the earlier outcome or reports paid: settled, and pays again only once the chain shows the earlier authorization expired unused. The payment identifier is chosen at random and kept with the key. Fixed in b08b49f. Tests

F-2026-191

withSettleDedupe refused a paid request whose first /settle answer was settlement_pending Low · sdk · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats R4 When /settle answers settlement_pending, the stock resource server settles again at once, and Rail402 answers that retry from its record with the repeat marker. withSettleDedupe took the marker for a copy of a payment already served and refused the buyer, who had paid, with seller_settlement_repeat. Each /settle the wrapper sends now carries an attempt id, which the facilitator records and echoes in every repeat’s marker. The wrapper serves, once, a marked success that echoes one of its own attempts that was answered pending or brought no answer, and still refuses every other marked success. Fixed in 9355cd2. Tests

F-2026-192

Pubnet searches could wait on a search-index build queued on the shared discovery pool Low · search · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats N11, D5 A search-index build is shared by every search waiting for the same revision, and it read the catalog through the pool of the search that started it. A pubnet search could therefore join a build waiting for a connection of the shared discovery pool, which testnet or unscoped load had filled, and be shed with it. Builds and the background index check now read the catalog through a pool of their own (2 connections) that no request takes, and a search whose build cannot read the catalog answers from the previous index. Fixed in 3549435. Tests

F-2026-193

Anyone could take over the fixture smart accounts a pubnet conformance run funds Low · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats CF1 The test kit’s smart-account payer ran a contract whose init set its owner in a separate call that needed no authorization and that anyone could repeat. Another account could make itself the owner of a fixture account a pubnet run had funded with USDC and drain it. The fixture is now a contract built in this repository whose constructor binds the owner key in the transaction that creates the account, and nothing can change the key afterwards. Before a pubnet run sends USDC to a contract account, it reads the account on chain and refuses one that does not run a known account’s code, or a fixture account whose owner is not the run’s key. Fixed in f8c4e1c. Tests

F-2026-194

The Explorer could name an account as the payer of a custom-token payment it never signed Low · explorer · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats I13, I3 The exact indexing rule named the transfer’s from address as the payer whenever the authorization entry named it. A token that is not a Stellar Asset Contract can skip require_auth, so a transfer from any account could succeed with an entry nobody signed, and the Explorer recorded that account as having paid. Indexing rules v2 add check E7: an exact row names its payer only when the token is a Stellar Asset Contract, checked against its asset, or the payer’s own key signed the entry over the payload the network signs; other rows are excluded as unproven_authorization. The indexer reads ledgers indexed under v1 again, and until it has, the pages, API and report leave out rows of other rules. Fixed in 50e7461. Tests

F-2026-195

A refused upto rebroadcast finished as failed while its transaction could still land Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats F2 When the network refused an upto settlement’s rebroadcast for good before its bounds passed, for example once the sponsor key was frozen, the settlement was finished as failed with no funds moved. An earlier send may already have made its inner transaction public, and anyone can wrap it in a fee bump of their own until its bounds pass, so the payment could still land while the record and every repeat said failed. Such a settlement now keeps waiting until no later ledger can include its transaction, and is answered as settled when another fee bump lands it; one whose sends were all refused still finishes at once. Fixed in ebeea7a. Tests

F-2026-196

An exact payment included after its authorization expired hid a copy that had landed first Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats F1, F2 An exact transaction’s time bound can outlast the payer’s authorization. Included after the expiration, it fails with invalid_exact_stellar_signature_expired, since the host checks the expiration before the nonce, and that cause did not start the search for a copy of the authorization someone else had landed earlier. The settlement was answered as expired although the payment had settled. That cause now starts the same search as a consumed nonce, and a copy found is answered as settled with its hash. Fixed in ebeea7a. Tests

F-2026-197

A refused exact submission finished as failed while a copy of the payment could still land Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats F2, O2 When the network refused an exact settlement’s first submission for good, or another transaction took its channel’s sequence number before any send was accepted, the settlement was finished at once as failed, with no funds moved. The payer’s authorization stays valid until it expires, so the payer, the seller or anyone the payment passed through could still land a copy, and the reason asked the buyer to sign a new payment. Such a settlement now stays open until the authorization expired unused, and is answered as settled if a copy lands. Once the channel’s sequence number proves its own transaction can never apply, it gives the channel back at once and sends nothing more. Fixed in 9355cd2. Tests

F-2026-198

An exact transaction that failed on chain was answered as failed while its authorization stayed usable Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats F2, S1 An exact transaction included and failed on chain for a cause that leaves the authorization usable (a balance spent after simulation, a trustline removed, a smart account or token that refused it for now) was finished as failed at once. The failure rolls back, nonce included, so anyone holding the payment could still land it until it expired, while the record said failed and the SDK told the buyer nothing was paid, inviting a second payment. Such a settlement now answers settlement_pending, gives its channel back, is answered as settled if a copy lands, and with the on-chain cause once the authorization expired unused; only a consumed nonce, or an expiry past the payer’s own expiration ledger, finishes at once. The SDK, rail402-mcp and the CLI report those causes as paid: unknown and never retryable. Fixed in ebeea7a. Tests

F-2026-199

An Explorer account page aggregated its seller’s whole settlement history on every view Low · explorer · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats W3, I9 Every anonymous view of a seller’s account page counted the seller’s exact and upto payments and its distinct payers over the settlements table, with no precomputed total, so the work of one view grew with the seller’s history and ran on the Postgres the facilitator shares; only the reader role’s statement timeout bounded it, and revalidations ran it too. The indexer now keeps those counts in seller_summaries with the account totals (Explorer migration 0006, filled from the stored settlements), a view reads one row, and a seller’s newest upto payments are read through an index of their own. Fixed in 50e7461. Tests

F-2026-200

The network window published its anchor Horizon URL, including any provider key in its path Low · explorer · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats I11, I8 The network window job published the whole EXPLORER_ANCHOR_HORIZON_URL in its rebuild command and document, which the Ecosystem page, the report API and snapshot exports serve to anyone. The same variable is the indexer’s private anchor, and a paid provider’s key can only sit in the URL path. A window now names only a bare-origin Horizon: the anchor itself when its path is /, otherwise EXPLORER_NETWORK_WINDOW_PUBLIC_ANCHOR_URL, without which the job refuses to start (config_anchor_not_public); the window refuses to publish any anchor URL with a path, query or credentials. Fixed in 50e7461. Tests

F-2026-201

An address over its discovery limit still took places in the pubnet discovery lane Low · facilitator · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-28 · threats N1, D5 Per-client discovery limits ran only after a request held a lane slot, and each refused request still wrote one serialized upsert to the shared rate-limit row, so one address sending faster than that write drained could take the pubnet lane’s slots from other clients. One client now holds at most its share of a lane (DISCOVERY_MAX_IN_FLIGHT_PER_CLIENT, 8 by default) before the lane’s gate, and an address seen over its limit is refused from memory until its window ends, with no further write. Fixed in ebeea7a. Tests

F-2026-202

Listings their origin never confirmed were kept forever, with the payer’s content stored three times Low · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats N3, P5 A free testnet settlement with Bazaar metadata for any https resource creates a pending listing; when its origin does not confirm it, it is withdrawn. Such listings and their versions, each holding the payer-echoed content, were never deleted, and only the hourly per-payer quota bounded how many were created, so free settlements grew the database pubnet shares without bound. A listing never published that has stayed withdrawn for 7 days is now deleted with its versions, options, observations and origin check (listing_pruned, migration 0036); its content was never public, and a later settlement stages it again. Fixed in 2fd3600. Tests

F-2026-203

One party could fill pubnet’s published-listing capacity and keep every new seller out Medium · bazaar · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats P5, P13 Pubnet’s capacity of published listings was shared first come, first served, with no share per seller, and under pubnet’s defaults no published listing ever left: the idle retention was off and nothing re-checked a published listing’s origin. A party publishing from its own origins could fill the network and keep it full after abandoning them, refusing every new seller with bazaar_catalog_full. One payTo may now hold at most PUBNET_BAZAAR_MAX_PUBLISHED_PER_OWNER (250) published listings, and pubnet withdraws a listing after 90 days without a settlement (PUBNET_BAZAAR_IDLE_TTL_DAYS, was 0); a withdrawn listing comes back only when its origin confirms it again. Fixed in 2fd3600. Tests

F-2026-204

A contract release did not prove which revision of the third-party build workflow ran Low · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats C1, C5 The contract release calls stellar.expert’s reusable workflow at its main branch, which stellar.expert requires, with this repository’s contents, OIDC and attestation permissions. Its only check compared that branch with the reviewed revision before the call, a second read of a mutable name, and the release’s attestation was verified by signer repository alone, so a run of another revision would have gone unnoticed. The release now waits for a reviewer in the contract-release environment, and its attestation must name the reviewed workflow revision and the tag’s commit (—signer-digest, —source-digest); a pubnet deploy of the upto contract checks the same revision. Fixed in 32bb685. Tests

F-2026-205

The deploy gate accepted an image built for the tag name from another commit Medium · operations · Fixed · found by REV-2026-10-prelaunch on 2026-10-28, resolved 2026-10-29 · threats C2, C3 The deploy gate checked that the release tag was signed and that each image’s attestation came from the release workflow run for that tag name, but not that the build ran from the signed commit. Someone able to push a tag but without a release-signing key could point the tag at a commit whose own release.yml built and attested an image, then let the signed tag replace it; the gate would accept that image for the signed commit. The gate now requires each attestation to name the signed commit both as the build’s source and as the workflow file that ran (—source-digest, —signer-digest). Fixed in 32bb685. Tests

F-2026-206

A protocol upgrade the SDK could not read stopped every verification while /health and /ready said healthy Medium · operations · Fixed · found by REV-2026-11-protocol-30 on 2026-11-04, resolved 2026-11-04 · threats O3 The Stellar testnet moved to protocol 30, whose ledger headers carry a StellarValueType that @stellar/stellar-sdk 16.3.0 cannot decode. Every /verify then answered unexpected_verify_error, so no testnet payment settled, while the network poll (getHealth, getTransaction and balance reads, none of which decode a ledger header) kept /health and /ready healthy and no alert fired. 0.5.2 moves to stellar-sdk 16.4.1, which reads protocol 30, and the network poll now decodes the newest ledger header each time: an SDK that cannot read the network’s ledgers fails the RPC readiness check, so /ready answers 503 and the alerts on it fire before payments fail one by one. Fixed in 6079ae2. Tests