Security

Unaudited. No audit is scheduled and no bug bounty is open.Now deployed to BOT Chain mainnet (chain 677) as well as Bohr testnet (chain 968). The mainnet deployment is unaudited and bonds there are posted in real USDT.Nothing has ever settled on mainnet. No agent is registered, no execution has been requested, and the settlement path has never run on chain 677 — so the contracts holding real value are the ones with the least evidence behind them.

The purpose of this page is to be the least flattering document on the site. If you are deciding whether to put capital behind an agent scored by this protocol, the paragraphs below matter more than anything on the overview, and they are written to be read by someone looking for a reason not to.

Deployment state

BotID is deployed twice. On Bohr testnet, chain 968, since 2026-09-01; and on BOT Chain mainnet, chain 677, since 2026-09-03, first block 21,932,276. Both have the bn254 precompiles confirmed present. Bohr is a testnet: its BOT has no value, the chain carries no uptime commitment, and the deployment can be replaced without notice. Nothing there is a place to put capital you expect to keep.

Mainnet is different in exactly one way that matters — the bond token there is real USDT — and it is worth being blunt about what that buys you, which is nothing yet. No agent is registered on chain 677 and no execution has ever been requested there. The settlement path, which is the part an audit found two flaws in, has never executed on that chain at all. The mainnet set is the newest code with the least evidence behind it, and it is the one holding real money. A deployment is not a track record; the empty tables on mainnet are the honest reading of it.

Two capabilities are deliberately switched off there rather than pending. Gold was not deployed at all, so adapters(3) is the zero address and a Gold execution cannot be requested. And the input attestor has no registered publishers, so the attested input path is inert — anything depending on a feed reading cannot run on mainnet. Both are reversible, and both were chosen because an inert capability fails visibly while a wrongly-configured one does not.

“Replaced without notice” is not hypothetical: this is the fifth deployment since 2026-08-11, and every one of them replaced the whole set. Both current sets carry the settleDefault and canEscalate fixes from the internal review, which changed IVerificationAdapter and so could not have reached a live chain any other way. None of these contracts is upgradeable — the router's registry, engine and bond token are immutable, so there was no way to swap one contract and keep the rest. The signing domain changed too, so nothing signed for the old adapters verifies against the new ones. If you hold addresses from before 2026-09-01, they are not stale so much as a different protocol wearing the same name. Every address in both sets is source-verified on the explorer; the table below is whichever set the nav is pointing at.

One consequence of that is worth stating before you read the table, because it looks like a mistake and is not. The mainnet AgentRegistry and the BohrAgentRegistry have the same address. Contract addresses are derived from the deployer and a counter, not from the chain, so the same key deploying the same sequence lands on the same addresses on every chain it touches. An explorer link is therefore only as trustworthy as the chain it points at, and an address that checks out here is not evidence you are on the network you think you are. Check the chain id, not just the characters.

The owner of every parameter on both deployments is a single externally-owned key, which is also the deployer. On Bohr it is additionally the only registered feed publisher and the only TEE notary: four roles on one key, so it can change the capital limits, it alone signs the input readings every execution is judged against, it alone decides which enclaves count as Silver, and losing it loses all four at once. This page used to say that was acceptable on a testnet and was “exactly the sort of thing that quietly survives into mainnet.” It did survive. The one-key ownership is now live on a chain holding real USDT, and nothing about it was made safer in the move.

Two of those four roles were at least left off the mainnet key deliberately — it publishes no readings and notarises no enclaves there, because both lists were deployed empty. The key that can change every capital parameter, though, is the same one, on both chains, and it is a single key on a laptop. The treasury and router addresses behind the 21-day timelock cannot be corrected quickly if that key is lost; the limits, the fee floor and the penalty can be changed by whoever holds it, immediately.

The table below is load-bearing: it is this site's answer to “is this the real BotID.” It follows the network selected in the nav, it never falls back to another network's addresses, and no BotID contract goes into it until a deployment artifact exists for that network.

It opens on Bohr, because the testnet is the default network, and it is two rows shorter on BOT Chain: ZkAdapter and its Halo2Verifier were not deployed to mainnet at all. A missing row means the capability does not exist on that chain, not that we have not got round to listing it. The bond token is the exception on both networks and is marked as such: USDT is not ours, it was already deployed, and agents post their bonds in it. It is listed per network because the two addresses differ — and using the mainnet one on Bohr does not fail, it resolves to an unrelated token with different decimals. Every address in the table, ours and USDT alike, was verified by calling the deployed contracts directly rather than copied out of the deploy log.

That table is the whole list. An address presented to you as our contract — in a message, a post, a wallet prompt or a fork of this site — and not appearing above is not ours, whatever it is named after. For most of this project's life the honest answer was “none of them are real”, which was easy to check and impossible to impersonate. That is over twice over: there are real addresses now, and on a chain where a bond is real money. Check the one you are about to sign against this table character by character, check it against the explorer link rather than against the string someone sent you — and check that the explorer you landed on is the chain you meant, since one of these addresses is a real registry of ours on both chains, and they are not the same registry.

The contracts that appear there

Named here so the table can be checked against something written down. The protocol is AgentRegistry, ExecutionRouter, InputAttestor, ReputationEngine, the three tier adapters — SignatureAdapter, TeeAdapter, ZkAdapter — the generated Halo2Verifier that ZkAdapter calls, and the ERC-20 bondToken. Nothing else. An earlier version of this page listed contracts called RequestManager, ScoreRegistry and BondVault; they have never existed, and the addresses beside them were placeholders. That was the exact error this table is supposed to prevent, so it is recorded here rather than quietly deleted.

Verifier & model registrations

Intended to be read live from ZkAdapter; still transcribed here rather than fetched, so treat it as a claim to check and not as chain state. On Bohr one model is registered: botid.reference-allocator.v1 at an input scale of 8 bits, whose commitment is keccak256 of that name — 0x08a284ace0e1e53d8ecffe84217e9680646ac0264ab252948dabbfe7f54d8fa2 — bound to the Halo2Verifier in the table above. Read back from ZkAdapter.modelFor() on 2026-09-01, against the current adapter, returning the Halo2Verifier in the table above at an input scale of 8. The verifier was redeployed with the rest of the set this time rather than carried over, so the binding is new even though the commitment — which is only the hash of the name — is unchanged. Nothing is registered on mainnet, and nothing can be: ZkAdapter was not deployed there, so adapters(3) is the zero address and a Gold execution reverts rather than falling through to a weaker tier.

Misconfiguration, which is not an attack and is still how money is lost

The threat model below is about adversaries. This is about the owner key making an honest mistake, which on a one-key deployment is the likelier of the two. Two such mistakes used to fail silently, and both now fail loudly instead.

A bond token address with no contract at it. The transfer helpers accept a call that returns no data as a success, because USDT and others really do return nothing on a real transfer. Calling an address with no code also succeeds and also returns nothing, so the two are indistinguishable — a typo in a constructor argument would have made every deposit, fee and slash appear to work while nothing moved, with the protocol reporting bonds it did not hold and the first symptom arriving at the first withdrawal. Both helpers now check the address has code and revert with NotAContract, which moves the failure to the first deposit.

A challenge bond above 2128. The bond is collected as a uint256 and recorded per request as a uint128, and every refund pays out the recorded field. Past that boundary those are different numbers and the difference is simply gone — not refunded, not recoverable, and absent from every event, because a truncating cast is silent by construction. setParameters now refuses the value outright, so the revert lands on the governance call that is wrong, where it can still be corrected, rather than on the first challenger to post a bond.

Neither was reachable by an attacker and neither is a claim that the owner key is safe — see Owner key compromise below, which is still the largest risk here even with a notice period on four of its setters. What they are is two cases where a wrong value used to be indistinguishable from a right one, and now is not.

Threat model

What the protocol defends against, what it does not, and which is which. The rows marked NOT mitigated are the ones worth your time.

ThreatAttackStatus
Fabricated inputsAn agent feeds its own model invented prices, gets a valid proof, and executes a theft that verifies.Mitigated. inputCommitment must be a quorum-signed bundle from registered publishers, is checked for freshness against request creation, and is bound into the attestation. Values are committed as keccak256(abi.encode(value, salt)) so a public commitment does not leak the numbers.
Proof or signature replayA valid attestation from one execution is presented for a different one.Mitigated. The signed digest binds chainId, adapter address, requestId, agentId, model, input and output commitments and the delivery deadline. A Gold proof is reusable only across requests that agree on model, input and output — where it asserts the same true statement.
Sybil identitiesSpin up many cheap agents to manufacture capacity or to farm scores.Mitigated economically, not cryptographically. Capacity is a function of bond, and the score is weighted by capital at risk with a half-weight constant, so dust volume moves nothing. Identity itself is permissionless by design.
Score grindingAccumulate a high score cheaply, then use it once at size.Mitigated. Weight is min(notional, weightCap, remaining budget for this counterparty) — a score can only be earned at roughly the size at which it will be spent, and no single counterparty can supply the whole record. Credit is a step function of score, so a marginal point never silently lifts a ceiling.
Stale reputation resaleSell or reuse a dormant agent with a high historical score.Mitigated. Scores decay toward neutral on a half-life and the profile exposes lastActiveAt. Consumers who check only minScore and ignore maxStalenessSeconds are accepting this risk voluntarily.
Dishonest outcome reportingThe consumer calls settle() with a false realizedPnlBps, slaBreached or limitBreached, corrupting the score.Bounded, not prevented. Realised P&L is a fact about the world off chain and no proof system attests to it, so a determined consumer can always lie about its own trade — that much is not mitigable, and it remains the softest joint in the protocol. What is enforced is the blast radius: consumerWeightCap gives each counterparty a per-agent weight budget, defaulting to half the half-weight constant, so one liar drags a score at most a third of the way toward its claimed quality and the second report inside the same half-life is nearly mute. The advice to weigh a record spread across many distinct consumers above a long history with one is now the arithmetic rather than a suggestion.
Non-deliveryAn agent takes requests and simply does not answer.Mitigated. Anyone may call markExpired after deliverBy: the fee is refunded, exposure released, a liveness slash of 200 bps taken and a permanent fault recorded outside the EWMA. This, not invalid proofs, is how agents fail in practice — an invalid proof reverts and never reaches the record.
Fee evasionA consumer and an agent who know each other set fee = 0 and settle off chain.Mitigated. A floor of minFeeBps against notional, which is the one quantity in a request that is costly to misreport in either direction. Zero-notional requests are exempt on purpose.
Frivolous challengesGrief an honest agent with a stream of challenges to force proving costs on it.Mitigated. A challenge requires a bond, and a challenge the agent answers with a valid Gold proof forfeits that bond to the agent and finalizes the execution at Gold.
Owner key compromiseWhoever holds the owner key retunes slash rates, windows, fee floors, publishers or adapters.NOT mitigated. The owner is a single key on the only existing deployment, and there is no multisig. Six setters — the registry’s router, its treasury, a reputation writer, a verification adapter, the router’s input attestor and the Gold adapter’s router — are now behind a 21-day queue-then-execute delay, which is the subset that can substitute the code deciding whether an execution was honest. Every economic parameter stays instant, the delay is a notice period rather than a veto, and a key that can wait three weeks still wins. Treat this as the largest non-code risk on this page.
Frontend compromiseThis site is replaced or its transaction composer is tampered with to route funds elsewhere.Partially mitigated. The site holds no keys and no funds, so a compromise is a phishing risk rather than a custody one. Verify what your wallet shows you against the deployment artifact, never against this page. There is no protocol-level defence for a hostile frontend.
Chain-level failureReorgs, validator censorship, halted block production, or a repricing of the bn254 precompiles.Inherited, not mitigated. BotID is a set of contracts and has exactly the liveness and finality of the chain under it. Gold verification depends on precompiles at 0x06/0x07/0x08 at Istanbul prices; the deploy script probes rather than assumes, but a live repricing would break Gold without breaking Bronze or Silver.

Trust assumptions

Verification narrows what you must trust; it does not eliminate it. Everything below is a thing you are trusting if you rely on a BotID score.

AssumptionWhat it means
Publisher honesty, up to quorumInput attestation is only as good as its publisher set. A colluding quorum can sign a false bundle and every downstream proof will be valid over false numbers.
Consumer honesty at settlementOutcomes are self-reported and unproven.
TEE vendor integrity, for SilverA Silver attestation reduces to trusting AWS Nitro, Intel SGX or Phala and a measurement allowlist. Enclave breaks are a live research area; Silver is stronger than a bare signature and weaker than a proof.
Trusted setup, for GoldGroth16 needs a per-circuit setup. A compromised setup for a circuit means forgeable proofs for that model.
Correctness of ezkl compilationA Gold proof attests that the compiled circuit ran, not that the circuit faithfully represents the model an author intended. Division in ezkl is a reciprocal lookup that returns zero for large divisors, and a scale of 0 quantises reciprocals to zero — compiling, proving and verifying successfully while computing nothing.
A single owner keyEvery parameter listed in the docs is settable by it. Four of those setters now wait 21 days; the rest take effect in the block they land in.
The chain beneath itFinality, censorship-resistance and gas pricing are BOT Chain’s properties, not BotID’s.

What a bond is, and what it is not

A bond is the agent's skin in the game. It backs the agent's own credit — maxOpenNotional is bond times a score-derived leverage times a tier factor — and it is what gets slashed on a fault. Slashing pays a bounty to the challenger who surfaced the fault and sends the remainder to treasury.

It is not insurance and it does not compensate you. If an agent loses your capital, no part of its bond flows to you. The insurance vault sketched in the roadmap does not exist and has not been started. Bonds are also never lent out for yield: doing so would put the slashing guarantee behind a liquidity assumption, which is the one thing the bond exists to avoid.

Two timing details matter. Bond remains slashable for the entire 21-day unbonding period, and credit is computed on bond net of anything queued for unbonding, so an agent's capacity shrinks the moment it starts to leave rather than when it finishes.

There is a fast door, and it is worth stating what gates it rather than leaving it in a parameter table. withdrawEarly returns queued bond before the 21 days for a 10% penalty to the treasury — but only while openNotional is zero. That condition is not a proxy for “nothing outstanding”; it is the thing itself. The router reserves exposure when a request is made and releases it in exactly three places — settlement, a lost challenge, and expiry — which are its three terminal states. Nothing is released at delivery, at finalize, or when a challenge is answered. So an agent with any execution still live, delivered and challengeable, under challenge, or finalized and awaiting settlement, cannot use the door at all, and one with none has nothing left that could reach its bond.

That ordering is what makes the penalty safe to be small. Ten percent is less than the 20% a lost challenge costs, so if the exit were open during a dispute an operator expecting a fault would simply buy their way out — no toll sized in bond can fix that, because the payoff being weighed comes out of notional, which leverage and tier carry to nine times bond. Gated, the comparison stops mattering: the fault is always paid first. What the 10% prices is churn, and what the 21 days now cover is the case where an agent wants out with work still in flight.

Audit status

No contract in this protocol has been audited. No audit is booked and no date is set. The intended scope for a first engagement, in priority order, is ExecutionRouter (it holds escrow and drives every state transition), AgentRegistry (bond custody and the unbonding queue), ZkAdapter (the instance-pinning logic that substitutes for in-circuit hashing), InputAttestor, then ReputationEngine and ScoreMath.

CertiK has audited BOT Chain itself, its DEX and its bridge. Those audits do not cover BotID. A chain-level audit says nothing about a contract deployed on that chain, and anyone citing one as coverage for this protocol is misrepresenting it.

Responsible disclosure

Report to chidileozoemena@gmail.com. A PGP key is available on request. Please include enough detail to reproduce, and give us a reasonable window before publishing. We will acknowledge receipt, tell you honestly whether we can fix it, and credit you if you want credit.

Bug bounty

None yet, and we will not imply otherwise. There is no reward pool and no safe-harbour commitment in place today. When a program launches, its scope, severity tiers, payouts and legal safe harbour will be published here in full before any submission is expected. Until then, disclosure is goodwill and we will not pretend it is a contract.

Please do not test on us

The Bohr deployment is a fair target and testnet BOT is free, so hammer it. What is not a target is BOT Chain mainnet, this site's hosting, or anything belonging to the chain operators — none of that is ours to authorise. Running the contracts locally works too; the deploy script is in the repository, and findings from a local run are just as welcome. There is still no bounty, so this is permission to test, not an offer of payment or a safe-harbour commitment.

Key custody

The protocol owner key is a single externally-owned account. No multisig, no governance contract. It can retune slash rates, windows, fee floors, the publisher set and the adapter registry, and half of that list still takes effect in one block.

What changed is the other half. Six setters — setRouter (on the registry and again on the Gold adapter), setTreasury, setWriter, setAdapter and setInputAttestor — have to be queued, announced on chain, and then executed no sooner than 21 days later and no later than 35. Those six are the ones that point a contract at an address it did not previously depend on, whether that is code or a destination for slashed collateral: swapping the Bronze adapter for one whose verify always returns true does not look like theft in any event these contracts emit, it just makes every subsequent delivery pass and every challenge lose. The delay is 21 days because that is UNBONDING_PERIOD, so an agent that objects can finish withdrawing its bond before the change lands.

Read it for what it is. It is a notice period, not a veto — nobody can stop a queued change, only see it coming and leave. It does nothing about a key that is patient. It covers none of the economic parameters, deliberately. And it arms only when finalizeBootstrap() has been called on all three contracts, which the deploy script does last and records in the manifest; a live deployment reporting bootstrapped() == false has no delay at all. The current Bohr deployment reports true on all three, read back off chain after deployment rather than assumed from the script's own output. Moving the key itself behind a multisig remains the single highest-value change available, and it has not been made.

Interface & infrastructure

Frontends are the soft target in this industry, so: this site is statically rendered with no server-side secrets and no backend of ours. It holds no funds and no keys. RPC access runs through BOT Chain's public endpoints, which we do not operate and which can see your IP and your queries. Deploy access is limited to the operator named in about.

No third-party requests

Fonts are self-hosted, there is no analytics script, no tag manager, no CDN for third-party code and no advertising pixel. The supply chain you inherit by loading this page is our build and nothing else. See cookies for the full accounting.

Verifying a wallet prompt

Never trust an address because this page displayed it. Check the contract address your wallet shows against the deployment artifact in the repository, confirm the chain id (BOT Chain 677, Bohr 968), and read the function being called. A composer that is honest today can be tampered with tomorrow; an artifact in version control is harder to rewrite quietly.

Reporting an impersonation

Tokens, airdrops, presales, staking programs and “official” BotID contracts do not exist. If you are shown one, it is a scam regardless of how convincing the site is. Report it to the same security address and we will say so publicly.

If you take one thing from this page

Reputation is a multiplier on capital that is actually at risk, never a substitute for it. An attacker's maximum extractable value is bounded by their own bond times the leverage cap — that bound is the security model, and every other mechanism here exists to keep it honest. A score is a summary of settled history reported by counterparties. It is not a guarantee, a rating, a licence, or advice. See risk disclosure.