Threat model
This page is the executable threat model. It enumerates the assets BabyFX Pay protects, the actors we care about, the trust boundaries they cross, and the mitigations in place. If a control is not listed here, it is not implemented.
Trust boundaries
flowchart LR
subgraph TB0["Trust boundary 0: Public internet (untrusted)"]
direction TB
U[("Customer
mobile wallet")]
MH[("Merchant
web/POS")]
end
subgraph TB1["Trust boundary 1: Edge - gateway"]
direction TB
RP["OpenResty RPC proxy
(per-IP rate limit,
batch cap, debug block)"]
end
subgraph TB2["Trust boundary 2: Application - pay/"]
direction TB
PA["pay/api
(HMAC auth,
idempotency)"]
PS["pay/settlement
(block poll)"]
PW["pay/webhooks
(HMAC sign)"]
PSP["pay/sweep
(holds key)"]
end
subgraph TB3["Trust boundary 3: Core - chain/"]
direction TB
CN["BabyChain node
(CometBFT, PoA)"]
end
subgraph TB4["Trust boundary 4: Data plane"]
direction TB
DB[("PostgreSQL
SyncDB")]
KM[("HSM / KMS
(prod only)")]
end
U -->|"eth_* JSON-RPC"| RP
MH -->|"REST + API key"| PA
RP -->|"filtered passthrough"| CN
PA --> DB
PS --> CN
PS --> DB
PW --> MH
PSP -->|"signs with key"| CN
PSP -.->|"key custody (prod)"| KM
PSP -.->|"DEV ONLY mnemonic"| ENV[("env: PAY_MNEMONIC
(NEVER in prod)")]
classDef t0 fill:#fee2e2,stroke:#dc2626
classDef t1 fill:#fef3c7,stroke:#d97706
classDef t2 fill:#dbeafe,stroke:#2563eb
classDef t3 fill:#dcfce7,stroke:#16a34a
classDef t4 fill:#f3e8ff,stroke:#7c3aed
class U,MH t0
class RP t1
class PA,PS,PW,PSP t2
class CN t3
class DB,KM,ENV t4
Assets
| Asset | Where it lives | Sensitivity |
|---|---|---|
api_key (per merchant) |
merchants.api_key_hash (Argon2id) |
High — anyone with this can create invoices |
webhook_secret (per merchant) |
merchants.webhook_secret_ciphertext (AES-256-GCM) |
High — anyone with this can forge webhooks from the merchant''s perspective |
PAY_SECRET_KEY |
env / KMS | High — key-encrypting-key for all webhook secrets |
PAY_MNEMONIC |
env / HSM | Critical — derives every invoice deposit address |
| Sweep gas-funder private key | env / HSM | Critical — funds sweeps |
| Settlement address private keys (merchant) | merchant''s wallet, never on our side | High — funds land here |
| Customer funds in deposit addresses | chain, in derived addresses | High until swept |
| Database rows | PostgreSQL | Medium — contains PII like webhook URLs |
Threat actors
| Actor | Capability | Motivation |
|---|---|---|
| Curious customer | Has a wallet, can read chain | Reconnaissance |
| Hostile customer | Has a wallet, can craft custom txs | Underpay, replay, grief |
| Compromised merchant POS | Has a valid api_key |
Invoice spam, fund theft |
| External attacker (no creds) | Public internet only | DoS, recon, exploit-hunting |
| Compromised gateway host | Read access to OpenResty config | Traffic manipulation |
Compromised pay/api host |
Read access to DB + secrets | Fund theft |
Compromised pay/sweep host |
Has PAY_MNEMONIC in env (dev) |
Total fund theft |
| Insider (read-only dev) | Has source + local dev access | Code exfiltration |
| Insider (operator) | Has prod access | Privilege abuse |
Threats and mitigations
T1. RPC amplification via batch calls
Risk: A single POST with 10,000 eth_calls in a batch.
Mitigation: OpenResty enforces a configurable batch-cap at the proxy (max_batch_size, default 50).
T2. debug_* namespace exposure
Risk: debug_traceTransaction leaks internal state.
Mitigation: Hard-blocked at the OpenResty proxy. Returns 403 regardless of source IP.
T3. Customer underpayment
Risk: Customer sends 0.001 2BABY to a 4.50 USD invoice.
Mitigation: Settlement matches only tx.value >= invoice.baby_amount_wei. Underpayment is silently ignored; merchant is not notified.
T4. Customer overpayment
Risk: Customer sends 100 2BABY to a 4.50 USD invoice. Mitigation: No automatic refund. Excess is swept along with the rest to the merchant''s settlement address. Merchant must handle out-of-band.
T5. Webhook spoofing
Risk: Attacker POSTs a fake payment.confirmed to the merchant''s webhook URL.
Mitigation: Every webhook carries an HMAC-SHA256 signature in x-babyfx-signature. Merchants MUST verify before acting. See Webhooks.
T6. Replay of a captured webhook
Risk: Attacker captures a real payment.confirmed and replays it to the merchant''s endpoint.
Mitigation: Signed payload includes a unix timestamp; receivers reject signatures whose t is more than 5 minutes from server clock. Delivery id is also unique per attempt.
T7. api_key leak from merchant POS
Risk: A compromised POS exposes its api_key.
Mitigation: api_key is Argon2id-hashed at rest. The raw value is never logged. Merchants can rotate by deleting and re-creating.
T8. Loss of PAY_MNEMONIC
Risk: Compromise of the env var leaks every deposit address derivation key. Mitigation (dev): none — env is the only storage. Mitigation (prod): HSM / KMS. Derivation is a remote signing call; the mnemonic never leaves the HSM.
T9. Sweep gas starvation
Risk: Sweep wallet runs out of gas. Confirmed funds sit in deposit addresses.
Mitigation: Sweep wallet self-refills from the GAS_FUNDER_PRIVATE_KEY. Operator is paged when the gas funder itself runs dry.
T10. Reorg that drops the paying block
Risk: The block containing the customer''s payment is reorged out.
Mitigation: Confirmation counter decrements naturally. Invoice returns to PAID (or PENDING on deep reorgs). A new block will re-fire the confirmation ladder and sweep.
T11. Database exfiltration
Risk: Attacker reads webhook_secret_ciphertext from a DB backup.
Mitigation: Ciphertexts are useless without PAY_SECRET_KEY, which lives in a separate secret store. Backups are encrypted at rest.
T12. DoS at the gateway
Risk: A burst of requests overwhelms the proxy. Mitigation: Per-IP token bucket at the OpenResty layer. Hard cap on simultaneous upstream connections.
Out of scope
- Insider with database root. The threat model assumes operators are not malicious. Add a separate insider-threat program if needed.
- Compromise of the customer''s device. Out of scope; addressed in the wallet security model.
- Quantum attacks on secp256k1. Not yet on the roadmap.
