Docs/Introduction

Protocol documentation

Agents that buy, sell and pay onchain — under a policy you wrote

Strix Hood is an intent-based commerce layer for autonomous agents. An agent states what it wants; the protocol decides whether it is allowed, how it should be routed, and proves what happened. This is the reference for the intent format, the policy engine, the security model and the onchain contracts.

Protocol v1.4.2 API 2026-07-01 Chains 7 Status Public beta

01 — Overview

Introduction

Large models can already decide what to buy. They cannot be trusted with a private key. Strix Hood exists to close that gap: it accepts a declarative intent from an agent, checks it against a signed spending policy, simulates it against live chain state, auctions execution to competing solvers, settles it, and writes an attestation that can be audited later.

The problem

Every practical approach to agent-driven commerce today fails in one of three ways.

ApproachFailure modeConsequence
Give the agent a hot walletUnbounded authorityOne prompt injection drains the balance. No recovery, no recourse.
Human signs every transactionLatency and attentionDefeats autonomy. A 20-second approval loop loses the fill.
Custodial API brokerCounterparty risk, no proofYou trust an operator's ledger. Nothing is verifiable onchain.

All three collapse the same distinction: capability (the agent can produce a transaction) versus authority (the transaction is permitted). Strix Hood separates them. The agent produces intents. Authority lives in a policy that the agent cannot edit, committed onchain as a hash and enforced by the account's validator module at signing time.

The core promise

An agent holding a Strix Hood session key can spend only what the policy allows, only on the actions the policy names, only on the chains the policy lists, and only if the simulated asset diff matches what the intent claimed. Everything else reverts before it reaches the mempool.

0.25%Protocol fee on settled notional
~0.9 sp50 intent to inclusion on Base
5Independent enforcement layers
7Settlement networks

Who it is for

  • Agent developers shipping an autonomous trader, treasury manager, procurement bot or research agent that needs to move value without a human in the loop for every action.
  • Applications that want to offer "let the assistant do it" without becoming a custodian or building a policy engine, simulator and router themselves.
  • Solvers and market makers competing for agent order flow through the routing auction, and earning a share of the 0.25% fee.
  • Risk and compliance owners who need a signed, replayable record of why every automated transaction was permitted.

What it is not

Strix Hood is not a wallet, not an LLM, and not a custodian. It never holds user funds outside of the atomic settlement window, it does not generate the agent's reasoning, and it does not decide whether a trade is a good idea. It decides whether a trade is permitted and executes it well. Read Security model for the explicit list of risks it does not remove.

02 — Get running

Quickstart

This walkthrough gets a policy-governed agent from zero to a settled swap on Base Sepolia. It uses test keys throughout; nothing here touches mainnet value.

1. Install a client

The SDKs are thin, typed wrappers over the REST API. Every method in them maps to exactly one endpoint, so you can drop to raw HTTP at any point without losing behaviour.

npm install @strixhood/sdk
# or: pnpm add @strixhood/sdk / bun add @strixhood/sdk
pip install strixhood
# requires Python 3.10+
cargo add strix-hood --features rustls,stream

2. Create an API key

Keys are created in the console under Settings → API keys. Two prefixes exist and they are not interchangeable.

PrefixEnvironmentWhere it may be usedScopes
strx_sk_test_TestnetsServer side onlyAll
strx_sk_live_MainnetsServer side onlyGranted per key
strx_pk_live_MainnetsBrowser, mobile, agent runtimequotes:read, prices:read
Never ship a secret key into an agent runtime

An agent that can read its own sk_ key can create a new policy for itself. Keep secret keys on a server the model cannot reach, and give the agent runtime a pk_ key plus a scoped session key. See the five enforcement layers.

export STRIX_API_KEY="strx_sk_test_9f2c41bd7a084e6cb35d0e17"
export STRIX_ENV="testnet"

3. Write the policy first

Policies are created before agents, not after. An agent without a bound policy can be registered but cannot be issued a session key, so it can never sign anything.

{
  "name": "dca-conservative",
  "limits": {
    "per_tx_usd": 250,
    "daily_usd": 1000,
    "monthly_usd": 20000,
    "max_open_intents": 4
  },
  "allow": {
    "chains": ["eip155:8453", "eip155:42161"],
    "actions": ["swap", "transfer"],
    "tokens": ["USDC", "WETH", "cbBTC"],
    "venues": ["uniswap_v4", "aerodrome", "curve"]
  },
  "deny": {
    "categories": ["leverage", "gambling", "unverified_contract"]
  },
  "simulation": {
    "require_success": true,
    "max_price_impact_bps": 120,
    "min_liquidity_usd": 250000
  },
  "hitl": {
    "threshold_usd": 200,
    "channels": ["webhook"],
    "timeout_sec": 180,
    "on_timeout": "reject"
  },
  "expires_at": "2027-01-01T00:00:00Z"
}
curl -sS https://api.strixhood.xyz/v1/policies \
  -H "Authorization: Bearer $STRIX_API_KEY" \
  -H "Content-Type: application/json" \
  --data @policy.json | jq '{id, version, hash}'
{
  "id": "pol_01JQ8ZK3M2X7YB4N6PA0RTVCWD",
  "version": 1,
  "hash": "0x7d41a9c0b83e5f2d16c4a87b90ef3524ca1d6b8f0472e93a5c18df6027ab4e91"
}

4. Register the agent

Registration mints an Agent NFT Passport and locks a 2,500 $STRX bond. On testnets the bond is waived and the passport is minted on Base Sepolia.

import { Strix } from "@strixhood/sdk";

const strix = new Strix({ apiKey: process.env.STRIX_API_KEY! });

const agent = await strix.agents.create({
  name: "dca-eth",
  kind: "trader",
  policyId: "pol_01JQ8ZK3M2X7YB4N6PA0RTVCWD",
  chains: ["eip155:8453"],
  sessionKey: { ttlSeconds: 86_400, rotate: true },
});

console.log(agent.id, agent.smartAccount, agent.passport.tokenId);
// agt_01JQ8ZM7T4B0KC2N9XE5RVDHQP  0x1F3c…9aE2  #4182
import os
from strixhood import Strix

strix = Strix(api_key=os.environ["STRIX_API_KEY"])

