TypeScript

Installation

Install the umbrella package or individual signers as needed:

# Umbrella package (includes all signers)
pnpm add @solana/keychain
# Or install individual packages
pnpm add @solana/keychain-core # Core interfaces (required for custom signers)
pnpm add @solana/keychain-memory # Local keypair signer
pnpm add @solana/keychain-vault # HashiCorp Vault
pnpm add @solana/keychain-aws-kms # AWS KMS
pnpm add @solana/keychain-gcp-kms # GCP KMS
pnpm add @solana/keychain-privy # Privy
pnpm add @solana/keychain-turnkey # Turnkey
pnpm add @solana/keychain-fireblocks # Fireblocks
pnpm add @solana/keychain-cdp # Coinbase Developer Platform
pnpm add @solana/keychain-crossmint # Crossmint
pnpm add @solana/keychain-dfns # Dfns
pnpm add @solana/keychain-openfort # Openfort
pnpm add @solana/keychain-para # Para
pnpm add @solana/keychain-utila # Utila
pnpm add @solana/keychain-fordefi # Fordefi
# Kit client plugins (keychainSigner / keychainPayer / keychainIdentity)
pnpm add @solana/keychain-kit-plugin

Basic Usage

Use createKeychainSigner with a discriminated config to create any backend:

import { createKeychainSigner } from "@solana/keychain";
import { signTransactionWithSigners } from "@solana/signers"; // requires @solana/signers ≥ 6.5
const signer = await createKeychainSigner({
backend: "privy",
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});
// Sign an already-compiled transaction
const signedTx = await signTransactionWithSigners(
[signer],
compiledTransaction
);

Or install an individual signer package for a smaller dependency footprint:

import { createPrivySigner } from "@solana/keychain-privy";
const signer = await createPrivySigner({
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});

Using with Transaction Messages

All signers implement the SolanaSigner interface, which is compatible with @solana/kit and @solana/signers:

import { signTransactionMessageWithSigners } from "@solana/signers";
import {
createTransactionMessage,
setTransactionMessageFeePayerSigner,
pipe
} from "@solana/kit";
async function signWithKeychain(signer: SolanaSigner) {
// Check availability (useful for remote signers)
if (!(await signer.isAvailable())) {
throw new Error("Signer offline");
}
// Use with @solana/kit transaction builder
const transaction = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(signer, tx)
// ... add instructions
);
// Sign with the standard signers API
const signedTx = await signTransactionMessageWithSigners(transaction);
return signedTx;
}

Using with a Kit Client (keychainSigner plugin)

@solana/keychain-kit-plugin installs a keychain signer directly on a Kit client, wiring it up as the payer and identity so you skip the manual pipe setup shown above:

import { createClient } from "@solana/kit";
import { keychainSigner } from "@solana/keychain-kit-plugin";
const client = await createClient().use(
keychainSigner({
backend: "privy",
appId: process.env.PRIVY_APP_ID!,
appSecret: process.env.PRIVY_APP_SECRET!,
walletId: process.env.PRIVY_WALLET_ID!
})
);
client.payer; // SolanaSigner — also a Kit TransactionSigner
client.identity; // same signer instance

The plugin accepts the same backend-tagged config as createKeychainSigner, and only the backend it dispatches to is bundled (backend packages load via dynamic import()).

Use keychainPayer or keychainIdentity to set just one role, and mix backends on a single client:

import { createClient } from "@solana/kit";
import { keychainIdentity, keychainPayer } from "@solana/keychain-kit-plugin";
const client = await createClient()
.use(
keychainPayer({
backend: "memory",
privateKeyPath: "~/.config/solana/id.json"
})
)
.use(keychainIdentity({ backend: "turnkey", ...turnkeyConfig }));

Already have a SolanaSigner? Every keychain signer is a valid Kit TransactionSigner, so you can install an existing one with @solana/kit-plugin-signer instead (pnpm add @solana/kit-plugin-signer):

import { signer } from "@solana/kit-plugin-signer";
import { createKeychainSigner } from "@solana/keychain";
const mySigner = await createKeychainSigner({
backend: "vault",
...vaultConfig
});
const client = createClient().use(signer(mySigner));

Backend Configuration

HashiCorp Vault

import { createVaultSigner } from "@solana/keychain-vault";
const signer = createVaultSigner({
vaultAddr: "https://vault.example.com:8200",
vaultToken: "hvs.xxxxx",
keyName: "my-solana-key",
publicKey: "base58_public_key"
});

AWS KMS

import { createAwsKmsSigner } from "@solana/keychain-aws-kms";
const signer = createAwsKmsSigner({
keyId: "alias/my-solana-key",
publicKey: "base58_public_key",
region: "us-east-1" // optional
});

Privy

import { createPrivySigner } from "@solana/keychain-privy";
const signer = await createPrivySigner({
appId: "app_id",
appSecret: "app_secret",
walletId: "wallet_id"
});

