Keychain provides one SolanaSigner interface across
many key-management backends: in-memory keys, cloud KMS/HSM, and managed wallet
services. Write your signing code once, then switch backends through
configuration.
The example below signs and sends a transfer with an in-memory signer. The same
code works with any backend that signs a transaction you own (most of them);
only the signer's construction changes. Crossmint and Fordefi native modes
rewrite or broadcast the transaction themselves, so they cannot act as a Kit
client signer: call them through signAndSendTransaction() from
@solana/keychain-core instead.
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";import { createMemorySigner } from "@solana/keychain-memory";import { rpcAirdrop, solanaRpc } from "@solana/kit-plugin-rpc";import { airdropSigner, signer } from "@solana/kit-plugin-signer";import { getTransferSolInstruction } from "@solana-program/system";// Most Keychain backends return a `SolanaTransactionSigner`. Swap this// in-memory signer for `createAwsKmsSigner`, `createVaultSigner`, ... and the// rest of this file is identical.const keychainSigner = await createMemorySigner({privateKey: crypto.getRandomValues(new Uint8Array(32)),});const recipient = await generateKeyPairSigner();const client = await createClient().use(signer(keychainSigner)).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900",}),).use(rpcAirdrop()).use(airdropSigner(lamports(1_000_000_000n)));const { context } = await client.sendTransaction([getTransferSolInstruction({source: keychainSigner,destination: recipient.address,amount: lamports(10_000_000n),}),]);console.log("Signature:", context.signature);
The in-memory signer holds a raw private key: use it for development and tests. In production, use a managed backend so keys stay in dedicated infrastructure. See Choosing a backend.
Use a production backend
To sign with a production backend, install its package (or the umbrella
@solana/keychain) and construct the signer through the unified factory. For
backends that sign a transaction you own, the signing code stays the same.
import { createKeychainSigner } from "@solana/keychain";const signer = await createKeychainSigner({backend: "aws-kms",keyId: "alias/my-solana-key",publicKey: "base58_public_key"});
import { createKeychainSigner } from "@solana/keychain";const signer = await createKeychainSigner({backend: "vault",vaultAddr: "https://vault.example.com:8200",vaultToken: "hvs.xxxxx",keyName: "my-solana-key",publicKey: "base58_public_key"});
See the TypeScript guide for every backend's configuration, or Choosing a backend to compare custody models.
Is this page helpful?