npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agent-authority

v0.1.6

Published

Authorization for AI agents: verifiable, scoped, revocable capability tokens (mandates) with attenuable delegation, for MCP and A2A. The reference implementation of agent authority — zero-dependency TypeScript & Python.

Readme

agent-authority

Authorization for AI agents — verifiable, scoped, revocable capability tokens with delegation, for MCP and A2A. Project name: Behalf. Zero dependencies, TypeScript and Python, offline-verifiable. Five verbs.

npm PyPI CI License: MIT

agent-authority is the reference implementation of agent authority: the authorization and delegation layer for AI agents. It gives any agent — and any sub-agent it delegates to — a verifiable, scoped, time-bound, revocable identity and permission chain, so a tool server, MCP host, or agent-to-agent (A2A) call can answer "is this agent actually allowed to do this, right now?" offline, with only a public key.

It solves AI agent authorization / agent permissions with capability-based security: least-privilege, attenuable (macaroon/biscuit-style) capability tokens, an OAuth 2.1 on-behalf-of–style principal→agent grant, and SPIFFE/SVID-style cryptographic agent identity — without cloud, model, or framework lock-in.

npm install agent-authority        # Node / TypeScript
pip install agent-authority        # Python (add the [seal] extra for sealed credentials)

Agent authority is becoming required infrastructure: multi-agent systems are already the norm, yet most tool servers ship with no auth at all. The standard for agent identity and delegation is being defined by NIST, the IETF, and the Linux Foundation's Agentic AI Foundation. This project doesn't try to win that race — it's the clean, neutral, AI-legible implementation of it. The requests of the agent era: MIT-licensed, the install nobody reinvents.

Everything is one primitive — a Mandate: a signed, scoped, time-bound capability token that proves who authorized what, within which limits, and through which chain of agents.

In plain words

Think of a Mandate as a permission slip for an AI agent.

Imagine you hire an assistant to run errands for you. You don't hand over your wallet and house keys — you write a note: "You may read my calendar and spend up to $50, and only for the next hour." That note is a Mandate.

