Skip to main content

Enroll your first agent

By the end of this tutorial you will have: one agent registered, one manifest reconciled, one human consent granted, and one authorize decision observed. That's the whole loop the rest of the docs build on, and it's the same loop the project's own adoption target is measured against: a new integrator enrolled in under 30 minutes.

You don't need to know what a "hop" or a "scope" is yet — see the callout at the end of each step for a plain-language definition the first time a new term shows up.

Prerequisites​

  • Docker (for PostgreSQL + Redis) and Python 3.12+
  • This repository cloned locally, with a working backend/.venv

Step 1 — Start the control plane​

From the repository root:

docker compose up -d # PostgreSQL + Redis
cd backend
alembic upgrade head # create the schema
uvicorn app.main:app --reload # control plane on http://localhost:8000

Leave this running — every step below talks to http://localhost:8000.

Term: control plane

The FastAPI service that stores manifests, compiles policy, decides authorize calls, and mints tokens. It's the "brain"; agents and their egress proxies are separate processes that call it.

Step 2 — Register your agent's manifest​

A manifest is your agent's declared authority: which tools it may call, which data classes it may read or write, which agents it may delegate to, and how many hops its delegation chain may go. This repo ships an example manifest for a kb-search-agent that reads two internal tools:

backend/examples/kb-search-agent.yaml
apiVersion: agentic-auth/v1
kind: AgentManifest
metadata:
agent: kb-search-agent
spec:
tools:
- ref: mcp://kb.internal/search
data_classes: [kb.public, kb.internal]
privileged: false
- ref: mcp://accounts.internal/read_account
data_classes: [account.read]
privileged: false
data_classes:
reads: [kb.public, kb.internal, account.read]
writes: []
downstream_agents:
- ref: agent://ledger-agent
max_hop_depth: 2
hop_budget: 4

Register it:

curl -sS -X POST localhost:8000/v1/agents \
-H "Authorization: Bearer dev-admin-token" \
-H "Content-Type: application/json" \
-d @examples/kb-search-agent.json

Expected response: 201 with agent_id, manifest_version: 1, and status: "reconciled".

Term: reconciled

Your declared manifest was checked against what the target tools actually advertise (/.well-known/oauth-protected-resource). reconciled means the declaration matched — nothing was claimed that the tool doesn't really expose. If it doesn't match, status is needs_review or rejected and the response names the exact offending line; see Register and reconcile a manifest for how to read and fix that diff.

Step 3 — A human consents to the scope​

Nothing runs yet. Before any agent can act, a human has to consent to the scope the manifest compiled into — this is the root of every delegation chain that follows.

curl -sS -X POST localhost:8000/v1/consent \
-H "Authorization: Bearer dev-admin-token" \
-H "Content-Type: application/json" \
-d '{"scope": "kb-search-agent", "user_sub": "user:alice"}'

Expected response: 201 with session_id, root_grant (a JWT — this is your hop-1 token), policy_version, hop_budget, and expires_at.

Save the two values you'll need next:

export SESSION=<session_id from the response>
export ROOT_GRANT=<root_grant from the response>
Term: scope

The name a manifest compiles under — here, the agent's own name. Consenting to a scope pins one specific, immutable policy version for the whole session; every decision in that session is judged against that exact version, even if a newer one is published later.

Step 4 — Ask the PDP for a decision​

Now make the call your agent would actually make: "can user:alice's session call mcp://kb.internal/search for kb.public data?"

curl -sS -X POST localhost:8000/v1/authorize \
-H "Authorization: Bearer $ROOT_GRANT" \
-H "Content-Type: application/json" \
-d '{"session_id": "'"$SESSION"'", "caller": "user:alice",
"target": "mcp://kb.internal/search", "data_class": "kb.public"}'

Expected response: 200 with "outcome": "allow", "hop": 1, and an action_id — the row this decision was recorded under in the audit log.

Term: hop

One link in a delegation chain. Hop 1 is the human's own session; hop 2 is the first agent it delegates to; and so on, up to the manifest's hop_budget. Depth is derived from the verified token, never from a number the caller supplies.

What you just did​

  • Registered an agent's declared authority and had it automatically checked against reality (reconciliation)
  • Rooted a session in one human's explicit consent, pinned to one immutable policy version
  • Got a real allow/deny decision back from the Policy Decision Point, with an audit row to prove it

Where to go next​

  • Want the whole product in one sitting — delegation three hops deep, a parameter-bound privileged call, a prompt injection that gets refused, the audit chain back to the human, a CI drift gate, a revocation and an outage? See the End-to-end walkthrough: an executed notebook where every output is from a real run.
  • Want to narrow that root grant into a hop-scoped token and forward a real call through the sidecar? See Deploy the sidecar PEP.
  • Want to see why the system is built this way — why authority narrows per hop, why a tainted result can't reach a privileged call? Start at Architecture overview.
  • Want the exact request/response shape for every endpoint you just used? See Reference → API.