Skip to content

Repository files navigation

Deliverator

A single-binary Go CLI: the non-custodial execution + tracking layer an autonomous agent (OpenClaw) drives to trade and manage a Hyperliquid portfolio.

Codename ref: Snow Crash — the Deliverator. Fast, precise, no-BS.

OpenClaw is the reference autonomous trading agent that drives Deliverator; Deliverator itself is agent-agnostic — any LLM agent or script that speaks the CLI contract (deliverator tools / AGENTS.md) can drive it.

Deliverator is the safe harness you put between an autonomous agent and a Hyperliquid account. It owns the hard, dangerous parts once — EIP-712 signing, nonce management, precision rounding, builder attach, risk caps, rate-limit pacing — and exposes them as a disciplined, machine-parseable CLI. The agent decides what to trade; Deliverator makes sure the how is correct, bounded, and auditable.

Because it signs with a Hyperliquid agent/API wallet that physically cannot withdraw, the worst a confused or compromised agent can do is place bad trades — never drain funds. That non-custodial guarantee is the whole point.

No backend. No telemetry. scp the binary to a box and go.

⚠️ This places real orders with real money on a live exchange. init and onboard configure mainnet — switch to testnet (config set network testnet, or the console's network panel) for a paper-funds dry run first. The non-custodial key means a bug can't drain your account, but it can still place trades you didn't intend. Provided under the MIT license, with no warranty (LICENSE). You are responsible for what your agent does.


Target Use Case & Positioning

Deliverator is the execution and safety layer for autonomous agents — particularly LLM-driven ones — that trade on Hyperliquid. The intro above covers what it does; this section is about when to reach for it instead of the alternatives.

The problem it targets

When an LLM is in the decision loop, a handful of failure modes recur:

  • Hallucinated sizes, prices, leverage, or order types
  • Brittle retry logic that double-fills
  • Missing or inconsistent risk checks
  • Custody risk when full wallet keys are exposed to the agent

Deliverator is built to absorb exactly these. Most general-purpose Hyperliquid tooling wasn't.

Where other tools fit

Category Examples Primary strength Best for Limitations for autonomous agents
Low-level SDKs Official hyperliquid-python-sdk Full control, lightweight, official Custom development & deep integration No built-in safety, risk engine, or agent ergonomics
Persistent bot frameworks Hummingbot (Hyperliquid connector) Mature strategy engine + risk controls Rule-based / market-making bots Heavier for dynamic, decision-making agent loops
Human + scripting CLIs hyperliquid-cli (TS), similar community CLIs Nice TUI, real-time monitoring, JSON output Human traders + light automation Limited portfolio-level risk enforcement & agent contract
Agent execution layer Deliverator Non-custodial safety + machine-native contract LLM agents & autonomous trading loops Newer project (early 2026)

Choosing between them

  • Deliverator — you're running an autonomous agent (especially an LLM) that must trade real capital in a loop, with non-custodial keys and strong safety guarantees, and you don't want to babysit it.
  • Official SDK — you want maximum flexibility and are prepared to build your own safety, retry, and risk layers on top.
  • Hummingbot — you need persistent, strategy-driven automation (especially market making) rather than dynamic, decision-making agent loops.
  • Human-oriented CLI — the main user is a person at a terminal, or you only need occasional scripted automation.

Deliverator isn't trying to replace the Hyperliquid SDK or Hummingbot. It occupies a specific niche: a safe execution harness purpose-built for agents that trade real capital without supervision.


Why it exists

The caller is an LLM, not a human. So Deliverator is:

  • Deterministic & parseable — one JSON object per command (schema v1), NDJSON for streams.
  • Branchable — every command returns a documented exit code ($?), never relies on stdout text.
  • Idempotent via protocol — every write carries a cloid. The exchange does not reject a duplicate cloid (a blind resend places a second order), so the defined timeout-retry protocol — check order status by cloid before resubmitting — is what actually prevents double-fills.
  • Self-correcting — errors are actionable text with concrete numbers ("round px to 64000"), not opaque codes.
  • Bounded — hard risk caps enforced in core, not the surface. Switching invocation can't bypass them.
  • Self-describingmarkets, schema, tools, version let the agent discover capability at runtime.

The agent contract is the product. See TOOLS.md (also deliverator tools).


Install

Homebrew (macOS/Linux):

brew install --cask erickuhn19/tap/deliverator

Or with Go:

go install github.com/erickuhn19/deliverator@latest
# or build from source:
go build -o deliverator .

Building from source requires Go 1.25+. macOS/Linux (uses flock + the OS keychain).

Upgrading in place on macOS — replace the file, don't overwrite it

On macOS, copying a new binary over a running one invalidates its code signature, and every subsequent invocation dies instantly with SIGKILL — no error message, no envelope, nothing to diagnose from. This bites hardest when long-lived processes (deliverator serve, deliverator stream …) still have the old inode mapped, which is exactly the upgrade path an operator reaches for.

Replace the file so the new binary lands on a fresh inode, then re-sign:

rm -f "$(command -v deliverator)"          # fresh inode — do NOT cp over the old file
cp ./deliverator "$(command -v deliverator)"
codesign -f -s - "$(command -v deliverator)"

Restart any serve/stream processes afterwards: they keep running against the old mapped image until they do.


Verifying a download

Every release ships sha256 checksums.txt and SLSA build-provenance attestations — signed proof that each binary was built by this repo's CI from a specific commit (so a tampered or substituted binary won't verify). To check a downloaded archive:

