Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

CapabilityDescription
55+ AI ProvidersOpenAI, Anthropic, Gemini, Bedrock, DeepSeek, Groq, Together, OpenRouter, xAI, Mistral, and 45 more. Priority routing with circuit breaker.
Multi-ChainSolana + Jupiter, EVM + Uniswap v3/v4, Hyperliquid perps, Polymarket prediction markets.
Unified VaultSingle 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 EnginePermissionEngine with bot-scoped policies, wallet roles (Observe / Trade / Sweep), and scoped authorization.
Web DashboardReact + Vite + WebSocket UI served directly by koyal gateway. No separate Node server required.
MCP ServerBuilt-in Model Context Protocol server. All harness tools exposed as MCP calls. External MCP servers (Solana, Chainstack) configurable.
CLI + TUI20+ subcommands via clap. koyal tui for ratatui terminal UI. koyal dashboard open for the browser.
IntegrationsTelegram alerts/control, webhook outbound, Prometheus metrics.
Supply ChainCycloneDX 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 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.d and 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-api defines stable traits and data contracts.
  • koyal-config parses agent frontmatter without regex overhead.
  • koyal-memory uses SQLite WAL mode and FTS5 for recall.
  • koyal-runtime coordinates ticks, approvals, guardrails, and scheduler work.
  • koyal-gateway exposes 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 / TypeFilePurpose
AgentHarnessharness.rsTick engine — tick(), capabilities(), supports()
ModelProviderprovider.rsAI provider interface — call(), health_check(), capabilities()
Memorymemory.rsStorage — store(), recall(), forget(), prefetch(), sync_turn()
TradingExecutorHandlerexecutor.rsDEX executor — execute(), validate(), lifecycle
Tooltool.rsAgent tool interface — execute(), schema(), is_read_only()
ApprovalDecisionapproval.rsPermission result: Allow, Deny(String), Prompt{request_id}
ClassifiedErrorerror.rsError taxonomy: 15 FailoverReason values with recovery hints
ManagedAgentagent_types.rsAgent + Bot + Loop + Executor + Wallet data types

koyal-config — Configuration

FilePurpose
schema.rsAppConfig — env-driven with dotenvy, serde derive
frontmatter.rsYAML 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

FilePurpose
sqlite.rsSqliteMemory — rusqlite with FTS5 full-text search, WAL mode, agent isolation
store.rsAgentMemory — high-level memory operations
jsonl.rsAppend-only JSONL for audit-compatible trade ledgers
journal.rsStructured journal.md writer
learnings_store.rslearnings.md with word-overlap deduplication
positions.rsDerive positions from trade ledger
vault.rsMarkdownVault — agent bible manager (soul, instructions, memories, bots)

koyal-providers — AI Providers

FilePurpose
lib.rsresolve_provider() — priority-based provider selection
openai.rsOpenAI Chat Completions
anthropic.rsAnthropic Messages API
catalog.rs55+ provider presets (OpenRouter, DeepSeek, Groq, Together, etc.)
auth_store.rsAES-256-GCM encrypted credential vault
model_router.rsFailover chain with circuit breaker
health_monitor.rsPeriodic provider health pings
secret_mask.rsAPI key redaction for safe logging
usage_store.rsToken/latency usage tracking

koyal-core — Stores and Types

In-memory stores for the runtime state:

StorePurpose
ManagedAgentStoreAgent registry
BotStoreBot registry, scoped by agent
AgentLoopStoreLoop definitions and state
TradeStoreTrade proposal queue
OrderStoreExecutor order tracking
WalletStoreWallet registry
SettingsStoreRuntime settings key-value
LogStoreStructured log buffer
McpServerStoreMCP server registry
ConnectionStoreIntegration connections

koyal-chains — Chain Protocols

ModulePurpose
solana/Solana RPC client, transaction signing, Ed25519 keys
evm/alloy provider, SECP256k1 signing, multi-chain RPC
wallet.rsBIP39 mnemonic generation, BIP32 derivation

koyal-dexes — DEX Integrations

ModulePurpose
jupiter/Jupiter v6 swap API — quote, route, execute
uniswap/Uniswap v3 SDK + v4 SDK wrappers

koyal-exchanges — Centralized / Perp Venues

ModulePurpose
hyperliquid/hyperliquid-rust-sdk + hypersdk. Orders, positions, leverage.
polymarket/polymarket-client-sdk. Market queries, CLOB orders.

koyal-runtime — Tick Engine

The core execution engine.

