Live incident · Coston2 · request 44928272

The tests passed. The ledger proved us wrong.

Signet paid a redemption obligation that another party had already paid, because ACTIVE on Flare does not mean unpaid on XRPL, and every guard Signet had watched the wrong chain.

What happened

Ledger 19825006

The agent paid

From its own underlying address.

Meanwhile

Coston2 said ACTIVE

Which means not yet confirmed on Flare, not unpaid.

Ledger 19825042

Signet paid again

36 ledgers later.

Correction

S021_PAYMENT_ALREADY_OBSERVED

Schema V2 requires the boundary's own observation.

Root cause

Signet had three duplicate-payment guards and all three are real: the registry rejects a repeated action, the coordinator database permits one completion per obligation, and the XRP ledger refuses a consumed sequence. Every one of them prevents Signet paying twice. None can see a payment made by somebody else.

The defect was not a missing check. It was a missing chain.

Reason codes this created

S021_PAYMENT_ALREADY_OBSERVED
A validated payment already carries this reference to this destination.
S022_UNDERLYING_STATE_UNAVAILABLE
No observation, an unavailable one, or too few agreeing sources.
S023_UNDERLYING_STATE_DISAGREEMENT
Endpoints disagree. Deliberately not retried automatically.
S024_UNDERLYING_OBSERVATION_STALE
The observation is too old to rely on.

The residual, stated rather than hidden

S021 fires on a payment that has validated. Between another party submitting a payment and that payment validating, Signet can still observe nothing and authorize. That window cannot be closed by observation. Closing it needs exclusive signing authority over the underlying account, which is Signet's production architecture and is not instantiated by this deployment.

No third party lost funds. That is luck about the test setup, not a property of the system.

The claim this incident backsverified

A FAssets redemption status of ACTIVE does not mean the obligation is unpaid; it means the underlying payment has not yet been confirmed on Flare. Signet paid a live Coston2 obligation that the agent had already paid 36 ledgers earlier. Schema V2 corrects this: the decision now requires the signing boundary's own XRP ledger observation, refuses when a matching validated payment already exists, refuses when the observation is missing, unavailable, insufficiently sourced, contradictory or stale, and binds the observation into the authorization commitment. The incident is replayed against both implementations as a permanent regression test, which also proves no V2 input reproduces the original authorization.

What it does not prove

  • The residual window cannot be closed: a competing payment validated after Signet's observation and before Signet's own payment validates is not detectable. It measured 4 ledgers in the one V2 run this build produced, which is one sample rather than a bound, and is publicly measurable for any payment because observedAtLedger is bound into the commitment.
  • A fabricated observation is detectable by the independent verifier, which re-observes the ledger and fails a receipt whose observation does not match what it finds, but only while endpoints still retain the window. After that it is neither prevented nor detectable. Preventing it needs the TEE this build does not have.
  • The production configuration under which no independent legitimate payer exists, Signet holding the agent's only XRPL signing authority, is a design claim and is not demonstrated: this build does not control an FAssets agent account.

Permanent regression: scripts/lifecycle/incident-44928272.test.mjs, which asserts no V2 input can reproduce the original authorization. It runs in make verify.

See the transactions