agent = strix.agents.create(
    name="dca-eth",
    kind="trader",
    policy_id="pol_01JQ8ZK3M2X7YB4N6PA0RTVCWD",
    chains=["eip155:8453"],
    session_key={"ttl_seconds": 86400, "rotate": True},
)

print(agent.id, agent.smart_account, agent.passport.token_id)
use strix_hood::{Strix, CreateAgent, SessionKey};

let strix = Strix::from_env()?;

let agent = strix
    .agents()
    .create(CreateAgent {
        name: "dca-eth".into(),
        kind: "trader".into(),
        policy_id: "pol_01JQ8ZK3M2X7YB4N6PA0RTVCWD".into(),
        chains: vec!["eip155:8453".into()],
        session_key: Some(SessionKey { ttl_seconds: 86_400, rotate: true }),
    })
    .await?;

println!("{} {}", agent.id, agent.smart_account);

5. Submit the first intent

An intent is a statement of outcome, not a calldata blob. You never encode a router call, choose a pool or set a gas price — the solver auction does that, bounded by your constraints.

curl -sS https://api.strixhood.xyz/v1/intents \
  -H "Authorization: Bearer $STRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dca-2026-08-16-0900" \
  -d '{
    "agent_id": "agt_01JQ8ZM7T4B0KC2N9XE5RVDHQP",
    "action": "swap",
    "chain": "eip155:8453",
    "params": {
      "sell_token": "USDC",
      "buy_token": "WETH",
      "sell_amount": "150.00"
    },
    "constraints": { "max_slippage_bps": 40, "route_preference": "best_price" }
  }'
{
  "id": "int_01JQ8ZP1V6C3MD8R0YF2WKGSTA",
  "object": "intent",
  "status": "policy_check",
  "agent_id": "agt_01JQ8ZM7T4B0KC2N9XE5RVDHQP",
  "policy_version": 1,
  "estimated": { "buy_amount": "0.04129", "fee_usd": 0.375, "price_impact_bps": 6 },
  "created_at": "2026-08-16T09:00:00.412Z"
}

6. Follow it to settlement

Poll GET /v1/intents/{id} if you must, but the stream is authoritative and costs no rate-limit budget. Both surfaces emit the same status values.

const stream = strix.stream.executions({ agentId: agent.id });

for await (const evt of stream) {
  console.log(evt.status, evt.txHash ?? "-");
  if (evt.status === "settled") {
    console.log("filled", evt.fills[0].buyAmount, "WETH");
    console.log("attestation", evt.attestation.uid);
    break;
  }
}
What just happened

The intent was normalised, checked against policy version 1, simulated on a fork of the pending Base block, auctioned to three solvers, signed by a session key whose validator contains your policy hash, included, and attested. The full sequence is documented in Architecture.

03 — Vocabulary

Core concepts

Seven objects carry the whole protocol. Everything else in this documentation is a detail of how they interact.

Intents

An intent is a signed, expiring statement of a desired outcome — "end up holding at least 0.041 WETH, spending at most 150 USDC, on Base, within 90 seconds". It contains no calldata, no route and no gas parameters. That deliberate omission is what makes intents safe to hand to a language model: the worst an adversarial prompt can produce is a request that the policy engine rejects.

Intents are immutable once accepted. Changing terms means cancelling and submitting a new one. Every intent carries an idempotency_key; replaying the same key inside 24 hours returns the original intent rather than creating a second one.

Agents

An agent is the protocol-side identity of an autonomous actor. It owns an ERC-4337 smart account, one bound policy, zero or more session keys, a reputation score, and a $STRX bond that can be slashed. An agent is not a wallet: the smart account is controlled by the owner's root key, and the agent only ever holds a scoped session key with an expiry.

Agent kindTypical actionsDefault bond
traderswap, transfer, equity_order2,500 STRX
collectornft_bid, nft_buy, transfer2,500 STRX
treasuryswap, transfer, subscribe10,000 STRX
serviceagent_hire, transfer2,500 STRX
verifiedAny, plus marketplace listing25,000 STRX

The Agent NFT Passport

Registration mints an ERC-721 passport to the agent's owner. The passport is the portable record of what an agent is allowed to be, and it is the only object in the protocol that survives a full redeploy of the agent runtime.

  • IdentitytokenId is the canonical agent reference onchain; agent_id is its offchain mirror.
  • Dynamic metadata — level, lifetime settled notional, success rate and capability traits are re-rendered on every 1,000 settlements or on demand.
  • Permission traits — the passport records which capability modules are equipped (execution, data, payment, intelligence, security). Equipping a module raises specific policy ceilings; it never lowers a policy floor.
  • Revenue rights — for service agents, marketplace fees settle to the passport holder, so selling the NFT transfers the income stream.
  • Slashing surface — the bond is escrowed against the tokenId. A slashed passport keeps its history; the history is the point.
Transfer semantics

Transferring a passport revokes every live session key for that agent in the same transaction and forces a policy re-bind by the new owner. There is no window in which the previous owner's policy governs the new owner's funds.

The policy engine

A policy is a versioned document of limits, allow lists, deny lists, simulation thresholds and human-in-the-loop rules. It is evaluated offchain for speed and committed onchain as a bytes32 hash for enforcement. The two must agree: the session key validator recomputes nothing, but it refuses to validate a user operation whose attached policy hash is not the one the PolicyRegistry currently holds for that agent. Full schema in Policy engine.

Solvers and routing

Solvers are independent parties that compete to fill intents. When an intent clears policy and simulation, the router broadcasts a sealed request for quotes; solvers respond with a committed execution path and an output guarantee. The best quote by the intent's route_preference wins, and the winner is bound to its quote — under-delivering slashes 25% of the solver's bond and refunds the difference to the agent.

route_preferenceObjectiveTypical use
best_priceMaximise output after fees and gasDefault. Rebalancing, DCA.
fastestMinimise time to inclusionLiquidations, NFT snipes.
lowest_gasMinimise gas paidBatched maintenance work.
privatePrivate orderflow, no public mempoolSize that would be sandwiched.

Settlement

Settlement is atomic per intent. The winning solver's path is executed through SettlementVault, which enforces the output guarantee in the same transaction: if the agent would receive less than the quoted minimum, the whole call reverts. Protocol fees are taken from the output leg at 0.25% and split on settlement. Cross-chain intents settle as two locally atomic legs with a bonded relayer, never as an optimistic promise.

