This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Main branch:
main(protected, requires PRs) - Release branch:
release/{id}(requires PRs) - Commit format: Use conventional commits for automatic releases
feat:→ minor version bump (1.0.3 → 1.1.0)fix:→ patch version bump (1.0.3 → 1.0.4)BREAKING CHANGE:→ major version bump (1.0.3 → 2.0.0)chore:,docs:,refactor:→ patch version bump
- Rust crates: Automatic publishing when files change + conventional commits
- TypeScript SDKs: Changeset-based releases (require
pnpm changeset) - Individual crate releases: Each crate publishes independently when modified
- GitHub releases: Auto-generated with commit-based release notes
Kora is a Solana paymaster node that provides a JSON-RPC interface for handling gasless transactions and fee abstractions. It enables developers to build applications where users can pay transaction fees in tokens other than SOL.
The repository consists of 2 main workspace crates:
kora-lib: Core library with integrated RPC server functionality, signers, transaction handling, and configurationkora-cli: Unified command-line interface with RPC server and configuration commandstests: Integration tests for the entire workspacesdks/: TypeScript SDKs for client integration
Kora supports two authentication methods that can be used individually or together:
- API Key Authentication: Simple header-based auth using
x-api-keyheader - HMAC Authentication: Request signature auth using
x-timestampandx-hmac-signatureheaders
Testing:
make test-integration # Run all integration tests# Build all workspace packages
make build
# Build specific packages
make build-lib # Build the lib crate
make build-cli # Build the CLI tool
# Install all binaries
make install
# Check formatting
make check
# Format code
make fmt
# Run linter with warnings as errors
make lint
# Run linter with auto-fix
make lint-fix-all# Run unit tests
make test
# Run integration tests (automatically handles environment setup)
make test-integration
# Run all tests
cargo test --workspaceIntegration tests are fully automated using a Rust test runner binary that handles sequential test execution:
Quick Start:
make test-integrationWhat happens automatically:
- Solana Validator: Starts local test validator with reset
- Test Environment Setup: Creates test accounts, tokens, and ATAs
- Sequential Test Phases: Runs 3 test suites with different configurations
Test Phases (Configured in tests/src/test_runner/test_cases.toml):
Regular Tests
- Config:
tests/src/common/fixtures/kora-test.toml(no auth) - Tests: Core RPC functionality, token operations, compute budget
Auth Tests
- Config:
tests/src/common/fixtures/auth-test.toml(auth enabled) - Tests: API key and HMAC authentication validation
Payment Address Tests
- Config:
tests/src/common/fixtures/paymaster-address-test.toml(payment address) - CLI ATA Initialization: Automatically runs
kora rpc initialize-atasbefore tests - Tests: Payment address validation and wrong destination rejection
Multi-Signer Tests
- Config:
tests/src/common/fixtures/signers-multi.toml - Tests: Multiple signer configurations
TypeScript Tests
- Tests: TypeScript SDK integration tests
File Structure:
tests/
├── src/
│ ├── common/
│ │ ├── fixtures/
│ │ │ ├── kora-test.toml # Regular tests config
│ │ │ ├── auth-test.toml # Auth tests config
│ │ │ └── paymaster-address-test.toml # Payment address config
│ │ ├── local-keys/
│ │ │ ├── fee-payer-local.json # Fee payer keypair
│ │ │ ├── payment-local.json # Payment address keypair
│ │ │ ├── sender-local.json # Sender keypair
│ │ │ └── usdc-mint-local.json # USDC mint keypair
│ │ └── setup.rs # Test environment setup
│ ├── test_runner/ # Test runner modules
│ │ ├── accounts.rs # Account management
│ │ ├── commands.rs # Test command execution
│ │ ├── config.rs # Test configuration
│ │ ├── kora.rs # Kora server management
│ │ ├── output.rs # Output handling
│ │ ├── test_cases.toml # Test phase configurations
│ │ └── validator.rs # Solana validator management
│ └── bin/
│ └── test_runner.rs # Main test runner binary
├── integration/ # Regular integration tests
├── auth/ # Authentication tests
└── payment-address/ # Payment address tests
Test Runner Commands:
# Run all integration tests (default)
make test-integration
# Run with verbose output
make test-integration-verbose
# Force refresh test accounts (ignore cached)
make test-integration-fresh
# Run specific test
cargo run -p tests --bin test_runner -- --filter regular
cargo run -p tests --bin test_runner -- --filter auth
cargo run -p tests --bin test_runner -- --filter payment_address
cargo run -p tests --bin test_runner -- --filter multi_signer
cargo run -p tests --bin test_runner -- --filter typescript_basic
cargo run -p tests --bin test_runner -- --filter typescript_authThe test suite uses environment variables for configuration specified in tests/src/common/constants.rs.
Make sure to update the appropriate config file (kora.toml for production, tests/common/fixtures/kora-test.toml for testing) to reflect the public key of TEST_USDC_MINT_KEYPAIR.
# Basic server run (production config)
make run
# Run with test configuration (for integration testing)
cargo run -p kora --bin kora -- --config tests/common/fixtures/kora-test.toml --rpc-url http://127.0.0.1:8899 rpc --signers-config tests/common/fixtures/signers.toml
# Run with debug logging
RUST_LOG=debug cargo run -p kora --bin kora -- rpc start
# Run RPC server with all parameters
cargo run -p kora --bin kora -- --config kora.toml --rpc-url https://api.devnet.solana.com rpc start \
--port 8080 \
--logging-format standard
# Run with Turnkey signer
cargo run -p kora --bin kora -- rpc start --signers-config path/to/turnkey-signers.toml
# Run with Privy signer
cargo run -p kora --bin kora -- rpc start --signers-config path/to/privy-signers.toml
# Run with Vault signer
cargo run -p kora --bin kora -- rpc start \
--signers-config path/to/vault-signers.toml
# Configuration validation commands
cargo run -p kora --bin kora -- config validate
cargo run -p kora --bin kora -- config validate-with-rpc
# Generate OpenAPI documentation
cargo run -p kora --bin kora --features docs -- openapi -o openapi.json# In sdks/ts/
pnpm run build
pnpm run test
pnpm run lint
pnpm run format-
signer/ - Abstraction layer supporting multiple signer types (configured in
signers.toml)SolanaMemorySigner- Local keypair signingVaultSigner- HashiCorp Vault integrationTurnkeySigner- Turnkey API integrationPrivySigner- Privy API integration- Unified
KoraSignerenum with trait implementation - optionally,
--no-signerflag to run Kora without a signer
-
transaction/ - Transaction processing pipeline:
- Fee estimation and calculation
- Transaction validation against configuration rules
- Paid transaction verification
- Solana transaction utilities
- Lookup Table Resolution: V0 transactions require address lookup table resolution for proper fee calculation and validation. The system uses
VersionedTransactionExttrait andVersionedTransactionResolvedwrapper for efficient caching of resolved addresses.
-
token/ - SPL token handling:
- Token interface abstractions (SPL vs Token-2022)
- Token account management
- Token validation and metadata
-
oracle/ - Price feed integration:
- Jupiter API integration for token pricing
- Price calculation for fee estimation
-
config.rs - TOML-based configuration system with validation
-
state.rs - Global signer state management
-
cache.rs - Token account caching
-
rpc.rs - Solana RPC client utilities
-
server.rs - HTTP JSON-RPC server setup with middleware:
- CORS configuration
- Rate limiting
- Proxy layer for health checks
- Uses
jsonrpseefor JSON-RPC protocol
-
method/ - RPC method implementations:
estimateTransactionFee- Calculate gas fees in different tokenssignTransaction- Sign transaction without broadcastingsignAndSendTransaction- Sign and broadcast to networktransferTransaction- Handle token transfersgetBlockhash- Get recent blockhashgetConfig- Return server configurationgetSupportedTokens- List accepted payment tokenssignTransactionIfPaid- Conditional signing based on payment verificationgetPayerSigner- Get the payer signer and payment destination- (client-only)
getPaymentInstruction- Get a payment instruction for a transaction
-
openapi/ - Auto-generated API documentation using
utoipa -
args.rs - RPC-specific command line arguments
- Unified command-line interface with subcommands:
kora config validate- Validate configuration filekora config validate-with-rpc- Validate configuration with RPC callskora rpc start --signers-config path/to/signers.toml- Start the RPC server with all signer options (kora.toml in cwd)kora --config path/to/kora.toml rpc start --signers-config path/to/signers.toml- Start the RPC server with specific config and signerskora openapi- Generate OpenAPI documentation
- Global arguments (rpc-url, config) separated from RPC-specific arguments
- All signer types supported for RPC server mode
Example CLI usage:
# Validate configuration
cargo run -p kora --bin kora -- --config kora.toml config validate
# Start RPC server with local private key
cargo run -p kora --bin kora -- --config path/to/kora.toml --rpc-url https://api.devnet.solana.com rpc start --signers-config path/to/signers.toml
# Start RPC server with Turnkey signer
cargo run -p kora --bin kora -- rpc start --signers-config path/to/turnkey-signers.toml
# Generate OpenAPI documentation
cargo run -p kora --bin kora --features docs -- openapi -o openapi.json- kora-turnkey - Turnkey key management API integration (separate crate)
- kora-privy - Privy wallet API integration (separate crate)
- VaultSigner - HashiCorp Vault integration (built into kora-lib)
- Remote signers integrate via HTTP APIs to external services
- sdks/ts/ - Main TypeScript SDK for client integration
- Provide typed interfaces for all RPC methods
[kora]
rate_limit = 100 # Requests per second
[validation]
max_allowed_lamports = 1000000 # Maximum transaction value
max_signatures = 10 # Maximum signatures per transaction
price_source = "Mock" # Price source: "Mock", "Jupiter", etc.
# Allowed Solana programs (by public key)
allowed_programs = [
"11111111111111111111111111111111", # System Program
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", # Token Program
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", # Associated Token Program
"AddressLookupTab1e1111111111111111111111111", # Address Lookup Table Program
"ComputeBudget11111111111111111111111111111111", # Compute Budget Program
]
# Supported tokens for fee payment (by mint address)
allowed_tokens = [
"4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", # USDC devnet
]
# SPL tokens accepted for paid transactions
allowed_spl_paid_tokens = [
"4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", # USDC devnet
]
disallowed_accounts = [] # Blocked account addresses
# Fee payer policy controls what actions the fee payer can perform
# All default to true for backward compatibility
[validation.fee_payer_policy]
allow_sol_transfers = true # Allow fee payer to be source in SOL transfers
allow_spl_transfers = true # Allow fee payer to be source in SPL token transfers
allow_token2022_transfers = true # Allow fee payer to be source in Token2022 transfers
allow_assign = true # Allow fee payer to use Assign instructionGeneral:
RUST_LOG=debug # Logging levelThe fee payer policy system provides fine-grained control over what actions the fee payer can perform in transactions. By default, all actions are permitted to maintain backward compatibility with existing behavior.
The fee payer policy is configured via the [validation.fee_payer_policy] section in kora.toml:
Core Structure (crates/lib/src/config.rs):
FeePayerPolicystruct with 4 boolean fieldsDefaultimplementation sets all fields totrue(permissive)#[serde(default)]attribute ensures backward compatibility
Validation Logic (crates/lib/src/transaction/validator.rs):
TransactionValidatorstores the policy configurationis_fee_payer_source()method checks policy flags before validating restrictions- Different validation logic for each program type (System, SPL Token, Token2022)
Supported Actions:
- SOL Transfers - System program Transfer and TransferWithSeed instructions
- SPL Token Transfers - SPL Token program Transfer and TransferChecked instructions
- Token2022 Transfers - Token2022 program Transfer and TransferChecked instructions
- Assign - System program Assign instruction (changes account owner)
Kora supports multiple private key formats for enhanced usability and compatibility with different tooling, each specified in signers.toml
Traditional Solana private key format - base58-encoded 64-byte private key:
KORA_PRIVATE_KEY=your_base58_private_keyComma-separated byte array format compatible with Solana CLI outputs:
KORA_PRIVATE_KEY="[123,45,67,89,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56,78,90,12,34,56]"Path to a JSON file containing the private key:
KORA_PRIVATE_KEY="/path/to/keypair.json"The system automatically detects the format based on input patterns:
- File path - Attempts to read as file first
- U8Array - Detects
[...]format - Base58 - Default fallback format
All private key environment variables support the same multiple formats.
Kora handles both legacy and V0 versioned transactions. V0 transactions use address lookup tables to compress transaction size by referencing frequently used addresses. Before processing V0 transactions for fee calculation or validation, these lookup tables must be resolved to get the actual addresses.
Trait-Based Abstraction: The system uses the VersionedTransactionExt trait to provide a unified interface for both legacy and V0 transactions:
pub trait VersionedTransactionExt {
fn get_all_account_keys(&self) -> Vec<Pubkey>;
fn get_transaction(&self) -> &VersionedTransaction;
}Caller Responsibility Pattern: Following Rust best practices, the system uses caller responsibility where expensive operations (RPC calls to resolve lookup tables) are explicit:
VersionedTransaction- Implements trait directly, returns only static account keysVersionedTransactionResolved<'a>- Wrapper that caches resolved addresses after callingresolve_addresses(rpc_client).await
- Client Request - Client submits transaction to RPC endpoint
- Validation - Transaction validated against configuration rules including fee payer policy
- Fee Calculation - Fee calculated based on token type and current prices
- Signing - Transaction signed using configured signer backend
- Response - Signed transaction returned or broadcast to network
- Trait-based design - All signers implement unified
Signertrait - State management - Global signer state with thread-safe access via
get_signer() - Multiple backends - Runtime selection between Memory, Vault, Turnkey, Privy
- Initialization - Lazy initialization with validation on first use
- API Integration - Turnkey and Privy use HTTP APIs for remote signing
- All RPC methods are async
- Use
tokioruntime for async execution - Signer operations are async to support remote API calls
Kora is designed for high-performance concurrent operations:
- Global State Management: Use
Arc<Mutex<T>>for shared state across threads - Signer State: Global signer accessed via
get_signer()with thread-safe initialization - RPC Server: Handles multiple concurrent requests using
jsonrpseeasync framework - Cache Operations:
TokenAccountCachesupports concurrent access for token account lookups - Token Account Access: Always prioritize cache lookups before making on-chain RPC calls
All I/O operations and external API calls are async:
- RPC Client Operations: Solana RPC calls are async to avoid blocking
- Remote Signer APIs: Turnkey and Privy API calls are async HTTP requests
- Database Operations: Token cache operations are async
- Error Propagation: Use
?operator with async functions
Use structured logging throughout the codebase:
- Error Level (
log::error!): System failures, critical errors, panics - Warn Level (
log::warn!): Recoverable errors, validation failures - Info Level (
log::info!): Important state changes, successful operations - Debug Level (
log::debug!): Detailed execution flow, parameter values - Trace Level (
log::trace!): Very verbose debugging information
Logging Guidelines:
- Include relevant context (transaction IDs, user addresses, amounts)
- Log entry and exit points for important operations
- Use structured data when possible for better parsing
- Never log sensitive information (private keys, secrets)
- Log errors with full context for debugging
- CLI Output: Use
println!for CLI command results and user-facing output (notlog::info!)
- Error Transformation: Convert external errors to
KoraErrorat module boundaries - Error Context: Add meaningful context when propagating errors up the call stack
- Error Classification: Distinguish between recoverable validation errors and critical system failures
- Error Responses: Structure JSON-RPC error responses consistently across all methods
- Memory Allocation: Minimize allocations in hot paths, reuse buffers where possible
- Connection Pooling: Reuse HTTP clients and RPC connections across requests
- Batch Operations: Prefer batch APIs when available for multiple token account operations
- Rate Limiting: Implement client-side rate limiting for external API calls
- Secret Handling: Never log, print, or serialize sensitive data (keys, tokens, secrets)
- Input Sanitization: Validate all user inputs against allow-lists and size limits
- Audit Trail: Log security-relevant events (authentication, authorization, signing)
- Fail Secure: Default to restrictive behavior when validation or authentication fails
- Secure Communication: Use secure communication for remote signer APIs
- Rate Limiting & Authentication: Implement proper rate limiting and authentication
Kora supports two optional authentication methods:
- API Key Auth (
api_keyin kora.toml): Simple header-based auth usingx-api-key - HMAC Auth (
hmac_secretin kora.toml): Secure signature-based auth usingx-timestamp+x-hmac-signature(SHA256 of timestamp+body)
Both skip /liveness endpoint and can be used simultaneously. Implementation uses async tower middleware for non-blocking concurrent requests.
- Test Organization: Mirror source code structure in test file organization
- Mock Strategy: Mock external dependencies (RPC clients, HTTP APIs) consistently
- Test Data: Use deterministic test data, avoid random values in tests
- Integration Coverage: Test complete request/response cycles for all RPC methods
- Error Scenarios: Test error conditions and edge cases, not just happy paths
- Unit tests - Located in
src/directories alongside source code - Integration tests - Located in
tests/directory for end-to-end workflows - API tests - Include example JSON payloads in
tests/examples/ - SDK tests - TypeScript tests in
sdks/*/test/directories
The project uses a Rust-based test runner for integration testing:
/tests/src/
├── bin/test_runner.rs # Main test runner binary
├── test_runner/ # Test runner modules
│ ├── test_cases.toml # Test phase configurations
│ ├── accounts.rs # Account management & caching
│ ├── commands.rs # Test execution logic
│ ├── kora.rs # Kora server lifecycle
│ └── validator.rs # Solana validator management
└── common/ # Shared test utilities
Key Features:
- Rust Test Runner: Single binary manages all test phases
- TOML Configuration: Test phases defined in
test_cases.toml - Account Caching: Reuses test accounts for faster execution
- Isolated Ports: Each test phase uses unique ports to avoid conflicts
- TypeScript Integration: Seamlessly runs TS SDK tests alongside Rust tests
- Always run linting and formatting commands before committing
- Use the Makefile targets for consistent builds across the workspace
- Test both unit and integration levels when making changes
- Verify TypeScript SDK compatibility when changing RPC interfaces
- Follow existing patterns for RPC method implementations
- Add new signer types by implementing the
Signertrait - Update configuration schema when adding new validation rules
- Keep OpenAPI documentation in sync with method signatures
✅ Complete Sequential Test Runner
- Makefile-based orchestration with 3 automated phases
- Proper server lifecycle management (start/stop/restart)
- Config isolation between test suites
- Zero manual intervention required
✅ CLI Integration for Payment Address Tests
- Automated
kora rpc initialize-atasexecution before payment tests - Real-world workflow simulation (CLI → tests)
- Payment address ATA creation and validation
✅ Payment Validation Testing
- Tests payment address validation logic (not transaction simulation)
- Proper positive/negative test cases:
- ✅ Transfer to payment address → should succeed
- ✅ Transfer to wrong destination → should fail validation
- Removed transaction simulation dependency (caused AccountNotFound issues)
✅ Test Infrastructure
- File reorganization:
tests/src/common/structure for proper Rust crate organization - Config files:
kora-test.toml,auth-test.toml,paymaster-address-test.toml - Binary setup:
setup_test_envbinary for account/token initialization - Path resolution fixes for workspace vs. crate directory differences
Test Results:
- Integration tests: 26/26 passed ✅
- Auth tests: 4/4 passed ✅
- Payment address tests: 2/2 passed ✅
- CLI ATA initialization: Working ✅