Turnkey

import { createTurnkeySigner } from "@solana/keychain-turnkey";
const signer = createTurnkeySigner({
apiPublicKey: "api_public_key",
apiPrivateKey: "api_private_key",
organizationId: "org_id",
privateKeyId: "private_key_id",
publicKey: "base58_public_key"
});

Fireblocks

import { createFireblocksSigner } from "@solana/keychain-fireblocks";
const signer = await createFireblocksSigner({
apiKey: "api_key",
privateKeyPem: "-----BEGIN RSA PRIVATE KEY-----\n...",
vaultAccountId: "0",
assetId: "SOL" // or "SOL_TEST" for devnet
});

CDP (Coinbase Developer Platform)

import { createCdpSigner } from "@solana/keychain-cdp";
const signer = await createCdpSigner({
apiKeyId: "api_key_id",
apiKeySecret: "api_key_secret",
walletSecret: "wallet_secret",
address: "base58_address"
});

Crossmint

import { createCrossmintSigner } from "@solana/keychain-crossmint";
const signer = await createCrossmintSigner({
apiKey: "api_key",
walletLocator: "wallet_locator"
});

Dfns

import { createDfnsSigner } from "@solana/keychain-dfns";
const signer = await createDfnsSigner({
authToken: "auth_token",
credId: "cred_id",
privateKeyPem: "-----BEGIN EC PRIVATE KEY-----\n...",
appId: "app_id",
walletId: "wallet_id"
});

Para

import { createParaSigner } from "@solana/keychain-para";
const signer = await createParaSigner({
apiKey: "api_key",
walletId: "wallet_id"
});

Fordefi

Fordefi has two signing modes, selected by whether you pass chain. API requests are authenticated with an ECDSA P-256 request signature — supply the signing key as privateKeyPem, or keep it in a KMS/HSM by passing a requestSigner.

Native Solana mode (recommended for on-chain use): set chain, and Fordefi signs and broadcasts the transaction itself. Because Fordefi may replace the blockhash or fees before signing, the signer is a Kit TransactionSendingSigner — use it with signAndSendTransactionMessageWithSigners, not the partial-signer flow shown above.

import { createFordefiSigner } from "@solana/keychain-fordefi";
import { signAndSendTransactionMessageWithSigners } from "@solana/signers";
import { readFileSync } from "node:fs";
const signer = await createFordefiSigner({
accessToken: process.env.FORDEFI_ACCESS_TOKEN!,
vaultId: process.env.FORDEFI_VAULT_ID!,
publicKey: process.env.FORDEFI_PUBLIC_KEY!, // Solana vault address (base58)
privateKeyPem: readFileSync("./secret/private.pem", "utf8"),
chain: "solana_devnet", // or "solana_mainnet"
fee: { type: "custom", priority_fee: "1000" } // optional
});
const signature =
await signAndSendTransactionMessageWithSigners(transactionMessage);

Black box mode: omit chain. Fordefi signs raw bytes and does not broadcast; you assemble and submit the transaction yourself. Use this with a Fordefi black box vault.

import { createFordefiSigner } from "@solana/keychain-fordefi";
import { readFileSync } from "node:fs";
const signer = await createFordefiSigner({
accessToken: process.env.FORDEFI_ACCESS_TOKEN!,
vaultId: process.env.FORDEFI_BB_VAULT_ID!,
publicKey: process.env.FORDEFI_BB_PUBLIC_KEY!,
privateKeyPem: readFileSync("./secret/private.pem", "utf8")
});

SolanaSigner Interface

The SolanaSigner interface extends @solana/signers types for full compatibility:

interface SolanaSigner<TAddress extends string = string>
extends TransactionPartialSigner<TAddress>, MessagePartialSigner<TAddress> {
// Public key address
readonly address: Address<TAddress>;
// Health check for remote signers
isAvailable(): Promise<boolean>;
// Sign messages (@solana/kit compatible)
signMessages(
messages: readonly SignableMessage[]
): Promise<readonly SignatureDictionary[]>;
// Sign transactions (@solana/kit compatible)
signTransactions(
transactions: readonly Transaction[]
): Promise<readonly SignatureDictionary[]>;
}

Building Custom Signers

Implement the SolanaSigner interface to create custom signers:

import { SolanaSigner } from "@solana/keychain-core";
import type { Address } from "@solana/addresses";
class MyCustomSigner implements SolanaSigner {
readonly address: Address;
constructor(address: Address) {
this.address = address;
}
async isAvailable(): Promise<boolean> {
return await myBackend.healthCheck();
}
async signMessages(messages) {
return await myBackend.signMessages(messages);
}
async signTransactions(transactions) {
return await myBackend.signTransactions(transactions);
}
}

See the Adding Signers guide to integrate additional key management services.

Is this page helpful?

© 2026 Solana Foundation. All rights reserved.