Reputation and slashing

Every settled intent updates two scores. Agent reputation is a decayed ratio of settled to submitted intents weighted by notional, and gates marketplace visibility. Solver reliability is the ratio of honoured to won quotes, and gates auction participation. Both are recomputed onchain at each epoch (7,200 blocks on Ethereum, daily elsewhere). Slashing conditions and amounts are listed in Slashing.

04 — How it runs

Architecture

One intent travels through eight stages. Four of them can terminate it. The diagram below is the authoritative flow; the table under it names the component that owns each stage and what it is allowed to do.

Human approval gate status: awaiting_approval 01 Received REST · SDK · WS 02 Parsed normalise · resolve 03 Policy check hash-bound rules 04 Simulation fork · asset diff Rejected 422 · reason recorded 05 Routing sealed solver auction 06 Execution session key · ERC-4337 07 Settlement atomic · fee split 08 Attestation EAS receipt · webhook happy path terminal rejection human escalation p50 ≈ 0.9 s on Base 01 Received REST · SDK · WebSocket 02 Parsed normalise · resolve symbols 03 Policy check hash-bound rules Human approval gate only above policy.hitl.threshold_usd 04 Simulation fork · asset diff · drainer scan Rejected 422 from stage 02, 03 or 04 05 Routing sealed solver auction 06 Execution session key · ERC-4337 07 Settlement atomic · 0.25% fee split 08 Attestation EAS receipt · webhook
Intent lifecycle. Stages 02, 03 and 04 are the only ones that can reject; stages 05 onward can fail but never silently change the terms the agent asked for.

Stage reference

StageOwnerDoesCan terminatep50
01 receivedAPI gatewayAuthenticates the key, enforces rate limits, deduplicates on Idempotency-Key, assigns a ULID.No3 ms
02 parsedIntent compilerResolves token symbols to canonical addresses per chain, normalises decimals, expands defaults, validates the schema.Yes — invalid_request_error6 ms
03 policy checkPolicy engineLoads the bound policy at its committed hash, evaluates limits, lists and rolling windows. Escalates to the human gate above hitl.threshold_usd.Yes — policy_violation4 ms
04 simulationSimulatorExecutes the candidate path on a fork of the pending block with state overrides. Produces a signed asset diff. Runs drainer, approval-sweep and honeypot detectors.Yes — simulation_failed118 ms
05 routingRouterSealed request for quotes to eligible solvers, 150 ms window, ranks by route_preference, binds the winner to its output guarantee.No — falls back to direct route176 ms
06 executionBundlerBuilds the ERC-4337 user operation, signs with the scoped session key, submits to the bundler or private relay.No41 ms
07 settlementSettlementVaultEnforces the minimum output onchain, takes the 0.25% fee from the output leg, splits it, and emits Settled.Reverts on shortfall1 block
08 attestationAttestorWrites an EAS attestation binding intent hash, policy hash, simulation digest, solver and receipt. Fires execution.settled.No92 ms

Latencies are p50 measured over the last 30 days on Base and exclude block time. The end-to-end budget from received to a broadcast user operation is 440 ms; anything slower than 1,200 ms trips an internal alert and the intent is re-quoted rather than executed on a stale price.

Failure modes

SymptomCauseProtocol behaviour
Solver wins then under-deliversAdverse move between quote and inclusionSettlement reverts. Solver forfeits 25% of bond, agent is refunded gas, intent is re-quoted once.
No solver respondsIlliquid pair or all solvers rate-limitedFalls back to the direct canonical route inside the same slippage bound. Marked route_fallback.
Simulation and execution disagreeState changed between fork and inclusionOnchain minimum-output check reverts the whole call. Intent moves to failed, funds never leave.
Human gate times outNo approval inside timeout_secon_timeout decides: reject (default) or hold until explicit action.
Chain reorg after settlementReorg deeper than the finality targetAttestation is marked reorged, webhook execution.reverted fires, balances re-derived from the canonical chain.
05 — Wire format

Intent specification

The intent object is the single input surface of the protocol. It is stable across REST, WebSocket and all three SDKs; the SDKs only change the casing convention.

The intent object

Top-level fields
FieldTypeRequiredDescription
idstringread onlyULID with an int_ prefix. Monotonic, so it doubles as a pagination cursor.
objectstringread onlyAlways "intent".
agent_idstringrequiredThe agent that will execute. Must be active and hold a live session key for chain.
actionenumrequiredOne of the seven values in Action types. Determines the shape of params.
chainstringrequiredCAIP-2 identifier, for example eip155:8453 or solana:5eykt4Us…. Must appear in policy.allow.chains.
paramsobjectrequiredAction-specific payload. Unknown keys are rejected rather than ignored.
constraintsobjectoptionalExecution bounds. Defaults come from the policy, never from the market.
policy_idstringoptionalOverrides the agent's bound policy. The override must be stricter on every axis or the request is rejected.
expires_attimestampoptionalRFC 3339. Defaults to created_at + 300 s. Maximum 24 hours; NFT bids may set up to 30 days.
simulate_onlybooleanoptionalRuns stages 01–05 and returns the quote and asset diff without signing. Default false.
idempotency_keystringrecommended≤ 128 chars. Also accepted as the Idempotency-Key header. Retained 24 hours.
metadataobjectoptionalUp to 20 string keys, 512 bytes per value. Echoed on every webhook and included in the attestation payload.
statusenumread onlySee Status values.
execution_idstring | nullread onlySet once the intent enters routing.
rejectionobject | nullread only{ code, message, rule, stage }rule is the exact policy path that failed, e.g. limits.daily_usd.
created_attimestampread onlyMillisecond precision, UTC.

constraints

FieldTypeDefaultDescription
max_slippage_bpsinteger501–5000. Enforced onchain as a minimum-output amount, not as a router hint.
max_fee_usdnumbernullCeiling on protocol fee plus solver fee. Intent is rejected before routing if unreachable.
max_gas_usdnumbernullCeiling on gas paid by the agent. Sponsored intents ignore this.
limit_pricedecimal stringnullQuote asset per base asset. Present makes the intent a resting order; absent makes it marketable.
valid_aftertimestampnullDo not route before this time. Used for scheduled DCA legs.
route_preferenceenumbest_pricebest_price, fastest, lowest_gas, private.
mev_protectionbooleantrueRoutes through a private relay and rejects public-mempool solver paths.
partial_fillbooleanfalseAllows multiple fills against one intent. Each fill settles and attests independently.