gh attestation verify deliverator_<version>_<os>_<arch>.tar.gz --repo erickuhn19/deliverator

brew install enforces integrity automatically via the cask's pinned sha256.


Quickstart

Easiest — guided setup (creates your account via the referral link for a fee discount, then takes your API wallet key and configures everything):

deliverator onboard

Or set up manually:

# 1. Generate a fresh agent key locally (stored in the OS keychain) + a config template.
deliverator init
#    → prints the agent ADDRESS and the approval steps.

# 2. In the Hyperliquid web UI, approve that agent address (API → approve agent).
#    Then point Deliverator at your MASTER address (the query target):
deliverator config set wallet.master_address 0xYOURMASTER...

# 3. Preflight.
deliverator connect          # key, account, network, clock skew, API, meta age

# 4. Read state (one call = full snapshot).
deliverator portfolio --json

# 5. Place a bounded, idempotent order (preview first).
deliverator buy BTC 0.01 --limit 64000 --alo --dry-run
deliverator buy BTC 0.01 --limit 64000 --alo --cloid 0x...   # for real

# 6. Safety.
deliverator dms set 60       # arm the dead-man's switch (refresh via cron: dms heartbeat)
deliverator halt on          # emergency stop — rejects all new orders
deliverator panic --yes      # cancel-all + flatten-all (works during a halt)

