The authorization layer for AI agents · MIT

Your agent logs in as you.
It shouldn't get to be you.

Policy, approvals, and receipts on every agent tool call — dependency-free, framework-neutral, no SaaS.

prompt-injected agent · one deny rule

$ agent reads an email — injected instructions inside

model obeys: email.forward → [email protected]

BLOCKED before the tool ran — deny:email.forward (exfiltration)

? email.delete? held for a human — denied

§ every decision sealed on the receipt chain

chain verifies — doctored log detected

the model was compromised. the policy didn't care.

Works with the stack you already run

Run it yourself — no API keys, no network

An injected agent tries to exfiltrate your data. Policy says no.

The tools are wrapped with nominee.guard(). The model obeys the injection anyway — models do — and the deny rule fires before the tool runs. Every attempt, allowed or refused, is sealed into a signed, tamper-evident receipt chain. One command:

$ node run.mjs

2. the model obeys the injection and tries to exfiltrate

BLOCKED before the tool ran — deny:email.forward (exfiltration)

5. the receipt chain (signed, tamper-evident)

  #0 policy.decision email.read     allow  5493c2c54cd5

  #1 policy.decision email.forward  deny   ca6a069febdb

  #2 policy.decision email.delete   ask    d2fe628a5202

  #4 approval.resolved email.delete denied 2b0ac4aa3ad8

  #5 policy.decision email.forward  allow  fd17436d92c0

chain verifies: 6 receipts intact

doctored log detected — broken at #1

Tokens, the supporting act — settled live

Same agent. One outlives its token.

Two agents call a protected API across a session longer than the token lives (~8s here). Left grabs a token once and reuses it. Right calls nominee.token() each time. The real nominee package runs in your browser — open the Network tab and watch the refresh.

starting…
captured token grab once, reuse forever — what every orchestrator leaves you to do
nominee await nominee.token() on every call

Start on your DB → Auth0 → Nango. Your agent code never changes — only the strategy line does. The Passport.js of agent auth, scoped to the no-lock-in tail:

agent.ts
const nominee = new Nominee({
  strategy: ({ user, connection }) => db.refreshToken(user, connection), // zero signup
})
// everything below is identical, whatever the strategy ↓
const token = await nominee.token({ user, connection: 'github' })

The race above runs the oauth2 line. Want the auth0 line for real? → watch a live agent act on your GitHub, after your approval.

How it works

Checked against policy. Held for a human. On the record.

You're the principal; your agent is the nominee, acting for you within a scope. nominee checks every tool call against your policy, holds the risky ones for a person, and seals every decision — including refusals — into a tamper-evident record. No SaaS in the middle.

A prompt-injected agent tries to exfiltrate the inbox; nominee's deny rule blocks the tool call before it runs, holds the delete for a human, and seals a signed receipt of every decision.
  • Policy on every call

    Declarative allow / deny / ask rules — glob patterns, argument-level conditions, call budgets. A deny fires before the tool runs; the model can't talk its way past it.

  • Held for a countersignature

    Gate the risky calls behind a real person. The agent pauses, a human approves from a phone or a UI, then it resumes — no stale token on the other side.

  • §

    On the register

    Every decision, approval, and token grant is a hash-chained, optionally signed receipt: you → agent → service, who authorized what, and when. Edit or delete a record and verification breaks.

Install and go

A policy, not a platform. No service to sign up for.

Give nominee a policy — ordered allow / deny / ask rules over your tools. Approvals, receipts, and fresh tokens ride on top, same API wherever your agent runs.

agent.ts
import { Nominee, allow, deny, ask } from 'nominee'

// a boolean flag (needsApproval: true) is one Y/N prompt for everything.
// a policy is allow / deny / ask — per tool, argument-aware:
const nominee = new Nominee({
  policy: [
    allow('email.read'),
    deny('email.forward', { reason: 'exfiltration' }),
    ask('email.delete'), // a human decides, every time
  ],
})

// one line — every call checked before the tool runs
const tools = nominee.guard({ 'email.read': readEmail, 'email.forward': forwardEmail }, { user: 'alice' })

Human in the loop

Hold the risky moves until a person signs.

An ask rule in your policy holds deletes, payments, or outbound mail for a real approval — deny means deny. Use the built-in engine, or a native flow like Auth0 CIBA that pings the user's phone. The agent pauses, then resumes exactly where it left off.

