spec

The Permissioning Protocol — v0.1 (Draft for Comment)

A robots.txt for agent actions.
A minimal, open standard that lets any organization declare — in one machine-readable file — which actions autonomous AI agents may take against its systems, under what conditions, and with what audit trail.

Status: Draft 0.1  ·  License: CC-BY-4.0 (spec), Apache-2.0 (reference code)
Discussion: GitHub Issues  ·  Author: Uljan Sinani — permissioning.ai


1. Problem

Autonomous agents now read, write, purchase, deploy, and delete. Today, the only controls are (a) the credentials an agent happens to hold, and (b) prompt-level pleading. Neither is a policy. OAuth scopes govern API access, not agent behavior: an agent with a valid token to a CRM can mass-delete contacts at 3 a.m., and nothing in the stack distinguishes that from a human clicking once.

There is no standard way for a system owner to say: “Agents may read invoices, may draft but not send emails, may never touch records older than 90 days, and every write must carry an attributable agent identity.”

This protocol defines that declaration.


2. Design Principles

  1. Declaration before enforcement. Like robots.txt, a published policy has value even before enforcement exists — it establishes intent, liability boundaries, and a target for tooling.
  2. Boring formats. JSON, served over HTTPS at a well-known path. No new wire protocol, no blockchain, no DSL that requires a parser PhD.
  3. Deny-by-default for writes, allow-by-default for reads — overridable, but the default reflects how every security team actually thinks.
  4. Identity-agnostic. Works whether agent identity comes from OAuth, mTLS, API keys, or MCP session metadata.
  5. Composable with MCP. MCP defines how agents call tools; this protocol defines whether they may. An MCP server can fetch and enforce a permissioning manifest with zero changes to the MCP spec.

3. The Manifest

A site or service publishes a single file at a well-known path:

https://example.com/.well-known/agent-permissions.json

3.1 Minimal Example

{
  "permissioning_version": "0.1",
  "owner": "example.com",
  "updated": "2026-06-11",
  "default": { "read": "allow", "write": "deny", "execute": "deny" },
  "rules": [
    {
      "id": "crm-read",
      "resource": "api.example.com/crm/*",
      "actions": ["read"],
      "effect": "allow"
    },
    {
      "id": "email-draft-only",
      "resource": "api.example.com/mail/*",
      "actions": ["create:draft"],
      "effect": "allow",
      "conditions": { "deny_actions": ["send", "delete"] }
    },
    {
      "id": "payments-human-gate",
      "resource": "api.example.com/payments/*",
      "actions": ["write", "execute"],
      "effect": "require_approval",
      "approval": { "type": "human", "timeout_s": 3600 }
    }
  ],
  "audit": {
    "required": true,
    "fields": ["agent_id", "principal", "action", "resource", "timestamp", "task_context"],
    "sink": "https://example.com/agent-audit"
  },
  "contact": "security@example.com"
}

3.2 Core Objects

default — fallback effects per action class (read / write / execute / delete).
Effects: allow, deny, require_approval, rate_limit.

rules[] — ordered, first-match-wins. Each rule has:

Field Description
resource Glob over hostnames/paths, or an MCP tool name (mcp:server-name/tool-name).
actions Verbs, optionally namespaced (create:draft, update:status).
effect allow | deny | require_approval | rate_limit
conditions (optional) Time windows (hours_utc), record-age limits (max_record_age_days), value caps (max_amount, currency), volume caps (max_per_hour), identity constraints (require_agent_id, allowed_issuers).
approval (optional) human | secondary_agent | mfa, with timeout.

audit — fields every governed action must log, and where.
task_context is the agent’s stated task — the field that makes 3 a.m. incident review possible.

escalation (optional, top-level) — what enforcement layers should do on violation: block, block_and_alert, revoke_session.

