Policies & Fine-Grained Authorization
Bulwark's policy engine combines scope-based authorization (what an agent can do) with relationship-based FGA (what resources it can access).
Two Layers of Authorization
Layer 1: Scopes (What)
Scopes define the categories of operations an agent can perform:
read:customers — read customer records
write:tickets — create/update support tickets
credential:github — use the GitHub credential proxy
approve:payments — trigger CIBA payment approvals
Scopes are declared at agent registration and enforced on every request.
Layer 2: FGA (Which)
FGA (Fine-Grained Authorization) defines which specific resources an agent can access:
// Agent can view document:doc_abc
await bulwark.fga.write({
writes: [{ user: "agent:agent_01j", relation: "viewer", object: "document:doc_abc" }],
});
// Check at runtime
const { allowed } = await bulwark.fga.check({
user: "agent:agent_01j",
relation: "viewer",
object: "document:doc_abc",
});
Relationship Model
FGA uses a simple (user, relation, object) triple model:
user:usr_01j —[owner]→ document:doc_abc
agent:agent_01j —[viewer]→ document:doc_abc
team:eng —[member]→ user:usr_01j
Relations can be inherited through parent relationships:
// If a user is an owner, they're also a viewer
viewer from owner
Policy Evaluation
When an agent makes a request, Bulwark evaluates:
1. Does the agent's Biscuit token include the required scope?
→ If no, reject with 403 INSUFFICIENT_SCOPE
2. Does the agent have the required FGA relation on the resource?
→ If no, reject with 403 FORBIDDEN
3. Are there any CIBA requirements for this action?
→ If yes, initiate human approval flow
Common Patterns
Resource ownership
// User creates a resource — set as owner
await bulwark.fga.write({
writes: [{ user: `user:${userId}`, relation: "owner", object: `doc:${docId}` }],
});
// Share with an agent
await bulwark.fga.write({
writes: [{ user: `agent:${agentId}`, relation: "viewer", object: `doc:${docId}` }],
});
Team-based access
// Add team members
await bulwark.fga.write({
writes: [
{ user: `user:${userId}`, relation: "member", object: `team:engineering` },
{ user: `team:engineering`, relation: "viewer", object: `project:bulwark` },
],
});
Filter a list
// Return only docs the agent can view
const { allowed } = await bulwark.fga.filter({
user: `agent:${agentId}`,
relation: "viewer",
type: "doc",
objects: allDocIds.map((id) => `doc:${id}`),
});
Trust Level Policies
Trust levels can gate specific FGA relations automatically:
| Trust Level | Can access |
|-------------|------------|
| low | Public resources only |
| medium | Resources explicitly shared with agent |
| high | Resources owned by the delegating user |
| critical | All resources (subject to CIBA) |