These configuration options are available starting in v2.2.0-beta.1. Add them
to your existing kora.toml alongside stable configuration.
Transaction Plugins
The [kora.plugins] section configures transaction plugins that run during
signing flows. Plugins validate the shape and contents of transactions before
Kora signs them. They execute for signTransaction, signAndSendTransaction,
signBundle, and signAndSendBundle — but not for estimateBundleFee.
[kora.plugins]enabled = ["gas_swap"]
| Option | Description | Required | Type |
|---|---|---|---|
enabled | List of enabled transaction plugins | No (default: []) | string[] |
gas_swap Plugin
The gas_swap plugin enforces a strict transaction shape for gasless
token-for-SOL swap operations. When enabled, every transaction submitted through
signing flows must contain exactly:
- One SPL Token transfer (SPL Token or Token-2022) — from a non-fee-payer owner
- One System SOL transfer (
TransferorTransferWithSeed) — from the fee payer
Compute Budget instructions (set compute unit limit/price) are allowed alongside the two required instructions. Any additional or non-swap outer instructions are rejected.
Configuration Requirements
The gas_swap plugin validates your config at startup and will error if
requirements are not met:
- System Program must be in
allowed_programs - At least one token program (SPL Token or Token-2022) must be in
allowed_programs - At least one token must be in
allowed_tokens - Pricing model must not be
Free— set a margin or fixed price
[kora.plugins]enabled = ["gas_swap"][validation]allowed_programs = ["11111111111111111111111111111111", # System Program"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", # SPL Token Program"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", # Token-2022 Program]allowed_tokens = ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", # USDC][validation.price]type = "margin"margin = 0.0
Warning: When using
gas_swapwith Fixed pricing, ensure the fixed token fee is worth at leastmax_allowed_lamportsin SOL to avoid a drain condition where the SOL sent exceeds the token received.
Bundle Configuration
The [kora.bundle] section enables Jito bundle support for atomic
multi-transaction execution:
[kora.bundle]enabled = true[kora.bundle.jito]block_engine_url = "https://mainnet.block-engine.jito.wtf"
| Option | Description | Required | Type |
|---|---|---|---|
enabled | Enable bundle functionality | No (default: false) | boolean |
Jito Configuration
| Option | Description | Required | Type |
|---|---|---|---|
block_engine_url | Jito block engine URL | Yes (when bundles enabled) | string |
Available Jito block engine URLs:
- Mainnet (public):
https://mainnet.block-engine.jito.wtf - Mainnet (private): Contact Jito for access
Important: When using bundles with Jito tips paid by Kora, set
allow_transfer = truein[validation.fee_payer_policy.system]to allow the signer to transfer SOL for the tip.
For a complete guide on implementing Jito bundles with Kora, see the Jito Bundle Guide.
Lighthouse Fee Payer Protection
The [kora.lighthouse] section enables Lighthouse fee payer protection. When
enabled, Kora adds balance assertion instructions to transactions, protecting
the fee payer from drainage attacks by verifying the fee payer's balance doesn't
drop below expected levels.
[kora.lighthouse]enabled = truefail_if_transaction_size_overflow = true
| Option | Description | Required | Type |
|---|---|---|---|
enabled | Enable Lighthouse assertions for fee payer protection | No (default: false) | boolean |
fail_if_transaction_size_overflow | Reject transaction if adding assertion exceeds size limit. If false, skips adding assertion silently. | No (default: true) | boolean |
How It Works
When Lighthouse is enabled, Kora fetches the fee payer's current balance and
adds a Lighthouse assertion instruction that verifies the balance doesn't drop
below (current_balance - estimated_fee) at transaction completion. This
prevents malicious transactions from draining the fee payer beyond expected
costs.
Method Compatibility
Lighthouse protection only works with signTransaction and signBundle. It
does NOT work with signAndSendTransaction or signAndSendBundle.
When Lighthouse adds an assertion instruction, it modifies the transaction
message. This invalidates any pre-existing client signatures. The signAndSend*
flows would fail because:
- Client signs transaction
- Kora adds Lighthouse assertion (modifies message)
- Client's original signature becomes invalid
- Network rejects with "signature verification failure"
Recommended pattern with Lighthouse:
signTransaction → client receives modified tx → client re-signs → client sends to network
Configuration Requirements
When enabling Lighthouse, add the Lighthouse program to your allowed_programs:
[validation]allowed_programs = [# ... other programs ..."L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95", # Lighthouse Program]
Kora validates this on startup and will error if Lighthouse is enabled but the
program is not in allowed_programs.
Note: If
signAndSendTransactionorsignAndSendBundleare enabled alongside Lighthouse, Kora will log a warning that these methods won't have fee payer protection.
reCAPTCHA Bot Protection
reCAPTCHA v3 provides invisible bot protection for sensitive endpoints. Unlike API Key and HMAC which authenticate all requests, reCAPTCHA only protects specific high-risk methods (signing methods by default).
Server Configuration
Add KORA_RECAPTCHA_SECRET to your environment variables (has priority), or add
a recaptcha_secret to your kora.toml:
[kora.auth]recaptcha_secret = "your-recaptcha-v3-secret-key"recaptcha_score_threshold = 0.5 # Optional: 0.0-1.0, default 0.5protected_methods = ["signTransaction", "signAndSendTransaction", "signBundle", "signAndSendBundle"] # Optional
| Option | Description | Default |
|---|---|---|
recaptcha_secret | Your reCAPTCHA v3 secret key from Google | - |
recaptcha_score_threshold | Minimum score to pass (0.0 = all pass, 1.0 = none pass) | 0.5 |
protected_methods | RPC methods requiring verification | Signing methods |
How It Works
- Client obtains a reCAPTCHA token from Google's reCAPTCHA v3 API
- Client includes the token in the
x-recaptcha-tokenheader - Server verifies the token with Google and checks the score
- If score >= threshold, request proceeds; otherwise returns 401 Unauthorized
reCAPTCHA runs after API Key/HMAC authentication succeeds (if configured). Unprotected methods bypass reCAPTCHA verification entirely.
Client Implementation
Using Kora SDK:
const { KoraClient } = require("@solana/kora");const kora = new KoraClient({rpcUrl: "http://localhost:8080",apiKey: process.env.KORA_API_KEY,// Callback called for each request - return fresh tokengetRecaptchaToken: async () => {return await grecaptcha.execute("your-site-key", { action: "sign" });}});// Token is automatically included for all requestsconst result = await kora.signTransaction({ transaction: "base64..." });
Using fetch:
async function callKoraProtectedMethod(method, params = {}) {const recaptchaToken = await grecaptcha.execute("your-site-key", {action: "sign"});const response = await fetch("http://localhost:8080", {method: "POST",headers: {"Content-Type": "application/json","x-recaptcha-token": recaptchaToken},body: JSON.stringify({jsonrpc: "2.0",method,params,id: 1})});return response.json();}
Getting reCAPTCHA Keys
- Go to Google reCAPTCHA Admin Console
- Create a new site with reCAPTCHA v3
- Use the Site Key in your frontend (client-side)
- Use the Secret Key in your Kora configuration (server-side)
Usage Limits
The [kora.usage_limit] section configures per-wallet usage limiting to prevent
abuse and ensure fair usage. This can also be used to create rewards programs to
subsidize users' transaction fees up to a certain limit.
Note: This feature requires Redis when enabled across multiple Kora instances.
[kora.usage_limit]enabled = truecache_url = "redis://localhost:6379"fallback_if_unavailable = true
| Option | Description | Required | Type |
|---|---|---|---|
enabled | Enable per-wallet usage limiting | No (default: false) | boolean |
cache_url | Redis connection URL for shared usage tracking | No | string |
fallback_if_unavailable | Allow transactions if Redis is unavailable | No (default: true) | boolean |
fallback_if_unavailable
Usage Limit Rules
The beta introduces granular, rule-based usage limits. Instead of a single
max_transactions count, you can define multiple rules that target specific
transaction types or individual instructions, with optional time windows.
[[kora.usage_limit.rules]]type = "transaction"max = 100window_seconds = 86400 # 100 transactions per day[[kora.usage_limit.rules]]type = "instruction"program = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"instruction = "Transfer"max = 50window_seconds = 3600 # 50 SPL transfers per hour
| Rule Field | Description | Required |
|---|---|---|
type | "transaction" (counts all transactions) or "instruction" (counts specific instruction types) | Yes |
max | Maximum count before the wallet is blocked | Yes |
window_seconds | Time window in seconds. If omitted, the limit is permanent. | No |
program | Program address to match (required for instruction type) | Conditional |
instruction | Instruction name to match, e.g. "Transfer", "Burn" (required for instruction type) | Conditional |
When window_seconds is set, the counter resets after the time window expires.
Without it, the limit is permanent — once reached, the wallet is blocked until
manually cleared from Redis.
New Enabled Methods
The following methods have been added to [kora.enabled_methods]:
[kora.enabled_methods]get_version = trueestimate_bundle_fee = truesign_bundle = falsesign_and_send_bundle = false
| Method | Description |
|---|---|
get_version | Return the Kora server version |
estimate_bundle_fee | Estimate fees for a bundle of transactions |
sign_bundle | Sign a bundle of transactions without sending |
sign_and_send_bundle | Sign and submit a bundle to Jito |
See Bundle Methods for full API documentation.
Is this page helpful?