Skip to content

Keys and signers

Corridor never needs to hold a customer’s money or keys. A licensed payment company runs Corridor in its own environment (or a managed instance dedicated to it) and plugs in the signer it already trusts. The saga engine, route planner and ledger decide what to sign; the customer’s signer is the only thing that can sign.

Customer-held keys

Vault keys live in the customer’s KMS, HSM or MPC provider. Corridor sends a 32-byte hash and gets a signature back. Key material never enters Corridor’s process.

One key for Stellar and Solana

Both use Ed25519. The same held key addresses a Stellar vault in StrKey and a Solana vault in base58, so a deployment needs one key, not two.

// @corridor/signer
interface Ed25519Signer {
readonly kind: string; // "local", "turnkey", …
readonly address: string; // base58 Solana address
signBytes(message: Uint8Array): Promise<Uint8Array>;
}
interface Secp256k1Signer {
readonly kind: string;
publicKey(): Promise<Hex>; // uncompressed, 65 bytes
signDigest(digest: Hex): Promise<EcdsaSignature>;
}

ed25519PublicKeyBytes(signer) returns the raw 32-byte key, which is all Stellar needs:

// @corridor/chain-stellar
interface StellarSigner {
readonly kind: string;
readonly address: string; // G… StrKey public key
sign(hash: Uint8Array): Promise<Uint8Array>; // the transaction hash, 32 bytes
}
const vault = stellarClient({
network: "mainnet",
signer: ed25519StellarSigner(held, ed25519PublicKeyBytes(held)),
});

ed25519StellarSigner refuses anything that is not a 32-byte public key, encodes it with StrKey.encodeEd25519PublicKey, and signs over the transaction hash — so the signer never sees transaction semantics.

Adapter Status Notes
localStellarSigner(secret) Built Development and testnet. Takes a Stellar secret seed from .env.local.
localEd25519Signer(secretKey) Built Development and testnet keys behind the same interface.
ed25519StellarSigner(signer, publicKey) Built, tested Any Ed25519 signing service — an MPC provider, or a KMS with Ed25519 keys — reaches Stellar through this.
turnkeyEd25519Signer, turnkeySecp256k1Signer Built, tested Keys in the customer’s Turnkey organisation. Raw-payload signing, API-key stamped requests, and polling while an activity waits for policy approvals. The secp256k1 public key is recovered from a probe signature, so no read permission is needed.
awsKmsSigner(kms, keyId) Built, tested Key spec ECC_SECG_P256K1, for the EVM side. KMS returns DER signatures with no recovery bit and no low-s rule; the adapter normalises s and recovers yParity against the key’s public key.
localSecp256k1Signer(privateKey) Built Development and testnet on the EVM spoke.

Corridor does not bundle the AWS SDK. Wrap the customer’s own KMSClient:

import { KMSClient, GetPublicKeyCommand, SignCommand } from "@aws-sdk/client-kms";
import { awsKmsApi, awsKmsSigner } from "@corridor/signer";
const kms = awsKmsApi(new KMSClient({ region: "eu-west-1" }), { GetPublicKeyCommand, SignCommand });
const signer = awsKmsSigner(kms, "alias/corridor-evm-relay");

The runtime selects the signer from the environment: CORRIDOR_SIGNER=aws-kms with <KEY_NAME>_KMS_KEY_ID, or CORRIDOR_SIGNER=turnkey with TURNKEY_ORGANIZATION_ID, TURNKEY_API_PRIVATE_KEY and <KEY_NAME>_TURNKEY_SIGN_WITH. The default is a local key from .env.local. The same flows run unchanged.

A vault only ever signs two kinds of thing, and both are checked before they reach it:

  • A payment on Stellar, built by Corridor from the route plan. Signing is over the transaction hash.
  • A SEP-10 challenge, which is verified first: the challenge must be built by the anchor’s own SIGNING_KEY and must name this vault, so a stray endpoint cannot get the vault to sign something else. See the anchor interface.
  • Unit tests sign through the interface and check the resulting addresses and signatures, including a KMS stand-in that returns real DER signatures with high-s half the time.
  • ed25519StellarSigner is covered in packages/chain-stellar/src/stellar.test.ts: a held key produces the expected G… address and a valid signature over a transaction hash.
  • Every Stellar testnet run in Evidence signs through StellarSigner.
Model Who runs Corridor Who holds keys Fits
Self-hosted The customer, in its cloud The customer Banks and larger licensed operators
Dedicated managed instance Corridor, one instance per customer The customer (Corridor calls its KMS or MPC) Mid-size payout companies
Read-only Either Nobody: no keys needed The first 60 days. See Read-only pilot.