AGORA.GRIPE
Securitycheat-monkey

Watch the SDK catch a cheating gateway

This gateway really tries to cheat. It mints real signed invoices, then skims, swaps the recipient, or fakes the payment. Run it and watch your SDK refuse each one.

// what this is

A live adversary, not a fixture

Most demos mock the attack. This one is the real Rust gateway, built with a cheat-monkey flag. It signs genuine BOLT11 invoices and then breaks exactly one thing. The SDK runs the same checks it runs in your app, from the invoices alone. Every response also names the cheat it tried, so you can hold the gateway's confession next to the SDK's independent verdict.

// live

Pick a cheat, or let it surprise you

Cheat to attempt

The SDK verdict comes from decoding the invoices, never from the cheat the gateway declared.

// the attacks

What it tries, what the SDK returns

CheatWhat the gateway doesSDK verdict
honesteverything correct, the control caseverified
hashmismatchrecipient invoice carries a different payment hashhash_mismatch
skimrecipient invoice is for less than the donationamount_skim
overchargedonor invoice charges far above the donationamount_overcharge
testnetrecipient invoice is a worthless testnet one reusing the hashnetwork_mismatch
custodialtakes custody despite require_trustlesscustodial
fakesuccessclaims success with a preimage that does not hash to Hpreimage_mismatch
substituterecipient invoice signed by a different noderecipient_mismatch
// the honest ceiling

What this proves, and what it cannot

// do it yourself

Point the SDK at the cheat and catch it

typescript
import { ThunderBridge, GatewayCheatError } from "thunder-bridge";

// Point the SDK at the cheating endpoint. Every response also declares the
// cheat it tried; the SDK ignores that and decodes the invoices itself.
const tb = new ThunderBridge("https://thunder-bridge.agora.gripe/cheat-monkey");

try {
  // recipientNodeId is the recipient's node id, known out of band. The SDK
  // pins it, so a substituted recipient throws instead of slipping through.
  await tb.createDonation({
    destination: "anon@chaos.test",
    amountSat: 21,
    recipientNodeId: "02989c0b...5f6f",
  });
} catch (e) {
  if (e instanceof GatewayCheatError) {
    // e.code: amount_skim, recipient_mismatch, preimage_mismatch, ...
  }
}