# approval.md: human approval for agent actions **A harness-agnostic, open-source framework for approving agent actions with a human in the loop** > The full content of https://approval.md and https://approval.md/features/ as > plain markdown, including the feature index the features page carries as JSON. > Generated by hand alongside those pages; where they disagree, the pages are > what shipped. The landing page is the onboarding below; everything from > "Try the loop" onward lives at https://approval.md/features/. ## Install ``` npm install -g approval-md ``` v0.3.0. Use a source checkout to contribute or to run unreleased work: ``` git clone https://github.com/approval-md/approval.md && cd approval.md && npm ci && npm run build && npm link ``` ## Getting started The landing page shows three graphics between these steps: an action pausing at the gate until a tap approves it while a `read.*` action passes straight through; a terminal typing an `APPROVAL.md` with both fenced blocks; and an email held at the gate, then delivered to the inbox after the tap, with `approval.granted` and `execution.completed` appended to the log. 1. **Install.** `npm install -g approval-md`. Node 20 or newer. 2. **Scaffold.** In your project: `approval init`. Writes `APPROVAL.md` and an empty `.approval/`. 3. **Configure your APPROVAL.md.** Specify agent autonomy across classes of actions: ``` { autonomy: autonomous } # actions flow freely through the gate { autonomy: supervised-retro } # actions flow, a sample is reviewed afterwards { autonomy: supervised-live, live_rate: 0.01 } # a declared fraction pauses at the gate for approval { autonomy: manual } # every action pauses at the gate for approval { autonomy: human-only } # the human performs the action; agents cannot request it ``` Add human values to inform agent judgement across the gate. Specify what you love, like, dislike, and how you give feedback. 4. **Sign it.** `approval setup identity`, then `approval policy attest --as human:`. The gate only functions while the latest policy has been attested. 5. **Turn it on.** `eval "$(approval env)"` then `approval up`. Run `approval setup channel telegram` first if you want the tap on your phone. 6. **Harness-agnostic.** Put the gate in front of whichever agent you run. Each one classifies every shell command and file edit against your policy. - Claude Code: a PreToolUse hook in `.claude/settings.json` running `approval hook claude-code`. https://github.com/approval-md/approval.md/blob/main/docs/claude-code-hook.md - Cursor: a preToolUse hook in `.cursor/hooks.json` running `approval hook cursor`, with `failClosed: true`. https://github.com/approval-md/approval.md/blob/main/docs/cursor-hook.md - Claude Agent SDK: a HookMatcher that pipes the event to `approval hook claude-code`. https://github.com/approval-md/approval.md/blob/main/docs/agent-sdk-hook.md - Any MCP client: `approval mcp serve`. https://github.com/approval-md/approval.md#gate-your-coding-agent - Codex: `approval codex prepare` writes the config templates; native enforcement is still experimental. https://github.com/approval-md/approval.md/blob/main/docs/codex-hook.md 7. **Release your approved agents.** Tweak your `APPROVAL.md` to find good human-agent harmony: loosen the gate for some actions, tighten it for others, re-attest, keep going. Alternatively run `approval quickstart` for a walkthrough. Links: feature set https://approval.md/features/ · README · SPEC · GitHub · npm · llms.txt. Apache 2.0 code · CC0 specification and schemas · releases approved through approval.md. # approval.md/features: the feature set and reference Feature index: approval-md 0.3.0. Everything below is the content of https://approval.md/features/. ## Try the loop This is a local simulation. The buttons change the card only: no request is sent, no token is minted, and no log is written. The prompt has the shape a real request takes. Its content: ``` approval.requested · seq 41 · agent:claude-admin · expires 13:07 UTC Send the deposit chaser to agency@example.co.uk Computed by the runtime action_key aprv-042:chaser:2026-09-05 class communicate.email.external resolved manual, by the irreversibility floor reversible false payload_hash 6f9b042133d9b1f0a66e4566c901acc2… chain head seq 40, 3e539bd4c77d… Claimed by the agent, unverified est_cost_usd 0 summary Chase the outstanding deposit, one recipient, no attachment [ Approve ] [ Reject ] ``` The log tail before a decision: ``` $ approval log tail {"event":"task.registered","seq":39,"actor":"agent:claude-admin"} {"event":"approval.requested","seq":41,"actor":"agent:claude-admin"} ``` On Approve, the verdict reads "granted, token minted at the terminal" and the tail continues: ``` {"event":"approval.granted","seq":42,"actor":"human:you","payload":{"token_sha256":"b9a0900adaed…"}} {"event":"execution.started","seq":43,"actor":"agent:claude-admin"} {"event":"execution.completed","seq":44,"payload":{"exit_code":0}} ``` On Reject, the verdict reads "rejected, no token exists, nothing ran" and the tail continues: ``` {"event":"approval.rejected","seq":42,"actor":"human:you","payload":{}} ``` ## The feature set 70 feature cards across nine facets: gate, channels, adapters, hooks, mcp, log, daemon, verification and examples. A card can have several facets. Tags mark what is shipped, what is specified, what is an example, what is behind a policy key, what is behind a flag, and what is human-only. The same list is embedded in features/index.html as JSON at `script#feature-index`. ### gate - **APPROVAL.md, the policy file** [shipped, spec]. A fenced `yaml approval-policy` block in markdown at your repo root; the runtime ignores the prose. https://github.com/approval-md/approval.md/blob/main/README.md#define-what-needs-approval - **Attestation and hash-mismatch** [shipped]. `approval policy attest` stores the file's SHA-256; a later edit refuses with `policy-not-attested`, detail `hash-mismatch`. https://github.com/approval-md/approval.md/blob/main/README.md#define-what-needs-approval - **Five autonomy levels** [shipped]. `human-only`, `manual`, `supervised-live`, `supervised-retro`, `autonomous`; bare `supervised` is an alias of `supervised-retro`. https://github.com/approval-md/approval.md/blob/main/schema/policy.schema.json - **The irreversibility floor** [shipped, spec]. An action declared `reversible: false` raises nonmanual autonomy to `manual`; `human-only` stays denied. https://github.com/approval-md/approval.md/blob/main/SPEC.md - **Policy loading fails closed** [shipped]. An unparseable policy resolves every class to `manual`, an unmatched action takes `defaults.autonomy`, and an unknown key fails validation. https://github.com/approval-md/approval.md/blob/main/README.md#define-what-needs-approval - **Single-use execution tokens** [shipped]. Minted on grant, delivered directly or sealed to the requester, and logged by hash; a second spend refuses `token-consumed`. https://github.com/approval-md/approval.md/blob/main/README.md#put-approvals-on-your-phone - **An approval binds bytes** [shipped]. The envelope declares a `payload_hash`, and bytes that hash to anything else are refused `payload-mismatch`. https://github.com/approval-md/approval.md/blob/main/README.md#put-approvals-on-your-phone - **Class comes from the envelope** [shipped]. Class, cost and reversibility are read from the registered envelope, never from a flag at request time. https://github.com/approval-md/approval.md/blob/main/examples/backlog-md-project/README.md - **Budgets computed from the log** [shipped]. Class `limits` and `budgets` scopes are conjunctive, and consumption is computed from the log over rolling windows. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#policy-reference - **Protected paths widen, never narrow** [shipped]. `protected_paths` adds paths, or routes each to a `policy.edit` sub-class, above a floor that never narrows. https://github.com/approval-md/approval.md/blob/main/README.md#define-what-needs-approval - **Amend a policy with the verb** [shipped]. `approval policy amend` lands new policy bytes and their attestation together, printing a semantic diff of what changed. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#policy-amend - **The open window** [human-only]. `approval gate open` requires a terminal and typed `understood`; gated shell and protected-file bypasses append `gate.bypassed`. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#gate - **Import your AGENTS.md prose** [shipped]. `approval import agents-md` turns a permissions section into a draft policy block with a fixed keyword table, no model. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#import-agents-md ### channels - **Telegram, the reference channel** [shipped]. The prompt carries a COMPUTED block, a CLAIMED block marked unverified, the full payload, and two buttons. https://github.com/approval-md/approval.md/blob/main/README.md#put-approvals-on-your-phone - **Paced or burst delivery** [behind a policy key]. `channels.telegram.delivery` is `paced` by default, showing one pending request at a time, or `burst`. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#policy-reference - **Prompt rows a policy orders** [behind a policy key]. `channels..prompt.rows` sets order only, never a whitelist, and `.hide` is refused for rows a decision needs. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#channel - **Payloads rendered by shape** [shipped]. The canonical email view shows every recognized field, its payload hash, and the raw-byte store path. https://github.com/approval-md/approval.md/blob/main/README.md#put-approvals-on-your-phone - **A local web queue on loopback** [shipped]. `approval channel web` binds hard-coded `127.0.0.1`; a returned grant token is displayed once, then discarded. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#security-and-evidence-limits - **A terminal channel, and withdrawal** [shipped]. `approval channel cli` decides at a terminal, and `approval withdraw` takes a pending request back. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#channel-cli - **A channel in the other direction** [shipped]. `approval journal write`, `approval values` and `approval feedback` carry operator guidance that never reaches enforcement. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#journal ### adapters - **A supported API for adapter authors** [new, shipped in 0.2.0]. `approval-md/adapters` exports the execution contract, credential provider and conformance runner with TypeScript declarations. https://github.com/approval-md/approval.md/blob/main/docs/adapter-api.md - **A vault the policy names** [shipped]. `approval vault set` encrypts one credential under a passphrase named by `vault.passphrase_env`; there is no `approval vault get`. https://github.com/approval-md/approval.md/blob/main/README.md#hand-a-grant-to-a-real-credential - **Email over SMTP, inside the token window** [shipped]. `approval adapter email` verifies the token, re-hashes the payload, opens the vault, sends over STARTTLS, closes it. https://github.com/approval-md/approval.md/blob/main/examples/email-demo.md - **AgentMail, and the two-key split** [shipped]. The agent holds an AgentMail key without `draft_send`, and the key that can send waits in the vault. https://github.com/approval-md/approval.md/blob/main/examples/agentmail-demo.md - **ZZZ threads and replies, gated** [new, shipped in 0.2.0]. After consuming the execution grant, `approval adapter zzz` sends with a vaulted ZZZ credential and bound payload. https://github.com/approval-md/approval.md#first-class-zzzbot-messages - **A draft is mutable, so the grant binds its bytes** [shipped]. `approval payload agentmail-draft` snapshots the draft, and one edited after the grant refuses `agentmail-draft-drifted`. https://github.com/approval-md/approval.md/blob/main/examples/agentmail-demo.md - **The Message-ID ties the mail to the chain** [shipped]. It is derived from the action key, the payload hash and the sender, so both identify each other later. https://github.com/approval-md/approval.md/blob/main/README.md#hand-a-grant-to-a-real-credential - **Setup fills the vault and proves it** [shipped]. `approval setup adapter email` reads the adapter's credential manifest, then probes the server without sending anything. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#setup-adapter ### hooks - **The Claude Code PreToolUse hook** [shipped]. `approval hook claude-code` classifies commands the harness runs itself and answers allow or deny, never ask. https://github.com/approval-md/approval.md/blob/main/docs/claude-code-hook.md - **The Cursor hook** [shipped]. `approval hook cursor` classifies `Shell`, `Write` and `Delete`, and requires `failClosed: true`. https://github.com/approval-md/approval.md/blob/main/docs/cursor-hook.md - **Codex direct patches, normal activation blocked** [experimental, shipped in 0.2.0]. `approval hook codex` experimentally gates direct patches; hidden execution directories keep Bash denied and outcomes diagnostic. https://github.com/approval-md/approval.md/blob/main/docs/codex-hook.md - **See how a command classifies first** [shipped]. `approval hook classify` prints a command line's class, the union over its segments, and touches nothing. https://github.com/approval-md/approval.md/blob/main/README.md#gate-your-coding-agent - **A human commits the hook entry** [shipped]. The entry lives in `.claude/settings.json` or `.cursor/hooks.json`, and the classifier treats both as gate configuration. https://github.com/approval-md/approval.md/blob/main/docs/claude-code-hook.md - **Two edges, stated up front** [shipped]. The hook never creates a log, denying `hook-log-unreachable`, and a hook grant mints no token. https://github.com/approval-md/approval.md/blob/main/docs/claude-code-hook.md - **The harness path is budgeted and sampled** [shipped]. A covered gated action appends `execution.started` marked `execution: "harness"`, and budgets and the sampler both draw on it. https://github.com/approval-md/approval.md/blob/main/docs/claude-code-hook.md ### mcp - **Codex over the supported stdio MCP route** [shipped]. Codex can use `approval mcp serve` for gate tools; it does not intercept ordinary tools, and repository setup awaits human installation and attestation. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#mcp-serve - **An MCP server over stdio** [shipped]. `approval mcp serve` publishes the agent's verbs as tools built from the registry behind `approval instructions --schemas`. https://github.com/approval-md/approval.md/blob/main/examples/mcp-demo.md - **Grant never travels over MCP** [shipped]. `grant`, `reject`, `revoke`, `policy attest` and `vault set` are absent from the tool list. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#mcp-serve - **A tool call cannot name an actor** [shipped]. `--as` is deleted from every published input schema, and the server's identity is appended last to every argv. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#mcp-serve - **Streamable HTTP, one session each** [behind a flag]. `--http` serves streamable HTTP on `127.0.0.1`, one transport per session, capped at 20 at once. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#mcp-serve - **Guest mode, one stranger per connection** [behind a flag]. `--guest` mints `agent:guest-<6 hex>` per session, allowlists nine tools, and clamps `wait` to five seconds. https://github.com/approval-md/approval.md/blob/main/examples/grok-bot-connector/runbook.md ### log - **Append-only, hash-chained JSONL** [shipped, spec]. Every record chains to the previous hash under `sha256/jcs`, and appends go through compare-and-append. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#security-and-evidence-limits - **One command answers for the chain** [shipped]. `approval log verify` walks it end to end and reports clean, torn-tail or corrupt, repairing nothing. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#log-verify - **Anchoring, the check a file cannot make about itself** [behind a flag]. `approval log verify --anchor` checks the working file against the newest committed copy this checkout can see. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#log-verify - **Human-signed checkpoints** [behind a policy key, human-only]. `approval log checkpoint` signs the head with an Ed25519 key from `audit.checkpoint_keys`; a due checkpoint warns, never refuses. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#log-checkpoint - **Git evidence, a second independent record** [behind a flag]. `approval daemon run --git-evidence` commits per tick in the log's own repository, changing no verdict. https://github.com/approval-md/approval.md/blob/main/docs/git-evidence.md - **Read and verify the log** [shipped]. `tail` prints the last N records and `export` streams every stored line byte for byte, both read-only. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#log - **Syncing a log two writers touched** [shipped]. Hash chains do not survive a git merge, so `approval log sync` reconciles a committed copy that moved. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#log-sync ### daemon - **The whole gate in one foreground process** [shipped]. `approval up` runs the daemon loop and every channel the policy configures, restarting one that falls over. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#up - **A preflight that fast-forwards and rebuilds** [shipped]. `approval up` fetches, fast-forwards when safe, rebuilds a stale `dist/`, then re-execs the freshly built binary. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#up - **It refuses rather than choosing for you** [shipped]. The preflight makes exactly two writes, a `--ff-only` merge and `npm run build`; anything else stops it. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#up - **Supervised-live draws** [behind a policy key]. `supervised-live` with a `live_rate` blocks that fraction on the gate, selected by HMAC over the `payload_hash`. https://github.com/approval-md/approval.md/blob/main/SPEC.md - **Retrospective sampling on a secret you hold** [behind a policy key]. `audit.supervised_sample_rate` escalates a fraction of supervised actions for review, under a secret the policy only names. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#policy-reference - **Incremental read proof** [behind a policy key]. `daemon.read_proof: incremental` hashes only the appended bytes, re-proving in full on a configured cadence. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#policy-reference - **Start it at login, having read the unit** [shipped]. `approval setup service` writes the launchd or systemd user unit and prints it for you to read. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#setup-service - **Secrets live in the OS keystore** [shipped]. `approval setup channel telegram` stores the token in the OS keystore, and `.approval/env` records only where it lives. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#env ### verification - **doctor: one command for the machine** [shipped]. `approval doctor` checks the build, identity, attestation, the chain, the channels, the vault and more, repairing nothing. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#doctor - **status reports the system, doctor the machine** [shipped]. `approval status` answers from the log; `approval doctor` answers for this machine, where a stale binary shows up. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#status - **coverage joins witnesses this project does not write** [shipped]. `approval coverage` reports effects witnessed by git, `gh` and providers, each with an evidence seq or `none`. https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md#coverage - **Conformance vectors for a second implementation** [shipped]. Six language-neutral vector files cover policy, chain, gate verdicts, JCS, refusal unions and schema, under a SHA-256 manifest. https://github.com/approval-md/approval.md/blob/main/conformance/README.md - **Exit codes an agent branches on** [shipped]. Seven frozen CLI codes; ordinary gate refusals use 1, while hook protocols also use 0 and 2. See the table below. - **Three check tiers, classified from the diff** [shipped]. `npm run check:changed` classifies the working tree into light, records or full, and ambiguity resolves to full. https://github.com/approval-md/approval.md/blob/main/docs/README-extended.md#running-the-checks - **Validate at the write boundary** [shipped]. Every event and envelope passes its JSON Schema before it is appended, and the policy schema is closed. https://github.com/approval-md/approval.md/blob/main/schema/event.schema.json ### examples - **Request, a tap on a phone, an executed run** [example]. A walkthrough against real Telegram, with a scripted twin that runs in CI against a mock Bot API. https://github.com/approval-md/approval.md/blob/main/examples/telegram-demo.md - **An agent's chaser, sent as real mail** [example]. The gate, the binding, the vault, the token window and the chain, against a real mail provider. https://github.com/approval-md/approval.md/blob/main/examples/email-demo.md - **The same grant, over AgentMail** [example]. The mail is composed as a draft, and the key that can send it waits in the vault. https://github.com/approval-md/approval.md/blob/main/examples/agentmail-demo.md - **An MCP client asks, the phone grants** [example]. Claude Code on one side and a phone on the other, including the refused `grant` tool call. https://github.com/approval-md/approval.md/blob/main/examples/mcp-demo.md - **A Backlog.md board behind the gate** [example]. One envelope on one task file, then `register`, `request`, `wait` and `run`, with what each prints. https://github.com/approval-md/approval.md/blob/main/examples/backlog-md-project/README.md - **Demo day: a web agent behind the gate** [example]. One laptop, one phone, one projector, with the queue and the log on screen while an attendee sets a task. https://github.com/approval-md/approval.md/blob/main/examples/web-agent-demo/runbook.md - **A Grok Bot agent on the far end** [example]. An agent in xAI's cloud asks for a branch push and an email, then is told to skip the gate. https://github.com/approval-md/approval.md/blob/main/examples/grok-bot-connector/runbook.md ## How this compares Four kinds of tool already cover part of this problem. Each entry says what it gives you and what it leaves to you. 1. **Harness-native permissions**: Harness permission and isolation controls are product-specific. This project's Claude Code and Cursor hooks append its portable log for covered actions; Codex direct patches remain experimental. 2. **AGENTS.md permissions prose**: `approval import agents-md` converts permissions prose into a draft policy. Enforcement still depends on wired hooks and adapters. 3. **Framework approval steps**: A framework integration can call approval.md at its existing pause or approval step. Policy, human channels and the verifiable log remain in approval.md. 4. **Hosted approval services**: With approval.md, you operate the daemon and channels and keep the log yourself. Hosted services vary in routing, retention and operational support. 5. **What is different here**: An attested markdown policy and a hash-chained local log, with optional vault-backed adapter credentials and single-use grants on paths that require human approval. ## Global invariants SPEC.md section 11.1 holds these across every surface of the runtime. They bind every task, and each is pinned by a named test file. 1. **Enforcement paths read only verified records.** Gate decisions are computed from log state that has passed chain verification, and a surface that cannot reach the log fails closed. 2. **Gate-typed events never accept caller timestamps.** `ts` is assigned by the runtime at the write boundary, and a caller-supplied value is refused. 3. **Raw secrets never appear in the log.** What appears is a hash, or ciphertext sealed to a recipient key the log does not hold. 4. **Self-reported fields never reduce scrutiny.** Values authored by the party under oversight may raise scrutiny and never lower it. 5. **Every check-then-append passes through compare-and-append.** No path reads decision-relevant state and appends on it without the atomic head check. 6. **Refusals are machine-readable and distinct**, every code union is pinned by a test, and the unions are frozen public API. 7. **Configuration is never loaded implicitly from the working tree.** The environment a gate operation runs under is established by the human who launched the process. 8. **A gate verdict whose event cannot be appended is a refusal.** No surface returns proceed, prints an allow, or hands back a token before the record exists. 9. **`human-only` classes are inert to agents.** Every gate verb refuses one with the same stable code, `class-human-only`, and appends no record on that path. 10. **Guidance never reaches enforcement.** The values block and the feedback reactions are human-authored guidance, and no path computing a verdict, a draw, a budget or a token reads either. ## Exit codes An agent branches on the exit code before it reads stdout, so these numbers are frozen. Code 1 and code 4 stay apart because a tampered log and an unreadable one are different facts. | Code | Meaning | | --- | --- | | 0 | success; harness pre-execution allow or deny verdict | | 1 | integrity failure, or an ordinary gate/runtime refusal | | 2 | usage error, or a post-execution hook report that did not land | | 3 | torn tail | | 4 | I/O error | | 5 | missing execution token (`approval run` only) | | 6 | timeout (`approval wait` only) | ## What this does not defend From SPEC.md section 11. This is an oversight layer for broadly cooperative agents. Hard enforcement applies where an adapter alone holds the credential. Human identity in v0.1 is config-declared, so the trust boundary is the local machine, and anyone who can set that configuration and write to the log is inside it. Attestation proves that someone with local control signed off, and never who. Undefended: an agent with direct credential access outside the adapters, a compromised host, collusion between an agent and a careless approver, and persuasion about whether bytes you did see should be approved. You run the daemon and listener yourself. The optional command sandbox has platform limits; it does not isolate a whole agent harness. The vault and adapter protect actions routed through them; single-use grants bind the payloads that require human approval. ## Read on - SPEC.md: https://github.com/approval-md/approval.md/blob/main/SPEC.md - README: https://github.com/approval-md/approval.md/blob/main/README.md - CLI reference: https://github.com/approval-md/approval.md/blob/main/docs/cli-reference.md - CLAUDE.md: https://github.com/approval-md/approval.md/blob/main/CLAUDE.md - Conformance suite: https://github.com/approval-md/approval.md/blob/main/conformance/README.md - GitHub: https://github.com/approval-md/approval.md - npm: https://www.npmjs.com/package/approval-md - Short index for language models: https://approval.md/llms.txt Apache 2.0 code. The specification and schemas are CC0. Releases of approval.md are approved through approval.md. Contact: hello@approval.md