FilePurpose
harness.rsCoreHarness — coordinates everything
tick.rsSingle agent tick: load bible → RAG recall → provider call → approval check → execute
permission.rsPermissionEngine — bot policies, wallet roles, scoped authorize
approval.rsApproval flow manager
scheduler.rsCron scheduler for loops
skills/SkillRegistry — load, state, pin/unpin
guardrails.rsPre/post tick safety checks

koyal-gateway — HTTP API

Axum-based HTTP gateway.

FilePurpose
lib.rsAppState, create_router_with_state(), start_server()
routes.rsAll 50+ route handlers
static_files.rsServes web/dist/ — SPA fallback, asset caching
mcp.rsMCP server — harness_tools(), dispatch_mcp_call()
connectors.rsIntegration connector catalog and defaults
setup.rsSetup checklist logic

koyal-integrations — Loop Blueprints and Channels

ModulePurpose
loops/blueprints.rsBuilt-in loop blueprints: price_watcher, wallet_monitor, etc.
loops/runner.rsLoop 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.

FilePurpose
src/lib.rsCommands enum (clap derive), all subcommand enums
src/main.rsmain() — parse args, dispatch to crates
src/gateway/mod.rsRe-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:

  1. Read soul.md — agent identity and values
  2. Read instructions.md — strategy rules
  3. Recall relevant memories via SQLite FTS5 (semantic + keyword search)
  4. Retrieve recent learnings (last N, plus relevance-scored older ones)
  5. Load active skill definitions
  6. Append bot-scoped configs for the current bot_id (if scoped tick)
  7. 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:

  1. The harness captures the outcome (PnL, fill rate, slippage)
  2. write_learning() formats a structured learning entry
  3. Word-overlap deduplication filters near-duplicate learnings
  4. Learning is appended to learnings/<date>.md
  5. 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 — list
  • POST /api/approvals/respond {"requestId": "...", "decision": "allow"} — respond
  • Dashboard: ApprovalBanner at top of page

Wallet Roles

Each wallet assigned to a bot has a role:

RoleDescription
ObserveRead-only. Balance queries only. No trade permissions.
TradeCan execute swaps up to bot risk limits.
SweepCan 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:

  1. Check global halt — deny immediately if halted
  2. Check bot lifecycle state — deny if not live
  3. Validate wallet role for the requested operation
  4. Check risk config bounds (position size, drawdown, daily trade count)
  5. Check bot-scoped policy rules from vault bots/<id>.md
  6. Return Allow, Deny(reason), or Prompt{request_id}

Security Notes

  • Private keys are never stored in plain text. koyal-providers uses AES-256-GCM for encrypted credential storage.
  • The gateway binds to 127.0.0.1 by default. Binding to 0.0.0.0 requires 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

ProviderEnv VarNotes
OpenAIOPENAI_API_KEYGPT-4o, o1, o3, GPT-4.1-mini
AnthropicANTHROPIC_API_KEYClaude 3.5, Claude 4 family
Google GeminiGEMINI_API_KEYGemini 2.0 Flash, 1.5 Pro
AWS BedrockAWS_*Claude, Llama, Titan, Nova

Tier 2 — OpenAI-compatible endpoints

ProviderGateway IDNotes
DeepSeekdeepseekR1, V3, Coder
GroqgroqLlama 3.3, Mixtral, Gemma
Together AItogetherLlama, Qwen, WizardLM
OpenRouteropenrouter200+ models via one key
MistralmistralLarge, Nemo, 7B
Perplexityperplexitysonar-pro, sonar-research
xAIxaigrok-3, grok-2
FireworksfireworksLlama, Mixtral
CerebrascerebrasFast inference
Lepton AIleptonLlama, Mistral
CoherecohereCommand R+
AI21ai21Jamba
ReplicatereplicateCommunity models
HuggingFacehuggingfaceInference API

Plus 35+ more in the catalog (crates/koyal-providers/src/catalog.rs).


Provider Selection

resolve_provider() in koyal-providers selects the active provider:

  1. Check AI_GATEWAY env / config setting
  2. If not set, try providers in priority order based on available API keys
  3. On failure, activate circuit breaker and try next in failover chain
  4. 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
