The machine-readable contract lives in openapi/zero-paper-api.v1.yaml. Compatibility rules live in docs/api-compatibility.md.
The first public API contract is deliberately small. It describes the behavior that must remain stable while the private engine is ported into the public runtime.
Defines hard local safety limits:
max_notional_usdmax_position_notional_usdmax_leveragemin_confidence
All values must be positive. min_confidence must be less than or equal to 1.
Represents a requested order:
symbolsidequantitypriceconfidencereduce_only
reduce_only=true means the order can only reduce risk. The public safety
contract allows reduce-only orders even when confidence is low.
Returned by safety evaluation:
allowedreason
The reason must be human-readable and stable enough for tests, logs, and CLI rendering.
Evaluates a proposed order before paper fill.
Rejects when:
- confidence is below
limits.min_confidence - order notional exceeds
limits.max_notional_usd - projected position notional exceeds
limits.max_position_notional_usd
Allows when:
- order is reduce-only
- all risk checks pass
Computes the paper position that would exist if the order were accepted.
Evaluates the intent, records rejected orders, records accepted fills, and updates paper positions.
The method must not require exchange credentials or network access.
submit(intent, source="manual") returns a RiskDecision and appends a
DecisionRecord to engine.decisions.
Inspectable paper decision log entry:
intentdecisionas_ofsource
Use to_dict() for JSON output in examples, tests, and CLI inspection. Every
paper decision should name its source, such as manual, scenario:<name>, or
strategy:<name>.
PaperEngine accepts an optional clock callable so tests and examples can
produce deterministic as_of timestamps.
zero-paper-api starts a paper-only HTTP server on 127.0.0.1:8765.
It requires no secrets and implements the CLI-facing subset of the engine
contract:
GET /,/health,/v2/statusGET /positions,/risk,/briefGET /regime,/decision/stack,/evaluate/{coin},/pulse,/approaching,/rejections,/journalGET /metrics,/immune,/memory,/genesis,/evolve,/research,/audit/exportGET /deployment/claim,/deployment/heartbeat,/network/profile,/network/leaderboardGET /intelligence/snapshot,/intelligence/catalog,/intelligence/commercial,/intelligence/model-gatewayGET /v1/intelligence/snapshots,/v1/intelligence/history,/v1/intelligence/cohorts,/v1/intelligence/benchmarksGET /hl/status,/hl/account,/hl/reconcile,/market/quoteGET /live/preflight,/live/cockpit,/live/certification,/live/receipts,/live/evidenceGET /operator/statePOST /executePOST /auto/togglePOST /operator/eventsPOST /network/publishPOST /network/ingestPOST /intelligence/exportPOST /v1/intelligence/webhooks,/v1/intelligence/exportsPOST /live/heartbeat,/live/pause,/live/resume,/live/kill,/live/flatten
POST /execute runs through PaperEngine.submit, records a decision with
source api:/execute, and returns simulated=true by default. When the caller
sends X-Zero-Mode: live, the same endpoint routes to the optional live
executor instead. If no live executor is configured, the engine returns
accepted=false, simulated=false, and reason="live executor not configured".
It honors the request idempotency key so repeated submissions with the same key
do not create duplicate paper fills or duplicate live order submissions.
Live responses include public-safe request_hash and receipt_hash fields
when the live executor records the attempt; the raw idempotency key remains
local to the response and is excluded from public evidence packets.
The interactive CLI command /execute <coin> <buy|sell> <size> uses this same
endpoint after the operator-state friction ladder clears. Non-interactive
zero run refuses risk-increasing execution commands because the typed-confirm
surface is TTY-only.
Every HTTP response carries X-Zero-Trace-Id. When an HTTP POST /execute
creates a paper decision, that trace ID is written into the decision journal
and echoed in the HTTP response. Idempotency replays return the original
decision trace, which keeps audit trails tied to the first accepted action.
GET /journal?limit=50 returns the most recent paper decisions in the same
shape as DecisionRecord.to_dict(). When zero-paper-api --journal PATH is
used, records are read from the append-only JSONL journal at PATH; otherwise
the endpoint returns the in-memory decision log for the current process.
On startup with --journal PATH, zero-paper-api replays the journal before it
serves traffic. The replay restores paper decisions, fills, open positions,
rejections, and idempotency keys, so a repeated POST /execute with an already
journaled key returns the original simulated response instead of creating a
duplicate fill. /health and /v2/status include a recovery object with the
journal mode, recovered counts, and current runtime counts.
GET /metrics returns JSON runtime counters for API requests, status codes,
execute outcomes, idempotency hits, decision counts, fill counts, rejection
counts, and recovery state.
GET /memory?limit=20 returns a zero.memory.snapshot.v1 public-safe view of
local memory. Without a configured memory store, the endpoint extracts an
ephemeral snapshot from current paper decisions. With a MemoryStore, it reads
active append-only memory entries and omits expired records. format=md also
returns generated knowledge.md content. Memory output is explicitly redacted:
no live prices, wallet material, exchange order ids, or private keys.
GET /genesis returns a zero.genesis.snapshot.v1 plan-only view of genesis
proposal classifications. It never applies code changes. The fixture-backed
snapshot demonstrates one accepted proposal, one rejected proposal with
insufficient sample size, and one escalated proposal touching protected live
execution paths. Protected execution, sizing, stops, circuit breaker, live
adapter, and immune-core proposals require human review.
GET /evolve returns a zero.evolve.snapshot.v1 paper-first view of the
builder, red-team, paper-canary, calibration, promotion, promotion-plan,
rollback-plan, and promotion-verification gates. It never mutates the checkout,
pushes a branch, deploys a service, or promotes a change. The fixture-backed
snapshot selects the accepted genesis docs/example proposal, materializes a
sandbox candidate tree, and proves that promotion remains local-only,
rollback-gated, and human-approved. Checkout mutation is intentionally excluded
from the HTTP API; local apply and rollback are CLI-only and emit
zero.evolve.apply_receipt.v1 plus zero.evolve.rollback_receipt.v1 receipts.
GET /research returns a zero.research.snapshot.v1 paper-only view of the
research command chain: hunt, edge, convergence, thesis, score, meta, and
sharpen. It never mutates the checkout, pushes a branch, deploys a service,
places an order, or claims live PnL. The fixture-backed snapshot exists so
agents can inspect what ZERO should study next before genesis/evolve gates are
used.
GET /runtime/parity returns zero.runtime.production_parity.v1. It runs the
bundled paper OODA loop, mirrors the same idempotency keys and intents through a
disabled live executor, proves the live shadow path refuses every order, emits
checksum-chained runtime-bus integrity, dry-run live certification status, and a
rejection/execution-quality feedback packet. It places no live orders and does
not claim live trading proof. The separate
docs/proof/live/live-trading-evidence.json packet carries redacted
operator-owned live evidence for public verification.
GET /decision/stack?coin=BTC returns zero.decision.stack.v1, the public
lens/layer/modifier shape behind paper evaluation. Lenses explain weighted
views, layers explain gates, and modifiers explain bounded confidence or
operator-friction adjustments. The public stack is read-only and always reports
allowed_to_execute_live=false; live authority remains with the local
self-custodial runtime and its preflight controls. GET /evaluate/{coin} keeps
the CLI-compatible fields and embeds the same stack as decision_stack.
GET /audit/export?limit=100 returns a structured zero.audit.v1 export with
runtime summary, retention/redaction metadata, metrics, recovery state, and the
most recent decisions. The public paper runtime records no secrets.
zero-engine-run --runtime-bus DIR writes checksum-chained local runtime events
to DIR/events.jsonl and a fast boot snapshot to DIR/state-snapshot.json.
The bus is not an HTTP API yet; it is the local event contract for OODA cycles,
decisions, fills, rejections, positions, health, and future operator commands.
See runtime-bus.md.
GET /deployment/claim returns a zero.deployment.claim.v1 public-safe,
signature-ready identity packet for the local runtime. It binds deployment
metadata, operator audit handle, aggregate evidence counts, and signature status
without including raw decisions, symbols, trace IDs, idempotency keys, wallet
material, or exchange credentials.
GET /deployment/heartbeat returns a zero.deployment.heartbeat.v1
public-safe liveness packet bound to the deployment claim hash. It exposes
paper-only, fresh, or expired dead-man state without exposing raw decisions,
trace IDs, idempotency keys, credentials, or private runtime details.
GET /network/profile returns a zero.network.profile.v1 public-safe profile
packet with aggregate behavior, verification badges, a proof hash, deployment
claim hash, deployment heartbeat hash, and privacy metadata. It excludes raw
decisions, trace IDs, idempotency keys, wallet addresses, exchange order IDs,
private notes, strategy source labels, and per-trade symbols. Publication is
disabled by default.
GET /network/leaderboard returns a zero.network.leaderboard.v1 local row
derived from the same redacted profile. The first leaderboard model ranks
verified process data such as decision count and rejection rate, not PnL
screenshots.
POST /network/publish requires {"consent": true} and
ZERO_NETWORK_PUBLISH_PATH. When both are present, the runtime appends the
redacted profile packet to a local JSONL proof log. It does not upload to a
ZERO-hosted service from the public runtime.
POST /network/ingest accepts one redacted profile packet or a list of
packets and returns zero.network.ingestion.v1. It is the public-safe
hosted-compatible validation contract for ZERO Network: profile publication
must be explicitly enabled, proof hashes must match recomputed aggregate
evidence, aggregate metrics must be internally consistent, duplicate accepted
handles/proofs are refused, and deployment claim/heartbeat hashes must bind
when present. The response includes accepted/refused records plus an
accepted-only leaderboard. It never requires raw journals, exchange
credentials, wallet material, trace IDs, idempotency keys, or per-trade
symbols.
GET /intelligence/snapshot returns a zero.intelligence.snapshot.v1 delayed
public intelligence packet derived from the verified ZERO Network profile. It
contains aggregate signals such as activity level, rejection discipline,
execution pressure, journal quality, and proof hash. It excludes raw decisions,
trace IDs, idempotency keys, per-trade symbols, credentials, and private notes.
GET /intelligence/catalog returns a zero.intelligence.catalog.v1 commercial
API contract. The contract makes the open-core rule explicit: the runtime,
paper mode, self-custodial operation, public profiles, public leaderboards, and
delayed snapshots are public; realtime feeds, history, cohorts, benchmarks,
webhooks, bulk exports, commercial redistribution, and SLOs are paid surfaces.
GET /intelligence/commercial returns
zero.intelligence.commercial.v1, the billing-ready hosted Intelligence API
contract. It names the open/commercial/not-sold boundary, bearer-token hosted
auth shape, plan scopes, datasets, endpoint usage events, rate-limit headers,
webhook delivery contract, export rules, reliability tiers, and privacy rules.
It is not enforced by the local open-source runtime and does not require
operator secrets, raw journals, custody transfer, or exchange credentials.
The /v1/intelligence/* endpoints are a hosted-compatible reference surface
for ZERO Intelligence API. They are intentionally small and runnable inside the
public paper server so contributors can build clients against the commercial
shape before the production warehouse exists:
GET /v1/intelligence/snapshotsserves delayed public snapshots without a token and returns realx-zero-ratelimit-*headers.GET /v1/intelligence/snapshots?freshness=realtime,/v1/intelligence/history,/v1/intelligence/cohorts, and/v1/intelligence/benchmarksrequireAuthorization: Bearer <token>whenZERO_INTELLIGENCE_API_TOKENis configured.POST /v1/intelligence/webhooksreturns a subscription record plus a verifiable HMAC-SHA256 signature fixture. It never returns signing key material.POST /v1/intelligence/exportsreturns a reference export job for aggregate JSONL or CSV data.
Reference env vars:
ZERO_INTELLIGENCE_API_TOKEN=...
ZERO_INTELLIGENCE_API_PLAN=team_fund
ZERO_INTELLIGENCE_API_ACCOUNT_ID=acct_...
ZERO_INTELLIGENCE_WEBHOOK_SIGNING_KEY=...
ZERO_INTELLIGENCE_STORE_PATH=/data/zero/intelligence.jsonl
These endpoints prove auth boundaries, scopes, usage events, rate-limit headers, webhook signing behavior, and durable aggregate reference persistence. They do not imply the local runtime is a hosted billing system or a production warehouse.
GET /intelligence/model-gateway returns zero.model_gateway.status.v1, a
public-safe provider and routing status packet. The default mode is
fail_closed: no configured provider means no model-derived certainty. Mock
providers report local_ready; explicitly configured external providers report
external_ready. All model output is advisory-only and never bypasses
execution safety. Provider status also reports bounded retry and timeout policy;
usage counters include attempts, token counts when available, and public-safe
cost-estimate source.
GET /intelligence/model-gateway/health returns
zero.model_gateway.health.v1. By default it is a config-only health probe and
does not make network calls. Passing ?network=true runs an explicit structured
provider probe through the selected model boundary and returns only provider,
attempt, token-count, and status metadata.
GET /intelligence/model-gateway/audit returns
zero.model_gateway.audit.v1, a production-readiness bundle for model
operations. It includes status, config-only health, usage totals, fail-closed
controls, evidence requirements, and privacy assertions without prompts, raw
outputs, headers, request IDs, or secret values.
POST /intelligence/export requires {"consent": true} and
ZERO_INTELLIGENCE_EXPORT_PATH. When both are present, the runtime appends the
redacted delayed snapshot to a local JSONL packet log for future hosted
ingestion. It does not upload to a ZERO-hosted service from the public runtime.
GET /live/preflight returns a structured zero.live_preflight.v1 readiness
packet for the Hyperliquid live executor. It never accepts private keys over
HTTP. The runtime reads local process configuration only, redacts key
diagnostics, verifies account-read access when a wallet address and read adapter
are present, validates a dry-run order locally, and refuses live mode unless
custody, journal, risk limits, and emergency controls are all present.
Live execution is local opt-in. Install the optional dependency group and start the API with process-local credentials:
pip install -e "engine[live]"
ZERO_LIVE_EXECUTION_ENABLED=true \
ZERO_HYPERLIQUID_WALLET_ADDRESS=0x... \
ZERO_HYPERLIQUID_API_PRIVATE_KEY=0x... \
ZERO_LIVE_MAX_NOTIONAL_USD=1000 \
ZERO_LIVE_MAX_DAILY_LOSS_USD=250 \
ZERO_LIVE_MAX_ORDERS_PER_MINUTE=6 \
ZERO_LIVE_DEAD_MAN_TIMEOUT_S=30 \
zero-paper-api --journal .zero/decisions.jsonl --hyperliquid-live-pricesThe live executor enforces:
- deterministic idempotency keys and exchange client order IDs;
- no automatic retry on order-submission POSTs;
- exchange dead-man heartbeats through
POST /live/heartbeat; POST /live/pauseand/live/resumefor entry control;POST /live/killfor kill switch plus open-order cancellation;POST /live/flattenfor reduce-only close orders;- max notional, max daily loss, and max orders per minute.
GET /live/certification returns a dry-run zero.live_certification.v1
evidence packet. It runs fake-exchange drills for heartbeat, idempotency,
exchange outage, pause, reduce-only flatten, kill, rejected dead-man scheduling,
rate limit, and daily-loss behavior. It never needs secrets and must report
orders_placed_live=0.
GET /live/cockpit returns a consolidated zero.live_cockpit.v1 operator
packet. It joins preflight failures, immune breakers, reconciliation status,
dry-run certification, heartbeat expiry, recent live records, operator actions,
the resolved zero.operator_context.v1 audit identity, and the next required
action. It is read-only and safe to expose in diagnostics.
GET /live/receipts returns zero.live_execution_receipts.v1: a local,
public-safe receipt bundle for live executor attempts. Each receipt includes
the exact order intent plus hashes for the request, operator context, trace
token, idempotency token, and venue acknowledgement. It never includes wallet
material, exchange credentials, raw venue responses, trace tokens, or raw
idempotency tokens.
GET /live/evidence returns a public-safe zero.live_evidence.v1 packet for
supervised tiny-capital canary rehearsal. It hashes preflight, cockpit,
live execution receipts, reconciliation, immune, certification, audit export,
deployment claim, and deployment heartbeat artifacts instead of embedding raw
private data. Set
ZERO_LIVE_EVIDENCE_SIGNING_KEY to attach a local HMAC-SHA256 signature without
echoing key material.
GET /live/canary-policy returns zero.live_canary_policy.v1: the public
policy lifecycle for supervised live canary evidence. It reports readiness,
policy arm/disarm state, launch-window rules, required evidence, shadow review,
qualification, follow-through status, whether refusal evidence is qualified,
whether accepted live evidence is publishable, and the next recommended
operator action. It never arms live risk by itself.
scripts/live_canary_rehearsal.py URL --mode refusal is the maintained local
collector for the canary path. It captures preflight, heartbeat, cockpit,
certification, reconciliation, fail-closed live execute evidence when the
engine is not ready, receipts, live evidence, metrics, audit export, manifest,
and SHA256SUMS. --mode canary can submit a real live order only after an
explicit confirmation string and ready live gates.
scripts/live_canary_verify.py DIR verifies the local bundle before it is used
as launch evidence. It recomputes SHA256SUMS, checks required packets and
status codes, compares manifest receipt/evidence fields to the packet payloads,
and fails on common unredacted trace/idempotency/token shapes.
scripts/live_canary_exchange_evidence.py DIR hyperliquid-export.json attaches
public-safe exchange-side evidence to the same canary bundle. It hashes the raw
source file, hashes raw venue identifiers, normalizes only symbol/side/quantity
and optional price, matches accepted ZERO receipts by symbol/side/quantity, and
refreshes SHA256SUMS. scripts/live_canary_verify.py DIR --require-exchange-evidence then fails unless the packet is present and every
accepted ZERO receipt has exchange-side evidence.
scripts/live_canary_operator.py URL --mode refusal wraps the full public-safe
operator workflow: collect rehearsal bundle, attach exchange evidence, run the
verifier, embed the live canary policy, and write operator_report.json. For
accepted live canary receipts, the operator workflow refuses to produce a
passing report unless a matching Hyperliquid order/fill export is attached and
the policy qualifies the report.
scripts/live_canary_operator_verify.py DIR independently verifies the
operator workflow directory. It checks recursive SHA256SUMS, operator-report
privacy flags, common redaction leaks, accepted-live exchange-evidence rules,
the embedded policy, and the nested canary bundle verifier.
scripts/live_cockpit_drill.py URL collects the read-only live cockpit stack
into a public-safe drill bundle. It captures /health, /v2/status,
/live/preflight, /live/cockpit, /immune, /hl/reconcile,
/live/certification, /live/receipts, /live/evidence,
/live/canary-policy, /metrics, and /audit/export?limit=100, then writes
manifest.json and SHA256SUMS. In public paper mode it fails unless live
readiness remains fail-closed.
scripts/live_cockpit_drill_verify.py DIR independently verifies a captured
bundle by recomputing checksums, checking packet schemas, replaying the
manifest summary from packet payloads, and enforcing redaction rules.
scripts/live_cockpit_drill_tamper_rehearsal.py DIR proves the verifier
rejects both checksum drift and semantic cockpit tampering before the bundle is
used as operator-readiness evidence.
GET /operator/context returns the operator audit identity currently attached
to requests. The engine resolves it from X-Zero-Operator-* headers,
ZERO_OPERATOR_* environment variables, or the local default. Live control
responses and /audit/export include the same packet so incidents can be
reviewed per operator without recording secrets.
GET /immune returns the zero.immune.v1 breaker packet used by live
preflight and the CLI. It names each risk-blocking breaker, whether new risk is
allowed, and the evidence behind open breakers. Risk-reducing live controls
remain available even when /immune reports risk_increasing_allowed=false.
Public Railway paper deployments should not set live credentials. Their
expected behavior is ready=false, live_mode=refused, and POST /live/*
returning ok=false with reason="live executor not configured".
GET /hl/status returns disabled metadata by default. When zero-paper-api --hyperliquid is used, it queries Hyperliquid's public info endpoint for
read-only mids and returns secrets_required=false. This endpoint must not
place orders, sign payloads, or require exchange credentials.
GET /hl/account returns a normalized zero.hl_account.v1 snapshot for the
configured ZERO_HYPERLIQUID_WALLET_ADDRESS: redacted wallet id, account
value, margin used, withdrawable balance, open positions, and open orders. It
uses read-only Hyperliquid info calls only.
GET /hl/reconcile returns a zero.reconciliation.v1 packet comparing local
runtime positions with the Hyperliquid account snapshot. Status values include
ok, not_configured, stale_data, local_lag, exchange_rejection, and
critical_mismatch. Live risk-increasing POST /execute calls are refused
unless reconciliation reports risk_increasing_allowed=true; reduce-only
controls remain available for risk reduction.
GET /market/quote?symbol=BTC returns the price source currently feeding paper
mode. By default it returns deterministic fixture prices with source=paper:static.
When zero-paper-api --hyperliquid-live-prices is used, it returns cached
Hyperliquid mids with source=hyperliquid:allMids; POST /execute,
GET /evaluate/{coin}, and GET /positions use the same quote path. Missing
symbols or unavailable live market data fail closed instead of falling back to
fixtures.
Shared JSON fixtures live in contracts/paper-api/. Python tests assert the
local paper API emits these exact payloads, and Rust client tests deserialize the
same files into zero-engine-client models. Any endpoint change that affects
the CLI contract should update the fixture and both test expectations together.
The fixture set currently covers:
GET /v2/statusGET /positionsGET /riskGET /briefGET /rejectionsPOST /executeaccepted and rejected responses
Loads a deterministic paper scenario from JSON.
Required behavior:
modemust bepaper.- At least one order must be present.
- Symbols are normalized to uppercase.
- Side values are parsed as
buyorsell.
The public examples use this path so contributor demos are data-driven and easy to extend without touching engine code.
Immutable OHLCV candle:
symboltsopenhighlowclosevolume
Validation keeps public fixtures honest: prices must be positive, high/low must bound open and close, and volume must be non-negative.
Protocol for deterministic market data:
candles(symbol, limit=None)latest(symbol)
Adapters must return candles in chronological order and must not require secrets for paper examples.
Loads newline-delimited JSON candles from disk. This is the public template for future adapters because it is deterministic, reviewable, and CI-friendly.
Paper-only proposal object:
symbolsideconfidencequantityreason
A signal is not a fill. It must be converted to an OrderIntent and submitted
through PaperEngine.submit.
Protocol for paper strategies:
namepropose(market, symbol)
Strategies receive a MarketDataAdapter and return either a StrategySignal or
None.
Minimal deterministic template. It proposes a buy signal when the latest candle
closes above its open by at least min_move_pct.
Converts a strategy proposal into an OrderIntent priced at the latest candle
close. The returned order still needs safety evaluation.
- Additive fields are preferred over breaking changes.
- Reasons used by tests should not be renamed casually.
- Paper-mode behavior must stay deterministic.
- Live adapters must not change paper-mode defaults.