Action types

actionRequired paramsOptional paramsNotes
swapsell_token, buy_token, sell_amount | buy_amountrecipient, poolsExactly one of sell_amount / buy_amount. The other becomes the guaranteed side.
transfertoken, amount, tomemoto must clear allow.contracts or be an EOA on the agent's address book.
nft_bidcollection, max_price, currencytoken_id, traits, marketplaces, expiryTrait bids are matched continuously until expires_at. One fill per bid unless partial_fill.
nft_buycollection, token_id, max_pricemarketplacesImmediate purchase at or below max_price, aggregated across listed marketplaces.
equity_ordersymbol, side, quantity | notional, order_typelimit_price, time_in_force, venueTokenized equities only. Subject to the issuer's transfer-agent hours; see the FAQ.
subscribemerchant, token, amount, intervalstart_at, max_cyclesCreates a recurring child intent per cycle. Each child is policy-checked at its own execution time.
agent_hiretarget_agent_id, task, max_pricedeadline, spec_uriEscrowed agent-to-agent payment. Released on the target agent's signed completion attestation.

Examples

Swap

A market swap with a hard 40 bps slippage bound and private routing. The sell_amount is exact; buy_amount is guaranteed at a minimum by the settlement contract.

{
  "agent_id": "agt_01JQ8ZM7T4B0KC2N9XE5RVDHQP",
  "action": "swap",
  "chain": "eip155:8453",
  "params": {
    "sell_token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "buy_token": "0x4200000000000000000000000000000000000006",
    "sell_amount": "1500.000000"
  },
  "constraints": {
    "max_slippage_bps": 40,
    "max_fee_usd": 6.00,
    "route_preference": "private",
    "mev_protection": true
  },
  "idempotency_key": "rebalance-2026-08-16T09:00Z",
  "metadata": { "strategy": "weekly-rebalance", "leg": "1/3" }
}

NFT trait bid

A standing bid across two marketplaces for any token in the collection matching both traits, live for seven days, denominated in WETH.

{
  "agent_id": "agt_01JQ8ZQ4E9F1NH7T2XM6BKWCVD",
  "action": "nft_bid",
  "chain": "eip155:1",
  "params": {
    "collection": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8",
    "traits": [
      { "type": "Background", "value": "Cosmic" },
      { "type": "Eyes", "value": "Laser" }
    ],
    "max_price": "3.85",
    "currency": "WETH",
    "marketplaces": ["opensea", "blur"]
  },
  "constraints": { "partial_fill": false, "route_preference": "fastest" },
  "expires_at": "2026-08-23T09:00:00Z"
}

Tokenized equity order

A limit order for tokenized Apple equity, good until cancelled, settled onchain against the issuer's transfer agent. Fractional quantities are permitted to six decimals.

{
  "agent_id": "agt_01JQ8ZR8H2K5PM3W9YT0CDNXBF",
  "action": "equity_order",
  "chain": "eip155:42161",
  "params": {
    "symbol": "AAPLX",
    "side": "buy",
    "notional": "2500.00",
    "order_type": "limit",
    "limit_price": "231.40",
    "time_in_force": "gtc",
    "venue": "backed_rwa"
  },
  "constraints": { "max_fee_usd": 8.00, "route_preference": "best_price" },
  "expires_at": "2026-09-16T20:00:00Z",
  "metadata": { "mandate": "core-equity", "reviewed_by": "risk-desk" }
}
Equity orders are not 24/7 in every venue

Tokenized equity intents submitted outside the venue's session are accepted and held in routing until the session opens, unless expires_at falls first. Check venue.session on the quote before assuming immediate fill.

Status values

statusTerminalMeaning
receivedNoAccepted by the gateway, not yet compiled.
policy_checkNoBeing evaluated against the bound policy.
awaiting_approvalNoEscalated to a human. Clock is hitl.timeout_sec.
simulatingNoFork execution and asset-diff analysis in progress.
routingNoSolver auction open, or waiting on valid_after / venue session.
submittedNoUser operation broadcast. tx_hash is populated.
settledYesIncluded and attested. fills[] is final.
rejectedYesFailed a check. rejection.rule names the exact clause.
failedYesReverted onchain or the solver defaulted. No value moved.
expiredYesPassed expires_at without a fill.
cancelledYesCancelled by the owner before submitted.
06 — Authority

Policy engine

A policy is the only thing standing between an agent and your balance. It is written by a human, versioned, hashed, committed onchain, and enforced twice — once offchain for a fast rejection, once onchain because offchain checks can be bypassed.

The policy object

FieldTypeRequiredDescription
idstringread onlyULID with a pol_ prefix.
namestringrequired1–64 chars, unique per account. Used in approval prompts, so make it readable by a human at 3 a.m.
versionintegerread onlyIncrements on every update. Old versions stay readable for audit.
hashbytes32read onlykeccak256 of the canonicalised document. See Policy hash.
limitsobjectrequiredSpending ceilings. At least per_tx_usd must be set.
allowobjectrequiredPositive lists. An empty list means "nothing", never "everything".
denyobjectoptionalNegative lists. Evaluated after allow and always wins.
simulationobjectoptionalThresholds applied to the simulated asset diff.
hitlobjectoptionalHuman-in-the-loop escalation. Absent means never escalate.
expires_attimestamprecommendedAfter this, every intent under the policy is rejected. A policy without an expiry is a standing grant.
commitmentobjectread only{ chain, registry, tx_hash, block, committed_at } for the onchain hash commitment.

Spending limits

All limits are denominated in USD and evaluated against the notional of the intent at the price observed during simulation, not at submission. Rolling windows are true sliding windows, computed over settled and in-flight intents so two concurrent requests cannot both slip under a cap.

