---
title: Adding New Signers to Kora
description:
  This guide is for wallet service providers who want to integrate their key
  management solution into Kora. By adding your signer to Kora, you'll enable
  node operators to use your service for secure transaction signing.
---

## Architecture Overview

Kora uses the external
[`solana-keychain`](https://github.com/solana-foundation/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:

1. **First**: Add your signer implementation to the `solana-keychain` crate
2. **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](https://github.com/solana-foundation/solana-keychain/blob/main/docs/ADDING_SIGNERS.md)
- [ ] Wait for PR approval and crate publication

**Part 2: Add Kora Configuration Support**

- [ ] Update Cargo.toml's dependency so that `solana-keychain` crate uses the
      latest version (that includes your signer)
- [ ] Add configuration struct for your signer's environment variables
- [ ] Update `SignerTypeConfig` enum in `crates/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](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):

```toml
[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:

```rust
/// 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`:

```rust
/// Signer type-specific configuration
#[derive(Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum SignerTypeConfig {
    // Existing signer variants
    Memory { #[serde(flatten)] config: MemorySignerConfig },
    // ... existing variants ...

    // Add your signer here
    YourService {
        #[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:

```rust
impl SignerConfig {
    pub async fn build_signer_from_config(config: &SignerConfig) -> Result<Signer, KoraError> {
        match &config.config {
            // ... existing cases

            SignerTypeConfig::YourService { config: your_service_config } => {
                Self::build_your_service_signer(your_service_config, &config.name).await
            }
        }
    }

    // Add this new method
    async fn build_your_service_signer(
        config: &YourServiceSignerConfig,
        signer_name: &str,
    ) -> Result<Signer, KoraError> {
        // Update the environment variable names to match your signer's configuration
        let 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 crate
        Signer::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:

```rust
impl SignerConfig {
    pub fn validate_individual_signer_config(&self, index: usize) -> Result<(), KoraError> {
        // ... existing validation

        match &self.config {
            // ... existing cases

            SignerTypeConfig::YourService { config } => {
                Self::validate_your_service_config(config, &self.name)
            }
        }
    }

    // Add this new validation method
    fn validate_your_service_config(
        config: &YourServiceSignerConfig,
        signer_name: &str,
    ) -> Result<(), KoraError> {
        // Update the environment variable names to match your signer's configuration
        let 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):

```rust
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`:

```rust
impl SignerPoolConfigBuilder {
    // ... existing methods

    pub 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:

```rust
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`

```bash
# YourService Signer Configuration
YOUR_SERVICE_API_KEY=your_api_key_here
YOUR_SERVICE_API_SECRET=your_api_secret_here
YOUR_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:

```toml
# 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:

```toml
[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 port
tests = ["your_service"]
```

#### 3. Running Tests

Make sure your environment is set up:

```bash
# Install binaries and dependencies
just install
just install-ts-sdk
just build-ts-sdk

# Set environment variables for your service
export 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:

```bash
# Run all integration tests (includes your new signer phase)
just test-integration

# Run tests with verbose output
just test-integration-verbose

# Run specific test phase with filter
cargo 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`](https://github.com/solana-foundation/solana-keychain/blob/main/docs/ADDING_SIGNERS.md)
guide, explaining the prerequisites, setup, and usage.

````markdown
## YourService Signer

[YourService](https://yourservice.com) provides [brief description of your
service].

### Prerequisites

- YourService account
- API credentials
- Funded wallet

### Setup

1. 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-keychain` crate
- [ ] Updated `solana-keychain` dependency in Cargo.toml to latest version
- [ ] Added configuration struct for your signer
- [ ] Added `SignerTypeConfig` variant
- [ ] 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 test` and `make test-integration`)
- [ ] Documentation added following
      [`ADDING_SIGNERS.md`](https://github.com/solana-foundation/solana-keychain/blob/main/docs/ADDING_SIGNERS.md)
- [ ] Example configuration files created (`.toml` and `.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-keychain`](https://github.com/solana-foundation/solana-keychain)
  repository
- **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`](https://github.com/solana-foundation/kora/blob/v2.0.5/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.
