DEV Community

Cover image for Stop Giving AI Agents Your Master Password: UCAN Delegation
fcn06
fcn06

Posted on

Stop Giving AI Agents Your Master Password: UCAN Delegation

Why handing a permanent API key to an autonomous AI agent is a bad idea — and a simpler way to grant it just enough trust, for just long enough, using cryptographic "visas" called UCANs.

Status: work in progress. This is an early write-up of something I'm actively building, not a finished product announcement. I'm sharing it specifically to get feedback — on the idea, the explanation, and the approach itself — before taking it further. See the last section for exactly what kind of feedback would help most.


The problem, in one picture

Imagine you hire an intern to add one meeting to your Google Calendar. Would you hand them:

  • A) Your Google password — which also opens your email, your Drive, and your saved credit cards, or
  • B) A note that says "You may add one event, today, between 2 and 4 PM. Nothing else."

Everyone picks B for a human intern. Almost nobody picks B for an AI agent.

Today, most AI agent frameworks (LangChain, CrewAI, AutoGen, and friends) are wired up with Option A: a permanent, all-powerful API key pasted straight into the agent's code or environment variables. If that agent gets tricked by a malicious webpage, hallucinates, or simply has a bug, it doesn't just fail — it fails with full permissions. Wiped databases and drained accounts are not hypothetical; they're a Tuesday.

The obvious fix — "just ask the human before every single click" — kills the entire point of having an autonomous agent. Nobody wants to approve every calendar invite by hand.

We wanted a third option: agents that can act on their own, but only within a narrow, time-boxed, cryptographically provable slice of permission. That's what this article walks through.


The idea: give agents a passport, not a master key

Think about how you cross a border. You don't hand a customs officer your entire identity and bank access — you hand them a passport, and inside it, a visa that says exactly which country you can enter, for how long, and for what purpose.

We apply the same idea to AI agents:

  • A UCAN (User Controlled Authorization Network) is the digital equivalent of a visa: a small, signed piece of proof that says "person X allows agent Y to do Z, until this time."
  • A Virtual Passport is the little folder an agent carries around, holding all the UCANs (visas) it has been issued by different people or departments.
 ┌──────────────────────────────────────────────────────────┐
 │                AGENT'S VIRTUAL PASSPORT                  │
 │  Agent: "Supervisor Agent #7721"                          │
 │                                                            │
 │  🎫 Visa #1 — issued by Alice                              │
 │     Allowed: create a Google Calendar event                │
 │     Valid for: 10 minutes                                  │
 │                                                            │
 │  🎫 Visa #2 — issued by Finance                            │
 │     Allowed: approve refunds up to $50                     │
 │     Valid for: 1 hour                                      │
 └──────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Each visa is signed with real cryptography (Ed25519 signatures), so nobody can forge one or quietly extend its lifetime. Under the hood, a UCAN is just a small, signed JSON object:

{
  "issuer": "did:twin:alice...",
  "audience": "did:twin:supervisor-agent...",
  "capabilities": [
    { "resource": "google_calendar", "action": "create_event" }
  ],
  "expiry": 1787491200
}
Enter fullscreen mode Exit fullscreen mode
  • issuer — who is granting the permission (Alice)
  • audience — who receives it (the agent)
  • capabilities — exactly what the agent may do, nothing more
  • expiry — the moment this visa stops working, automatically

No central authority has to revoke anything — the token simply stops being valid. That's the whole trick.


A narrower visa for a narrower job: delegation

Here's the part that makes this genuinely useful for multi-agent systems, not just a single bot: an agent can hand a smaller visa to another agent.

Say Alice asks her "Supervisor" agent to schedule a client meeting. The Supervisor spins up a small "Worker" agent just to touch the calendar. It doesn't hand the Worker its own visa (which might also include refund permissions) — it mints a new, narrower visa, stripped down to exactly calendar: create_event, and nothing else.

This is called attenuation: every time permission is delegated, it can only get narrower, never wider. A worker agent can never end up with more power than its supervisor had. That single rule is what makes it safe to build swarms of agents that spawn other agents without the whole thing turning into a permissions free-for-all.


What actually happens when the agent tries to act

Two things are worth noticing:

  1. The agent never talks to Google Calendar directly with a permanent credential. It shows its visa to a middleman — the Trust Gateway — which checks that everything is legitimate and in-scope, then issues a tiny, single-use pass valid for just 30 seconds.
  2. That 30-second pass is also cryptographically tied to the exact action requested (the specific meeting title and time, hashed). Even if it leaked, it couldn't be replayed or repurposed for a different action.