FieldTypeWindowDescription
per_tx_usdnumberMaximum notional of a single intent. Required.
daily_usdnumber24 h slidingSum of settled plus in-flight notional.
weekly_usdnumber7 d slidingApplied after daily_usd.
monthly_usdnumber30 d slidingApplied after weekly_usd.
max_open_intentsintegerinstantConcurrency ceiling. Prevents a runaway loop from queueing a thousand orders.
max_position_pctnumberinstantCeiling on any single asset as a percentage of the agent's portfolio after the trade.
gas_budget_daily_usdnumber24 h slidingSeparate from notional. Stops gas-griefing loops.
In-flight notional counts

An intent reserves against its window from policy_check until it reaches a terminal status. This is why a rejected intent frees budget immediately while a routing intent does not.

Allow and deny lists

Evaluation is strict: an intent must match every relevant allow dimension and no deny entry. Omitting a dimension from allow denies it entirely. There is no wildcard for contracts.

{
  "allow": {
    "chains":      ["eip155:8453", "eip155:42161"],
    "actions":     ["swap", "transfer", "equity_order"],
    "tokens":      ["USDC", "WETH", "cbBTC", "AAPLX"],
    "collections": [],
    "contracts":   ["0x2626664c2603336E57B271c5C0b26F421741e481"],
    "venues":      ["uniswap_v4", "aerodrome", "backed_rwa"],
    "categories":  ["spot", "rwa_equity"]
  },
  "deny": {
    "tokens":     ["*_LEVERAGED", "*_3L", "*_3S"],
    "contracts":  ["0x0000000000000000000000000000000000000000"],
    "categories": ["gambling", "leverage", "unverified_contract", "sanctioned"]
  }
}
Built-in categories
CategoryMatches
unverified_contractTarget has no verified source on the canonical explorer, or its proxy implementation changed within 72 hours.
leveragePerpetuals, margin, leveraged tokens, and any position with a liquidation price.
gamblingPrediction markets, lotteries, casino contracts on the maintained registry.
sanctionedAddresses on OFAC SDN and the equivalent EU/UK lists, refreshed hourly.
low_liquidityPair depth below simulation.min_liquidity_usd at simulation time.
rwa_equityTokenized equities and ETFs with a named transfer agent.

Human-in-the-loop

The human gate is a policy outcome, not a separate product. Above the threshold the intent moves to awaiting_approval, an approval.requested webhook fires with the full simulated asset diff, and the clock starts.

{
  "hitl": {
    "threshold_usd": 500,
    "actions": ["transfer", "nft_buy"],
    "always_for_new_counterparty": true,
    "channels": ["webhook", "push"],
    "timeout_sec": 300,
    "on_timeout": "reject",
    "approvers": ["usr_01JQ8ZS2M4N6P8R0T2V4X6Z8B0"],
    "quorum": 1
  }
}
  • threshold_usd — escalate any intent whose notional exceeds this.
  • actions — escalate these actions at any notional. Union with the threshold rule.
  • always_for_new_counterparty — escalate the first interaction with any address the agent has never settled with before, regardless of size.
  • on_timeoutreject fails safe (default); hold keeps the intent pending until an explicit decision, at the cost of a stale price.
  • quorum — number of distinct approvers required. Approvals are signed by the approver's key and included in the attestation.

Policy hash and onchain commitment

The hash is what makes the policy enforceable rather than advisory. It is computed as follows, and the algorithm is fixed for the lifetime of an API version.

  1. Drop every server-assigned field: id, version, hash, commitment, created_at, updated_at.
  2. Canonicalise the remainder with RFC 8785 JSON Canonicalisation Scheme — keys sorted by UTF-16 code unit, no insignificant whitespace, numbers in shortest round-trip form.
  3. Prefix the domain separator "strixhood.policy.v1" as UTF-8 bytes.
  4. Take keccak256 of the concatenation. That 32-byte digest is policy.hash.
  5. Call PolicyRegistry.commit(agentId, policyHash, version). The registry stores one live hash per agent and emits PolicyCommitted.
import { canonicalize } from "@strixhood/sdk/jcs";
import { keccak256, toBytes, concat } from "viem";

export function policyHash(policy: Record<string, unknown>): `0x${string}` {
  const { id, version, hash, commitment, created_at, updated_at, ...doc } = policy as never;
  const body = toBytes(canonicalize(doc));
  const domain = toBytes("strixhood.policy.v1");
  return keccak256(concat([domain, body]));
}

// Recompute locally and compare with what the registry holds.
const local = policyHash(await strix.policies.get("pol_01JQ8ZK3M2X7YB4N6PA0RTVCWD"));
const onchain = await registry.read.policyOf([agent.passport.tokenId]);
if (local !== onchain) throw new Error("policy drift: refuse to sign");

The session-key validator module reads the committed hash on every user operation and compares it with the hash embedded in the key's permission blob at issuance. Rotating a policy therefore invalidates every session key issued under the previous version — by construction, not by convention.

Evaluation order

Order matters because the first failure short-circuits and is reported as rejection.rule. Knowing the order tells you which rule to loosen.

#CheckFailure code
1Policy is live, not expired, and its hash matches the registrypolicy_stale
2chainallow.chainschain_not_allowed
3actionallow.actionsaction_not_allowed
4Every token, collection, contract and venue in params is allow-listedasset_not_allowed
5Nothing in params matches denydenied
6per_tx_usdlimit_exceeded
7Sliding windows, daily then weekly then monthlylimit_exceeded
8max_open_intents, max_position_pct, gas budgetlimit_exceeded
9Human gate evaluationapproval_required
10Simulation thresholds, after stage 04 returnssimulation_failed
07 — Trust

Security model

This section is written to be argued with. It states what the protocol enforces, what it assumes, and what it cannot do. If a claim here is not testable against the contracts, it should not be here.

The five layers

Each layer is independent. Removing any one of them still leaves the others enforcing; none of them depends on another being honest.