The five verbs are just the things you can do with that note:

  • grantwrite the permission slip. "This agent may do X, up to this limit, until this time."
  • authorizecheck the slip before acting. The agent must show the slip (and prove it's really theirs) before it's allowed to do something.
  • attenuatemake a smaller copy for a helper. If the agent asks a sub-agent for help, it can only pass on the same powers or fewer — never more.
  • revoketear the slip up. Cancel it instantly, and every copy handed downstream stops working too.
  • auditthe logbook. Every check is written down, so you can see exactly what happened.

Two things make the slip safe:

  1. It can't be faked or upgraded. It's signed with cryptography. A helper can shrink the powers but can never widen them, and nobody can secretly edit it.
  2. Holding the paper isn't enough. To use a Mandate, an agent must prove it's the rightful holder (it holds a matching secret key). So a stolen copy, by itself, is useless.

That's the whole idea. The rest of this page shows how to do each of these in code.

Who it's for

  • App & AI developers — stop an autonomous agent from doing more than you intended: scope, cap, expire, and revoke what it can do, in five lines.
  • Platform & infra teams — one authorization primitive across every agent and tool server, offline-verifiable, with a control plane for org-wide revocation and audit.
  • Enterprises, security & compliance — least-privilege delegation, tamper-evident audit trails, multi-tenant isolation, and cryptographic agent identity — the controls auditors ask for.
  • AI agents themselves — discover and use it natively over MCP (request_mandate / check_authority). See Use it from an AI agent.

Use cases

Concrete, copy-paste scenarios live in USE_CASES.md. A taste:

| You're building… | agent-authority gives you | |---|---| | An autonomous coding / ops agent | scoped repo + spend limits, auto-expiring | | A customer-support / personal assistant | per-user, short-lived access | | A payments / finance agent | hard spend: and rate: caps at authorize | | Multi-agent orchestration | delegation that can only narrow + cascade revoke | | An MCP tool server | drop-in withBehalf enforcement on every tool | | Agent-to-agent calls | a verifiable chain the callee checks offline | | A multi-tenant SaaS of agents | per-tenant isolation, one-call revocation, central audit | | Compliance / audit | hash-chained, checkpoint-anchored decision log |

→ Full scenarios with code: USE_CASES.md.

Secure an entire agent in ~6 lines

import { withBehalf } from "agent-authority/mcp";

const server = withBehalf(myMcpServer, {
  policy: {
    send_email: "write:email",
    read_calendar: "read:calendar",
    transfer_funds: (args) => `spend:usd<=${args.amount}`,
  },
  onDenied: "throw", // or "prompt" for just-in-time user consent
});

Every tool call through that server is now automatically checked against the caller's mandate — scope, limits, expiry, revocation, and audit — with no per-tool code.

The five verbs

import { Behalf } from "agent-authority";

// 1. GRANT — a user authorizes an agent: scoped, capped, short-lived
const mandate = await Behalf.grant({
  principal: user.id,
  agent: "research-agent",
  can: ["read:calendar", "spend:usd<=50"],
  expiresIn: "1h",
});

// 2. AUTHORIZE — before any action, prove authority (throws if denied)
await mandate.authorize("spend:usd=20");

// 3. ATTENUATE — hand a narrowed mandate to a sub-agent; can only shrink scope
const child = mandate.attenuate({ can: ["read:calendar"], expiresIn: "10m" });

// 4. REVOKE — kill a mandate and its whole downstream chain, instantly
await Behalf.revoke(mandate.id);

// 5. AUDIT — every authorize() already wrote a hash-chained record
const trail = await Behalf.audit(mandate.id);

That's the entire core surface. Five verbs: grant, authorize, attenuate, revoke, audit. There is no sixth.

Capability grammar

Scopes are both human- and machine-writable:

read:calendar           # simple capability
write:repo/acme-app     # resource-scoped (path segments)
spend:usd<=50           # quantitative limit
send:email rate<=10/h   # rate limit
*                       # wildcard (discouraged; lint warns)

When authorizing, name a concrete amount — spend:usd=20 is checked against the grant's spend:usd<=50.

Design principles (non-negotiable)

  1. Thin & end-of-chain. Zero runtime dependencies — built on the platform's own crypto.
  2. One obvious way. Exactly one canonical method per task.
  3. AI-legible by default. Ships with an MCP server, llms.txt, and typed schemas so coding agents discover and call it correctly.
  4. Standards-tracking, not standards-defining. A clean facade over SPIFFE / OAuth 2.1 OBO / capability tokens.
  5. Neutral. No cloud, model, or framework lock-in.
  6. Offline-verifiable. Signature chain, scope, expiry, and proof of possession are all checked locally (asymmetric, public-key only). Only revocation and the shared rate cap need a network check.

The intersection rule is structural

A mandate is an Ed25519 signature chain (biscuit-style). Block 0 (the root grant) is signed by the issuer; each block publishes a fresh public key, and the next block is signed by the matching private key. A holder attenuates by appending a narrowing block signed with the key it was handed — keylessly with respect to the issuer, and offline. An agent's effective authority is always:

principal's grant  ∩  every narrowing along the chain

A compromised middle agent can never widen scope; no block can be edited, removed from the middle, or truncated off the end. Editing/middle-removal is blocked by the signature chain; truncation is blocked by a proof of possession at authorize time (below). This closes the OAuth "delegation-chain splicing" weakness — see test/delegation.test.ts and test/asymmetric.test.ts.

Authorizing requires proving possession (no bearer tokens)

A serialized mandate is not a usable bearer credential. To authorize, the holder must prove possession of the chain's terminal private key — the one each delegation hands to the next agent. A truncated prefix would need that prefix's terminal key, which a downstream holder does not have, so it cannot escalate by dropping its own block.

// In-process holder: mandate.authorize() mints + checks the proof for you.
await mandate.authorize("spend:usd=20");

// Across a trust boundary, the holder presents the token + a fresh,
// action-bound proof; the verifier needs only the issuer's PUBLIC key:
const verifier = createBehalf({ trust: [issuer.publicKey] });
await verifier.authorize(mandate.token, "spend:usd=20", mandate.prove("spend:usd=20"));

For an advisory "would this token's scope allow X?" check that does not prove possession (e.g. tooling/dashboards), use engine.inspect(token, action). Over HTTP, agent-authority/a2a's present() attaches the proof automatically.

Proofs are bound to the action and fresh within proofSkewMs (default 5 min). For true single-use anti-replay, the verifier issues a challenge: const nonce = verifier.challenge() → holder binds it with mandate.prove(action, { nonce }) → the verifier consumes it on use. Engines created with requireNonce: true refuse nonce-less proofs entirely.

There are two serializations, and the difference matters:

  • mandate.serialize() — the public token. Safe to show anyone; after import it can be inspected and verified, but not authorized, proved, or delegated (it carries no key).
  • mandate.serializeWithKey() — the holder credential (token + delegation key). This is how you hand a delegated mandate to a sub-agent in another process: after import it has full holder powers. Treat it as a secret and deliver it only over a secure channel.

Binding a mandate to an agent identity (SVID-style)

A holder credential is a secret, so a leak is a real risk. Bind the mandate to a specific agent identity and a stolen credential is inert without the agent's private key — possession of the credential is no longer sufficient to act.

Grant (or attenuate) with bindAgent set to the agent's public key; that adds an agentKey caveat. Authorizing then requires a proof of possession of the matching private key, in addition to the chain's terminal key:

const agent = newKeyPair();                       // the agent's long-lived identity
const mandate = issuer.grant({
  principal: user.id,
  agent: "research-agent",
  can: ["spend:usd<=50"],
  expiresIn: "1h",
  bindAgent: exportPublicKey(agent.publicKey),     // ← cryptographic binding
});

// The agent proves BOTH the terminal key and its identity:
await verifier.authorize(
  mandate.token,
  "spend:usd=20",
  mandate.prove("spend:usd=20", { agentKeys: [agent] }),
);
// An engine configured with `agentKey: agent` proves it automatically on the
// in-process path (mandate.authorize(...)) and over A2A via present(..., { agentKeys }).

Bindings are conjunctive: every agentKey caveat in the chain must be satisfied. A thief who steals the credential cannot strip the caveat (it is signed into a block) and cannot bypass it by appending their own binding — doing so only adds another requirement. The agent's private key is provisioned out of band; Behalf never puts it on the wire.

Sealing a holder credential (encrypted delivery)

serializeWithKey() is a secret. bindAgent makes a stolen one inert; for the delivery channel itself, seal the credential to the recipient so it's unreadable to anyone in between:

import { newSealKeyPair } from "agent-authority";

const recipient = newSealKeyPair();          // recipient's X25519 sealing key
// ...recipient publishes recipient.publicKey...

const sealed = mandate.sealForRecipient(recipient.publicKey);  // encrypted blob
// ...deliver `sealed` over any channel...
const mine = engine.importSealed(sealed, recipient);           // only the recipient opens it
await mine.authorize("read:calendar");

The scheme (seal-1) is ephemeral X25519 → HKDF-SHA256 → AES-256-GCM and is wire-compatible across both ports (seal in TypeScript, open in Python or vice versa). It's native in Node; in Python it needs the optional cryptography package (the rest of the port stays dependency-free, and importSealed raises a clear error if it's missing). The sealing key is X25519 and is separate from the Ed25519 bindAgent identity — combine both for delivery + use protection.

