Koyal
Zero-overhead AI trading harness. Rust native. Permission-gated. Multi-chain.
Koyal is a local-first control plane for crypto research, portfolio monitoring, AI-driven trading bots, and approval-gated execution across chains. A single binary that ships the gateway, web dashboard, and CLI together.
curl -fsSL https://raw.githubusercontent.com/s1dc0des/koyal/main/install.sh | sh
koyal doctor
# {"status":"ok","version":"0.1.0","harness":"rust","gateway":8790}
koyal gateway # starts HTTP gateway + serves web dashboard
koyal dashboard open # opens browser to http://127.0.0.1:8790/
What Koyal Is
- A harness, not an exchange. Koyal coordinates agents, proposals, and execution. It does not hold funds or custody keys beyond what you configure.
- Review-first by default. Every trade proposal is gated by an explicit approval step. The global halt switch pauses all execution immediately.
- Local-first. Keys, config, agent memory, and trade history stay on your machine. No cloud sync, no vendor dashboard.
- Self-learning. Agents write learnings back to their vault after every tick. Outcome capture drives genuine adaptation.
Key Capabilities
| Capability | Description |
|---|---|
| 55+ AI Providers | OpenAI, Anthropic, Gemini, Bedrock, DeepSeek, Groq, Together, OpenRouter, xAI, Mistral, and 45 more. Priority routing with circuit breaker. |
| Multi-Chain | Solana + Jupiter, EVM + Uniswap v3/v4, Hyperliquid perps, Polymarket prediction markets. |
| Unified Vault | Single source of truth per agent: soul.md, instructions.md, memories, learnings, skills, bot configs. compose_bible builds the full system prompt for every tick. |
| Permission Engine | PermissionEngine with bot-scoped policies, wallet roles (Observe / Trade / Sweep), and scoped authorization. |
| Web Dashboard | React + Vite + WebSocket UI served directly by koyal gateway. No separate Node server required. |
| MCP Server | Built-in Model Context Protocol server. All harness tools exposed as MCP calls. External MCP servers (Solana, Chainstack) configurable. |
| CLI + TUI | 20+ subcommands via clap. koyal tui for ratatui terminal UI. koyal dashboard open for the browser. |
| Integrations | Telegram alerts/control, webhook outbound, Prometheus metrics. |
| Supply Chain | CycloneDX SBOM, SHA256SUMS + GitHub Attestations, cargo deny, daily cargo audit. |
Crate Structure
koyal-api ← public trait boundary (no heavy deps)
koyal-config ← env + frontmatter parser
koyal-memory ← SQLite FTS5, JSONL ledger, MarkdownVault
koyal-chains ← Solana, EVM
koyal-dexes ← Jupiter, Uniswap v3/v4
koyal-exchanges ← Hyperliquid, Polymarket
koyal-providers ← 55+ AI provider implementations
koyal-core ← in-memory stores, type registry
koyal-runtime ← tick engine, PermissionEngine, scheduler
koyal-gateway ← axum HTTP + WebSocket + MCP + static files
koyal-integrations ← loop blueprints, Telegram, webhook
koyal ← CLI (clap) + binary facade
See Architecture for the full crate reference and dependency contract.
Quick Links
- Quick Start — install and run in 2 minutes
- CLI Reference — all 20+ commands documented
- API Reference — all gateway routes with JSON shapes
- Vault Guide — how the agent bible works
- Web Dashboard — start, open, configure
- Release Runbook — tag and ship
Quick Start
Install from a GitHub Release when one exists:
curl -fsSL https://raw.githubusercontent.com/s1dc0des/koyal/main/install.sh | sh
koyal doctor
Build from source:
git clone https://github.com/s1dc0des/koyal
cd koyal
cargo build --release --locked
./target/release/koyal doctor
Run an agent tick and gateway:
koyal agent-tick --agent solana_jupiter
koyal gateway --host 127.0.0.1 --port 8790
For the complete command surface, see the CLI reference.
Install
Koyal supports release-binary installers, source installs, package-manager manifests, and direct Cargo builds.
Unix installer:
./install.sh --version 0.1.0 --binary
./install.sh --source .
./install.sh --minimal --source .
Windows installer:
.\install.ps1 -Version 0.1.0 -Binary
.\install.ps1 -FromSource
setup.bat -Version 0.1.0 -Binary
Package surfaces are staged under dist/ for AUR, Scoop, Homebrew taps, nFPM Linux packages, and FreeBSD services. Before publishing, run:
scripts/release/readiness.sh
scripts/release/preflight.sh
The distribution notes live in docs/distribution.md.
Containers and Services
Build and smoke-test the container:
docker build -t koyal .
docker run --rm koyal doctor
Run the gateway through Compose:
docker compose up koyal-gateway
The image runs as UID/GID 65534, writes runtime state below /workspace/data, and exposes the gateway on port 8790.
scripts/ci/check_container_contract.sh keeps those guarantees aligned across
Dockerfile, Compose, GHCR workflow, smoke test, and docs.
Service samples are available for:
- systemd:
scripts/koyal.service - Kubernetes and OpenShift:
deploy-k8s/ - FreeBSD
rc.dand jail deployments:dist/freebsd/ - NixOS module:
nix/module.nix
scripts/ci/check_deploy_contract.sh keeps those service samples private,
non-root, and aligned with the documented gateway port, package wiring, and
data paths.
Container publishing and attestations are handled by .github/workflows/container.yml.
Nix
Koyal includes a flake for reproducible development shells and package builds.
nix develop
nix build
./result/bin/koyal doctor
The developer shell includes Rust, rust-analyzer, cargo-deny,
cargo-audit, just, mdbook, lychee, actionlint, Node.js, Go, and
supporting CLIs used by the local quality gates.
Use the same check script as CI:
scripts/ci/nix_flake_check.sh
Set KOYAL_FULL_NIX_CHECK=1 to build the default package through the script.
Without nix installed, the script exits successfully with a clear skip
message so non-Nix contributors can keep using the Cargo and Docker paths.
scripts/ci/check_nix_contract.sh statically keeps the flake package, dev
shell tooling, runtime checker, docs, and just nix-check target aligned.
NixOS Module
Koyal also exports nixosModules.default and nixosModules.koyal:
{
imports = [ inputs.koyal.nixosModules.default ];
services.koyal.instances.gateway = {
package = inputs.koyal.packages.${pkgs.system}.koyal;
gatewayHost = "127.0.0.1";
gatewayPort = 8790;
environmentFile = "/run/agenix/koyal.env";
};
}
Each services.koyal.instances.<name> entry creates an isolated systemd unit,
system user/group, and state directory. Set
package = inputs.koyal.packages.${pkgs.system}.koyal explicitly because Koyal
is consumed as an out-of-tree flake module. nix/test.nix covers gateway and
agent-tick unit generation plus hardening defaults for module maintainers.
Architecture
Koyal is organized as a trait-first Rust workspace:
koyal-api
koyal-config
koyal-memory
koyal-chains
koyal-dexes
koyal-exchanges
koyal-providers (api + config)
koyal-core (api + memory)
koyal-runtime (api + config + memory + providers)
koyal-gateway (api + config + core + runtime)
koyal-integrations (api + config + runtime)
koyal
Layered Dependency Contract
koyal-api is the public trait boundary. Leaf implementation crates depend on
koyal-api; composition crates are limited to documented edges such as
runtime using config/memory/providers and gateway using config/core/runtime.
scripts/ci/check_workspace_contract.sh keeps the Cargo workspace graph and
architecture docs aligned.
The fast path is intentionally small:
koyal-apidefines stable traits and data contracts.koyal-configparses agent frontmatter without regex overhead.koyal-memoryuses SQLite WAL mode and FTS5 for recall.koyal-runtimecoordinates ticks, approvals, guardrails, and scheduler work.koyal-gatewayexposes axum HTTP routes for dashboard integration.
See the full architecture reference for crate boundaries and migration mapping.
Crate Reference
Koyal is an 11-crate Cargo workspace plus the root CLI/library package. Strict dependency layering is validated by scripts/ci/check_workspace_contract.sh on every CI run.
Dependency Graph
koyal-api (no heavy deps)
↑
├── koyal-config (api)
├── koyal-memory (api)
├── koyal-chains (api)
├── koyal-dexes (api + chains)
├── koyal-exchanges (api)
├── koyal-providers (api + config)
├── koyal-core (api + memory)
└── koyal-runtime (api + config + memory + providers + core)
↑
├── koyal-gateway (api + config + core + runtime + integrations)
└── koyal-integrations (api + config + runtime)
↑
└── koyal (CLI facade — depends on everything)
koyal-api — Public Traits
Zero heavy dependencies. Every other crate depends on this one. Defines all trait boundaries.
| Trait / Type | File | Purpose |
|---|---|---|
AgentHarness | harness.rs | Tick engine — tick(), capabilities(), supports() |
ModelProvider | provider.rs | AI provider interface — call(), health_check(), capabilities() |
Memory | memory.rs | Storage — store(), recall(), forget(), prefetch(), sync_turn() |
TradingExecutorHandler | executor.rs | DEX executor — execute(), validate(), lifecycle |
Tool | tool.rs | Agent tool interface — execute(), schema(), is_read_only() |
ApprovalDecision | approval.rs | Permission result: Allow, Deny(String), Prompt{request_id} |
ClassifiedError | error.rs | Error taxonomy: 15 FailoverReason values with recovery hints |
ManagedAgent | agent_types.rs | Agent + Bot + Loop + Executor + Wallet data types |
koyal-config — Configuration
| File | Purpose |
|---|---|
schema.rs | AppConfig — env-driven with dotenvy, serde derive |
frontmatter.rs | YAML frontmatter parser for legacy agent.md files |
Environment variables: AI_GATEWAY, AI_MODEL, OPENAI_API_KEY, ANTHROPIC_API_KEY, SOLANA_RPC_URL, SOLANA_KEYPAIR_PATH, etc.
koyal-memory — Persistence
| File | Purpose |
|---|---|
sqlite.rs | SqliteMemory — rusqlite with FTS5 full-text search, WAL mode, agent isolation |
store.rs | AgentMemory — high-level memory operations |
jsonl.rs | Append-only JSONL for audit-compatible trade ledgers |
journal.rs | Structured journal.md writer |
learnings_store.rs | learnings.md with word-overlap deduplication |
positions.rs | Derive positions from trade ledger |
vault.rs | MarkdownVault — agent bible manager (soul, instructions, memories, bots) |
koyal-providers — AI Providers
| File | Purpose |
|---|---|
lib.rs | resolve_provider() — priority-based provider selection |
openai.rs | OpenAI Chat Completions |
anthropic.rs | Anthropic Messages API |
catalog.rs | 55+ provider presets (OpenRouter, DeepSeek, Groq, Together, etc.) |
auth_store.rs | AES-256-GCM encrypted credential vault |
model_router.rs | Failover chain with circuit breaker |
health_monitor.rs | Periodic provider health pings |
secret_mask.rs | API key redaction for safe logging |
usage_store.rs | Token/latency usage tracking |
koyal-core — Stores and Types
In-memory stores for the runtime state:
| Store | Purpose |
|---|---|
ManagedAgentStore | Agent registry |
BotStore | Bot registry, scoped by agent |
AgentLoopStore | Loop definitions and state |
TradeStore | Trade proposal queue |
OrderStore | Executor order tracking |
WalletStore | Wallet registry |
SettingsStore | Runtime settings key-value |
LogStore | Structured log buffer |
McpServerStore | MCP server registry |
ConnectionStore | Integration connections |
koyal-chains — Chain Protocols
| Module | Purpose |
|---|---|
solana/ | Solana RPC client, transaction signing, Ed25519 keys |
evm/ | alloy provider, SECP256k1 signing, multi-chain RPC |
wallet.rs | BIP39 mnemonic generation, BIP32 derivation |
koyal-dexes — DEX Integrations
| Module | Purpose |
|---|---|
jupiter/ | Jupiter v6 swap API — quote, route, execute |
uniswap/ | Uniswap v3 SDK + v4 SDK wrappers |
koyal-exchanges — Centralized / Perp Venues
| Module | Purpose |
|---|---|
hyperliquid/ | hyperliquid-rust-sdk + hypersdk. Orders, positions, leverage. |
polymarket/ | polymarket-client-sdk. Market queries, CLOB orders. |
koyal-runtime — Tick Engine
The core execution engine.
| File | Purpose |
|---|---|
harness.rs | CoreHarness — coordinates everything |
tick.rs | Single agent tick: load bible → RAG recall → provider call → approval check → execute |
permission.rs | PermissionEngine — bot policies, wallet roles, scoped authorize |
approval.rs | Approval flow manager |
scheduler.rs | Cron scheduler for loops |
skills/ | SkillRegistry — load, state, pin/unpin |
guardrails.rs | Pre/post tick safety checks |
koyal-gateway — HTTP API
Axum-based HTTP gateway.
| File | Purpose |
|---|---|
lib.rs | AppState, create_router_with_state(), start_server() |
routes.rs | All 50+ route handlers |
static_files.rs | Serves web/dist/ — SPA fallback, asset caching |
mcp.rs | MCP server — harness_tools(), dispatch_mcp_call() |
connectors.rs | Integration connector catalog and defaults |
setup.rs | Setup checklist logic |
koyal-integrations — Loop Blueprints and Channels
| Module | Purpose |
|---|---|
loops/blueprints.rs | Built-in loop blueprints: price_watcher, wallet_monitor, etc. |
loops/runner.rs | Loop execution runner |
telegram/ | Telegram bot, sender pairing, channel provisioning |
webhook/ | Outbound webhook integration |
metrics/ | Prometheus metrics export |
polymarket/ | Polymarket integration hooks |
koyal — CLI
Root package. Thin facade over the workspace crates.
| File | Purpose |
|---|---|
src/lib.rs | Commands enum (clap derive), all subcommand enums |
src/main.rs | main() — parse args, dispatch to crates |
src/gateway/mod.rs | Re-exports from koyal-gateway |
Vault — Agent Bible
The Vault is the single source of truth for each agent. Every tick loads the full vault bible before the AI call. Every learning is written back after the tick completes.
Directory Layout
trading_agents/<agent_id>/
soul.md ← personality, values, identity
instructions.md ← trading rules and strategy
memories/ ← episodic memory entries (SQLite-backed)
learnings/ ← post-trade learnings (deduped Markdown)
skills/ ← agent skill definitions
bots/ ← per-bot config files
loops/ ← loop definitions
journal.md ← append-only tick journal
ledger.jsonl ← append-only trade ledger
compose_bible
compose_bible() in koyal-runtime assembles the full agent context:
- Read
soul.md— agent identity and values - Read
instructions.md— strategy rules - Recall relevant memories via SQLite FTS5 (semantic + keyword search)
- Retrieve recent learnings (last N, plus relevance-scored older ones)
- Load active skill definitions
- Append bot-scoped configs for the current
bot_id(if scoped tick) - Include recent journal entries for context continuity
The composed bible becomes the system prompt for the AI provider call.
RAG Retrieval
Memory recall uses SQLite FTS5 full-text search with:
- BM25 ranking
- Recency bias (recent memories scored higher)
- Agent isolation (each agent’s memories are scoped by
agent_id)
Retrieval is triggered at the start of every tick with the current market context as the query.
Self-Learning
After every tick with a trade outcome:
- The harness captures the outcome (PnL, fill rate, slippage)
write_learning()formats a structured learning entry- Word-overlap deduplication filters near-duplicate learnings
- Learning is appended to
learnings/<date>.md - Next tick’s
compose_bible()includes the learning in context
This is genuine self-improvement — agents get better at their specific strategy over time without fine-tuning.
CLI Access
# Read the full composed bible
koyal vault bible --agent trader1
# Read/write soul
koyal vault soul-read --agent trader1
koyal vault soul-write --agent trader1 --content "I trade conservatively..."
# Read/write instructions
koyal vault instructions-read --agent trader1
koyal vault instructions-write --agent trader1 --content "Rule 1: ..."
# List memories
koyal vault memories --agent trader1
# Take a snapshot (git-friendly)
koyal vault snapshot --agent trader1
API Access
# Get full bible
GET /api/vault/bible/{agent}
# Read/write soul
GET /api/vault/soul/{agent}
PUT /api/vault/soul/{agent} {"content": "..."}
# Memories
GET /api/vault/memories/{agent}
POST /api/vault/memories/{agent} {"key": "...", "value": "...", "tags": [...]}
# Learnings
GET /api/vault/learnings/{agent}
POST /api/vault/learnings/{agent}
# Propose an edit (goes through approval flow)
POST /api/vault/propose-edit/{agent}
Bot Scoping
Bots have their own config files inside the vault:
trading_agents/trader1/bots/
bot1.md ← bot1's strategy, risk config, loop links
bot2.md ← bot2's strategy
When a tick runs with bot_id = "bot1", compose_bible includes bots/bot1.md in the context, giving the AI full visibility into the bot’s specific rules on top of the agent’s soul and instructions.
Dashboard
The Vault page (/vault) in the web dashboard provides:
- Tree view of all vault files
- Inline editor for soul, instructions, and bot configs
- Memory browser with search
- Learnings timeline
- “Propose Edit” flow (edit goes through approval before writing to disk)
Permission Model
Koyal uses a review-first permission model. No trade executes without an explicit approval step. The system fails closed — halt is the default state on first boot.
Core Concepts
Global Halt Switch
The halt switch blocks all proposal execution globally.
- Default: halted on first boot until setup is complete
- CLI:
koyal halt on/koyal halt off - API:
POST /api/halt {"halted": true, "reason": "..."} - Dashboard: prominent red Halt button in the header
koyal halt status
# {"halted": false, "reason": null}
koyal halt on --reason "flash crash detected"
# Trading is now halted
Proposal Flow
Every trade follows this path:
AgentTick → TradeProposal (pending)
→ PermissionEngine.check()
→ Allow → execute() → write ledger
→ Deny → reject() → log reason
→ Prompt → approval queue → operator review → allow/deny
Prompt is the most common result for new bots or large trades. The operator reviews in the dashboard or via CLI and responds explicitly.
Approval Queue
Pending approvals appear in:
GET /api/permissions— listPOST /api/approvals/respond {"requestId": "...", "decision": "allow"}— respond- Dashboard: ApprovalBanner at top of page
Wallet Roles
Each wallet assigned to a bot has a role:
| Role | Description |
|---|---|
Observe | Read-only. Balance queries only. No trade permissions. |
Trade | Can execute swaps up to bot risk limits. |
Sweep | Can move funds out. Requires elevated approval. |
Bot Risk Config
Each bot has a risk config:
{
"maxPositionPct": 2.0,
"maxDrawdownPct": 5.0,
"maxDailyTrades": 10,
"maxSlippageBps": 100
}
The PermissionEngine enforces these limits before any trade executes.
PermissionEngine
PermissionEngine in koyal-runtime runs on every tick:
- Check global halt — deny immediately if halted
- Check bot lifecycle state — deny if not
live - Validate wallet role for the requested operation
- Check risk config bounds (position size, drawdown, daily trade count)
- Check bot-scoped policy rules from vault
bots/<id>.md - Return
Allow,Deny(reason), orPrompt{request_id}
Security Notes
- Private keys are never stored in plain text.
koyal-providersuses AES-256-GCM for encrypted credential storage. - The gateway binds to
127.0.0.1by default. Binding to0.0.0.0requires explicit operator action. - All API routes that modify state require the gateway to be in a non-halted state, or the route itself handles halt semantics.
- The MCP server only exposes read-safe tools by default; write tools require explicit bot context.
AI Providers
Koyal ships with 55+ AI provider presets. Providers are configured via environment variables and selected at runtime — no rebuild required.
Configuration
# Primary provider (used for all agent ticks unless overridden)
AI_GATEWAY=anthropic
AI_MODEL=claude-sonnet-4-6
# API keys (only the ones you use are required)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GEMINI_API_KEY=...
GROQ_API_KEY=gsk_...
Or configure via the dashboard: Settings → AI → Gateway and Settings → AI → Model.
Supported Providers
Tier 1 — Directly integrated
| Provider | Env Var | Notes |
|---|---|---|
| OpenAI | OPENAI_API_KEY | GPT-4o, o1, o3, GPT-4.1-mini |
| Anthropic | ANTHROPIC_API_KEY | Claude 3.5, Claude 4 family |
| Google Gemini | GEMINI_API_KEY | Gemini 2.0 Flash, 1.5 Pro |
| AWS Bedrock | AWS_* | Claude, Llama, Titan, Nova |
Tier 2 — OpenAI-compatible endpoints
| Provider | Gateway ID | Notes |
|---|---|---|
| DeepSeek | deepseek | R1, V3, Coder |
| Groq | groq | Llama 3.3, Mixtral, Gemma |
| Together AI | together | Llama, Qwen, WizardLM |
| OpenRouter | openrouter | 200+ models via one key |
| Mistral | mistral | Large, Nemo, 7B |
| Perplexity | perplexity | sonar-pro, sonar-research |
| xAI | xai | grok-3, grok-2 |
| Fireworks | fireworks | Llama, Mixtral |
| Cerebras | cerebras | Fast inference |
| Lepton AI | lepton | Llama, Mistral |
| Cohere | cohere | Command R+ |
| AI21 | ai21 | Jamba |
| Replicate | replicate | Community models |
| HuggingFace | huggingface | Inference API |
Plus 35+ more in the catalog (crates/koyal-providers/src/catalog.rs).
Provider Selection
resolve_provider() in koyal-providers selects the active provider:
- Check
AI_GATEWAYenv / config setting - If not set, try providers in priority order based on available API keys
- On failure, activate circuit breaker and try next in failover chain
- Log every provider call with tokens, latency, and cost estimate
Model Router
The model router enables failover chains:
# config.toml (example)
ai_gateway = "anthropic"
ai_model = "claude-sonnet-4-6"
# Failover: if anthropic is down, try openai
[[ai_profiles]]
gateway = "openai"
model = "gpt-4o"
priority = 2
Circuit breaker: after 3 consecutive failures, a provider is bypassed for 60 seconds.
Encrypted Credentials
Credentials are stored encrypted with AES-256-GCM:
# Credentials are read from env at startup and stored encrypted
# Never written to plain text on disk
koyal ai profile-list # list stored profiles
koyal ai health # test all configured providers
Per-Provider Health Monitoring
koyal ai health
# Checks each configured provider with a minimal API call
# Reports latency, token counts, and status
koyal ai budget --limit 100
# Shows token usage and estimated cost per provider
The dashboard System page shows provider health in real time via WebSocket updates.
Usage Tracking
Every AI call is tracked in UsageStore:
{
"provider": "anthropic",
"model": "claude-sonnet-4-6",
"purpose": "agent_tick",
"agent": "trader1",
"inputTokens": 1240,
"outputTokens": 380,
"latencyMs": 1823
}
Access via GET /api/usage or koyal ai budget.
CLI Reference
All commands use koyal as the binary name. Implemented with clap derive in src/lib.rs, dispatched by src/main.rs.
Run koyal --help or koyal <command> --help at any time.
Core
koyal doctor
Check installation and harness state.
koyal doctor
Output:
{"status":"ok","version":"0.1.0","harness":"rust","gateway":8790}
koyal gateway
Start the HTTP gateway and serve the web dashboard.
koyal gateway
koyal gateway --host 127.0.0.1 --port 8790
koyal gateway --host 0.0.0.0 --port 9090
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Bind host |
--port | 8790 | Bind port |
On startup prints:
[koyal] Gateway http://127.0.0.1:8790/api
[koyal] Dashboard http://127.0.0.1:8790/
koyal dashboard
Web dashboard controls.
koyal dashboard open # open browser
koyal dashboard url # print URL
koyal dashboard status # ping gateway, print JSON status
koyal dashboard open --port 9090 # non-default port
| Subcommand | Description |
|---|---|
open | Open dashboard in default browser (open / xdg-open / start) |
url | Print http://host:port/ |
status | Hit /api/health, print running/not-running JSON |
koyal tui
Launch the ratatui terminal UI.
koyal tui
Keybindings: q quit · r refresh · ←→ switch tabs · t trigger tick.
koyal agent-tick
Run one trading-agent tick and print the result as JSON.
koyal agent-tick --agent solana_jupiter
koyal agent-tick --agent solana_jupiter --propose
koyal agent-tick --agent solana_jupiter --propose --new-session
| Flag | Default | Description |
|---|---|---|
--agent | solana_jupiter | Agent ID (maps to trading_agents/<id>/) |
--propose | false | Allow proposal creation if strategy rules pass |
--new-session | false | Start a new trading session |
Executor Commands
koyal executor list
koyal executor list --agent solana_jupiter
koyal executor stop
koyal executor stop --id ex_solana_jupiter_1_1700000000 --reason "manual stop"
koyal executor reconcile
koyal executor reconcile --id ex_solana_jupiter_1_1700000000
koyal executor snapshot
koyal executor snapshot --agent solana_jupiter
AI Commands
koyal ai providers
List configured AI providers and their status.
koyal ai providers
koyal ai health
Run connectivity health checks on all configured providers.
koyal ai health
koyal ai budget
Show token usage summary.
koyal ai budget
koyal ai budget --agent solana_jupiter --limit 100
| Flag | Default | Description |
|---|---|---|
--agent | all | Filter by agent ID |
--limit | 50 | Max records to show |
koyal ai profile-list
List encrypted auth profiles.
koyal ai profile-list
Trade Commands
koyal trade list
List all trade proposals.
koyal trade list
koyal trade report
Show trade summary report.
koyal trade report
koyal trade jupiter-order
Preview a Jupiter swap order (does not execute).
koyal trade jupiter-order \
--input So11111111111111111111111111111111111111112 \
--output EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \
--amount 1000000000
Wallet Commands
koyal wallet balance
Show wallet balance.
koyal wallet balance
koyal wallet address
Show wallet address.
koyal wallet address
koyal wallet generate
Generate a new wallet with BIP39/BIP32 derivation.
koyal wallet generate --chain solana
koyal wallet generate --chain evm --index 1
koyal wallet generate --chain solana --mnemonic "word1 word2 ..."
| Flag | Default | Description |
|---|---|---|
--chain | solana | Chain: solana, evm |
--index | 0 | Derivation index |
--mnemonic | random | BIP39 seed phrase |
Halt Commands
koyal halt status
Show global halt state.
koyal halt status
koyal halt on
Enable trading halt — blocks all proposal execution.
koyal halt on
koyal halt on --reason "flash crash detected"
koyal halt off
Disable trading halt.
koyal halt off
Memory Commands
koyal memory summary
Show agent memory summary.
koyal memory summary
koyal memory summary --agent solana_jupiter
koyal memory search
Search agent memory with FTS5 full-text search.
koyal memory search --query "sol rally"
koyal memory search --query "loss" --agent solana_jupiter
Agent Commands
koyal agent list
List all managed agents.
koyal agent list
koyal agent create
Create a new managed agent.
koyal agent create --id trader1 --name "Trader One" --kind trading
| --kind values | trading, research, operations, assistant, custom |
koyal agent get
Get agent details.
koyal agent get --id trader1
koyal agent delete
Delete an agent.
koyal agent delete --id trader1
Bot Commands
koyal bot list
List bots for an agent.
koyal bot list --agent trader1
koyal bot create
Create a trading bot.
koyal bot create --id bot1 --agent trader1 --name "Jupiter Bot" --venue jupiter
koyal bot setup
Create a bot with strategy and loop (the recommended path).
koyal bot setup \
--agent trader1 \
--name jup_sol_bot \
--strategy "Conservative Jupiter swaps on Solana. Max 2% portfolio per trade." \
--loop price_watcher
| Flag | Default | Description |
|---|---|---|
--agent | required | Agent ID |
--name | required | Bot name |
--strategy | required | Strategy description (fed to agent) |
--loop-blueprint | price_watcher | Loop blueprint to attach |
--extra | "" | Extra context passed to strategy |
koyal bot transition
Change bot lifecycle state.
koyal bot transition --id bot1 --target live
koyal bot transition --id bot1 --target paused
States: paper → live → paused → paper
koyal bot assign-wallet
Assign a wallet to a bot.
koyal bot assign-wallet --bot-id bot1 --wallet wallet_id_here
Loop Commands
koyal loop list
List agent loops.
koyal loop list --agent trader1
koyal loop blueprints
List available loop blueprints.
koyal loop blueprints
Available blueprints: market_pulse, trade_watch, portfolio_rebalance, wallet_monitor, price_watcher, onchain_scanner.
koyal loop create
Create a loop from a blueprint.
koyal loop create --agent trader1 --blueprint price_watcher
koyal loop toggle
Enable or disable a loop.
koyal loop toggle --id loop_id_here
Skill Commands
koyal skills list
List all available skills.
koyal skills list
koyal skills info
Show skill details.
koyal skills info --name trading-agent-builder
koyal skills pin / koyal skills unpin
Pin a skill to prevent auto-archive.
koyal skills pin --name risk-guardrails
koyal skills unpin --name risk-guardrails
koyal skills refresh
Force refresh skill metadata.
koyal skills refresh
Vault Commands
The vault is the single source of truth (bible) for each agent.
koyal vault agents
List agents with a vault.
koyal vault agents
koyal vault bible
Read the full composed bible (soul + instructions + memories + learnings + bot configs).
koyal vault bible --agent trader1
koyal vault snapshot
Create a git-friendly snapshot of the entire bible.
koyal vault snapshot --agent trader1
koyal vault soul-read / koyal vault soul-write
Read or write soul.md.
koyal vault soul-read --agent trader1
koyal vault soul-write --agent trader1 --content "I trade conservatively."
koyal vault instructions-read / koyal vault instructions-write
Read or write instructions.md.
koyal vault instructions-read --agent trader1
koyal vault instructions-write --agent trader1 --content "Rule 1: ..."
koyal vault memories
List agent memories.
koyal vault memories --agent trader1
Telegram Commands
koyal telegram status
Show Telegram integration status.
koyal telegram status
koyal telegram senders
List paired Telegram senders.
koyal telegram senders
koyal telegram pair
Pair a Telegram sender to an agent channel.
koyal telegram pair --sender-id 123456789 --channel trader1
koyal telegram block
Block a Telegram sender.
koyal telegram block --sender-id 123456789
koyal telegram provision-channel
Create a new Telegram notification channel.
koyal telegram provision-channel --chat-id -100123456789 --label "Alerts"
API Reference
Base URL: http://127.0.0.1:8790 (default; configurable via --host and --port).
All responses are JSON. The gateway binds to localhost by default. Bind publicly only behind a trusted reverse proxy.
Start the gateway: koyal gateway
System
GET /api/health
Liveness probe.
{"status": "ok"}
GET /api/doctor
Full health check with version info.
{
"status": "ok",
"version": "0.1.0",
"harness": "rust",
"uptime_seconds": 142
}
GET /api/overview
Full dashboard state snapshot: agents, proposals, halt state, AI usage, wallets.
{
"status": "ok",
"agents": [],
"proposals": [],
"halt": {"halted": false, "reason": null},
"aiUsage": {
"totals": {"calls": 0, "inputTokens": 0, "outputTokens": 0}
}
}
GET /api/harness/status
Current harness tick state.
{
"totalSessionInputTokens": 0,
"totalSessionOutputTokens": 0,
"totalTicks": 0,
"guardrailTriggers": [],
"lastTickAt": null,
"lastTickAgent": null,
"lastTickResult": null
}
GET /api/harness/token-usage
Session token usage totals.
GET /api/harness/guardrails
Guardrail trigger history.
POST /api/harness/tick
Run one agent tick.
{"agent": "solana_jupiter", "propose": true, "newSession": false}
GET /api/logs
Fetch structured logs. Supports ?file=agent&limit=100&level=INFO.
GET /api/usage
AI provider token usage records.
Proposals
GET /api/proposals
List all trade proposals.
[
{
"id": "prop_1",
"agentId": "solana_jupiter",
"action": "swap",
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "1000000000",
"slippageBps": 50,
"status": "pending",
"createdAt": "2025-01-01T00:00:00Z"
}
]
POST /api/proposals
Create a new trade proposal.
{
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "1000000000",
"slippageBps": 50
}
POST /api/proposals/approve
Approve a proposal by ID.
{"id": "prop_1"}
POST /api/proposals/reject
Reject a proposal.
{"id": "prop_1", "reason": "price impact too high"}
POST /api/proposals/execute
Execute an approved proposal.
{"id": "prop_1"}
Halt Control
GET /api/halt
{"halted": false, "reason": null}
POST /api/halt
{"halted": true, "reason": "manual halt"}
Agents
GET /api/agents
List all managed agents.
POST /api/agents
Create a managed agent.
{
"id": "trader1",
"name": "Trader One",
"kind": "trading",
"model": "claude-sonnet-4-6",
"tools": ["get_quote", "swap"],
"memoryEnabled": true
}
Bots
GET /api/bots
List all bots. Supports ?agent=trader1.
POST /api/bots
Create a bot.
{
"id": "bot1",
"ownerAgentId": "trader1",
"name": "Jupiter Bot",
"venue": "jupiter"
}
PATCH /api/bots/{id}
Update bot fields.
DELETE /api/bots/{id}
Delete a bot.
POST /api/bots/{id}/lifecycle
Transition bot lifecycle.
{"target": "live"}
States: paper → live → paused
GET /api/bots/{id}/risk
Get bot risk config.
PUT /api/bots/{id}/risk
Set bot risk config.
{
"maxPositionPct": 2.0,
"maxDrawdownPct": 5.0,
"maxDailyTrades": 10
}
POST /api/bots/{id}/assign-wallet
Assign a wallet to a bot.
{"walletId": "wallet_id"}
Wallets
GET /api/wallets
List all wallets.
POST /api/wallets
Import a wallet.
{
"label": "Trade Wallet",
"address": "7R7tHW6...",
"chain": "solana",
"role": "trade"
}
POST /api/wallets/generate
Generate a new wallet.
{"chain": "solana", "index": 0}
Response includes address, public key, and (optionally) encrypted private key.
POST /api/wallets/{id}/derive
Derive a wallet for another chain from the same seed.
{"chain": "evm", "index": 0}
PATCH /api/wallets/{id}
Update wallet metadata.
DELETE /api/wallets/{id}
Remove wallet from store.
Executors
GET /api/executors
List active trading executors. Supports ?agent=trader1.
GET /api/executor-snapshot
Snapshot executor state for an agent.
GET /api/permissions
List pending permission requests (approval queue).
POST /api/approvals/respond
Respond to a pending approval request.
{"requestId": "req_1", "decision": "allow"}
Loops
GET /api/loops
List all loops. Supports ?agent=trader1.
POST /api/loops
Create a loop from a blueprint.
{"agentId": "trader1", "blueprint": "price_watcher"}
PATCH /api/loops/{id}
Update loop config.
DELETE /api/loops/{id}
Delete a loop.
POST /api/loops/{id}/toggle
Enable or disable a loop.
POST /api/loops/{id}/run
Run a loop immediately (ignores schedule).
GET /api/blueprints
List available loop blueprints.
[
{"id": "price_watcher", "name": "Price Watcher", "description": "..."},
{"id": "wallet_monitor", "name": "Wallet Monitor", "description": "..."}
]
Skills
GET /api/skills
List all skills with state.
GET /api/skills/catalog
Full skill catalog (installed + available).
GET /api/skills/{name}
Get a specific skill.
POST /api/skills/toggle
Toggle skill active/inactive.
{"name": "trading-agent-builder"}
POST /api/skills/refresh
Force refresh skill metadata from disk.
Vault
GET /api/vault/agents
List agents with a vault directory.
GET /api/vault/bible/{agent}
Full composed bible (soul + instructions + memories + learnings + bot configs).
GET /api/vault/bible-files/{agent}
List bible component files.
GET /api/vault/soul/{agent}
Read soul.md.
PUT /api/vault/soul/{agent}
Write soul.md.
{"content": "I trade conservatively..."}
GET /api/vault/instructions/{agent}
Read instructions.md.
PUT /api/vault/instructions/{agent}
Write instructions.md.
GET /api/vault/memories/{agent}
List memories.
POST /api/vault/memories/{agent}
Write a memory.
{"key": "risk_level", "value": "conservative", "tags": ["config"]}
GET /api/vault/learnings/{agent}
List learnings.
POST /api/vault/learnings/{agent}
Write a learning entry.
POST /api/vault/propose-edit/{agent}
Submit a vault edit proposal (goes through approval flow).
GET /api/vault/bots/{agent}
List bots in the vault for an agent.
AI / Chat
POST /api/chat
Send a message to the agent’s AI provider.
{
"messages": [{"role": "user", "content": "What's the SOL price?"}],
"agent": "trader1"
}
GET /api/providers
List AI providers and their configured status.
GET /api/context
Current agent context (config + vault summary).
MCP
GET /api/mcp
List configured MCP servers.
POST /api/mcp
Add an MCP server.
{
"name": "Solana",
"url": "https://mcp.solana.com/mcp",
"chain": "solana",
"toolPrefix": "solana_",
"enabled": true
}
PATCH /api/mcp/{name}
Update MCP server config.
DELETE /api/mcp/{name}
Remove an MCP server.
POST /api/mcp/test
Test connectivity to an MCP server.
GET /api/mcp/tools
List all tools from connected MCP servers.
POST /api/mcp/call
Call an MCP tool.
{"method": "solana_get_balance", "args": {"address": "..."}}
Connectors
GET /api/connectors/catalog
Full connector catalog.
GET /api/connectors
List active connections.
POST /api/connectors
Create a connection instance.
PATCH /api/connectors/{id}
Update a connection.
DELETE /api/connectors/{id}
Remove a connection.
Settings
GET /api/settings
Get all harness settings.
PATCH /api/settings
Update settings.
{"ai.gateway": "anthropic", "ai.model": "claude-sonnet-4-6"}
GET /api/settings/schema
JSON schema for all settings (used by the dashboard settings UI).
Setup
GET /api/setup
Setup checklist status. Used by the dashboard onboarding flow.
WebSocket
GET /ws
Real-time update stream. Connect with any WebSocket client.
const ws = new WebSocket('ws://127.0.0.1:8790/ws');
ws.onmessage = (e) => console.log(JSON.parse(e.data));
Message types: init, proposal, executor_update, learning, tick_complete, halt_change.
Configuration Reference
Koyal is configured via environment variables and a .env file in the working directory. All variables are optional — only configure the providers and chains you use.
Core
| Variable | Default | Description |
|---|---|---|
KOYAL_DATA_DIR | ./trading_agents | Root directory for agent vaults |
KOYAL_LOG_LEVEL | info | Log level: trace, debug, info, warn, error |
KOYAL_GATEWAY_HOST | 127.0.0.1 | Gateway bind address |
KOYAL_GATEWAY_PORT | 8790 | Gateway bind port |
AI Providers
| Variable | Description |
|---|---|
AI_GATEWAY | Active provider ID: openai, anthropic, gemini, deepseek, groq, openrouter, etc. |
AI_MODEL | Model name for the active provider |
OPENAI_API_KEY | OpenAI API key |
ANTHROPIC_API_KEY | Anthropic API key |
GEMINI_API_KEY | Google Gemini API key |
GROQ_API_KEY | Groq API key |
TOGETHER_API_KEY | Together AI API key |
OPENROUTER_API_KEY | OpenRouter API key |
DEEPSEEK_API_KEY | DeepSeek API key |
MISTRAL_API_KEY | Mistral API key |
PERPLEXITY_API_KEY | Perplexity API key |
XAI_API_KEY | xAI Grok API key |
FIREWORKS_API_KEY | Fireworks AI API key |
COHERE_API_KEY | Cohere API key |
REPLICATE_API_TOKEN | Replicate API token |
HUGGINGFACE_API_KEY | HuggingFace Inference API key |
AWS Bedrock
| Variable | Description |
|---|---|
AWS_ACCESS_KEY_ID | AWS access key |
AWS_SECRET_ACCESS_KEY | AWS secret key |
AWS_REGION | AWS region (default: us-east-1) |
AWS_SESSION_TOKEN | Optional session token for temporary credentials |
Solana
| Variable | Default | Description |
|---|---|---|
SOLANA_RPC_URL | Mainnet public RPC | Solana RPC endpoint |
SOLANA_KEYPAIR_PATH | — | Path to keypair JSON file (Ed25519) |
SOLANA_COMMITMENT | confirmed | Commitment level: processed, confirmed, finalized |
EVM
| Variable | Description |
|---|---|
ETH_RPC_URL | Ethereum mainnet RPC URL |
ARBITRUM_RPC_URL | Arbitrum RPC URL |
BASE_RPC_URL | Base RPC URL |
POLYGON_RPC_URL | Polygon RPC URL |
ETH_PRIVATE_KEY | EVM private key (hex, 0x-prefixed) |
Hyperliquid
| Variable | Default | Description |
|---|---|---|
HYPERLIQUID_USE_TESTNET | false | Use testnet instead of mainnet |
HYPERLIQUID_PRIVATE_KEY | — | Wallet private key for signing |
Telegram
| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN | Bot token from @BotFather |
Example .env
AI_GATEWAY=anthropic
AI_MODEL=claude-sonnet-4-6
ANTHROPIC_API_KEY=sk-ant-...
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
SOLANA_KEYPAIR_PATH=/home/user/.config/solana/id.json
KOYAL_GATEWAY_PORT=8790
KOYAL_LOG_LEVEL=info
Runtime Settings
Some settings can be changed at runtime without restart via koyal settings or PATCH /api/settings:
| Key | Description |
|---|---|
ai.gateway | Override active AI provider |
ai.model | Override active model |
halt.enabled | Global halt state |
halt.reason | Halt reason string |
koyal settings set ai.gateway openai
koyal settings set ai.model gpt-4o
Loop Blueprints
Blueprints are pre-built loop templates. Instantiate one with koyal loop create --blueprint <id> or POST /api/loops {"blueprint": "<id>"}.
Available Blueprints
price_watcher
Monitors an asset price at a configurable interval. Posts to Telegram or webhook on threshold breach.
{
"blueprint": "price_watcher",
"agentId": "trader1",
"params": {
"symbol": "SOL/USDC",
"intervalSecs": 60,
"alertThresholdPct": 3.0,
"notify": "telegram"
}
}
Actions: Read-only. No trades.
wallet_monitor
Polls wallet balances on a schedule. Alerts on balance drop beyond threshold.
{
"blueprint": "wallet_monitor",
"agentId": "trader1",
"params": {
"walletId": "wallet_1",
"chain": "solana",
"intervalSecs": 300,
"alertThresholdPct": 10.0
}
}
Actions: Read-only. Query balance, send alert.
dca
Dollar-cost averaging. Buys a fixed amount on a fixed schedule.
{
"blueprint": "dca",
"agentId": "trader1",
"params": {
"inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"outputMint": "So11111111111111111111111111111111111111112",
"amountUsdc": 100,
"intervalHours": 24,
"slippageBps": 50
}
}
Actions: Creates trade proposals on schedule.
pnl_reporter
Computes realized/unrealized PnL from the trade ledger and posts a summary.
{
"blueprint": "pnl_reporter",
"agentId": "trader1",
"params": {
"intervalHours": 24,
"notify": "telegram",
"includeLedgerSummary": true
}
}
Actions: Read-only. Reads ledger, sends report.
agent_tick_loop
Runs agent ticks on a cron schedule. The core loop for autonomous trading.
{
"blueprint": "agent_tick_loop",
"agentId": "trader1",
"params": {
"cron": "*/30 * * * *",
"propose": true,
"newSession": false
}
}
Actions: Calls POST /api/harness/tick. Full trading authority.
health_ping
Periodic self-health check. Alerts if the harness or providers become unhealthy.
{
"blueprint": "health_ping",
"agentId": "trader1",
"params": {
"intervalSecs": 300,
"notify": "telegram"
}
}
position_close
One-shot loop that closes all open positions for a bot. Useful for emergency deleveraging.
{
"blueprint": "position_close",
"agentId": "trader1",
"params": {
"botId": "bot1",
"maxSlippageBps": 100
}
}
CLI
# List available blueprints
koyal loop blueprints
# Create from blueprint
koyal loop create --blueprint price_watcher --agent trader1
# List running loops
koyal loop list --agent trader1
# Toggle a loop on/off
koyal loop toggle --id loop_id_1
# Run once immediately
koyal loop run --id loop_id_1
Custom Loops
Create a loop without a blueprint:
POST /api/loops
{
"agentId": "trader1",
"name": "My Custom Loop",
"cron": "0 */4 * * *",
"action": "tick",
"params": {"propose": true}
}
Supported action values: tick, price_check, balance_check, pnl_report, health_check, custom_script.
Performance
Koyal’s launch profile favors fast release binaries and predictable runtime storage:
- Release builds use high optimization, LTO, fewer codegen units, stripped symbols, and aborting panics.
- SQLite memory enables WAL mode, NORMAL synchronous writes, in-memory temp storage, and a busy timeout.
- Recall uses FTS5 prefix queries with a LIKE fallback when FTS is unavailable.
- The Dockerfile separates dependency resolution from source compilation for better BuildKit cache reuse.
Run the benchmark suite:
scripts/perf/bench.sh
For release-size guardrails and detailed notes, see docs/performance.md.
Web Dashboard
The web dashboard is a React SPA served directly by koyal gateway. No separate Node server required.
Starting the Dashboard
# Start gateway (serves API + dashboard)
koyal gateway
# Output:
# [koyal] Gateway http://127.0.0.1:8790/api
# [koyal] Dashboard http://127.0.0.1:8790/
Open the dashboard:
koyal dashboard open # opens in default browser
koyal dashboard url # prints URL to stdout
koyal dashboard status # checks if gateway is running
How It Works
koyal-gateway auto-detects the web/dist/ directory on startup and serves it as a static SPA:
/assets/{*path}→ hashed static files with long-lived cache headers/*→index.htmlSPA fallback
The detection probes these paths in order:
- Binary-adjacent
web/dist/(binary release tarballs) $CWD/web/dist/(source builds)/koyal-data/web/dist/(Docker)/usr/share/koyal/web/dist/(system packages)~/.local/share/koyal/web/dist/(XDG user install)
If none found, the gateway runs API-only mode (no dashboard UI, all API routes still work).
Dashboard Pages
| Route | Page | Description |
|---|---|---|
/ | Overview | Agent summary, halt state, pending proposals |
/agents | Agents | Agent list, create, tick |
/bots | Bots | Bot list, lifecycle control, risk config |
/wallets | Wallets | Wallet list, generate, assign to bot |
/vault | Vault | Agent bible editor — soul, instructions, memories |
/proposals | Proposals | Trade proposal queue, approve/reject |
/loops | Loops | Loop list, create from blueprint, toggle |
/skills | Skills | Skill catalog, toggle, refresh |
/mcp | MCP | MCP server list, add, test |
/connectors | Connectors | Integration connections |
/logs | Logs | Structured log viewer |
/system | System | Provider health, token usage, harness status |
/settings | Settings | Runtime settings editor |
Development
Run the dashboard in development mode (hot reload):
cd web
npm install
npm run dev
# Dashboard at http://localhost:3000, API at http://localhost:8790
The Vite dev server proxies /api and /ws to the gateway — configure in web/vite.config.ts.
Build for production:
cd web
npm run build
# Output: web/dist/
Docker
The official Docker image bundles web/dist/ at /usr/share/koyal/web/dist:
# Dockerfile excerpt
COPY web/dist /usr/share/koyal/web/dist
docker run -p 8790:8790 ghcr.io/your-org/koyal:latest
# Dashboard at http://localhost:8790
Reverse Proxy
To expose publicly, proxy with nginx or Caddy and add TLS:
server {
listen 443 ssl;
server_name koyal.example.com;
location / {
proxy_pass http://127.0.0.1:8790;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
The WebSocket (/ws) requires the Upgrade header passthrough for real-time updates to work.
Dashboard CLI Reference
koyal dashboard open [--host 127.0.0.1] [--port 8790]
# Open dashboard in default browser
koyal dashboard url [--host 127.0.0.1] [--port 8790]
# Print dashboard URL
koyal dashboard status [--host 127.0.0.1] [--port 8790]
# Check if gateway is running and print health JSON
Development
Use Koyal’s local workflow to rehearse CI and release-sensitive checks before a pull request.
cp .env.example .env
direnv allow
cargo test --all --locked
scripts/dev/install-hooks.sh
scripts/dev/act-local.sh --list
dev/ci.sh all
.envrc enters the Nix flake dev shell when direnv is enabled.
The hooks run a staged secret scan when gitleaks is installed, then run Rust
and config gates before push. Optional pre-push checks:
KOYAL_DOCS_CHECK=1 git push
KOYAL_PRECHECK_READINESS=1 git push
KOYAL_PREFLIGHT=1 git push
Dependabot covers Cargo, GitHub Actions, and Docker updates. CODEOWNERS routes release-sensitive changes to maintainers, while PR and issue templates collect validation, safety, compatibility, and private security-reporting context.
.github/actionlint.yaml keeps actionlint runner-label handling stable.
.vscode/tasks.json and .zed/settings.json provide checked editor tasks and
formatting defaults for release, benchmark, and launch-readiness work.
dev/ci.sh all wraps Rust, config, docs, package metadata, Cargo package
file-list, and non-strict readiness checks for local maintainer rehearsal.
scripts/ci/check_dev_contract.sh keeps the environment template, hooks,
local act rehearsal, Dependabot, review templates, and docs aligned.
Release and Distribution
Use readiness before a tag:
scripts/release/readiness.sh
scripts/release/preflight.sh
scripts/release/cut_release_tag.sh v0.1.0
git push origin v0.1.0
The preflight gate validates repository config, Rust checks, advisory policy, release build, package archives, SBOM, checksums, and asset verification. Checksum generation refuses empty artifact directories, so a release cannot publish an empty SHA256SUMS by accident. Deployment validation keeps systemd, Kubernetes/OpenShift, FreeBSD, NixOS, and deployment docs aligned around private non-root service defaults. Container validation keeps Dockerfile, Compose, GHCR workflow, smoke test, and docs aligned around non-root runtime, gateway port, data directory, BuildKit cache use, and SBOM/provenance publishing. Supply-chain validation keeps security policy, dependency policy, advisory workflows, SBOM generation, release/container attestations, and verification docs aligned. Installer validation keeps Unix and Windows binary installers aligned with supported release targets, checksum verification, docs, and CI smoke coverage. Package metadata drift for AUR, Scoop, Homebrew, nFPM, Nix, and local workspace dependency versions is checked in both the config gate and release preflight. Release target validation keeps the matrix, supported-platform docs, strict verifier, Scoop target, and Linux package target aligned. Release-notes validation keeps CHANGELOG, release-plz, tag flow, GitHub release notes, release docs, and bump-version coverage aligned. GitHub setup validation keeps publishing secrets, variables, and workflow permissions documented before package-manager workflows run. Package-manager publish validation keeps AUR, Scoop, and Homebrew dry-run guarded, secret checked, no-op safe, and aligned with their manifests. Cargo package file lists are also generated for the private workspace crates so missing includes and manifest drift are caught before tagging.
Strict readiness is for final release machines and requires no nested Git
metadata plus the local toolchain used by distribution checks: container
runtime, pwsh, workflow linting, Markdown linting, mdBook, link checking, and
just.
Release artifacts include:
- platform archives from
.github/workflows/release.yml - Linux packages generated through nFPM
SHA256SUMS- CycloneDX SBOM
- GitHub release asset attestations
- GHCR image provenance and SBOM attestations
The full release process is maintained in the release runbook.
Supply Chain
Launch releases should be reproducible enough for operators to verify what they install:
- Release archives carry licenses, notice text, and the README.
SHA256SUMSis generated for every release asset.- CycloneDX SBOM output is produced during preflight and release workflows.
cargo deny checkenforces license, source, and advisory policy.cargo audit --deny warningsprovides an independent RustSec pass using.cargo/audit.toml.scripts/security/check_advisory_waivers.pykeeps RustSec exceptions synchronized between cargo-deny, cargo-audit, and the security policy.scripts/ci/check_supply_chain_contract.shkeeps security policy, advisory workflows, SBOM generation, release/container attestations, and verification docs aligned.- GitHub release artifacts and GHCR images are attested by workflow.
Known upstream advisory exceptions are documented in deny.toml and the security policy. See docs/supply-chain.md for verification commands and operator notes.