Skip to main content
Hardware-backed signing for autonomous agents

Your trading agent shouldn't hold your private keys .

openKMS is a deny-by-default signing API for Solana and Cosmos, backed by a YubiHSM2 you actually own. Autonomous strategies request signatures over HTTP. Per-key policy stops the bad trades before the hardware ever touches them.

Chains
Solana · Cosmos
Hardware
YubiHSM2
License
Apache-2.0
GET /policy/solana-hot-0 200 OK
request
curl -sS \
  -H "Authorization: Bearer $(cat signer.token)" \
  http://pi.local:9443/policy/solana-hot-0
response
{
  "label": "solana-hot-0",
  "chain": "solana",
  "effective_enabled": true,
  "policy": {
    "enabled": true,
    "max_signs_per_minute": 30,
    "max_signs_per_day": 5000,
    "per_tx_cap_lamports": "5000000000",
    "allowed_programs": [
      { "id": "11111111111111111111111111111111" }
    ]
  },
  "runtime": {
    "sign_counts": {
      "per_minute": { "limit": 30, "used": 0 }
    }
  }
}
01
Request
Strategy submits a built transaction with a signer token.
02
Policy
Caps, allowlists, and rate limits run before the HSM is touched.
03
YubiHSM2
Signs inside the device. Keys never leave hardware.
04
Signature
Audit log + Prometheus update. Strategy broadcasts.
YubiHSM2-backed Apache-2.0 Solana + Cosmos Deny-by-default policy Append-only audit log Prometheus metrics OpenAPI v1 source of truth Runs on a Raspberry Pi

The problem

Hot wallets are a liability.

Every drained validator, every rug pull, every "compromised hot wallet" post-mortem traces back to the same mistake: keys lived next to the code that uses them.

Threat #1

Mnemonics in .env

Anyone who reads the host reads the seed. One leaked file, one stolen backup, one curious sysadmin — and the treasury is gone.

Threat #2

Unsupervised strategies

A buggy or compromised agent can drain an account in seconds. Spend caps and allowlists shouldn't live inside the same process that builds the trade.

Threat #3

Custody you don't own

Cloud custody and cloud KMS put your keys behind someone else's policy, someone else's audit log, and someone else's outage page.

How it works

Five gates between the strategy and your keys.

Every signing request crosses authentication, chain decoding, policy evaluation, replay protection, and only then reaches the YubiHSM2. Each layer is fail-closed and emits to the audit log.

TRUST: HOST TRUST: HSM 01 Strategy Openclaw / agent 02 Auth Bearer token 03 Decode Solana / Cosmos 04 Policy Caps · allowlists 05 Replay Cache 06 YubiHSM2 Sign in hardware Append-only audit log (JSONL + optional HMAC chain) Prometheus /metrics FAIL-CLOSED · DENY-BY-DEFAULT · NO PLAINTEXT EGRESS
01 — host plane

HTTP, policy, replay cache, audit log, metrics. Runs as a hardened systemd service on a dedicated user with NoNewPrivileges, PrivateTmp, and SystemCallFilter.

02 — hsm plane

Signing keys live only inside the YubiHSM2. The runtime auth key can sign but cannot export or mutate keys. Backups are wrap-encrypted under a key that never leaves the device.

03 — observability

Every accepted and denied signature lands in the JSONL audit log with optional HMAC chaining. Prometheus exports counters for sign attempts, denials, and policy reasons.

Add a chain by implementing the ChainSigner trait — policy, audit, and metrics are chain-agnostic. Full architecture

Sign over HTTP

One bearer token. Two chains. Zero key egress.

openKMS speaks plain HTTP on loopback or behind your proxy. Your agent posts a chain-native message; it gets back a signature, never a key.

POST /sign/solana request
curl -sS -X POST \
  -H "Authorization: Bearer $(cat signer.token)" \
  -H "Content-Type: application/json" \
  http://pi.local:9443/sign/solana \
  -d @sign-request.json
sign-request.json
{
  "label": "solana-hot-0",
  "expected_chain_id": "mainnet-beta",
  "message_b64": "<base64 VersionedMessage>",
  "address_lookup_tables": [
    { "key": "<ALT pubkey>", "addresses": ["<base58>", "..."] }
  ]
}
200 policy passed · HSM signed
{
  "signature_b64": "<base64 64-byte ed25519>"
}
403 policy denied · HSM never touched
{
  "error": "policy: per_tx_cap_lamports exceeded (cap=5000000000, attempted=12000000000)"
}

How we compare

Same threat model. Different blast radius.

Cloud custody and cloud KMS solve part of the problem. They still leave you trusting someone else's policy, audit log, and uptime.

Property
Hot wallet
Mnemonic in .env
Cloud custody
Fireblocks · Turnkey · Privy · Anchorage
Cloud KMS
AWS KMS · GCP Cloud KMS
openKMS
Self-hosted YubiHSM2 signer
Where keys live Strategy host (plaintext) Vendor cloud HSM Vendor cloud HSM Your YubiHSM2
Who writes the policy Nobody You, in their UI Generic IAM only You, in TOML + admin API
Chain-aware allowlists None Partial, vendor-defined Not supported Programs · message types · recipients
Audit log ownership App logs (if any) Vendor dashboard CloudTrail / Cloud Audit Logs Local JSONL + HMAC chain
Open source Closed Closed Apache-2.0
Runs on a Raspberry Pi Yes (and that's the problem) No — vendor cloud only No — vendor cloud only Yes — homelab is the design target
Pricing Free until you're drained Per signature / seat Per key / per request One-time hardware

Vendor names are trademarks of their respective owners. Listed for comparison only.

Built for autonomous agents

How an Openclaw strategy uses openKMS.

openKMS is the signing boundary, not the strategy. Agents construct transactions, ask for a signature, and broadcast — the policy engine keeps a buggy or compromised agent from draining an account.

  1. 01
    Strategy GET /policy/{label}

    Inspect effective limits and live counters before constructing a trade. If a daily cap is nearly exhausted, wait or escalate.

  2. 02
    Strategy Build the transaction

    Construct a chain-native message (VersionedMessage for Solana, SignDoc for Cosmos). openKMS never sees the strategy logic.

  3. 03
    openKMS POST /sign/{chain}

    Decode → policy → replay cache → HSM. A denial is a 403 / 429 with a precise reason. An accept records to the audit log.

  4. 04
    Strategy Broadcast through chain RPC

    openKMS never broadcasts. The strategy assembles signature + message and submits to the chain. The signing boundary stays narrow.

Self-throttling agents

Agents read policy before they sign. Runtime counters tell them when a window is full so they back off instead of probing for a denial.

Bounded blast radius

Per-tx caps limit a single bad trade. Daily caps limit sustained bad behavior. The admin kill switch stops a key without deleting it.

AgentSkills-compatible manual

An operator manual ships at .agents/skills/openkms/SKILL.md so any AgentSkills-aware agent can correctly call openKMS without bespoke onboarding.

Open the skill

Read the full integration guide

Trust boundaries · token scopes · admin overlays · operator hand-off.

Run it on a Pi today.

One BIP-39 mnemonic, a YubiHSM2, and a Raspberry Pi. Your strategy signs through a deny-by-default boundary and your keys never leave the device.