Issuer key rotation (with overlap)

const v2 = v1.rotate();          // fresh key, same stores, still trusts v1
// 1) distribute v2.publicKey to verifiers (verifier.trustKey(v2.publicKey))
// 2) new grants are signed by v2; old mandates keep verifying
// 3) after the longest outstanding mandate expires:
v2.untrustKey(v1.publicKey);     // end the overlap — old-key mandates retire

Audit checkpoints (anchoring)

The audit log is an unkeyed hash chain — verifiable, but a writer with store access could rewrite it and tail-deletion is invisible. checkpointAudit() signs the current head; store the checkpoint out of the writer's reach and verifyAuditCheckpoint(cp) later detects tail-deletion and rewrites:

const cp = await engine.checkpointAudit();   // ship to object storage / a ledger
// later, e.g. nightly:
const { ok, reason } = await engine.verifyAuditCheckpoint(cp);

What maps to the standard underneath

| Behalf concept | Standard it tracks | | ------------------------------- | ----------------------------------------------- | | Mandate (capability token) | Agentic JWT / capability tokens (IBCT-style) | | attenuate() (holder narrowing)| DeepMind macaroon-style attenuation | | Agent identity | SPIFFE / SVID compatible | | Principal → agent grant | OAuth 2.1 On-Behalf-Of / token exchange (RFC 8693) | | Audit record | provenance / non-repudiation records | | Transport bindings | MCP + A2A authorization layers |

