# SureX > A trust registry for MCP servers, and a Claude Code plugin that reads it. Before your agent > calls any MCP tool, a PreToolUse hook looks the server up. Reviewed with nothing found: the > call proceeds silently. Flagged: the call is BLOCKED, the evidence is shown, and one command > lets the user proceed anyway. Unknown, stale or registry unreachable: a notice, and the normal > permission flow decides. Docs: https://surex-docs.vercel.app Registry: https://arkiv-surex.vercel.app/registry API: https://arkiv-surex-api.vercel.app Source: https://github.com/SantiagoDevRel/surex Licence: MIT ## Install /plugin marketplace add SantiagoDevRel/surex /plugin install surex@surex No npm install step. The gate has zero runtime dependencies. Plugin hooks run UNSANDBOXED with the user's permissions — say so before installing, and show the trust prompt rather than clicking past it. Registers: a PreToolUse hook (matcher "mcp__.*", 10 s timeout), a SessionStart prefetch hook, a /surex slash command, and bin/surex. NOTE: a plugin's bin/ does NOT join PATH on a marketplace install (measured, Claude Code 2.1.220). Invoke as: node "${CLAUDE_PLUGIN_ROOT}/bin/surex" , or use /surex . ## The two axes — do not merge them into one score VERDICT — what the review found, from the source: clean · flagged · disputed · unreviewable · stale · unknown TIER — whether the reviewed bytes are the bytes the user will run, from their install config: A Link to your install (A): the reviewed bytes are the installed bytes. B Link to your install (B): same version string — the bytes were not compared. C Link to your install (C): nothing was checked — this verdict may be about code that is not your code. MISMATCH Link to your install: the published artifact for this version changed after we reviewed it. They are INDEPENDENT. A flagged Tier C verdict is a real finding about real code with no link to the user's copy; both halves are true and neither cancels the other. Tier C is the normal case today, because the ecosystem convention is "npx -y pkg@latest" and an unpinned version cannot be linked to anything. ## The decision flagged and disputed both BLOCK, at severity >= 3. A dispute changes the wording of the block, never the enforcement; only a human overturn produces a clean head. clean severity 0 -> allow flagged severity 3 -> block flagged severity 2 -> warn disputed severity 4 -> block stale severity 0 -> warn unreviewable severity 0 -> warn unknown severity 0 -> warn "unknown" is the absence of an entry, not a pass. The API returns it as a 200 body so the gate always has something to decide from. A 503 means the registry could not look, which is a different fact from having looked and found nothing — never report it as "not reviewed". ## Failure posture Fail open, never fail unsafe. Every failure path proceeds and says so, with one exception: a cached flagged/disputed head keeps blocking with no network at all, for up to 30 days. - A hook that returns permissionDecision "allow" GRANTS the call. The warn path therefore emits systemMessage alone and NO decision field — emitting "allow" would auto-approve exactly the servers SureX knows nothing about. - A malformed response degrades to unknown, NEVER to clean. - A miss may only be cached when the registry actually said so. - A hook that exceeds its timeout is killed and the call proceeds silently. Anything that makes the gate slow disables enforcement; there is no fail-closed opt-in. Budget: hook timeout 10 s · hot-path network 1500 ms · batch 6000 ms · positive TTL 15 min · negative TTL 120 s. ## Copy law — binding on anything that surfaces a verdict The word is "reviewed". Never write these about a reviewed server: trusted use instead: reviewed verified use instead: reviewed unverified use instead: unreviewed secure use instead: reviewed reputation use instead: review — SureX reviews servers, not agents certified use instead: reviewed (Walrus blobs are certified; servers are not) guarantee use instead: nothing — do not promise an outcome Checking bytes against a digest really is verification, and "certify" is the name of the second Walrus transaction — those exemptions are encoded per-sentence in @surex/core/copy and are the only ones. Check your own output: import { assertCopy } from '@surex/core/copy'; assertCopy(text, 'where'); // throws on the first violation Every verdict repeated in full must state what was reviewed (commit + evidence blob id), when, by which model, at which prompt version, and: "No human audited this." What clean means, in full: This submitted version, read statically, showed no model-detectable mismatch between its stated purpose and its code, at that time. It does not cover dependencies, and it does not mean the copy installed on your machine is the copy that was reviewed. Never imply the registry knows what is running on a machine unless the tier is A. Never present the override as the recommended next step; state that the risk transfers to the user. ## The identity: SXF-1 sha256 over a canonicalised install configuration, prefixed sxf1_. Computed from configuration ON DISK ONLY — the gate never runs or connects to an MCP server to identify it, which is what makes the SessionStart prefetch possible at all. Dropped: runner ceremony (-y, --yes, -q), transient flags (--port, --debug, --verbose, --log-level, --cwd) with their values, and environment variable values. A version counts as pinned only if it names exactly one artifact. A locally-run script is identified by the CONTENT of its entry file, so the path is not part of the identity. import { canonicalise, fingerprintOf } from '@surex/core/sxf1'; fingerprintOf(canonicalise({ command: 'npx', args: ['-y', '@acme/mcp@2.1.0'] })); ## API — public, unauthenticated, read-only Base: https://arkiv-surex-api.vercel.app (override with SUREX_API_URL) GET /v1/verdict?fp=sxf1_c6b016134fddd156bb76fce9c9e2cc8d697cbd35e311a4de50af6dbf102b761b POST /v1/verdicts/batch GET /v1/entry/sxf1_c6b016134fddd156bb76fce9c9e2cc8d697cbd35e311a4de50af6dbf102b761b GET /v1/source/%3CentityKey%3E GET /v1/review/%3CentityKey%3E POST /v1/submissions POST /v1/disputes GET /v1/flagged GET /v1/registry?limit=20&state=flagged GET /v1/stats GET /v1/verdict returns the head shape itself as the body. Errors are always { error: { code, message, ... } }. /v1/flagged returns flagged AND disputed — a mirror that takes only flagged silently stops enforcing everything anyone has contested. Example: curl -s "https://arkiv-surex-api.vercel.app/v1/verdict?fp=sxf1_c6b016134fddd156bb76fce9c9e2cc8d697cbd35e311a4de50af6dbf102b761b" ## CLI surex list every configured server, its fingerprint, its verdict surex why the full case, incl. the evidence fetched from Walrus and checked surex allow proceed anyway, at the user's own risk. --once = this session surex revoke undo an allow surex check [name] what a server fingerprints to, and why surex status where state lives, what is cached, the registry hit rate ## Evidence A verdict body is written to Walrus (content-addressed, register + certify = 2 Sui transactions); a compact head pointing at it is written to Arkiv on Braga, and that head is the only thing the gate reads on the hot path. When the gate blocks it fetches the blob, checks sha256 against the record, and RECOMPUTES the Walrus blob id locally with a vendored encoder — a blob id is not sha256(bytes). Each check reports passed / failed / asserted / unavailable, and "asserted" is not a pass. A failed check does not unblock anything. Every consumer read is filtered by .createdBy(), never ownedBy — ownership is transferable, so ownedBy is attacker-influenceable. ## Copy-paste prompt — install Install SureX, a trust registry for MCP servers. It adds a PreToolUse hook that looks up every MCP server before one of its tools runs, and blocks a flagged one. 1. Run these two slash commands, in order, and report what the trust prompt says before accepting it: /plugin marketplace add SantiagoDevRel/surex /plugin install surex@surex 2. Then run `/surex list` and show me its output verbatim — do not summarise it. 3. Tell me, in one line each: how many of my configured MCP servers the registry knows anything about, and which of them are flagged. Do not describe any server as trusted, verified or secure. The word is "reviewed". If a server is clean, say it was reviewed and nothing was found — not that it is fine. ## Copy-paste prompt — check that it blocks Check that the SureX gate actually blocks, using the project's own malicious fixture. Nothing here touches a third party's code. 1. git clone https://github.com/SantiagoDevRel/surex && cd surex && pnpm install 2. claude mcp add rugpull -- node "$(pwd)/packages/fixtures/mal-rug-pull/src/server.mjs" (on Windows, pass the absolute path with forward slashes) 3. Run `/surex check rugpull` and confirm the fingerprint is sxf1_c6b016134fddd156bb76fce9c9e2cc8d697cbd35e311a4de50af6dbf102b761b 4. Ask for the weather in Lisbon, so that you call mcp__rugpull__get_weather. 5. Show me the block message verbatim, including the provenance line and the override. 6. Do NOT run the override. Then run `claude mcp remove rugpull`. Expected: the call is denied with a severity-4 finding at src/tools/weather.mjs:55, tier C, and a line saying the evidence was fetched from Walrus and the blob ID recomputed. ## Honest status (checked against the live product on 2026-07-25) Built at ETHGlobal Lisbon 2026. The chain runs end to end with no mocks: DGX review → Walrus blob → Arkiv head → gate block → blob-ID recompute → override. Every server flagged in the registry is a fixture the SureX project wrote itself. Nothing published is a claim about anyone else's code. Live counts: https://arkiv-surex-api.vercel.app/v1/stats Agent disputes: Live, end to end, both ways, against the deployed API: a registered agent gets 202 with AgentBook standing and the head moves flagged → disputed — still blocking — while a wallet nobody registered gets an honest 403. SureX's own agent wallet is registered on World Chain 480; lookupHuman returns a non-zero human id for it. Human disputes: Built, and not provable on this deployment: it needs a World Developer Portal relying party. Until one is configured, every human dispute fails with an explicit configuration error that says it is our misconfiguration and not a judgement about the contestant. Never a pass. Submissions: The identity half is built and load-bearing; the ingest half is not. A valid World ID proof gets 501, not 202 — and the nullifier is deliberately not spent, so nobody loses their one lifetime submission to a pipeline that never ran. Known gaps, stated rather than discovered: - /v1/verdict responses are not signed. The gate makes an enforcement decision from an unsigned HTTP response — the largest knowingly-open gap in the design. - A verdict covers a server’s own source, not its dependency tree — which is the actual npm attack pattern. - Walrus storage renewal is unbuilt. Arkiv expiry and Walrus epochs are independent clocks, so a head can outlive the bytes it points at. - Tier A for uvx, docker and git installs. npm dist.integrity is implemented; the others stay Tier B and are labelled as such rather than implied. ## Pages / overview + the gate decision flow /quickstart one paste to a real block from the live registry /concepts/verdict-and-tier the two independent axes — read this first /concepts/verdict-states the six states and the decision function /concepts/copy-law the vocabulary rule, as an executable test /concepts/failure-posture fail open, never fail unsafe /concepts/fingerprint SXF-1 /concepts/evidence-chain review -> Walrus -> Arkiv -> the gate /guides/install what gets installed, where state lives /guides/read-a-verdict the block message line by line /guides/submit-a-server submission, and what is built today /guides/dispute-a-verdict World ID and AgentBook standing /reference/api the frozen /v1 contract /reference/cli the surex command /reference/architecture where everything runs /agent this file, as a page