LayerMechanismStopsEnforced
1 · Account abstractionERC-4337 v0.7 smart account. Session keys carry a permission blob: selector allowlist, value ceiling, chain, expiry, policy hash.Key exfiltration turning into unlimited spend. An escaped session key expires and cannot call unlisted selectors.Onchain, in the validator module
2 · Spending policyVersioned policy document, keccak256-committed to PolicyRegistry, evaluated offchain and bound into the key.Scope creep. An agent cannot widen its own authority, because it cannot produce a valid commitment.Offchain for speed, onchain for truth
3 · SimulationFork execution against the pending block with state overrides. Signed asset diff. Drainer, approval-sweep, honeypot and price-impact detectors.Malicious calldata that looks benign: infinite approvals, hidden transfer hooks, fee-on-transfer traps.Offchain, gates signing
4 · Contract auditSource verification, proxy-admin and implementation-age checks, deployer reputation, sanctions screening, maintained registries.Interaction with a contract deployed 40 seconds ago by an address with no history.Offchain, at policy-check time
5 · Human in the loopThreshold, action and new-counterparty escalation with signed approvals and a fail-safe timeout.Everything the first four layers considered permissible but a person would not.Offchain, blocks signing

Threat model

Assumed adversaries, and what the protocol does about each.

AdversaryCapability assumedMitigationResidual risk
Prompt injection into the agentFull control of the intents the agent emitsIntents carry no calldata; policy bounds every dimension; simulation checks the diff.Attacker can burn the agent's allowed budget on permitted actions — for example swapping USDC to WETH repeatedly within limits.
Compromised agent hostReads the session key from memoryKey is scoped, expiring and policy-bound; rotation is automatic; owner can revoke in one transaction.Value up to the remaining window limits until revocation lands.
Malicious counterparty contractArbitrary code at the target addressLayer 3 and 4: asset-diff must match intent; unverified and fresh contracts are denied by category.A contract that behaves correctly in simulation and maliciously on a later call path.
Dishonest solverWins the auction, then under-deliversOutput guarantee enforced onchain in SettlementVault; 25% bond slash; reliability score gates future auctions.Griefing by repeatedly losing the auction to delay a fill.
Searcher / MEVObserves the mempool, reordersmev_protection routes privately; minimum output enforced atomically.Timing leakage from public settlement events after the fact.
Leaked API keyFull REST access with the key's scopesScoped keys; secret keys cannot sign — only session keys can; policy is unchanged by API access without policies:write.A key with policies:write can widen a policy. Do not issue one to anything an agent can read.
Protocol operatorControls the offchain servicesOnchain policy hash and settlement checks are independent of the operator; attestations are verifiable by anyone.Operator can censor or delay intents. It cannot move funds outside policy.

What this does not protect against

Read this list before you fund an agent

Strix Hood bounds the blast radius of an autonomous actor. It does not make that actor correct, and it removes none of the following risks.

  • Your root key. If the owner key controlling the smart account is compromised, the attacker rewrites the policy and every layer above is void.
  • Bad strategy. A policy-compliant trade can still lose money. The protocol has no opinion on whether a permitted action is a good one.
  • Market and liquidity risk. Slippage bounds guarantee an execution price, not a fair one. Thin markets stay thin.
  • Third-party protocol failure. If you allowlist a lending market and it is exploited, funds you sent there are gone. Allowlisting is an explicit trust decision.
  • Oracle and price-feed failure. Notional limits use observed prices. A manipulated venue price manipulates the limit calculation with it.
  • RWA issuer and transfer-agent risk. Tokenized equities carry issuer credit risk, redemption risk and jurisdictional restrictions. The token is a claim, not the share.
  • Deep reorgs and chain halts. Attestations follow the canonical chain; a reorg beyond the finality target can unwind a settled intent. See Failure modes.
  • Phishing the human approver. The human gate is only as good as the person reading the diff. Approval fatigue is a real failure mode; set thresholds you will actually respect.
  • Regulatory and tax exposure. Automation does not change your obligations, and the protocol does not file anything on your behalf.
  • Availability. The protocol depends on the underlying chains, bundlers and relays. Degraded modes are published on the status page.

Audits and disclosure

No audit has been completed. No firm is engaged, no report exists, and every contract currently running on a testnet is unaudited. The table below is the scope we intend to put in front of an external firm, published now so the order is on the record before the engagement is.

ComponentScopeStatus
Core contracts — router, vault, registrySettlement path, minimum-output enforcement, fee split, registry writes.SCHEDULED
Session-key validator moduleERC-4337 validation, capability-to-selector mapping, expiry and revocation.SCHEDULED
Solver auction and bondingSealed-bid mechanics, bond accounting, slashing and the challenge window.NOT STARTED
RWA token and allow-listPermissioned transfer hooks, register reconciliation, corporate-action freeze.NOT STARTED
Indexer and APINo funds and no signing authority, so it is last — but it can still misreport state.NOT STARTED

There is no funded bug bounty. Report vulnerabilities to security@strixhood.xyz with the PGP key published at /.well-known/security.txt anyway — findings are credited and published, and the reward schedule is set out on the security page. Do not open a public issue. Disclosure target is 90 days or on-fix, whichever comes first.

08 — $STRX

Tokenomics

$STRX exists to make agent identity expensive to fake and dishonest execution expensive to attempt. It is a work token and a bond, not a payment rail — commerce settles in USDC, WETH and the assets being traded.

$STRX does not exist yet

No token is deployed, there has been no TGE, there is no market and there is no sale. The figures in this section are the designed parameters — supply, splits and unlock shape — not a description of anything you can hold or buy. Testnet STRX is a faucet token with no value. Any $STRX offered to you today is a scam.

Supply and distribution

1,000,000,000Total supply, fixed
BaseCanonical chain
18Decimals
NoneInflation after TGE
UnscheduledTGE — not deployed
Allocation and unlock schedule
AllocationShare$STRXUnlockControlled by
Community & Ecosystem40%400,000,00048-month linear emission to stakers, solvers and grant recipients. No TGE unlock.Emissions contract
Team & Advisors20%200,000,00012-month cliff, then 36-month linear vesting.Vesting escrow
Protocol Treasury15%150,000,000Unlocked, governance-gated. Spend requires a passed proposal and a 48-hour timelock.Governance timelock
Liquidity15%150,000,00020% at TGE for initial depth; remainder released against depth targets over 24 months.Liquidity multisig 4/7
Early Contributors10%100,000,0006-month cliff, then 24-month linear vesting.Vesting escrow

Protocol fee

Every settled intent pays 0.25% of settled notional, taken from the output leg inside the settlement transaction. There is no fee on rejected, failed or expired intents, and no fee on simulate_only calls. Subscription tiers in Rate limits & pricing are separate and buy throughput, not lower fees.