Because the 5-line API is a facade, the standard can evolve underneath without breaking anyone's code.

Usage

CLI

After npm run build, the agent-authority CLI manages mandates from the terminal (state lives under $BEHALF_HOME, default ~/.behalf):

node dist/cli.js pubkey
M=$(node dist/cli.js grant --principal alice --agent research \
      --can "read:calendar" --can "spend:usd<=50" --expires 1h)
# $M is a HOLDER credential (includes the delegation key — keep it secret).
# Add --public to emit the presentation-only token instead.
node dist/cli.js inspect "$M"
node dist/cli.js authorize "$M" "spend:usd=20"   # ALLOW (real check, proof of possession)
node dist/cli.js authorize "$M" "spend:usd=99"   # DENY
node dist/cli.js revoke <mandate-id>
node dist/cli.js audit  <mandate-id>

MCP server

A dependency-free stdio MCP server exposes the discovery tools to any MCP client:

node dist/mcp-server.js      # speaks JSON-RPC 2.0 over stdio
// register with an MCP client, e.g.:
{ "mcpServers": { "agent-authority": { "command": "node", "args": ["dist/mcp-server.js"] } } }

Quickstarts for any AI

agent-authority quickstart generates the wiring for any surface — Claude Code, Cursor, Copilot, Windsurf, Gemini CLI, OpenAI Agents (MCP), and GPT / Gemini APIs (function tools). Any other AI is configurable via a custom surface file or the generic MCP template. See QUICKSTART.md.

agent-authority quickstart --list
agent-authority quickstart claude-code
agent-authority quickstart gpt           # OpenAI function tools
agent-authority quickstart my-agent --surfaces ./surfaces.json   # bring your own AI

A2A — agent-to-agent over HTTP

agent-authority/a2a carries a verifiable delegation chain across the network. The caller attaches its mandate (optionally attenuating it first); the callee verifies the chain offline with only the issuer's public key, then authorizes the action:

import { behalfFetch, guard } from "agent-authority/a2a";

// callee: a node:http middleware that authorizes each request
const gate = guard({ engine: callee, capability: () => "spend:usd<=50" });
// ... in your http handler: if (!(await gate(req, res))) return;

// caller: forward the mandate, narrowed so the callee gets strictly less
await behalfFetch(url, mandate, { method: "POST" },
  { attenuate: { can: ["spend:usd<=20"] } });

Capability linting

lint() flags loose scopes (*, unbounded spend:, rate-less send:, ...) so agents and humans write tight capabilities by default:

import { lint } from "agent-authority";
lint(["spend:usd", "*"]); // → warnings: add a limit; avoid wildcard

