05 — Interface Contracts
Four contracts hold the system together. They are frozen: anything behind them can be rewritten without cross-team coordination, and any change to them is a versioned, announced event. This is what lets one stream own a plane end to end.
Contract 1 — Inference API (OpenAI-compatible)
Owner: hunter-gateway · Consumers: terminal, agent, CLI, SDK, external
agents.
We deliberately implement an existing standard rather than invent one: every
OpenAI SDK works by changing base_url, which removes all client-library work
and makes migration to Hunter a one-line change for customers.
POST /v1/chat/completions # streaming and non-streaming
POST /v1/embeddings
GET /v1/models # + credits_per_1k_tokens, min_tier, available
Auth: Authorization: Bearer hk_live_… or an x402 X-PAYMENT header.
Hunter-specific extensions (additive, never breaking):
- /v1/models entries carry credits_per_1k_tokens, min_tier, available.
- X-PAYMENT-RESPONSE response header on settled x402 calls.
- 402 bodies carry {x402Version, error, accepts[]}.
Stability rules: public model ids (hunter-small, …) are permanent aliases
— the upstream model behind an id may change, the id may not. Upstream ids are
never exposed.
Contract 2 — Platform API
Owner: hunter-platform · Consumers: gateway, terminal, workers,
public.
GET /entitlements/{wallet} → {tier, source, expires_at} [PLANNED]
POST /api/v1/signals (service token)
POST /api/v1/signals/{external_id}/outcome (service token)
GET /api/v1/scoreboard PUBLIC
GET /api/v1/signals PUBLIC
/entitlements/{wallet} is the single point where "what may this wallet do" is
answered off-chain. Its shape is fixed because the gateway's resolver depends
on it; the derivation (which contracts, what thresholds) may change freely
behind it.
The public endpoints are public on purpose — an accuracy record only has value if anyone can audit it without asking us.
Contract 3 — MCP tool plane
Owner: hunter-mcps · Consumers: agent, workers, [PLANNED] premium
tier customers.
Every capability is an MCP server: one domain per server, streamable-HTTP or
stdio transport, declared in MCP_CONFIG:
{ "mcpServers": { "twitter": { "url": "http://mcp-twitter:8000/mcp" } } }
hunter-agent translates this into langchain-mcp-adapters connections at
startup and exposes every discovered tool to the graph. Adding a tool
requires no agent code change — that is the whole point of the boundary.
Normalised tweet shape (stable regardless of provider):
{id, text, author, author_name, created_at,
likes, retweets, replies, quotes, views, url, is_reply, lang}
Because consumers code against this shape and not against twitterapi.io's, the provider swap in ADR-003 touched one file.
Contract 4 — On-chain events and ABIs
Owner: hunter-contracts · Consumers: indexer, dashboards, supply server,
explorers, anyone.
// Access
event Locked(address indexed user, uint256 amount, uint64 start, uint64 end);
event Unlocked(address indexed user, uint256 amount);
event EarlyExit(address indexed user, uint256 returned, uint256 penalty);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event Subscribed(address indexed user, uint256 indexed planId,
address payToken, uint256 paid, uint64 paidUntil);
// Value accrual
event Distributed(address indexed token, uint256 buybackAmount,
uint256 stakingAmount, uint256 treasuryAmount);
event BuybackExecuted(address indexed tokenIn, uint256 amountIn, uint256 burned);
event RewardPaid(address indexed user, uint256 reward);
Read functions consumed off-chain: tierOf(address), totalLocked(),
balanceOf(0x…dEaD), totalSupply(), isActive(address), earned(address).
Contracts are not upgradeable (ADR-006), so these signatures are immutable for the life of a deployment. A change means a new deployment and a migration — which is the correct amount of friction for money.
Contract change policy
| Change | Allowed? | Process |
|---|---|---|
| Add an optional response field | Yes | Ship it |
| Add an endpoint / tool / event | Yes | Ship it, document it |
| Add a required request field | No | New version |
| Change a field's type or meaning | No | New version |
| Remove anything | No | Deprecate ≥1 release, then new version |
| Rename a public model id | Never | Ids are permanent aliases |
Versioning: gateway paths are /v1/…; the platform API is /api/v1/…. A
breaking change means /v2 served alongside /v1 until consumers migrate.