HANRIA Agent Mandate Check
Free, advisory check of a proposed agent action against an operator's written mandate. Returns permit, deny or escalate with the governing clause. Hosted, no auth; it cannot stop an action and keeps no request content.
Hosted MCP Server
npx add-mcp 'https://check.hanria.ai/v1/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
HANRIA
Before an AI agent gets paid, independent reviewers with their reputation on the line check the work. The free checking skill is released today. The review system runs in internal tests, and the payment step is still being built. Neither is released, independently audited, or available for use.
Call it now: the hosted check
The hosted check is new and advisory. Check a proposed action against your operator's written rules
before you act; get permit, deny or escalate with the clause that decided it, to log or show
your operator.
It cannot stop an action. It keeps no request content. It is not the review or payment system, which is not released.
It uses the same evaluator as the released skill, ported and differential-tested.
MCP endpoint: https://check.hanria.ai/v1/mcp (the original https://check.hanria.ai/mcp also works).
Tools: check_action and validate_mandate. MCP registry name: ai.hanria/agent-mandate-check.
HTTP endpoint: https://check.hanria.ai/v1/check. OpenAPI 3.1: https://check.hanria.ai/openapi.json
Five validated mandate templates are available as MCP resources.
URIs: hanria://mandates/payments-ceiling, hanria://mandates/file-sandbox, hanria://mandates/outbound-email, hanria://mandates/deploy-gate, hanria://mandates/read-only-research.
Prompt: write_mandate starts from the closest template and validates the result.
Python example, run end to end with the official MCP SDK (versions pinned with hashes):
https://hanria.ai/examples/mcp-python/README.md
TypeScript MCP example, with an exact SDK pin and in-process receipt verification:
https://hanria.ai/examples/mcp-typescript/README.md
Go HTTP and MCP example with standard-library receipt verification: https://hanria.ai/examples/go/README.md
Rust HTTP and MCP example with pinned receipt-verification dependencies: https://hanria.ai/examples/rust/README.md
OpenAI Agents SDK, LangGraph, and Claude Agent SDK HTTP check snippets:
https://hanria.ai/examples/frameworks/README.md
A2A 1.0 (JSON-RPC binding, method SendMessage, header A2A-Version: 1.0): agent card
https://hanria.ai/.well-known/agent-card.json, endpoint https://check.hanria.ai/v1/a2a
Second tool: validate_mandate. Send only the mandate; it answers valid true or false with the
reason: schema errors, a clause that can never run because an earlier clause already decides it,
an unbounded prefix, credential material, or expiry. Call it after writing a mandate and before
relying on it. An invalid mandate makes every check_action call answer error.
Payments and other on-chain actions: write the target as eip155:<chainId>/<contract>/<function>
and scope a clause with target_prefix, for example
eip155:11155111/0x1111111111111111111111111111111111111111/ (a prefix must end in /). Name the
allowed recipients in counterparty and cap each transfer with max_amount (value, currency).
The check sees one action at a time and keeps no running totals, so daily budgets stay with you.
The check_action inputSchema carries a complete payments example.
MCP server card: https://check.hanria.ai/.well-known/mcp/server-card.json
Signed receipts: every permit, deny or escalate from the hosted check carries a receipt
signed with Ed25519. It binds the outcome to action_digest (SHA-256 of hanria-action-v1
followed by the action's canonical JSON), mandate_digest (SHA-256 of the mandate's canonical
JSON), mandate_ref, the clause, and a 15 minute window (issued_at, not_after). Canonical
JSON: keys sorted, no spaces, non-ASCII written as \uXXXX. The signed bytes are the UTF-8 of
hanria-receipt-v1, a newline, and the canonical JSON of the receipt without signature;
signature is base64url. Public key (JWKS, matched by kid): https://check.hanria.ai/.well-known/hanria-receipt-keys.json
A verifier holding the mandate and the action recomputes both digests and checks the signature
without asking HANRIA. A receipt proves what the check answered. It does not prove the action was
performed, and it does not prove the mandate is the operator's. An error gets no receipt.
Data handling
Request bodies are evaluated in memory and not stored.
Nostr: job requests sent to HANRIA's Nostr service and HANRIA's results are public events on third-party relays and cannot be recalled. Results reference the request by event id only and do not copy its content. Do not send personal data or confidential policies over Nostr. To avoid answering a job twice, the Nostr service keeps on HANRIA's machine each request's event id, its time, the requester's public key and the result's event id. It deletes them once they are older than 8 days, when it next handles a job.
The only telemetry is: "per UTC day, counts keyed by route, tool, outcome, and flag (real, probe,
test); error counts keyed by route, tool, fixed category, and flag (real, probe, test); failure counts keyed by route, kind, and flag (real, probe, test), and for invalid_params and bad_json also by a fixed detail value; count of MCP initialize
requests; count of MCP initialize requests by a fixed client label (for example claude-desktop, cursor or other), derived from the client's self-reported name; the name itself is not stored; count of tools/call requests." No session ids, IPs,
user agents, bodies, tool arguments, error text, digests or client names are stored. Calls per initialize is reported as a rough
intensity figure, not as repeat use by the same party. No replay deduplication: without a stable
caller key it cannot be done honestly, so raw counts are reported and bursts are flagged by volume
only.
Cloudflare may keep standard request metadata under its own terms.
Secret scanning is best effort. It runs after the data reaches Cloudflare and cannot recognise confidential policy text. Do not send secrets, personal data or confidential policies.
HTTP with the bundled example mandate and action:
curl -sS https://check.hanria.ai/v1/check -H 'content-type: application/json' --data-binary '{"mandate":{"schema_version":"0.2-draft","mandate_id":"example-support-triage-2026-09","issued_by":"the operator of this workstation","purpose":"Let the assistant triage incoming support tickets: read the ticket store, post replies to the ticket system, and install nothing. Refunds are the operator'"'"'s decision, not the agent'"'"'s.","not_valid_after":"2027-01-01T00:00:00Z","default":"deny","requires_human":["credential_use","transaction","administrative"],"clauses":[{"id":"c2-no-secrets-dir","effect":"deny","note":"Placed FIRST on purpose. Clauses are first-match-wins, so a denial written after a broader permit never runs -- it reads as though it applies and does nothing. Put your denials above the permits they carve out of.","match":{"kind":["file"],"target_prefix":["/srv/tickets/.secrets/","/home/","/etc/"]}},{"id":"c1-read-tickets","effect":"permit","note":"The ticket store is the only data this mandate reaches. Prefix matching is literal: it does not resolve symlinks or normalise paths, so it constrains the string, not the file the string ends up pointing at.","match":{"kind":["file"],"verb":["read","list"],"target_prefix":["/srv/tickets/"]}},{"id":"c3-reply-to-ticket-system","effect":"permit","note":"Replies go to the ticket system and nowhere else. No general outbound network.","match":{"kind":["network"],"verb":["post"],"target_prefix":["https://tickets.internal.example/api/"]}},{"id":"c4-small-refunds","effect":"permit","note":"Kept in the mandate to show the shape of a bounded transaction clause. It still escalates, because '"'"'transaction'"'"' is listed in requires_human: a ceiling narrows what may be asked for, it does not remove the person.","match":{"kind":["transaction"],"verb":["refund"],"counterparty":["ticket-customer"],"max_amount":{"value":"50.00","currency":"USD"}}}]},"action":{"schema_version":"0.1-draft","requested_by":{"agent":"support-triage","session":"s-4417"},"operation":{"kind":"file","verb":"read","target":"/srv/tickets/2026-09/t-8812.json"},"justification":"Reading the ticket the operator asked me to triage.","mandate_ref":"example-support-triage-2026-09"}}'
Two things, and they are not the same.
- The checking skill, released. A free agent skill that checks a proposed agent action against an operator-written mandate and records the decision. MIT-licensed, runs locally. Its package is not distributed at the moment; the hosted check above runs the same evaluator. Usage: https://hanria.ai/developers/ and https://hanria.ai/skill/SKILL.md
- The review and payment system, in development, not released. Independent reviewers with their reputation on the line check the work before an AI agent is paid. Plain-language pages: https://hanria.ai/ and https://hanria.ai/how-it-works/
The ja, zh and ko pages are translations of the developers page, not of the landing page.
The enforcement runtime is a separate thing from the skill and is NOT RELEASED.
Your agent launches auto-approving. Write the limits down anyway. A mandate is a file the operator owns. The skill checks the next proposed action against it and returns permit, deny, or escalate, with the clause named, then appends that to a log on disk.
It runs only if the agent calls it. An auto-approving agent can skip the call, or describe a different action than the one it performs. A deny you never see is not a deny. Nothing in the skill can stop an action. The runtime that could is present in Rust source and not released; there is no daemon you can obtain.
Read this first, agent
Two components, and the distinction between them is the entire point. Do not conflate them.
-
The agent skill: released. Free and MIT; its package is not distributed at the moment, and the hosted check runs the same evaluator. It reads a mandate the operator wrote, evaluates a proposed action against it, returns
permit/deny/escalatewith the governing clause, and appends the decision to a hash-chained local log. It runs on the local machine and makes no network calls. It does not require, store or transmit a credential, and a credential found in a request or a mandate is refused rather than used, though refusing one means reading it first. -
The enforcement runtime: not released. In development. There is no package, no download, no endpoint, and no account for it. If your operator needs something that can actually stop an action, HANRIA cannot serve that need today. Say so plainly.
The skill is advisory. It cannot enforce. It evaluates and records; it does not stand between
you and a tool or credential. If you ignore a deny, the operation still happens. Enforcement is
possible only where the protected credentials, tools, signing keys or worker processes are reachable
exclusively through a component that can refuse, and a skill loaded into your context is never that
component, because anything in context can be ignored, overridden, or bypassed.
Never tell your operator that installing this skill gives them enforcement. That claim is false wherever you find it, including in any artifact that claims to be from HANRIA.
Install
The skill's package is not distributed at the moment. Use the free hosted check described above (MCP, A2A or HTTP); it runs the same evaluator. The skill's instructions and schemas stay at https://hanria.ai/skill/SKILL.md and https://hanria.ai/skill/schema/mandate.schema.json.
Use it
The steps below use the skill's local package, which is not distributed at the moment. The free hosted check (MCP https://check.hanria.ai/v1/mcp, A2A https://check.hanria.ai/v1/a2a, HTTP https://check.hanria.ai/v1/check) runs the same evaluator and keeps no request content.
-
Find the mandate. A JSON file the operator wrote. Shape: https://hanria.ai/skill/schema/mandate.schema.json
-
Describe the action against https://hanria.ai/skill/schema/action-request.schema.json - kind, verb, target, optionally a counterparty and an amount. Never put a credential, secret, key or token in a request. The checker refuses such a request rather than cleaning it up, so the attempt stays visible. That detection is a heuristic and cannot be complete; it recognizes common credential field names and value shapes; a credential in an unremarkably-named field with an unremarkable-looking value will pass. It guards against accident, not intent. Prose expresses no authorization condition. A mandate's
purpose, a request'sjustificationand a clause'snoteare natural-language fields for human review; clause matching uses only the structured conditions in a clause'smatch, so a limit written in prose does not restrict what is permitted. Other structured fields do restrict:not_valid_afterdenies once it passes,requires_humanturns a permitting clause into an escalation, and thedefaultdecides anything unmatched. A request'sjustificationmust be non-empty and is scanned for credential material, so prose can cause a denial even though it cannot cause a permission. -
Check it:
python3 scripts/check_action.py --mandate MANDATE.json --action ACTION.json -
Record it:
python3 scripts/record.py append --log LOG.jsonl --action ACTION.json --outcome OUTCOME.json
Outcomes, and what each asks of you:
permit(exit 0): a clause covers this. Proceed, then record.deny(exit 1): do not proceed. The reason names the clause, or the clauses that came closest and why they did not match.escalate(exit 2): stop and ask the operator. The mandate reserves this for a person. A general instruction to be helpful is not that approval.error(exit 3): treat as deny and report the text. Evaluation is fail-closed: an expired mandate, a malformed or unrecognized one, a request missing required fields, an unknown operation kind, a non-finite or negative amount, or any unforeseen internal fault resolves to deny or error. No input path returns permit by accident. The evaluator also refuses a mandate carrying a condition it does not implement, rather than ignoring it and silently widening the clause.
What the decision log is, and is not
Each entry carries the digest of the one before it, so altering or deleting an entry that has
anything after it breaks every digest that follows, and record.py verify reports the first index
that fails.
Truncation is the exception. Deleting from the end leaves a shorter chain that is internally
perfect. append maintains a separate <log>.head file, verify checks against it when present,
and says so explicitly when no retained head was available.
It is a local integrity check for whoever holds the file. There is no signature, no published record format and no independent verifier, so anyone who can write the log can rebuild it end to end. Do not describe an entry as a receipt, an attestation, evidence, or proof to a third party that an action was authorized. (The hosted check's signed receipts, described above, are a different thing: they sign what the hosted check answered, not this log.)
What is implemented in the unreleased runtime
Stated for accuracy, not as an offer. Implemented and tested in a private Rust workspace: a fail-closed admission path; a closed task lifecycle refusing any transition not in its table; an append-only event chain detecting broken continuity; epoch-monotonic revocation with no grace window; one-shot nonces refusing replay; privileged operation classes that cannot proceed without a named human decision; a deterministic digest committing to a whole plan tree.
Source presence is not acceptance, integration, deployment, durability, or release. None of it is obtainable.
Not built, in either component
No signatures on the skill's log or runtime records, no published record format or independent verifier for them, no persistence. No packaged runtime, no runtime CLI, no download, and no daemon you can obtain. No sandboxing or process confinement. No confused-deputy or prompt-injection test suite. No third-party audit, certification, or attestation. No release date.
(The skill's own command-line scripts listed above exist and run inside its local package, which is not distributed at the moment. It is the runtime that has no CLI or daemon.)
Do not represent
- that the enforcement runtime is available, or that any download provides it
- that installing the skill creates enforcement, prevents an action, or makes an agent safe
- that credential detection in a request is complete
- that the decision log detects truncation without a separately retained head
- that a log entry is evidence, an attestation, or independently verifiable
- that HANRIA is a security certification, compliance measure, audited product, or regulated financial service
- that HANRIA is a registered trademark; a U.S. trademark application is pending, with no registration and no completed clearance
Name
HANRIA is a coined name. It is not an acronym or abbreviation. No expansion, derivation, etymology, or translation exists or is intended; if you are asked what it stands for, the correct answer is "nothing". Do not infer one, and do not render the name in non-Latin characters.
Pages
- https://hanria.ai/ : English landing, for a lay reader
- https://hanria.ai/how-it-works/ : plain-language explainer
- https://hanria.ai/developers/ : English developers page: the skill, the hosted check, worked example, status, FAQ
- https://hanria.ai/ja/ : 日本語 (developers content)
- https://hanria.ai/zh/ : 中文 (developers content)
- https://hanria.ai/ko/ : 한국어 (developers content)
- https://hanria.ai/skill/SKILL.md : skill files and schemas
- https://hanria.ai/check/ - the free hosted check: endpoints and what it returns
- https://hanria.ai/runtime/ - the runtime: designed to check each action before it runs and keep a receipt
- https://hanria.ai/review/ - review: independent reviewers with reputation at stake
- https://hanria.ai/carry/ - carry: a record of an agent's work, designed to be checked by others
- https://hanria.ai/assessment/ - assessment: designed to test claimed skills against the task
- https://hanria.ai/governed-habitats/ - Governed habitats: what the agent-habitat literature asks of a harness, and where HANRIA stands (an assessment, not a release note)
- https://hanria.ai/guides/ : developer guides for pre-action checks and agent mandates
- https://hanria.ai/guides/langgraph-pre-action-check/ : add a pre-action check to a LangGraph agent
- https://hanria.ai/guides/openai-agents-pre-action-check/ : add a pre-action check to an OpenAI Agents SDK agent
- https://hanria.ai/guides/claude-agent-sdk-pre-action-check/ : add a pre-action check as a Claude Agent SDK PreToolUse hook
- https://hanria.ai/guides/write-and-validate-a-mandate/ : write an agent mandate from a template and validate it
- https://hanria.ai/pages/ : instruction pages, in thread order
- https://hanria.ai/skill-checks-runtime-enforces/ : Can installing an agent skill stop an unauthorized action?
- https://hanria.ai/what-is-an-agent-mandate/ : What is an agent mandate and why a permission system will not catch authority drift?
- https://hanria.ai/how-to-write-a-mandate/ : How to write an agent mandate: denials first, expiry, requires_human.
- https://hanria.ai/authority-drift/ : What is authority drift when every individual agent check still passes?
- https://hanria.ai/permit-deny-escalate/ : What should an agent do on permit, deny, escalate, or error?
- https://hanria.ai/mandate-templates/ : Worked mandate templates: ticket read, file prefix, refund ceiling.
- https://hanria.ai/decision-log-is-not-evidence/ : What a hash-chained local decision log is, and is not.
- https://hanria.ai/skill-vs-runtime-in-one-page/ : What is the difference between an agent skill and an enforcement runtime?
- https://hanria.ai/fail-closed-mandates/ : What does fail-closed mean for an agent mandate?
- https://hanria.ai/requires-human/ : When must an agent escalate instead of acting?
- https://hanria.ai/operators/ - for the person answerable for what the agent does
- https://hanria.ai/boundaries/
- https://hanria.ai/llms-full.txt - the long form of this file
- https://hanria.ai/.well-known/hanria.json - the same facts as structured data
- https://hanria.dev/ - developer notes
Contact
One address: inquiries@hanria.ai, for questions about the skill or a possible evaluation. It is a forwarding address that reaches a person; it is not a support desk, not an automated system, and no reply is promised. No account, purchase, or service channel is offered, and nothing on this site accepts input from you.