Also available as agent-authority lint <cap> ... on the CLI.

Persistence

FileRevocationStore and FileAuditStore keep revocation and audit state across restarts with zero infrastructure:

import { createBehalf, FileRevocationStore, FileAuditStore } from "agent-authority";
const behalf = createBehalf({
  revocations: new FileRevocationStore("./revocations.json"),
  audit: new FileAuditStore("./audit.jsonl"),
});

Control plane (revocation propagation + audit retention)

For multi-agent deployments, the control plane centralizes revocation (revoke once, every agent sees it), retains one hash-chained audit log (integrity- chained; see Limitations for its threat model), and offers a consent/policy surface with a dashboard at /. It's a thin HTTP service over the same stores — point agents at it with the agent-authority/remote client stores and the five-verb API is unchanged.

node dist/control-plane.js     # bin: agent-authority-control-plane; dashboard at /
import { createBehalf } from "agent-authority";
import { HttpRevocationStore, HttpAuditStore, HttpRateStore } from "agent-authority/remote";

const behalf = createBehalf({
  revocations: new HttpRevocationStore("http://localhost:8787"),
  audit: new HttpAuditStore("http://localhost:8787"),
  rate: new HttpRateStore("http://localhost:8787"),
});
// revoke(id) propagates to every agent; audit is sealed centrally (race-free);
// and a `rate<=N/h` cap is enforced ONCE across all agents, not per process.
//
// Multi-tenant: createControlPlane({ tenants: { tokenA: issuerAPubKey } }) — a
// tenant token reads/writes only its own issuer's audit; `token` is admin.

// Optional: cache revocation checks with a bounded staleness window.
// import { CachingRevocationStore } from "agent-authority";
// revocations: new CachingRevocationStore(new HttpRevocationStore(url), { ttlMs: 5000 })

Cross-language interop

A mandate issued by either reference port verifies in the other: both encode keys as raw Ed25519 (base64url) and compute sorted-key canonical JSON for the signed bytes, so a TS-issued mandate authorizes under the Python verifier and vice versa — including attenuated multi-block chains and the action-bound possession proof. A committed fixture, vectors/mandate-vector.json, is verified by both test suites so the wire format can't drift; any third-party implementation should verify it too.

npm run test:interop   # PY⇄TS, issue in one port, verify/authorize in the other

Python

An identical-shape port lives in python/:

cd python
python3 -m unittest discover -s tests   # 85 tests, zero dependencies
from agent_authority import create_behalf

b = create_behalf()
mandate = b.grant(
    principal="alice", agent="research-agent",
    can=["read:calendar", "spend:usd<=50"], expires_in="1h",
)
mandate.authorize("spend:usd=20")
child = mandate.attenuate(can=["read:calendar"], expires_in="10m")

What ships

  • agent-authority (npm) — the core TypeScript library, near-zero deps.
  • agent-authority/mcp + agent-authority/a2a — drop-in enforcement middleware.
  • agent-authority (PyPI) — Python port, identical API shape.
  • MCP server + llms.txt + typed schemas — the agent-adoption kit.
  • Three reference integrations — data-access, spend-limited, two-agent delegation.

Use it from an AI agent

agent-authority is AI-legible by design — a coding assistant or autonomous agent can adopt it with no human in the loop:

  • llms.txt is a complete, copy-pasteable spec. Point your AI at it and it can use the library directly.
  • MCP discovery toolsrequest_mandate, present_mandate, check_authority — let an agent obtain and reason about authority natively. Run npx agent-authority-mcp.
  • One-command wiring for any surface: agent-authority quickstart claude-code (also cursor, copilot, gpt, gemini, windsurf, or a custom surface).
  • Typed JSON schemas for mandate and capability.

Agent recipe: call request_mandate to obtain authority, check_authority to test it, then act. Never widen scope; prefer the shortest expiresIn and tightest can.