init and onboard ship the opinionated default: mainnet. For a paper-funds dry run first, switch with deliverator config set network testnet (or the console's network panel) — changing the network warns loudly either way.


The agent contract

Every command emits one JSON envelope:

{
  "schema": "v1",
  "ok": true,
  "ts": 1750000000000,
  "cmd": "buy",
  "data": { "cloid": "0x..", "status": "resting", "oid": 123, "coin": "BTC", "size": "0.01", "limit_px": "64000" },
  "error": null,
  "warnings": ["size rounded 0.0123456 -> 0.0123"],
  "meta": { "network": "testnet", "account": "main" }
}

Prices and sizes are always strings. On failure, ok=false, data=null, and error is populated with {code, category, message, retryable, retry_after_ms, hint} plus, on write failures, cloids — the client order id(s) the action was signed with (all legs for a batch/grid), even when auto-generated — so the retry protocol below is always runnable.

Two optional read-health fields (additive, omitted when clean) mark a tolerant read that succeeded with incomplete data: degraded_dexs: ["<dex>"] — a HIP-3 sub-dex's state/orders were unreadable this call, so anything held there is missing from the response, not gone (never treat that absence as closed/flat; retry) — and truncated: true — a paged history read (fills --since, funding, ledger) stopped with more rows available (its safety cap, or a same-millisecond burst larger than one exchange page). Both always come with a matching warning.

Exit codes

Code Meaning Agent action
0 success proceed
10 validation (bad args / unknown coin) fix inputs
11 precision rejected (--strict) re-round and retry
20 risk-rejected (cap/allowlist/limit-only/leverage) respect the cap
21 halted stop trading
30 auth/key error operator fixes the key
40 network/unreachable retry w/ backoff
41 rate-limited back off (retry_after_ms)
42 timeout (outcome unknown) run the retry protocol — don't blind-resubmit
50 exchange-rejected read message, adjust
60 partial fill inspect fills, decide
70 clock skew outside nonce window fix the clock

Retry protocol (critical)

On exit 42, run deliverator order status --cloid <id>. If the order exists → it landed, don't resend. If absent → resubmit the same cloid. This is the #1 way naive agents double-fill.

The cloid(s) to check are carried in the failure envelope's error.cloids. Exit 42 covers every failure after the signed action may have reached the exchange (timeouts, connection resets, EOFs, unreadable responses, and gateway 5xx pages from an edge/proxy in front of the API); a failure before anything was sent (connection refused, DNS) is exit 40 — retrying that is safe. Exit 50 is reserved for a definitive exchange rejection.

The exchange can take ~1–2s to index a new order by cloid, so a status check immediately after a timeout may report "absent" for an order that actually landed. Wait briefly and re-check (or query by --oid) before resubmitting.

order status --cloid only reports "absent" from a live open-orders sweep that read every dex successfully; if the sweep is degraded (e.g. a sub-dex 429) and the order isn't found on a readable dex, it returns a retryable exit 40 (order_status_unconfirmed) instead of a confident answer — re-check when reads recover, don't resubmit.


Safety model

Guard Config Behavior
Coin allowlist automation.allowed_coins reject non-listed (20); empty = allow all
Max order notional risk.max_order_notional_usd reject (20)
Max position notional risk.max_position_notional_usd reject (20); per-coin, counts position + resting non-reduce-only orders (worst-case adds)
Min order notional risk.min_order_notional_usd reject sub-minimum orders pre-flight (10); default $10, mirrors HL's floor
Limit-only automation.limit_only block market orders and --trigger-market orders (they execute as market when fired) (20); exits/reduce-only exempt
Max leverage risk.max_leverage cap leverage changes (20)
Account leverage risk.max_account_leverage reject if resulting gross notional / equity exceeds it (20)
Net exposure risk.max_net_exposure_usd reject if resulting |long − short| exceeds it at the per-direction worst case (20) — resting non-reduce-only orders count on their own side, never offset
Per-coin concentration risk.max_concentration_pct_per_coin reject if one coin exceeds that % of equity (20)
Drawdown risk.max_drawdown_pct reject new exposure once equity is that % below its high-water (20); anchors keyed per network+account
Daily loss risk.max_daily_loss_usd / _pct reject new exposure once loss since UTC-midnight anchor exceeds it (20); anchors keyed per network+account
Max open positions risk.max_open_positions reject opening a new coin once at the concurrent-position cap (20); a coin with only a resting non-reduce-only order counts
Reduce-only guard (always on) reject a reduce-only order that cannot reduce: no open position, same side as the position, or larger than it (20)
Local rate cap automation.max_orders_per_min throttle before the exchange limit
Global halt deliverator halt on reject all new orders (21); panic still works — its flatten bypasses the halt core-internally (no flag), plain close/orders stay rejected
Dead-man's switch risk.dead_man_switch_secs schedule-cancel (resting orders only — positions stay open); refresh via dms heartbeat
Real-time failsafe deliverator watch --metric liq_distance_pct --below N --action panic|dms|alert stream-driven: trigger the action the moment the metric breaches — catches a mid-interval move the DMS/heartbeat can't
Dry-run --dry-run validate/round/attach, never send

The bold account-wide gates bound new exposure: reduce-only/close orders are exempt — including a batch/grid --reduce-only whose every leg is reduce-only (one exposure-adding leg keeps the whole batch gated) — so a tripped gate never blocks de-risking.

modify/modify-batch are gated too: a modify that grows an order's worst-case exposure (larger size, or larger size×limit notional) passes the per-coin caps and every account-wide gate, evaluated with the old order replaced by the new one (a routine re-price never double-counts itself); a modify that shrinks or holds exposure is de-risking and stays exempt, so a tripped gate never blocks shrinking. A small compliant order cannot be modified up past a cap.

All notional/portfolio caps count resting (unfilled) non-reduce-only orders as worst-case future exposure — sequential orders cannot ladder past a cap by each looking small against the filled book; reduce-only resting orders (TP/SL) count zero. When risk.max_position_notional_usd is set, each gated write costs at most one extra open-orders info read (weight 20) — the per-coin cap and the portfolio gates share a single fetch. If the account state a configured cap needs cannot be read (position, resting orders, equity — e.g. a 429 burst or a silently-degraded sub-dex read), an exposure-adding order fails closed with a retryable exit 40 (back off and retry — not an exit-20 cap breach), while reduce-only orders still pass: they carry reduce-only on the wire, so the exchange guarantees they only reduce. The drawdown/daily-loss anchors persist per network+account (a testnet peak never gates mainnet; accounts never share a daily anchor); the first gated write of a new network+account context anchors fresh at current equity and emits a warning, since fresh anchors under-protect until they move. Legacy unkeyed risk_state.json files from older versions are ignored (left on disk).

Alerting: set alerting.webhook_url (or DELIVERATOR_ALERT_WEBHOOK) to POST a JSON event on RED-state failures (halt/auth/timeout by default; add risk etc. via alerting.categories) — best-effort, never blocks the command. Wire it to Slack/Discord/a relay so an away operator hears within seconds.

The threat model is an LLM hallucinating a size, price, or leverage. The CLI is the only enforcement point — every value is treated as hostile until checked.


Wallet model

Wallet Funds Signs Where
Master yes approveAgent, approveBuilderFee, deposit/withdraw Browser/hardware — never here
Agent / API no orders, cancels, modifies, leverage, margin, schedule-cancel Deliverator (keychain)
  • Reads use the master address. Querying the agent address returns empty — the canonical bug.
  • The agent key lives in the OS keychain by default — add it with deliverator onboard (import your API wallet key) or deliverator init (generate one). For headless/CI hosts with no keychain, set DELIVERATOR_AGENT_KEY to inject the key; it's used only when set and otherwise ignored, so an unset/empty env can never hide the keychain key. There is no stored agent_key_source config (that indirection was the original "key looks deleted" bug). If no key is available, every write fails with auth/no_agent_key and a hint to run onboard.
  • Rotate with deliverator init (fresh address). Never reuse a deregistered agent address.
  • Deposits/withdrawals/transfers are out of scope by design — that exclusion is the guarantee.

Commands

Setup: init · connect/health · version · config [get|set|path] · account [add|ls|rm|set-default] · markets · schema · tools

Track (reads): snapshot (one-call tick: portfolio + limits + ctx[coins] + builder) · portfolio · positions · orders · order status · fills · funding · ledger · balance · pnl · book · bbo · mids · candles · ctx (perp + spot; carries impact_pxs for slippage) · builder status · referral status · limits · predicted-fundings (forward funding-carry signal) · historical-orders (closed-order lifecycle) · twap status (running TWAPs + slice fills) · leaderboard (official HL trader leaderboard — filter/sort/drill-down to find an address to copy) · reconcile (diff local audit vs live; run first after a restart) · preview (what-if: resulting leverage/margin/liq for an order, no signing) · info <type> [k=v] (raw passthrough to any HL info endpoint)

positions/portfolio also carry computed risk fields: distance_to_liq_pct per position, and account maintenance_margin + margin_ratio (divided by the equity that can actually back perp margin: the gates' unified-account equity on a unified account, the perp wallet(s) alone on a non-unified one — idle spot USDC cannot meet a perp margin call).

Submit (writes):

  • Orders: buy/sell/order — market, limit, IOC (--ioc), post-only (--alo), trigger (--trigger); --tp/--sl places a linked OCO bracket (one grouped normalTpsl action — a filled TP cancels the resting SL).
  • Many-at-once: batch (a JSON array of orders) · grid (a limit ladder) · modify-batch — each is one signed action (one nonce, atomic pre-flight).
  • Manage: modify · cancel (by --oid/--cloid, the --oids/--cloids lists, or --all [--coin]) · close (flatten a perp position or sell a spot holding) · position-tpsl (reduce-only TP/SL attached to a whole position) · chase (BBO-pegged passive-maker limit that re-prices as the book moves) · leverage · margin · twap (+ twap cancel / twap status).
  • Account: referral apply · onboard.
  • Find + mirror a leader: leaderboard screens the official HL leaderboard (filter by window PnL/ROI/volume/account-value, --profitable-in day,week,month for consistency, --sort, paging) → pick a data.rows[].addresscopy <leader> — non-custodial copy-trading; diff (read-only) by default, --execute --yes routes legs through the guarded order path (all risk gates apply). Stateless: pass --mirrored, persist data.mirrored_now.
  • Safety: dms · halt · panic · watch (real-time failsafe: evaluate a live risk metric, trigger alert/dms/panic on breach — the reactive counterpart to the DMS).

Stream (live NDJSON): stream book|bbo|trades|candles|fundings|active-asset|mids|fills|orders|webdata2|twap-fills|events

Run deliverator <cmd> --help for flags. Config lives at ~/.config/deliverator/config.toml (see deliverator config path).


Outcome Market Maker (outcome-mm)

A second binary in this module, outcome-mm, is a specialized market maker for HIP-4 outcome (prediction) markets. It consumes internal/core in-process — the same guarded client the CLI drives — so every quote passes the identical risk gauntlet (pre-trade checks, portfolio gates, plus two new outcome-specific gates) and signs with the same non-withdraw agent key. It is a long-lived daemon; run it under launchd/systemd with restart-on-crash, not cron.

go build -o outcome-mm ./cmd/outcome-mm
outcome-mm run                 # foreground TUI dashboard
outcome-mm run --headless      # daemon: no TUI, periodic JSON status on stdout
outcome-mm run --dry-run       # shadow: compute + render quotes, never sign

It never signs by default. A fresh install runs in shadow; live quoting requires the operator to explicitly set mm.enabled = true and mm.dry_run = false (and not pass --dry-run). Start on testnet or --dry-run first.

What it does each cycle: a slow selector ranks the daily-rotating outcome universe into a small active set (liquidity + spread-opportunity + model-confidence score, with hysteresis and per-underlying/expiry diversification); a fast loop prices each active market with a Black–Scholes-digital fair value off the underlying's live mark and EWMA realized vol, builds an inventory-skewed two-sided ladder, diffs it against the resting book, and applies the minimal place/modify/cancel as aggregated signed actions (staying inside max_orders_per_min). A cross-book YES/NO arb scanner and a blackout + hold-to-settle policy round it out. Direction is handled by inventory skew + small size, not a hedge (perp delta-hedge is v1.1; an LLM fair-value layer for event markets is v2).

Two new core gates back it (they apply to every invocation surface, not just the MM): risk.outcome_settle_blackout_mins refuses new outcome exposure near expiry (and always on a settled market), and risk.max_outcome_question_notional_usd caps total notional across all legs of one question (the Yes/No pair of a binary counts as one bet). Both default off.

Config lives in the same config.toml under an [mm] table (selection weights, spreads, ladder shape, TTL band, blackout window, pins/blacklist, priceable_underlyings). Set keys with deliverator config set mm.<key> <value> or edit them live in the TUI.

[mm]
enabled = false          # master switch for live quoting
dry_run = true           # shadow even when enabled; flip to false to sign

[mm.selection]
max_active_markets = 6
priceable_underlyings = ["BTC", "ETH", "HYPE"]
min_ttl_mins = 30

[mm.strategy]
base_spread = 0.02       # half-spread in probability units
levels = 3

[mm.settle]
blackout_mins = 15       # stop quoting this long before expiry; hold to settle

The dashboard (internal/tui/mm) shows the active markets (fair vs mid, your quotes, inventory, gate status), the ranked candidate pool with include/exclude reasons, account/risk, a PnL split, and the activity feed; p pauses, ! panics, +/-/b/P edit selection live. See the build spec for the full design.


Development

go test ./...                 # signing parity, risk gauntlet, engine, precision, envelope, nonce
go test -race ./...           # the suite is race-clean
go vet ./...
scripts/check-coverage.sh     # per-package coverage floors
go build -o deliverator .

Architecture: cmd/ is a thin cobra adapter; all correctness and safety live in internal/core, which drives internal/hl (the from-scratch Hyperliquid API client + EIP-712 signer). internal/output owns the schema-v1 envelope + exit codes + error catalog; internal/config the TOML; internal/wallet key sources; internal/state the nonce flock + audit log.

Client: a native internal/hl package talks directly to the Hyperliquid API — EIP-712 action signing ported from the official Python SDK, no third-party SDK.

See CONTRIBUTING.md for the architecture invariants a change must hold (thin surface, golden-vector signing, coverage floors, no secrets).


How this is funded

Deliverator is free and MIT-licensed. It's funded by an optional builder fee0.05% (5 bps), on by default — routed to the project's Hyperliquid builder address. It is graceful and non-custodial: the fee is only ever charged once you sign the one-time approveBuilderFee with your master wallet (the agent key can't — that's the whole guarantee). Until you approve it, every order trades fee-free — you're never blocked, and never charged without consent. Spot buys never carry it (Hyperliquid takes the taker fee in the base token).

If Deliverator is useful to you, approving the fee is the easiest way to support its development. To opt out or self-host, repoint builder.address at your own builder or set builder.attach_mode = "manual". New accounts also get a referral fee discount via deliverator onboard.


License & security

  • License: MIT — see LICENSE. Provided as-is, with no warranty.
  • Security: found a vulnerability? Please report it privately — see SECURITY.md. Do not open a public issue.
  • Contributing: CONTRIBUTING.md.

About

Non-custodial Hyperliquid execution + tracking CLI for autonomous LLM agents. Signs EIP-712 actions with an agent wallet that cannot withdraw funds.

Topics

Resources

Contributing

Security policy

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages