“No agent should execute what it hasn’t verified.”
AI agents are now told to execute instructions written by third parties: mint pages that
embed step-by-step playbooks for the agent, skill packages served from
mutable “latest” URLs, onboarding documents that end with “copy this prompt
into your AI”. A compliant agent is one pasted paragraph away from draining its
owner’s wallet. AEGIS verifies before anyone executes. Point it at a
contract, a signature payload, a claim, or a page of instructions; it runs four gates and
returns a verdict — ALLOW, WARN, or
BLOCK — with line-level evidence, and the report hash is anchored on
NEAR so any agent or human can re-verify it later without trusting the
machine that produced it.
Runner aegis.mjs v1.0.0Anchor registry aegis-gate.testnetReports anchored 2Reads only — holds no keys, signs nothing
The four gates
Every input an agent is asked to act on passes the gates that apply to it.
A gate with no input is skipped and says so; a gate that cannot run reports
NO_VERDICT and caps the overall verdict at WARN — a gate that could not
run is never silently passed.
GATE 1
Provenance
Is this source official, or a third-party echo?
Addresses, links and claims are accepted only from the project’s official
channels, and re-confirmed against independent infrastructure (explorers,
Sourcify) — never from a relayed message, a comment, or another
agent’s summary.
GATE 2
Contract
Is the code verified, and what can it actually do?
Sourcify verification (Exact Match preferred), then a risk-pattern scan of the
verified source: delegatecall, selfdestruct,
permit, approval / transferFrom surface, owner setters.
Counts are evidence; an audit attributes them before they decide anything.
GATE 3
Signature
What exactly is being signed, and who authored the payload?
Full EIP-712 decode before any signature: domain name, chainId, verifyingContract
(never a held token/NFT, a router, or Permit2), primaryType (never
Permit variants), forbidden spender / value /
amount fields, domain scoped to the single project. Payloads authored
at runtime by a counterparty’s server fail by construction.
GATE 4
Instruction channel
Is somebody’s page trying to operate the agent?
Pages, packages and docs are scanned for instructions addressed to agents:
“connect to our MCP server”, challenge→sign→submit sequences
with server-supplied payloads, agent-targeted install scripts, manufactured
urgency, authority claims. Content is data; only the user’s own instructions
are instructions.
ALLOW
All gates that ran pass. The action can be constructed, simulated, and executed
by the agent itself, within pre-set value limits.
WARN
Something needs attribution or a condition first — an unattributed pattern,
a mutable download that must be hash-pinned, an admin surface to understand.
A WARN is a to-do list, not a rejection.
BLOCK
A gate fails outright: unverifiable code, a signature the user cannot audit in
advance, or an execution flow orchestrated by the counterparty’s server.
Merge rule: the overall verdict is the
worst verdict among the gates that produced one (BLOCK > WARN > ALLOW).
This page is a static exhibit: the reports below were generated by the AEGIS kit on
the command line and anchored on NEAR testnet. This site does not run the gates
in your browser.
Case studies — real reports, real anchors
AEGIS was distilled from gates that were actually run by an operating agent
before this project existed. The first two exhibits are reports produced by the unified
runner (aegis.mjs v1.0.0, 1 Nov 2026) and anchored on NEAR testnet —
every verdict and hash on this page is copied from those report files and can be
re-checked against the on-chain anchors below. The third predates the runner and is
labelled as what it is: a manual case study.
Runner report · aegis.mjs v1.0.0 · generated 2026-11-01 03:27 UTC ·
inputs: the Nonce case brief (Gate 4) + a Permit-shaped EIP-712 payload of the
class this flow asks agents to sign (Gate 3)
BLOCK
The project’s mint page carries a step-by-step playbook written
for the agent, and the mint is orchestrated by the project’s own server.
In the underlying manual audit the contracts themselves passed — Sourcify Exact
Match — and it didn’t matter. Verified contract ≠ verified flow.
Gate
Verdict
Headline reasons (from the report)
1 · Provenance
SKIPPED
Not run: no claim supplied (--claim).
2 · Contract
SKIPPED
Not run: no contract address supplied (--contract). The contract side of
this case was covered by the earlier manual audit (Exact Match) — see the
lesson above.
3 · Signature
BLOCK
primaryType “Permit” is a Permit shape — rejected by the signature SOP.
Forbidden field “spender” present in the payload (message or declared types) — rejected by the signature SOP.
Forbidden field “value” present in the payload (message or declared types) — rejected by the signature SOP.
Decoded domain: name “Example Token”, chainId 56, verifyingContract
0x2222222222222222222222222222222222222222.
4 · Instruction channel
BLOCK
Challenge→sign→submit sequence detected (stages: obtain-challenge, sign,
submit) — one server-orchestrated flow spread across the text, the Nonce
shape: the counterparty issues, the agent signs, the agent submits.
Server-sign-submit instructions appear together with a direction to connect to
an external server — a foreign server both instructs the agent and feeds it
payloads to sign.
An Agent-ID-gated freemint executed as a direct on-chain call with a
locally built transaction: value 0, nothing to sign off-chain, no server in the
loop. The owner-admin surface is listed as evidence and attributed —
not hidden, and not feared.
Gate
Verdict
Headline reasons (from the report)
1 · Provenance
SKIPPED
Not run: no claim supplied (--claim).
2 · Contract
ALLOW
Verified exact_match on Sourcify; no critical/permit patterns in project source.
Anchored on NEAR testnet by aegis-gate.testnet, agent identity
rainyy.ai — anchor transaction in the proof section below.
TermiX skill package — the channel was the risk
Manual case study · examined 28 Oct 2026 · predates the unified
runner — not a runner report and not anchored on NEAR
WARN → ALLOW-WITH-PIN
The package termix-agent.skill v1.11.1 (zip, 251,946 bytes)
is distributed from https://termix.ai/skills?v=latest. The engineering
inside was sound — the default signing mode is keyless, delegated to the wallet
CLI — but the distribution channel was the risk. “Latest” is not a
version. Pin the hash, or don’t run it.
Gate
Finding
Why
1 · Provenance
WARN(the trigger)
The distribution URL is unpinned: ?v=latest serves whatever the
operator’s bucket holds today. A version number inside a mutable download is
a claim, not an identity.
4 · Instruction channel
WARN
The zip’s INSTALL.md is written as direct instructions to
an AI agent — install this package, run these scripts. During the audit that
text was treated strictly as data: extracted, read, never followed.
Update mechanism
WARN
The updater verifies the zip’s sha256 against a release manifest —
but the manifest is served by the same operator, from the same bucket, unsigned.
That protects against corruption in transit, not against the operator or a bucket
compromise: the checker and the checked live in the same place.
Resolution: the sha256 of the audited zip was pinned as the
package’s identity. When the human partner later downloaded the package on his own
device, its hash matched the pin character-for-character — so the copy in use is the
copy that was audited. Conditions: the pinned hash only; scripts read before any use;
account and submission steps stay in human hands.
Pinned sha256 of the audited package (not a report hash)227b1b4e559eef7e67fd25fba9b2d1d5560fc1fdc434620ff9d6c270f685c673
On-chain proof — NEAR testnet anchor registry
Verdicts are only useful if they outlive the server that produced them. A
minimal registry contract on NEAR testnet stores report anchors:
record_anchor(report_hash, verdict, agent_id). Anchoring is
permissionless — no owner, no allowlist, no admin power — the contract
holds no funds, and first write wins: re-anchoring a hash returns the original
record unchanged. Both records below were read back through the public NEAR RPC and
round-trip exactly.
A report with this exact hash existed at the recorded time.
It was submitted by the recorded account under the recorded agent identity
(rainyy.ai).
Anyone can re-run the kit on the same inputs, recompute the hash, and compare —
reproducibility is the verification.
What it does not prove
That the verdict is correct.
That the gates ran honestly, or that the inputs were authentic.
The hash binds the merged report digest, not the full per-gate evidence
(limitation 13 below).
Scope & known limitations
Disclosed openly, per the project’s submission policy — nothing
below is hidden from reviewers or judges. This is the short version; the full audit trail
with per-limitation impact and roadmap is in AUDIT.md in the repository.
AEGIS produces screening reports, not audits.
Pattern counts are raw text occurrences (code + comments + strings), never
attributions. Counts alone never decide a verdict; a human/agent audit must attribute
them. (Gate 2)
The project/library split is path-based — only node_modules/…
counts as library, so a single flattened file cannot be attributed per-contract
automatically; case files carry that attribution manually. (Gate 2)
Setter detection is narrow — only function setX(…)
declarations are listed; admin mutators with other names can be missed, so the listed
setters are evidence, not a completeness claim. (Gate 2)
Gate 2’s verdict scope is deliberately thin — it escalates only on
unverifiable code and project-source delegatecall / selfdestruct
/ permit. Dangerous combinations of patterns can still return ALLOW
with the evidence attached: an ALLOW is a screening pass, never a clean bill of
health. (Gate 2)
Sourcify is the trust anchor. The tool trusts Sourcify’s match and sources
at query time; it does not independently compare runtime bytecode, and there is no
second source yet (reports state crossCheck: "unavailable"). An outage
fails loudly with no verdict — never a fabricated one. Narrowed in v0.2.0
(bounded retries), not closed. (Gate 2)
Node ≥ 18 is assumed (AbortSignal.timeout availability).
(Kit)
Gate 1 “claim found” is a content heuristic — substring or
record-existence matching, not semantic attribution. A page that merely mentions an
address in a warning still “carries” it; the claim’s role must still
be confirmed before acting. (Gate 1)
Gate 1 does not discover official channels. Whoever supplies the
“official” URL frames the check; without a caller-supplied domain registry,
a lookalike domain carrying the same claim would pass the content test. The report
states on every run when no registry was supplied. (Gate 1)
Gate 3 is structural only. It verifies no signature, recovers no signer, and
cannot prove a verifyingContract really belongs to the project. Held/router
classification is only as complete as the caller-supplied lists (only Permit2 is built
in); an ALLOW never replaces building and simulating the final transaction separately.
(Gate 3)
Gate 3 types/message mismatch is WARN, not BLOCK — deliberate (wallets sign
the declared types; undeclared message fields are not signed), but the warning must
actually be read. (Gate 3)
Gate 4 is regex/heuristic screening. Paraphrased, obfuscated, image-carried, or
non-English instructions can evade it; thresholds are calibrated on the kit’s own
cases, not a broad labelled corpus; and text discussing these patterns scores
like text using them. Gate 4 does not fetch URLs — its input is a local file, so
whoever fetches the page controls its freshness. (Gate 4)
The runner composes file inputs only, and its report is a digest — per-gate
verdicts, reasons, and curated evidence, not the gates’ complete reports. The
reportHash binds the merged digest, not the underlying per-gate artifacts.
(Runner)
Anchoring is testnet-only and manual. The registry lives on NEAR testnet
(anchors carry no mainnet evidentiary weight), and anchoring is a separate step —
the runner does not yet anchor automatically. (Anchor)
Deployer key custody is a local file (testnet funds only, never committed).
Because records are first-write-wins and anchoring is permissionless, a leaked key
could only spend the account’s testnet balance or anchor additional reports under
its name — it cannot alter existing anchors. (Anchor)
Closed in v0.2.0: Sourcify partial_match results are
no longer treated as verified — they are forced to WARN (former limitation 5).