If an agent ever tries to do something outside what its visa allows — say, a $500 refund when it only has a $50 visa — the request doesn't fail silently or get approved anyway. It gets parked and sent to the human for a real approval (a fingerprint or Face ID tap on their phone), before anything happens.

So the agent gets to act autonomously within its lane, and a human only gets pulled in when something falls outside that lane. That's the balance the "God-mode key vs. approve-everything" dilemma from the start of this article was missing.


Trying it yourself

The gateway that does the checking — the Trust Gateway — is open source and written in Rust. Here's the shortest possible version of the flow above, using its API directly. All three calls below are handled entirely inside the open-source trust_gateway itself — no other service is involved in minting, validating, or checking policy on a token.

Swap in your own gateway's address wherever you see <YOUR-TRUST-GATEWAY-URL> (e.g. http://127.0.0.1:3060 if you're running it locally).

1. Alice mints a visa for her agent, scoped to calendar access only, for one hour:

curl -s -X POST https://<YOUR-TRUST-GATEWAY-URL>/v1/ucan/mint \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "did:twin:alice...",
    "audience": "did:twin:supervisor-agent...",
    "capabilities": [{ "resource": "google_calendar", "action": "create_event" }],
    "ttl_seconds": 3600,
    "issuer_seed_hex": "<alice_private_seed>"
  }'
Enter fullscreen mode Exit fullscreen mode

This returns a signed UCAN token — the digital visa.

2. Anyone can verify that visa, without contacting Alice, because the proof is self-contained:

curl -s -X POST https://<YOUR-TRUST-GATEWAY-URL>/v1/ucan/validate \
  -H "Content-Type: application/json" \
  -d '{
    "ucan_token": "<the token from step 1>",
    "required_resource": "google_calendar",
    "required_action": "create_event"
  }'
Enter fullscreen mode Exit fullscreen mode

3. The agent proposes the actual action. The Gateway checks the visa and, if everything lines up, mints the short-lived execution pass:

curl -s -X POST https://<YOUR-TRUST-GATEWAY-URL>/v1/actions/propose \
  -H "Content-Type: application/json" \
  -d '{
    "action_name": "google_calendar_create_event",
    "arguments": {
      "summary": "Architecture Review",
      "start_time": "2026-09-01T14:00:00Z"
    },
    "ucan_token": "<the token from step 1>"
  }'
Enter fullscreen mode Exit fullscreen mode

That's it — three calls, entirely served by the open-source gateway, and you've reproduced the whole "mint a scoped permission, verify it, use it once" cycle. The full API also lets an agent prove it holds one specific capability without revealing the rest of its passport (handy if it's carrying visas it shouldn't disclose), and lets you inspect an agent's live passport at any time. Both are just extensions of the same idea. Turning that final execution pass into a real side effect (actually touching Google Calendar) is the one piece that needs something outside the gateway — a downstream tool consumer to carry it out — but the entire token lifecycle and governance you just exercised runs on trust_gateway alone.


Open source

Everything described in this article — UCAN minting and validation, delegation, and execution-grant issuance — lives in the open-source Trust Gateway, written in Rust and runnable locally today:
👉 github.com/fcn06/trust_gateway


The takeaway

  1. Don't hand AI agents permanent, all-powerful API keys. It's the equivalent of giving an intern your master password.
  2. Give them a passport of short-lived, narrowly scoped visas instead — cryptographically signed, automatically expiring, and impossible to widen through delegation.
  3. Check every action at the door, with a lightweight gateway that turns a valid visa into a single-use, single-action execution pass. The agent still acts on its own. It just can't act beyond what it was actually trusted to do — and that trust is provable, not just assumed.

I'd like your feedback

This is still a work in progress, and I'd genuinely like to hear from people who read this — whether you build agents, work in security, or just have an opinion. A few specific things I'm unsure about:

  • The core idea: Does scoping AI agent permissions with short-lived, delegatable visas (UCANs) actually solve a problem you've run into, or does it feel like overkill for how you use agents today?
  • The explanation: Was the passport/visa analogy clear, or did it break down somewhere once the delegation and execution-grant parts came in?
  • The gaps: What's missing that would make you trust this in production — key rotation, revocation before expiry, auditability, something else?
  • The demo: If you tried the three curl calls yourself, did they work as described? Anything confusing about the request/response shapes? Comments here are the easiest way to reach me, but I'll also take issues or pull requests on the GitHub repo. If you spend five minutes poking holes in this, I'll read every one of them.

Top comments (0)