DestinationShare of feeMechanism
Protocol Treasury40%Accrues in the settled asset, swept to USDC weekly. Spend is governance-gated.
Stakers30%Streamed pro-rata to staked $STRX, claimable continuously, no epoch lock.
Buyback & burn30%Executed as TWAP over 24 hours by the treasury keeper; burned to 0x…dEaD with an onchain receipt.
settled notional        $1,500.00
protocol fee  0.25%     $    3.75   taken from the WETH output leg
  ├─ treasury    40%    $    1.50
  ├─ stakers     30%    $    1.125
  └─ buyback     30%    $    1.125  → TWAP buy, burn, receipt emitted
solver fee (quoted)     $    0.90   paid by the solver's own margin, not added on top
agent receives                       output − 3.75 USD equivalent, ≥ quoted minimum

Staking and registration bonds

Two distinct locks use the same token and must not be confused.

Registration bondFee stake
PurposeSybil resistance and slashable collateral for one agentClaim on 30% of protocol fees
Minimum2,500 STRXno minimum
Locked againstThe agent's passport tokenIdThe staker's address
SlashableYes — see belowNo
Unbonding14 days after the agent is retired21 days
YieldNoneFee share, streamed

Solvers post a separate bond sized to their maximum in-flight quote exposure, with a floor of 50,000 $STRX. A solver whose bond falls below its exposure is excluded from the auction until it tops up.

Slashing conditions

ConditionPenaltyDetectionDestination of slashed bond
Forged policy commitment — submitting a user operation whose policy hash does not match the registry100%Onchain, deterministicTreasury
Reputation fraud — wash volume, self-dealing between agents under one owner to inflate a score50%Graph analysis, challenge period of 7 days50% to the challenger, 50% burned
Solver default — winning a quote and settling below the guaranteed output25%Onchain, at settlement revertRefund to the affected agent, remainder to stakers
Liveness failure — a service agent accepting a hire and missing the deadline5%Deadline elapses without a completion attestationRefund to the hiring agent

Slashing is executed by the StakingBond contract. Everything except the deterministic onchain cases passes through a 7-day challenge window in which the accused can post evidence; an unchallenged claim executes automatically, a challenged one goes to governance.

09 — Deployments

Networks & contracts

Nothing is deployed to mainnet. Registry and settlement contracts run on three EVM testnets; four more networks are queued behind them. EVM contracts deploy with CREATE2 from the same factory, so every EVM chain will share one address per contract. Solana runs a separate program set.

Deployment status

The target set is seven networks. Where a contract exists today it exists on a testnet, and it is redeployed without notice. Treat every row below as the current state, not a roadmap.

NetworkTarget chainChain IDCAIP-2Finality targetStatusExplorer
EthereumSepolia1eip155:12 epochs (~13 min)TESTNETEtherscan
BaseBase Sepolia8453eip155:8453L1 inclusion (~3 min)TESTNETBasescan
Arbitrum OneArbitrum Sepolia42161eip155:42161L1 inclusion (~4 min)TESTNETArbiscan
OP MainnetOP Sepolia10eip155:10L1 inclusion (~3 min)QUEUEDEtherscan
Polygon PoSAmoy137eip155:137128 blocks (~4 min)QUEUEDPolygonscan
BNB ChainBNB Testnet56eip155:5615 blocks (~45 s)QUEUEDBscScan
SolanaDevnetsolana:5eykt4Us…32 slots (~13 s)QUEUEDSolscan

Use a strx_sk_test_ key against anything above. strx_sk_live_ keys exist in the key schema but have no mainnet to address, so they are rejected everywhere today.

Contract addresses

There are none to publish. No Strix Hood contract is deployed to any mainnet, there is no $STRX token contract, and the testnet deployments are not stable enough to pin. Addresses will appear in this table, in @strixhood/sdk/deployments.json and on the repository release tags at the same time — never one before the others.

Contract set — CREATE2, one address per contract across every EVM network
ContractPurposeMainnet addressStatus
IntentRouterAccepts routed intents, opens the solver auction, forwards the winner.not deployedTESTNET ONLY
PolicyRegistryOne live policy hash per agent. Source of truth for the validator module.not deployedTESTNET ONLY
AgentPassportERC-721 identity, dynamic metadata, capability traits, revenue rights.not deployedTESTNET ONLY
SettlementVaultEnforces minimum output, takes and splits the 0.25% fee, emits Settled.not deployedTESTNET ONLY
SolverRegistrySolver bonds, reliability scores, auction eligibility.not deployedTESTNET ONLY
StakingBondRegistration bonds, fee staking, slashing execution and challenges.not deployedTESTNET ONLY
STRX tokenERC-20, 18 decimals, fixed supply. Canonical deployment will be Base.not deployedNOT DEPLOYED
Solana — program set
ProgramPurposeProgram IDStatus
Intent routerIntent account creation and solver crank.not deployedQUEUED
PassportMetaplex-compatible agent passport with policy PDA.not deployedQUEUED
SettlementMinimum-output enforcement and fee split.not deployedQUEUED
There is no address to approve yet

Nobody from Strix Hood will ever DM you a contract address, and today anyone offering you one is lying, because none exist. When mainnet addresses are published, cross-check them against this page, the SDK's deployments.json and the explorer's verified-source badge before granting an allowance. Testnet contracts are unaudited and get redeployed without notice.

10 — Throughput

Rate limits & pricing tiers

Subscription tiers buy throughput and support. They do not change the 0.25% protocol fee, and they do not change what a policy allows. Nothing is billable today — testnet runs on the Sandbox tier and it is free.

Tiers

TierPriceRequests / minConcurrent intentsIntents / monthWS connectionsWebhooksNetworksSupport
Sandboxfree6021,00011Testnets onlyCommunity
BuilderTBA6002550,000510AllEmail, 2 business days
GrowthTBA3,000200500,0002550AllEmail, 8 h · 99.9% SLA
ScaleTBA12,0001,0005,000,000100200All + priority solver laneShared channel, 1 h · 99.95% SLA
EnterpriseTBANegotiatedNegotiatedUnmeteredNegotiatedNegotiatedAll + private solver poolNamed engineer · 99.99% SLA