Implementation status (v0.1): conditions (time windows, record-age limits, value/volume caps, identity constraints) are part of the v0.1 manifest schema but are not yet enforced by the reference implementation in /reference. The reference middleware and filesystem guard resolve a rule’s effect (allow / deny / require_approval / rate_limit) only; a conditional rule currently behaves as its base effect regardless of its conditions. Treat published conditions as declared intent, not as something the reference code checks. Enforcing them is planned for a later revision.

3.2.1 Action designation: the Agent-Action header

A rule’s actions field matches against the semantic action an agent intends, not merely the HTTP method of the request. HTTP methods are too coarse: a single POST /mail could be create:draft (benign) or send (consequential), and the manifest must be able to distinguish them.

To carry this intent, an agent SHOULD send an Agent-Action request header naming the semantic action it is performing, optionally namespaced:

Agent-Action: create:draft

Enforcement points resolve a request’s action as follows:

  1. If an Agent-Action header is present, its value is the action used for rule matching.
  2. If absent, the enforcement point falls back to a coarse mapping from HTTP method: GET/HEADread, POST/PUT/PATCHwrite, DELETEdelete.

Because the fallback maps an unlabeled write to the broad write class, an agent that does not declare a fine-grained action receives only whatever authority the manifest grants to write — which, under the recommended deny-by-default posture (§3.1), is none. This makes action declaration incentive-compatible: an agent that wants the narrower create:draft permission must explicitly claim it, and in claiming it becomes accountable for it in the audit log (§3.1, action field).

Security note. The Agent-Action header is self-asserted and unsigned in v0.1, exactly as HTTP methods are. It narrows authority but never widens it: an agent cannot escalate by lying, because a false Agent-Action either matches a more restrictive rule or fails to match and falls through to the default. Binding Agent-Action to a signed agent identity is reserved for a future version (§3.3, agent capability statements).

Implementation status (v0.1): The reference middleware in /reference implements this header. This section documents existing behavior; it is normative for v0.1.

3.3 Agent-Side Counterpart (optional, §future)

Agents MAY publish a capability statement (agent-card.json: identity issuer, action vocabulary, audit-sink support) so enforcement points can negotiate. Out of scope for v0.1 beyond reservation of the name.


4. Enforcement Points

The manifest is enforcement-neutral. Known viable enforcement points, in ascending order of strength:

Layer Mechanism Strength
Agent runtime Framework reads manifest before tool calls Honor system (still useful: liability + logging)
MCP server Server filters/wraps its own tools per manifest Strong for MCP traffic
API gateway / proxy Policy-enforcement middleware keyed on agent identity headers Strong, agent-framework-agnostic
IdP / token issuer Scopes minted to match manifest rules Strongest, slowest to adopt

A reference implementation (v0.1 target): a small reference middleware (Python/FastAPI, ~250 lines including comments and docstrings) that fetches a manifest, classifies inbound requests by agent identity header, and enforces allow/deny/require_approval. Enough to demo, small enough to audit. It enforces rule effects; it does not yet evaluate conditions (see §3.2).
→ See reference/middleware.py


5. What This Protocol Is Not


6. Open Questions (comment here)

  1. Is first-match-wins sufficient, or do real estates need explicit rule priorities?
  2. Should require_approval define a callback wire format in v0.2, or stay implementation-defined?
  3. Manifest discovery for non-web systems (databases, message queues) — well-known path equivalent?
  4. Agent identity: is a User-Agent-style header (Agent-Id, Agent-Issuer) acceptable bootstrap, or does v0.1 need signed attestation?
  5. Does deny-by-default-for-writes match your org’s posture, or would you invert it?

7. Why Publish a Spec Instead of a Product

Every enforcement vendor will fight over the control plane. The declaration format underneath should be neutral, boring, and shared — the same reason robots.txt isn’t owned by a crawler company. If you run agents against enterprise systems, or enterprise systems agents touch, break this draft: open an issue.


v0.1 — June 2026. Substantive criticism > stars.