Security and key management
Principles
Section titled “Principles”Vaults sign through the customer’s own KMS, HSM or MPC provider behind one interface. Corridor sends transaction hashes and receives signatures. See Keys and signers.
A pilot watches Stellar accounts by id and matches records. Nothing can move.
The SEP-10 challenge must be built by the anchor’s own SIGNING_KEY and must name this vault, or the vault does not sign it.
A look-alike token from another issuer is never booked as USDC, and an unverified issuer is left unset rather than guessed.
Webhooks are HMAC-signed, time-bounded and compared in constant time. Forgeries get a 401 and change nothing.
Funding and deliveries are recorded durably before and after they happen, so retries find them instead of repeating them.
| Key | Development and testnet | Production |
|---|---|---|
| Stellar vaults (treasury, escrow) | A Stellar secret seed in .env.local, generated on first run and funded by Friendbot |
The customer’s KMS, HSM or MPC provider, through @corridor/signer and ed25519StellarSigner |
| Solana and Base vaults | .env.local |
The same held key for Solana (Ed25519); AWS KMS or Turnkey for the EVM side |
Corridor Reference secret (CORRIDOR_TAG_SECRET) |
.env.local |
Secret manager; rotation by reference version |
| Anchor credentials and webhook secrets | .env.local |
Secret manager |
.env.local is gitignored. Keys are never committed, printed or sent anywhere.
SEP-10, before the vault signs
Section titled “SEP-10, before the vault signs”The vault key is the most valuable thing in the deployment, and SEP-10 asks it to sign a transaction an anchor supplies. SepAnchor checks the challenge first:
- it must be built by the anchor’s own
SIGNING_KEY, as published in itsstellar.toml; - it must name this vault as the client account;
- it must be on the expected network.
Only then is it signed, and the JWT that comes back is cached for its lifetime. A stray or hostile endpoint cannot use the handshake to get a vault to sign something else.
Issuer identity
Section titled “Issuer identity”On Stellar anyone can issue an asset whose code is USDC. assetOf matches code and issuer, so a look-alike is never booked. Issuers Corridor has not verified are left unset: asking for one raises an error that names the missing issuer rather than guessing. A deployment supplies its own with CORRIDOR_STELLAR_ISSUER_<CODE>_<NETWORK>. See ADR 0007.
Webhook verification
Section titled “Webhook verification”SEP-31 has no webhook — the sending anchor polls GET /transactions/{id}, which is what the saga’s wait loop does. For providers that do deliver webhooks, Corridor verifies them:
X-PAJ-Timestamp: <unix seconds>X-PAJ-Signature: v1=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>- The timestamp must be within 5 minutes.
- The
v1=value is compared in constant time (timingSafeEqual); during a secret rotation a header may carry several schemes, and any validv1passes. - Each order status is stored once; redeliveries are acknowledged without effect.
Exactly-once funding
Section titled “Exactly-once funding”A payout records its anchor order and the funding transaction (partner_order_links) durably. If a worker crashes after opening an order, the retry reuses it; if it crashes after funding, the retry finds the funding payment on Horizon by its memo and does not send again. A failure after funding stops at manual_review, because the money is at the anchor until its refund is confirmed.
Every commit also checks the saga’s fencing token, so a worker whose lease lapsed mid-leg can never book or mark a step done. See Saga engine.
Compliance posture
Section titled “Compliance posture”- Corridor serves licensed payment companies, which own the KYC and KYB of their end customers and hold the licences for each corridor.
- Corridor runs in the customer’s environment and signs only through the customer’s signer. It is software, not a custodian or a money transmitter.
- Fiat touchpoints go through regulated anchors and licensed partners in each corridor.
- Secrets live outside the repository.
Production hardening
Section titled “Production hardening”| Area | Today | Production deployment |
|---|---|---|
| Vault keys | Local seeds on testnet; signer interface on every vault | The customer’s KMS or MPC for every vault |
| Ledger | PGlite | Managed Postgres with the same schema and triggers |
| Horizon | The public instance | CORRIDOR_HORIZON_URL pointing at a private instance |
| Screening | None | Address screening and a Travel Rule hook before a payout is funded |
| Audit export | The reconciliation trail and a CSV | Signed, retained exports on a schedule |