The limits are real and enforced now; the prices are not set. Paid tiers are priced at mainnet, and no card is taken before then. Overage on intents per month will be billed per intent rather than blocked, so a traffic spike degrades your invoice and not your agents. Overage on requests per minute is never billed — it is rate limited.

Rate-limit headers

Every response carries the current window state. Read them; do not guess.

HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 574
X-RateLimit-Reset: 1786953600
X-Request-Id: req_01JQ8ZT6P0Q2S4U6W8Y0A2C4E6
Strix-Api-Version: 2026-07-01
HeaderMeaning
X-RateLimit-LimitRequests permitted in the current 60-second window.
X-RateLimit-RemainingRequests left. Treat 0 as "stop", not "try harder".
X-RateLimit-ResetUnix seconds at which the window resets.
Retry-AfterOnly on 429. Seconds to wait. Authoritative — ignore your own backoff if it is shorter.
X-Request-IdInclude this in any support request. It resolves to the full trace.

Bursts and backoff

The limiter is a token bucket refilled continuously at limit / 60 per second with a burst capacity of limit / 4. Short spikes pass; sustained overload does not. On 429, back off with full jitter and honour Retry-After.

async function withRetry<T>(fn: () => Promise<T>, tries = 5): Promise<T> {
  for (let attempt = 0; ; attempt++) {
    try {
      return await fn();
    } catch (err: unknown) {
      const e = err as { status?: number; retryAfter?: number };
      const retryable = e.status === 429 || (e.status ?? 0) >= 500;
      if (!retryable || attempt >= tries - 1) throw err;
      const ceiling = e.retryAfter != null ? e.retryAfter * 1000 : Math.min(2 ** attempt * 250, 8_000);
      await new Promise((r) => setTimeout(r, Math.random() * ceiling));
    }
  }
}
Streams are free

WebSocket frames do not consume the request budget. If you are polling GET /v1/intents in a loop, replace it with the intents channel and the rate limit stops being your problem.

11 — Questions

FAQ

Does Strix Hood ever hold my funds?

No, outside the settlement transaction itself. Assets live in your ERC-4337 smart account, which you control with your root key. SettlementVault touches them only inside the atomic call that fills your intent; if that call does not satisfy the minimum output, it reverts and nothing moved.

What happens if I lose the session key?

Nothing catastrophic. A session key is scoped, expiring and policy-bound. Revoke it with DELETE /v1/agents/{id}/session-keys/{keyId}, which submits an onchain revocation. Until that lands the key can still spend up to the remaining window limits, which is the argument for short TTLs.

Can the agent change its own policy?

Only if you gave the agent runtime a key with policies:write. Do not. The intended split is: server holds the secret key and writes policy; agent holds a publishable key plus a session key and writes intents.

Why is my intent stuck in routing?

Three common causes: valid_after has not passed; the venue's trading session is closed (tokenized equities); or no solver has quoted inside your slippage bound. The quote_status field on the execution object names which.

Are tokenized equities tradable 24/7?

Depends on the venue. Some issuers support continuous onchain secondary trading; primary issuance and redemption follow the transfer agent's hours and settlement calendar. The quote object returns venue.session with the next open and close, and orders outside a session are held rather than rejected.

How do I test without spending real money?

Use a strx_sk_test_ key against Base Sepolia or Arbitrum Sepolia. Test-mode agents skip the $STRX bond, and the faucet in the console funds the smart account. Alternatively set simulate_only: true on mainnet to get a real quote and a real asset diff without signing anything.

What is the difference between rejected and failed?

rejected means a check refused the intent before signing — policy, schema or simulation. Nothing was broadcast. failed means it was broadcast and reverted onchain, usually because state moved between simulation and inclusion. Both are terminal; only failed costs gas.

Can two agents share one policy?

Yes. A policy can be bound to many agents, and its limits then apply to the union of their activity — a shared $1,000 daily cap is $1,000 in total, not per agent. Use this for a fleet that must respect one budget.

Do I need $STRX to use the API?

Not on testnets, and not for simulate_only. Mainnet agent registration posts a bond, which the console can source for you at registration time. Fees are paid in the settled asset, not in $STRX.

Is the protocol upgradeable?

IntentRouter and SolverRegistry sit behind a governance timelock of 48 hours. SettlementVault, PolicyRegistry and AgentPassport are immutable — a new version means a new address and an explicit migration, never a silent implementation swap under your allowances.

12 — Terms

Glossary

Agent
Protocol-side identity of an autonomous actor: a smart account, one bound policy, session keys, a reputation score and a slashable bond.
Asset diff
The signed before/after balance delta produced by simulation. The intent is only signed if the diff matches what the intent claimed.
Attestation
An EAS record binding intent hash, policy hash, simulation digest, solver identity and receipt. The audit artefact.
Bond
$STRX locked against an agent passport or a solver's exposure, slashable under the conditions in Slashing.
CAIP-2
Chain-agnostic identifier standard, e.g. eip155:8453. Used everywhere a chain is named.
Execution
The object created when an intent enters routing. Holds quotes, fills, transaction hashes and the attestation.
Fill
One settled portion of an intent. An intent without partial_fill has exactly one.
HITL
Human in the loop. The policy clause that escalates an intent to a person before signing.
Intent
A declarative, expiring statement of a desired outcome with no calldata and no route.
Notional
USD value of an intent at simulation-time prices. The unit all policy limits are denominated in.
Passport
ERC-721 token that carries an agent's identity, level, traits and revenue rights.
Policy hash
keccak256 of the domain-separated, RFC 8785 canonicalised policy document. Committed onchain, embedded in session keys.
Route preference
The objective the solver auction optimises: price, speed, gas or privacy.
Session key
A short-lived signing key with a permission blob: selectors, value ceiling, chain, expiry and the policy hash it was issued under.
Simulation
Fork execution of the candidate path against the pending block, with state overrides, producing the asset diff.
Solver
A bonded third party that competes to fill intents and is contractually bound to the output it quoted.
ULID
The lexicographically sortable identifier format used for every object ID, which makes IDs usable as pagination cursors.
User operation
The ERC-4337 transaction envelope signed by a session key and validated by the account's validator module.