Architecture Overview
Kora uses the external
solana-keychain crate
for all signing operations. This architecture provides a unified signing
interface for signing Solana transactions. To add a new signer to Kora, you'll
need to:
- First: Add your signer implementation to the
solana-keychaincrate - Second: Add configuration support for your signer in Kora
Step-by-Step Integration Guide
Quick Integration Checklist
Part 1: Add Signer to solana-keychain Crate
- Implement your signer following the solana-keychain integration guide
- Wait for PR approval and crate publication
Part 2: Add Kora Configuration Support
- Update Cargo.toml's dependency so that
solana-keychaincrate uses the latest version (that includes your signer) - Add configuration struct for your signer's environment variables
- Update
SignerTypeConfigenum incrates/lib/src/signer/config.rs - Add validation logic for your signer's config
- Add build logic to construct your signer from config
- Export configuration struct in
crates/lib/src/signer/mod.rs - (Optional) Add test mock builder in
crates/lib/src/tests/config_mock.rs - Update example configuration files
- Update test scripts to include your signer (see below)
- Update documentation to include your signer (see below)
- Submit PR to Kora repository
Add Signer Support in Kora
First, ensure your signer is supported in the solana-keychain crate. If it is
not, follow the guide at:
https://github.com/solana-foundation/solana-keychain/blob/main/docs/ADDING_SIGNERS.md
Step 1: Update Cargo.toml
Update Cargo.toml's dependency so that solana-keychain crate uses the latest
version (that includes your signer):
[dependencies]solana-keychain = { version = "X.Y.Z", default-features = false, features = ["all","sdk-v3",] }
Step 2: Define Your Configuration Struct
In crates/lib/src/signer/config.rs, add a new configuration struct for your
signer that defines which environment variables are needed. For example:
/// YourService signer configuration#[derive(Clone, Serialize, Deserialize)]pub struct YourServiceSignerConfig {pub api_key_env: String,pub api_secret_env: String,pub wallet_id_env: String,}
Step 2: Add Your Signer to SignerTypeConfig Enum
Add your signer variant to the SignerTypeConfig enum in
crates/lib/src/signer/config.rs:
/// Signer type-specific configuration#[derive(Clone, Serialize, Deserialize)]#[serde(tag = "type", rename_all = "snake_case")]pub enum SignerTypeConfig {// Existing signer variantsMemory { #[serde(flatten)] config: MemorySignerConfig },// ... existing variants ...// Add your signer hereYourService {#[serde(flatten)]config: YourServiceSignerConfig,},}
Step 3: Add Build Logic
In the same file (config.rs), add a method to build your signer from
configuration in the SignerConfig implementation:
impl SignerConfig {pub async fn build_signer_from_config(config: &SignerConfig) -> Result<Signer, KoraError> {match &config.config {// ... existing casesSignerTypeConfig::YourService { config: your_service_config } => {Self::build_your_service_signer(your_service_config, &config.name).await}}}// Add this new methodasync fn build_your_service_signer(config: &YourServiceSignerConfig,signer_name: &str,) -> Result<Signer, KoraError> {// Update the environment variable names to match your signer's configurationlet api_key = get_env_var_for_signer(&config.api_key_env, signer_name)?;let api_secret = get_env_var_for_signer(&config.api_secret_env, signer_name)?;let wallet_id = get_env_var_for_signer(&config.wallet_id_env, signer_name)?;// Call the constructor from solana-keychain crateSigner::from_your_service(api_key, api_secret, wallet_id).await.map_err(|e| {KoraError::SigningError(format!("Failed to create YourService signer '{signer_name}': {}",sanitize_error!(e)))})}}
Note: The method name Signer::from_your_service() should match what you
implemented in the solana-keychain crate.
Step 4: Add Validation Logic
Add validation for your signer's configuration in the
validate_individual_signer_config method:
impl SignerConfig {pub fn validate_individual_signer_config(&self, index: usize) -> Result<(), KoraError> {// ... existing validationmatch &self.config {// ... existing casesSignerTypeConfig::YourService { config } => {Self::validate_your_service_config(config, &self.name)}}}// Add this new validation methodfn validate_your_service_config(config: &YourServiceSignerConfig,signer_name: &str,) -> Result<(), KoraError> {// Update the environment variable names to match your signer's configurationlet env_vars = [("api_key_env", &config.api_key_env),("api_secret_env", &config.api_secret_env),("wallet_id_env", &config.wallet_id_env),];for (field_name, env_var) in env_vars {if env_var.is_empty() {return Err(KoraError::ValidationError(format!("YourService signer '{signer_name}' must specify non-empty {field_name}")));}}Ok(())}}
Step 5: Export Your Configuration
Add your new config struct to the module exports in
crates/lib/src/signer/mod.rs (or at the top of the file if it's public):
pub use config::{MemorySignerConfig,PrivySignerConfig,SignerTypeConfig,TurnkeySignerConfig,VaultSignerConfig,YourServiceSignerConfig, // Add this// ... other exports};
Testing Your Integration
Add Test Mock Builder
To make testing easier, add a builder method to SignerPoolConfigBuilder in
crates/lib/src/tests/config_mock.rs:
impl SignerPoolConfigBuilder {// ... existing methodspub fn with_your_service_signer(mut self,name: String,api_key_env: String,api_secret_env: String,wallet_id_env: String,weight: Option<u32>,) -> Self {let signer = SignerConfig {name,weight,config: SignerTypeConfig::YourService {config: YourServiceSignerConfig {api_key_env,api_secret_env,wallet_id_env,},},};self.config.signers.push(signer);self}}
This allows other tests to easily create mock configurations that include your signer:
use crate::tests::config_mock::SignerPoolConfigBuilder;let config = SignerPoolConfigBuilder::new().with_your_service_signer("yourservice_test".to_string(),"YOUR_SERVICE_API_KEY".to_string(),"YOUR_SERVICE_API_SECRET".to_string(),"YOUR_SERVICE_WALLET_ID".to_string(),Some(1)).build();
Environment Variables
Add the example environment variables to the following files:
.env.example(root of the project).env(root of the project, for local testing)./sdks/ts/.env.example./sdks/ts/.env
# YourService Signer ConfigurationYOUR_SERVICE_API_KEY=your_api_key_hereYOUR_SERVICE_API_SECRET=your_api_secret_hereYOUR_SERVICE_WALLET_ID=your_wallet_id_here
Integration Tests
Kora uses a unified test runner (tests/src/bin/test_runner.rs) that manages
all integration testing phases including TypeScript tests. To add tests for your
new signer:
1. Add Test Configuration
Create a new signer configuration file in tests/src/common/fixtures/ for your
service:
# tests/src/common/fixtures/signers-your-service.toml[signer_pool]strategy = "round_robin"[[signers]]name = "yourservice_main"type = "your_service"api_key_env = "YOUR_SERVICE_API_KEY"api_secret_env = "YOUR_SERVICE_API_SECRET"wallet_id_env = "YOUR_SERVICE_WALLET_ID"
2. Add Test Phase to Test Runner
Update tests/src/test_runner/test_cases.toml to include a test phase for your
signer:
[test.your_service]name = "YourService Signer Tests"config = "tests/src/common/fixtures/kora-test.toml"signers = "tests/src/common/fixtures/signers-your-service.toml"port = "8090" # Use a unique porttests = ["your_service"]
3. Running Tests
Make sure your environment is set up:
# Install binaries and dependenciesjust installjust install-ts-sdkjust build-ts-sdk# Set environment variables for your serviceexport YOUR_SERVICE_API_KEY="your_key"export YOUR_SERVICE_API_SECRET="your_secret"export YOUR_SERVICE_WALLET_ID="your_wallet"
Run tests using the unified test runner:
# Run all integration tests (includes your new signer phase)just test-integration# Run tests with verbose outputjust test-integration-verbose# Run specific test phase with filtercargo run -p tests --bin test_runner -- --phases your_service
Documentation Requirements
When submitting your PR, include:
1. Update the Signers Guide
Add signer documentation following the
ADDING_SIGNERS.md
guide, explaining the prerequisites, setup, and usage.
## YourService Signer[YourService](https://yourservice.com) provides [brief description of yourservice].### Prerequisites- YourService account- API credentials- Funded wallet### Setup1. Get your API credentials from [dashboard link]2. Create a wallet...3. Configure environment variables:\```bash YOUR_SERVICE_API_KEY="your_api_key"YOUR_SERVICE_API_SECRET="your_api_secret"YOUR_SERVICE_WALLET_ID="your_wallet_id" \```### Configure signers.toml\```toml [signer_pool] strategy = "round_robin"[[signers]] name = "yourservice_main" type = "your_service" api_key_env ="YOUR_SERVICE_API_KEY" api_secret_env = "YOUR_SERVICE_API_SECRET" wallet_id_env= "YOUR_SERVICE_WALLET_ID" weight = 1 \```### Run Kora with YourService Signer\```bash kora rpc start --signers-config signers.toml \```
2. Update README
Add your service to the main README's signer list.
Submission Checklist
- Your signer is supported in the
solana-keychaincrate - Updated
solana-keychaindependency in Cargo.toml to latest version - Added configuration struct for your signer
- Added
SignerTypeConfigvariant - Added build logic in
build_signer_from_config - Added validation logic in
validate_individual_signer_config - Exported configuration struct in
mod.rs - (Optional) Added test mock builder method in
config_mock.rs - Code compiles without warnings
- All tests pass (
make testandmake test-integration) - Documentation added following
ADDING_SIGNERS.md - Example configuration files created (
.tomland.env.example) - No hardcoded values or secrets
- Error messages are helpful
- Follows Rust naming conventions (snake_case)
- Linting passes (
make lint) - Contact the Kora team with API Keys for integration testing
Getting Help
- For signer implementation: Open an issue in the
solana-keychainrepository - For Kora integration: Open an issue in the Kora repository for design discussions
- Join our community channels
- Review existing signer configurations in
crates/lib/src/signer/config.rs
Example PR Structure
For Kora repository:
feat(signer): add YourService signer configuration support- Add YourServiceSignerConfig struct- Add YourService variant to SignerTypeConfig enum- Add build and validation logic for YourService- Add example configuration files- Add documentation to SIGNERS.md- Add integration tests
Welcome to the Kora ecosystem! We're excited to have your key management solution as part of the platform.
Is this page helpful?