Fine-grained authorization for AI agents.

AgentFGA decides what each agent can see and do, down to a single document, tool or record. Check every tool call, filter what reaches the model, and give agents access that expires. It speaks the OpenFGA API, so the official SDKs and CLI work unchanged.

Building with a coding agent? Paste llms.txt into it. It has the steps to deploy AgentFGA and add permission checks to your agent.

agent.ts
import { OpenFgaClient } from "@openfga/sdk";

const fga = new OpenFgaClient({
  apiUrl, storeId, credentials,
});

// Before the agent uses a tool, check that it may.
const { allowed } = await fga.check({
  user: "agent:support-bot",
  relation: "can_use",
  object: "tool:issue-refund",
});

// Before retrieval, find what the user may see.
const { objects } = await fga.listObjects({
  user: "user:anne",
  relation: "viewer",
  type: "document",
});

The official OpenFGA SDK, pointed at AgentFGA.

§ 01 · Why agents

Why agents need fine-grained authorization.

An agent acts for a user, calls many tools and reads a lot of data, faster than anyone can review. Roles like “admin” are too coarse. You need to know whether this agent, acting for this user, may touch this specific thing.

Two overlapping sets: what the user may do, and what the agent was granted. Only the overlap is allowed. What the user may do What the agent was granted Allowed
Outside the overlap: denied.

01

Check every action

Ask before each tool call or write. A check takes about 6 ms on the server, and stayed that fast with 1.1 million relationships in a store.

Check · BatchCheck

02

Filter what the model sees

List every document a user may view, or check up to 50 retrieved results at once, so the model only gets what the user is allowed to read.

ListObjects · BatchCheck

03

Scoped access that expires

Give each agent its own identity and its own grants. Conditions limit a grant by time or by request details, such as access that ends after an hour.

Conditions · contextual tuples

04

One store per customer

Each customer's permissions live in their own store, kept apart from everyone else's. Stores run independently, so capacity grows with your customers.

Stores

§ 02 · How it works

Write the rules once. Check before every action.

AgentFGA uses the OpenFGA model: three steps, shown here with an agent.

  1. Write your rules

    Describe who can do what, once. Here: owners can view a document, and agents can get temporary access that also lets them view it.

    type document
      relations
        define owner: [user]
        define temp: [agent with not_expired]
        define viewer: owner or temp
  2. Grant access

    Store relationships as things change. Here: the support bot gets temporary access to the Q3 plan for one hour.

    user:      agent:support-bot
    relation:  temp
    object:    document:q3-plan
    condition: not_expired, 1h
  3. Check before acting

    Ask before the agent reads or changes something. Here: may the support bot view the Q3 plan right now?

    user:     agent:support-bot
    relation: viewer
    object:   document:q3-plan
    allowed: true

Where AgentFGA fits

Your agent or app asks AgentFGA before it acts. AgentFGA keeps the rules and relationships and answers.

Your agent or app Asks before each tool call or retrieval, with an OpenFGA SDK or plain HTTP.
AgentFGA Checks the API key and passes the question to the right store.
One store per customer Each with its own database, holding that customer's rules and relationships.

Each store's data sits right next to the code that checks it, so an answer never waits on a separate database. Stores are kept apart from each other, so adding customers adds capacity.

§ 03 · Evidence

Measured, then published.

Every number comes from the test harness in the repository. The reports have the method, the caveats and the commands to rerun them.

Check latency at the edge
6msOn the server, when the store shares a data center with the request.
Largest store tested
1.1MRelationships in one store. Checks were as fast as in a store ten times smaller.
Across 16 stores
6,799req/sWith zero errors. Capacity grows as you add customers.
Compared with OpenFGA
864,827Side-by-side requests against v1.22.0. Two differences, both caused by OpenFGA.

§ 04 · Get started

Run it locally, then deploy.

AgentFGA runs on Cloudflare Workers. You need Node.js to run it locally, and a Cloudflare account to deploy it.

  1. Run it on your machine

    The key you put in .dev.vars is the API token your agents and apps will send.

    shell
    npm install
    echo "AGENTFGA_AUTHN_PRESHARED_KEYS=dev-key-123" > .dev.vars
    npm run dev                      # http://127.0.0.1:8787
  2. Create a store

    Use the official fga CLI, or the SDK with apiUrl set to http://127.0.0.1:8787. Create one store per customer.

    shell
    fga store create --name acme \
      --api-url http://127.0.0.1:8787 --api-token dev-key-123
  3. Deploy

    Save your API keys as a secret, separated by commas, then deploy.

    shell
    npx wrangler secret put AGENTFGA_AUTHN_PRESHARED_KEYS
    npm run deploy
Configuration options

Set these as vars in wrangler.jsonc.

VariableDefault
AGENTFGA_AUTHN_METHODpreshared. none turns off authentication, for local use only.
AGENTFGA_AUTHN_PRESHARED_KEYSA secret. Required when the method is preshared.
AGENTFGA_MAX_TUPLES_PER_WRITE100
AGENTFGA_MAX_TYPES_PER_AUTHORIZATION_MODEL100
AGENTFGA_MAX_CHECKS_PER_BATCH_CHECK50
AGENTFGA_RESOLVE_NODE_LIMIT25
AGENTFGA_LIST_OBJECTS_MAX_RESULTS / _DEADLINE_MS1000 / 3000
AGENTFGA_LIST_USERS_MAX_RESULTS / _DEADLINE_MS1000 / 3000
AGENTFGA_DIAGNOSTIC_HEADERSUnset. true adds headers showing which data centers handled each request.

§ 05 · Compatibility

Compatible with OpenFGA.

AgentFGA covers OpenFGA's HTTP API: stores, models (schema 1.1 and 1.2), relationship tuples, Check, BatchCheck, ListObjects, ListUsers, Expand, change history, assertions and conditions. Every response, including error codes and messages, was compared with OpenFGA v1.22.0, across 864,827 side-by-side requests.

Not supported yet

  • OIDC sign-in. Clients authenticate with API keys only.
  • AuthZEN endpoints.
  • Metrics and tracing.
  • Experimental $expression conditions.

Works differently

  • Stores that don't exist return 404. OpenFGA accepts some requests for them, such as writing a model.
  • The consistency option is ignored, because every read is already up to date.
  • StreamedListObjects sends the full result at once instead of streaming it.
  • Condition errors are worded differently, and OpenFGA's cost limit for conditions isn't enforced.
  • Missing condition parameters can give a different result in rare cases (157 of 864,750), because OpenFGA decides how to handle them at random.
How compatibility was tested

Test tools send identical requests to AgentFGA and to the official OpenFGA v1.22.0 server, then compare every response. npm test replays the results, so they are rechecked on every run. Testing also found 9 bugs in OpenFGA itself; COMPATIBILITY.md lists them.

What was comparedRequestsDifferences
Random models, relationships and queries864,8272, both caused by OpenFGA
The same, with condition parameters sometimes missing864,750159, of which 157 are OpenFGA's random choice described above
Malformed and edge-case requests to every endpoint5,0200
Limits, cycles, pagination and other targeted scenarios790
All of the above against a deployed instance29,113 + 5,020 + 790
OpenFGA's own conformance tests 171 testsall but 180. The 18 use experimental $expression conditions.
A full app flow with the official @openfga/sdk1 flow each0

An OHQ product.

OHQ researches and builds the infrastructure agents run on, and releases it as open source. AgentFGA comes first, because deciding what agents may see and do is where they carry the most risk.