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.
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.
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.
The interface
Section titled “The interface”// @corridor/signerinterface 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-stellarinterface 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.
Adapters available
Section titled “Adapters available”| 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.
What the signer is asked to sign
Section titled “What the signer is asked to sign”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_KEYand must name this vault, so a stray endpoint cannot get the vault to sign something else. See the anchor interface.
Evidence
Section titled “Evidence”- 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.
ed25519StellarSigneris covered inpackages/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.
Deployment models
Section titled “Deployment models”| 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. |