FlagDefaultDescription
--host127.0.0.1Bind host
--port8790Bind 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
SubcommandDescription
openOpen dashboard in default browser (open / xdg-open / start)
urlPrint http://host:port/
statusHit /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
FlagDefaultDescription
--agentsolana_jupiterAgent ID (maps to trading_agents/<id>/)
--proposefalseAllow proposal creation if strategy rules pass
--new-sessionfalseStart 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
FlagDefaultDescription
--agentallFilter by agent ID
--limit50Max 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 ..."
FlagDefaultDescription
--chainsolanaChain: solana, evm
--index0Derivation index
--mnemonicrandomBIP39 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

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
FlagDefaultDescription
--agentrequiredAgent ID
--namerequiredBot name
--strategyrequiredStrategy description (fed to agent)
--loop-blueprintprice_watcherLoop 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: paperlivepausedpaper

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

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

VariableDefaultDescription
KOYAL_DATA_DIR./trading_agentsRoot directory for agent vaults
KOYAL_LOG_LEVELinfoLog level: trace, debug, info, warn, error
KOYAL_GATEWAY_HOST127.0.0.1Gateway bind address
KOYAL_GATEWAY_PORT8790Gateway bind port

AI Providers

VariableDescription
AI_GATEWAYActive provider ID: openai, anthropic, gemini, deepseek, groq, openrouter, etc.
AI_MODELModel name for the active provider
OPENAI_API_KEYOpenAI API key
ANTHROPIC_API_KEYAnthropic API key
GEMINI_API_KEYGoogle Gemini API key
GROQ_API_KEYGroq API key
TOGETHER_API_KEYTogether AI API key
OPENROUTER_API_KEYOpenRouter API key
DEEPSEEK_API_KEYDeepSeek API key
MISTRAL_API_KEYMistral API key
PERPLEXITY_API_KEYPerplexity API key
XAI_API_KEYxAI Grok API key
FIREWORKS_API_KEYFireworks AI API key
COHERE_API_KEYCohere API key
REPLICATE_API_TOKENReplicate API token
HUGGINGFACE_API_KEYHuggingFace Inference API key

AWS Bedrock

VariableDescription
AWS_ACCESS_KEY_IDAWS access key
AWS_SECRET_ACCESS_KEYAWS secret key
AWS_REGIONAWS region (default: us-east-1)
AWS_SESSION_TOKENOptional session token for temporary credentials

Solana

VariableDefaultDescription
SOLANA_RPC_URLMainnet public RPCSolana RPC endpoint
SOLANA_KEYPAIR_PATHPath to keypair JSON file (Ed25519)
SOLANA_COMMITMENTconfirmedCommitment level: processed, confirmed, finalized

EVM

VariableDescription
ETH_RPC_URLEthereum mainnet RPC URL
ARBITRUM_RPC_URLArbitrum RPC URL
BASE_RPC_URLBase RPC URL
POLYGON_RPC_URLPolygon RPC URL
ETH_PRIVATE_KEYEVM private key (hex, 0x-prefixed)

Hyperliquid

VariableDefaultDescription
HYPERLIQUID_USE_TESTNETfalseUse testnet instead of mainnet
HYPERLIQUID_PRIVATE_KEYWallet private key for signing

Telegram

VariableDescription
TELEGRAM_BOT_TOKENBot 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:

KeyDescription
ai.gatewayOverride active AI provider
ai.modelOverride active model
halt.enabledGlobal halt state
halt.reasonHalt 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.html SPA fallback

The detection probes these paths in order:

  1. Binary-adjacent web/dist/ (binary release tarballs)
  2. $CWD/web/dist/ (source builds)
  3. /koyal-data/web/dist/ (Docker)
  4. /usr/share/koyal/web/dist/ (system packages)
  5. ~/.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

RoutePageDescription
/OverviewAgent summary, halt state, pending proposals
/agentsAgentsAgent list, create, tick
/botsBotsBot list, lifecycle control, risk config
/walletsWalletsWallet list, generate, assign to bot
/vaultVaultAgent bible editor — soul, instructions, memories
/proposalsProposalsTrade proposal queue, approve/reject
/loopsLoopsLoop list, create from blueprint, toggle
/skillsSkillsSkill catalog, toggle, refresh
/mcpMCPMCP server list, add, test
/connectorsConnectorsIntegration connections
/logsLogsStructured log viewer
/systemSystemProvider health, token usage, harness status
/settingsSettingsRuntime 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.
  • SHA256SUMS is generated for every release asset.
  • CycloneDX SBOM output is produced during preflight and release workflows.
  • cargo deny check enforces license, source, and advisory policy.
  • cargo audit --deny warnings provides an independent RustSec pass using .cargo/audit.toml.
  • scripts/security/check_advisory_waivers.py keeps RustSec exceptions synchronized between cargo-deny, cargo-audit, and the security policy.
  • scripts/ci/check_supply_chain_contract.sh keeps 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.