Overview
Before an agent connects to an MCP server, which gets shell, filesystem and credential access, it pays $0.002 over x402 on Hedera for proof that a unique, domain-verified, bonded maintainer still stands behind this exact build and these exact tools. The connection is refused when the tools change underneath it.
Proofmark sells liveness, not provenance. Signatures prove an account built something. Proofmark proves a unique, domain-verified, bonded maintainer still backs it this minute, and lets them take it back. It does not prove the code is safe, and nothing here claims it does.
How a connect works
The agent client talks to the Proofmark proxy instead of the server. The proxy stays in the path for the whole session.
- The proxy signs the request with the agent wallet and asks the gateway for
GET /attest. - The gateway checks the Agent Pass, the agent's budget and the server's tier. A bad pass or a blocked tier is refused before any payment.
- The gateway answers
402. The proxy pays over x402 on Hedera with no human approving, and the settlement is anchored on HCS. - The proxy checks the maintainer signature, hashes the artefact it is about to run, spawns the server and hashes the tool list the server actually sent.
- Both hashes match, so the session proceeds. On every
tools/list_changedthe proxy hashes again, and every few seconds it re-checks liveness.
The Agent Pass gates authority to spend. It is not a per-payment approval.
The two hashes
| Hash | Source | Catches |
|---|---|---|
| Artefact | Registry integrity field: mcpb fileSha256, OCI digest, npm dist.integrity | A changed binary |
| Tool manifest | SHA-256 over sorted tool names, descriptions and input schemas | The same binary quietly offering a new tool |
The manifest is derived, never asked for. The attestation worker fetches the pinned artefact, verifies its hash, runs it with no network and captures tools/list itself. A server that later advertises a different manifest therefore implies a different binary. Canonicalisation sorts keys, normalises whitespace and strips volatile fields, so identical servers never produce false mismatches.
Tiers
| Tier | Condition | Result |
|---|---|---|
| Green | Claimed, attested, both hashes match, key live | Connect normally, full tool set, maintainer paid |
| Sandboxed | No maintainer has claimed the name | Container with no network, read-only filesystem and dropped capabilities. Read-only tools only. A server that cannot be containerised is not launched locally. |
| Blocked | Hash mismatch, revoked or expired key, or a name one to two edits from a claimed server | Refused, with the reason and the added tool named |
There is no amber tier. Pinning to the last attested version would let an attacker freeze everyone on a vulnerable build by simply stopping attestation.
Hedera

x402. The gateway is an x402 merchant on hedera:testnet using the official @x402/hedera exact scheme and the Blocky402 facilitator. The price and payTo account are set per maintainer and capped at $0.05. The asset is configurable and defaults to HBAR, because the testnet USDC token id is not reliable across facilitators.
HCS. Every attestation and every payment is one message on a public topic, carrying the hash pair and the settlement transaction id. The topic is replayable from any mirror node, and the HCS log page reads it back.
Every transaction id in the app links to HashScan.
ENSv2

The tree runs on Sepolia. Addresses and ABIs are loaded from the ENS deployments table, not hard-coded.
proofmark.eth Permissioned Registry, administered by Proofmark
acme.proofmark.eth maintainer org, owned by the claiming human
status, payto, x402.price, world.verified
alice.acme.proofmark.eth co-maintainer: attest.key, expires
bob.acme.proofmark.eth co-maintainer: attest.key, expires- One resolver per org. Argument-scoped grants apply to every name an instance serves, so a shared resolver would let one org write another's
payto. - Per-key roles. A co-maintainer's role covers
attest.keyand nothing else. Writingstatusreverts withEACUnauthorizedAccountRoles. - Surgical revocation. Revoking one subname invalidates what that key signed and leaves every other co-maintainer's attestations standing.
- Expiry. Subnames expire on their own, so a co-maintainer who drifts away loses the ability to attest without anyone cleaning up.
- Wildcard. Any name that is not claimed resolves to sandboxed, so there is an answer for every server.
- Gas. Claim and revoke are one write each. Nothing is written per attestation.
World

Agent Pass. The official World ID Agent Pass is the World IdP, an OpenID Connect issuer. An agent has no browser, so the gateway runs the Device Authorization Grant for it: a person approves in World, the gateway validates the ID token (signature, issuer, audience, expiry, auth_time, acr and amr) and records the validated pass on the agent's ENS name. The pass is a pure function of that record and the clock, so when the authentication is older than the allowed age the payment is refused and the connection never happens. Nothing can override it. The pass is spend authority, not maintainer identity.
IDKit at the claim. Claiming a server needs a World credential. The gateway signs the request context, forwards the proof to the World Developer Portal and stores the nullifier. A second claim with the same nullifier is rejected. The client result is never trusted. This limits mass sockpuppets and nothing more: World does not prove the person is the real maintainer, and a household member could share a credential. That is a known weakness, and it is why identity rests on two more layers, a verified namespace (a GitHub account or a domain) and a forfeitable bond.
Minimum sufficient assurance. For a maintainer, uniqueness is the property needed: one real person, one claim. The claim accepts any one credential that proves that (Proof of Human, a passport or a national ID) and the gateway rejects a proof that used none of them. Nothing about the person is stored beyond the nullifier, and weaker credentials such as a device or a selfie alone are not accepted. For the agent, the pass only has to show that a verified person authorized it to spend.
When verification does not succeed. Closing the World dialog collects no proof, so Claim stays on the human step and nothing is written to ENS. A proof the gateway rejects (wrong action, bad signature, or a nullifier that already claimed) returns an error and no organisation is registered. An agent with no pass, an expired pass or too weak a credential is refused before any payment, so the protected action never happens.
MCP
Name ownership is solved by the official registry, so Proofmark uses it: mcpName in package.json, an mcp-name: line in the README, or an OCI label. There is no challenge file.
The proxy is a transparent MCP server. It exposes the real server's tools once verified, plus proofmark_explain and proofmark_budget. MCP has no protocol-level per-tool access control, so the proxy filters what reaches the model and the container bounds what reaches the machine.
Prior art
| Work | What it does | What is left |
|---|---|---|
| Stacklok ToolHive | Verifies Sigstore signatures and build attestations on MCP images before deployment | No revocation, no human identity beyond a GitHub account, nothing once deployed |
| Tool-definition hash pinning | Canonical SHA-256 over tool names, descriptions and schemas, rechecked on reconnect | Nobody ships or signs it, and nothing binds it to a person |
| Official MCP registry | Verifies name ownership. mcpb entries carry fileSha256 | No signing, no revocation, no identity |
| Trustworthy registry paper | Sigstore signing plus runtime JWS | No revocation, no human identity, no payments |
Proofmark adds three narrow things: revocation that reaches a running fleet, a unique, domain-verified, bonded maintainer instead of one account, and a recurring payment that makes the freshness check sustainable.
API
| Endpoint | What it does |
|---|---|
GET /attest?server= | x402-protected. Returns the signed hash pair with a live revocation check. |
GET /check?server= | Public and free. Resolves any name through the wildcard. |
POST /claim | Registry ownership, World proof, ENS registration. |
POST /comaintainers | Add an expiring co-maintainer subname. Owner signature required. |
POST /revoke | Revoke one co-maintainer key. Owner signature required. |
POST /gossip | A client reports the manifest hash it observed. |
GET /hcs | The public anchor log read from the mirror node. |
Run it
npm install cp .env.example .env # fill in the values npm run hcs:create -w @proofmark/gateway npm run agents:create -w @proofmark/gateway -- 2 5 1 npm run gateway # http://localhost:8787 npm run web # http://localhost:3000