FAQ

What problem does agent-authority solve? It's authorization for AI agents: it makes an agent's permissions scoped, time-bound, verifiable, and revocable, so an autonomous or multi-agent system can't do more than you intended — and you have an audit trail proving what it did.

How is this different from OAuth? OAuth grants a token to an app. agent-authority adds what agents need on top: attenuable delegation (a sub-agent can only get less power), offline verification (check a whole chain with just a public key), proof of possession (a stolen token isn't usable), and instant cascade revocation. It maps onto OAuth 2.1 On-Behalf-Of (RFC 8693) underneath.

Do I need a server or cloud account? No. The core is a zero-dependency library that verifies offline. The optional control plane (for org-wide revocation/audit) is a thin HTTP service you run yourself — no vendor, no lock-in.

Which languages? TypeScript/Node and Python, with identical APIs; mandates issued in one verify in the other.

Is it production-ready? The API is stable and fully tested in both ports, but it has not had an independent cryptographic audit — commission one before 1.0 / production positioning.

How do I use it with MCP / Claude / Cursor / GPT? agent-authority quickstart <surface> prints ready-to-paste config; or run the MCP server npx agent-authority-mcp. See QUICKSTART.md.

Is it free / open source? Yes — MIT licensed, use it anywhere including commercially.

Design notes & trade-offs

How it behaves at scale, in plain words — the deliberate trade-offs and the knobs for each. None of these weaken the core guarantees (unforgeable, attenuation-only, truncation-resistant, offline-verifiable mandates); they're about scale, storage, and deployment, and each ships with a built-in option:

  • Sharing one control server between separate customers? Give each customer their own token. Then their logs, policies, revocations, rate counters, and consent requests stay separate. (Set tenants: { token: issuerPub }; admin revocations stay global. For strict isolation, add requireTenant: true so the server refuses any non-tenant request.) Without per-customer tokens, treat one server as belonging to a single team — run one server per team.

  • A shared limit (e.g. "10 emails/hour across all agents") asks the server every time. On the allow path that check can't be cached, or agents could cheat past the cap; the server uses its own clock so no one can fudge the timing. Everything else — signature, scope, expiry — is checked instantly and offline. (Wrap the rate store in CachingRateStore to stop re-asking while a client is already over its cap; revocation can be cached for a few seconds via CachingRevocationStore.)

  • Python and TypeScript fully understand each other. A permission slip made in one can be used and narrowed in the other — they store keys and compute the signed bytes identically. (Verified both directions in the interop check.)

  • The pure-Python signer is correct, but the safest version turns on by itself. On a shared machine, plain-Python signing could leak tiny timing hints; if a hardened crypto library (cryptography, then PyNaCl) is installed, it's used automatically — pip install "agent-authority[seal]" to be sure. (Node always uses hardened crypto.)

  • Two ways to enforce rate limits — pick one. The default counts actions in a rolling time window; TokenBucketRateStore instead allows a short burst, then a steady drip. Both drop into the same slot.

  • The logbook (audit) catches tampering, but isn't bulletproof on its own. It detects edits and reordering, but someone with full write access to the storage could rewrite the whole book. The fix: periodically sign the latest page (checkpointAudit(), or startAuditCheckpointing() to do it automatically on a timer) and keep that signature somewhere they can't reach — later, verifyAuditCheckpoint() reveals any rewrite or deletion. Write-once storage is the strongest option.

  • The agent's name on a slip is just a label — but you can make it cryptographic. Add bindAgent at grant/attenuate time and the agent must prove it owns a secret key to use the slip, so a stolen copy is useless. You hand that key to the agent yourself (Behalf enforces the binding but doesn't distribute keys). See "Binding a mandate to an agent identity".

This implementation has not had an independent cryptographic audit — commission one before any 1.0 / production positioning.

License

MIT — see LICENSE. Open source, use it anywhere, including commercially. Contributions welcome.