Awaiting countersignature

repo.delete

Delete repository alice/old-project

DenyApprove
you → triage-bot → github · on the register

Where nominee sits

A thin, neutral seam — not another platform.

Your framework runs the agent, but authorization is left as a Y/N flag — or nothing at all. nominee is the layer in between: every tool call is checked against your policy before it runs, risky ones pause for a human, every decision lands on the receipt chain — and your tools and vault stay swappable underneath.

When you don't need nominee

  • A read-only agent with no authority worth guarding.
  • Your platform's native permission system covers you end-to-end and you're happy inside it.
  • You want one fully-managed vendor for tools + auth + policy — use Arcade or Composio directly.

Reach for nominee when you want policy + approvals + receipts that are framework-neutral, no-SaaS, and bring-your-own-everything — the same policy wherever your agent runs.

Packages

One authorization layer, wherever your agent runs.

Core

nominee

Policy engine, approvals, hash-chained receipts, token brokering. Zero runtime deps.

Vercel AI SDK

nominee-ai

guardTools wraps your AI SDK tools in one line; nomineeTool for per-tool config. Runs on Cloudflare Agents too.

Vercel Eve

nominee-eve

Policy enforcement, portable approvals, and fresh tokens inside Eve tools.

Optional · Strategy

nominee-supabase

Store provider tokens in Supabase; nominee reads and refreshes them. Zero deps.

Optional · Managed

nominee-auth0

Auth0 Token Vault for federated tokens, and CIBA approvals on the user's phone.

FAQ

The questions people actually ask.

Isn't this just an if-statement with extra steps?

The check itself, yes. What you're buying is everything around it: ask-rules that escalate to a human instead of silently failing, budgets that let 20 calls through and ask on the 21st, delegation that a sub-agent can only narrow, a tamper-evident receipt of every decision, and the same policy enforced whether your agent runs on the Vercel AI SDK, Eve, or a bare function — one enforcement point instead of scattered if-statements per tool, per framework.

Can the model talk its way past a deny?

No. The model never sees the policy — it only ever gets the tools you handed it via nominee.guard(). A deny rule throws PolicyDeniedError inside the wrapper, before your tool's own code runs. There's no prompt for the model to argue with; the call physically doesn't happen.

Do receipts store my data?

Not by default. Tool inputs are recorded as inputHash — a SHA-256 of the canonical JSON — so you can prove what an approver saw without writing user data into the log. Pass receipts: { input: 'raw' } if you want the full input on the record instead, or 'none' to skip it entirely.

Can receipts be forged?

Tamper-evident, not non-repudiation — say that plainly. Without a signing key, the chain is publicly re-computable: anyone can verify it wasn't edited, but anyone with write access to the whole log could also rewrite it consistently. Pass an HMAC key and only key-holders can produce valid hashes — that's the guarantee to reach for once receipts leave your own process.

How is this different from Arcade, Composio, or Vercel Connect?

Those are hosted platforms for tool-calling and connectors — good products, but SaaS: per-call billing, and the policy brain lives on their side. nominee is an MIT, in-process, framework-neutral policy layer that can sit on top of any of them (or your own DB, OAuth2, or Auth0) as the token strategy. Bring your own vault; keep the policy, approvals, and receipts local.

Does nominee replace my auth provider (Auth0, Clerk, WorkOS)?

No — authentication (who is the user) is a separate, solved problem. nominee is the layer above it, deciding what an already-authenticated agent may do as that user. It composes with whatever you already use; Auth0 is one optional strategy among several, not a requirement.

What happens if I don't configure a policy?

Everything is allowed — and still receipted. nominee doesn't restrict anything until you add rules, so you can adopt it incrementally: wrap your tools first, watch the receipt chain to see what your agent actually does, then tighten the policy once you know.

When don't I need nominee?

A read-only agent with no authority worth guarding. Your platform's native permission system already covers you end-to-end. Or you want one fully-managed vendor for tools, auth, and policy together — use Arcade or Composio directly.

Give your agent a nominee.

Open source, MIT, zero-dependency core. It works the moment you install it.

★ Star on GitHub

▶ Watch a real agent act on your GitHub, after your approval →

GitHub stars npm version npm downloads license