Vendor-neutral authorization library — unified RBAC, ABAC, TBAC, ReBAC for all actor types
Project description
open-guard
A unified Python authorization library — RBAC, ABAC, time, relationships, delegation, and AI-agent identity chains, in one engine.
open-guard answers one question for any kind of caller — human, service, AI agent, robot, or edge device:
Is this subject allowed to perform this action on this resource, right now, in this tenant?
It does this with five composable evaluators (RBAC, ABAC, TBAC, ReBAC, Trust), a delegation engine with usage budgets and leases, an async approval workflow, an explainable decision trace, and a clean ports-and-adapters core that runs in-memory, on SQLite, or on PostgreSQL.
You can use it as a plain authorization library, mount its REST surface inside a FastAPI app, or run it as a standalone HTTP service.
Table of contents
- Install
- 30-second quick start
- What you get
- Authorization models
- AI-era features
- Integrations
- Storage backends
- Architecture
- Status
- Release history
- Development
- License
Install
pip install open-guard-python
The bare install gives you the in-memory engine and all five evaluators. Optional extras pull in framework or storage dependencies:
| Extra | What it adds | Install |
|---|---|---|
sql |
SQLAlchemy stores (SQLite, PostgreSQL) | pip install open-guard-python[sql] |
sql-async |
Async SQLAlchemy + aiosqlite |
pip install open-guard-python[sql-async] |
fastapi |
AdminRouter for mounting into FastAPI |
pip install open-guard-python[fastapi] |
service |
Standalone HTTP service (FastAPI + uvicorn) | pip install open-guard-python[service] |
client |
Python SDK for the HTTP service | pip install open-guard-python[client] |
mcp |
MCP tool-call adapter | pip install open-guard-python[mcp] |
cli |
open-guard CLI |
pip install open-guard-python[cli] |
The PyPI package is
open-guard-python. The import path isopen_guard(the bareopen-guardPyPI name was unavailable due to an unrelated existing project).
30-second quick start
from open_guard import Guard, Subject, Action, Resource, Policy, PolicyEffect
# One-line factory — wires every store and every evaluator
guard = Guard.from_config(backend="memory")
# Allow anyone with role "editor" to read or write
guard.policy_store.add(Policy(
tenant_id="acme",
evaluator_type="rbac",
effect=PolicyEffect.ALLOW,
subject_match={"roles": ["editor"]},
action_match=["read", "write"],
))
decision = guard.authorize(
subject=Subject(id="user-1", attributes={"roles": ["editor"]}),
action=Action(name="write"),
resource=Resource(id="doc-1", resource_type="document"),
tenant_id="acme",
)
print(decision.allowed) # True
print(decision.reason) # Human-readable explanation
Need async? Swap Guard for AsyncGuard:
from open_guard import AsyncGuard
guard = await AsyncGuard.from_config(backend="memory")
decision = await guard.authorize(subject, action, resource, tenant_id="acme")
That's the entire happy path. The rest of this README is about everything else open-guard can do for you when one role + one action isn't enough.
What you get
| Capability | open-guard | Casbin | OPA / Cedar |
|---|---|---|---|
| RBAC + ABAC | ✅ | ✅ | ✅ |
| ReBAC (Zanzibar relation tuples) | ✅ | partial | ❌ |
| Time-based & task-scoped (TBAC) | ✅ | ❌ | ❌ |
| Trust-score gating | ✅ | ❌ | ❌ |
| On-behalf-of identity chains | ✅ (recursive, depth-capped) | ❌ | ❌ |
| Delegation with usage budgets + leases | ✅ (push/pull, cascade revocation) | ❌ | ❌ |
| Async approval workflows | ✅ (tri-state decisions) | ❌ | ❌ |
| Action-pattern evaluator routing | ✅ (OperationChain) | ❌ | ❌ |
| Decision traces | ✅ (per-policy, JSON-serializable) | partial | ✅ |
| MCP tool-call adapter | ✅ | ❌ | ❌ |
| Prompt-injection defense | ✅ (taint marking, read-only views) | ❌ | ❌ |
| Native async (non-blocking I/O) | ✅ (AsyncGuard) |
❌ | ❌ |
| Multi-tenancy by design | ✅ (every API takes tenant_id) |
manual | manual |
| Standalone HTTP service + SDK | ✅ ([service] + [client]) |
❌ | OPA only |
Authorization models
open-guard ships five evaluators. You can use one, combine many, or define which evaluators run for which action via OperationChainConfig.
RBAC — Role-Based
Role matching with optional resource scoping, hierarchy support, priority-based conflict resolution, and NIST-compliant session-aware role subset activation.
Policy(evaluator_type="rbac",
subject_match={"roles": ["editor"]},
action_match="write")
ABAC — Attribute-Based
Condition-based evaluation over subject, resource, action, and context attributes. 14 operators (eq, in, regex, contains_any, is_subset, …), OR logic via condition_groups, chain-aware predicates (chain.root_actor.*), session namespace (session.scope), and trusted_only modifier for prompt-injection defense.
Policy(evaluator_type="abac", conditions={
"subject.department": {"op": "eq", "value": "engineering"},
"resource.classification": {"op": "in", "value": ["public", "internal"]},
})
TBAC — Time- and Task-Based
Absolute time windows, recurring schedules (IANA timezone, DST-aware), ISO-8601 TTLs, and task-scoped permissions with a lifecycle state machine and parent-child hierarchy that narrows permissions down the tree.
Policy(evaluator_type="tbac", conditions={
"time_window": {"not_before": "2026-04-16T00:00:00Z",
"not_after": "2026-04-17T00:00:00Z"},
"task_id": "task-123",
})
ReBAC — Relationship-Based
Google Zanzibar-style relation tuples with recursive graph traversal, computed permissions (union, intersection, exclusion, arrow), wildcards, subject sets, memoization, and max-depth cycle prevention.
Policy(evaluator_type="rebac", conditions={
"object_type": "document",
"permission": "can_view",
"subject_type": "user",
})
Trust — Trust-Score Gating
Chain-aware trust evaluation built for agent systems. Reads subject.attributes["trust_score"]. Three scopes:
immediate(default) — check just the requesting actorroot— check the human at the top of the chainall— every actor in the chain must clear the threshold
Policy(evaluator_type="trust", conditions={
"min_trust_score": 0.7,
"trust_scope": "all",
})
ABAC condition operators
| Operator | Example | Description |
|---|---|---|
eq / neq |
{"op": "eq", "value": "engineering"} |
Equals / not equals |
in / not_in |
{"op": "in", "value": ["a", "b"]} |
In / not in a list |
contains / not_contains |
{"op": "contains", "value": "ml"} |
List or string contains |
gt / gte / lt / lte |
{"op": "gte", "value": 9} |
Numeric comparison |
regex / not_regex |
{"op": "regex", "value": ".*@co\\.com$"} |
Regex match |
exists |
{"op": "exists", "value": true} |
Attribute exists |
contains_any |
{"op": "contains_any", "value": ["a", "b"]} |
Any element overlaps |
contains_all |
{"op": "contains_all", "value": ["a", "b"]} |
All elements present |
is_subset |
{"op": "is_subset", "value": ["a", "b", "c"]} |
Left subset of right |
any_matches |
{"op": "any_matches", "value": {...}} |
Chain ancestor sub-condition |
AI-era features
Traditional RBAC + ABAC handle "user logs in, gets a role, accesses a resource." That breaks the moment an AI agent acts on a user's behalf, spawns a sub-agent, and calls a tool. open-guard was built so the same engine that secures your SaaS also secures your agent stack.
Every actor is a first-class subject
from open_guard import Subject, ActorType
Subject(id="user-1", actor_type=ActorType.USER, attributes={"roles": ["admin"]})
Subject(id="agent-42", actor_type=ActorType.AGENT, attributes={"trust_score": 0.85})
Subject(id="svc-billing", actor_type=ActorType.SERVICE)
Subject(id="sensor-5", actor_type=ActorType.DEVICE, attributes={"zone": "warehouse"})
Subject(id="robot-arm-3", actor_type=ActorType.ROBOT, attributes={"trust_score": 0.95})
Subject(id="edge-gw-7", actor_type=ActorType.EDGE_DEVICE, attributes={"region": "us-west"})
Subject(id="conv-123", actor_type=ActorType.CONVERSATION)
Identity chains (on-behalf-of)
Authorization in agent systems is rarely a single hop. Subject.on_behalf_of lets you model the full chain, and policies can reason over it:
user = Subject(id="user-1", actor_type=ActorType.USER,
attributes={"roles": ["researcher"], "trust_score": 0.95})
agent = Subject(id="agent-1", actor_type=ActorType.AGENT,
attributes={"trust_score": 0.8}, on_behalf_of=user)
sub_agent = Subject(id="sub-1", actor_type=ActorType.AGENT,
attributes={"trust_score": 0.6}, on_behalf_of=agent)
# Trust scope "all" requires every actor in the chain to clear the threshold.
# ABAC predicates can reach chain.depth, chain.root_actor.*, chain.ancestors.
Chain depth is configurable and capped (default 5) at both construction and runtime.
Action-pattern evaluator routing
Different operations want different combinations of checks. OperationChain routes each action pattern to the right evaluators (fnmatch glob, first match wins):
from open_guard import Guard, OperationChainConfig, OperationRule
guard = Guard.from_config(backend="memory", operation_chain=OperationChainConfig(rules=[
OperationRule(action_pattern="knowledge:write", evaluators=["rbac", "abac", "trust"]),
OperationRule(action_pattern="knowledge:read", evaluators=["rbac", "abac"]),
OperationRule(action_pattern="admin:*", evaluators=["rbac"]),
]))
Delegation with budgets and leases
Agents shouldn't have inherent permissions. They should receive bounded, time-limited delegation:
from open_guard import DelegationScope
from datetime import timedelta
guard.delegate(
grantor=user, grantee=agent,
scope=DelegationScope(action="read", resource_type="document"),
max_uses=100, # Budget: 100 reads
lease_ttl=timedelta(hours=1), # Auto-expires unless renewed
tenant_id="acme",
)
# Each use atomically consumes from the budget (CAS-safe in SQL).
decision = guard.authorize_and_consume(agent, action, resource, tenant_id="acme")
# Revocation cascades to every sub-delegation that descended from this one.
guard.revoke_delegation(delegation_id, tenant_id="acme")
Async approval — tri-state decisions
Some actions need a human in the loop. open-guard returns PENDING_APPROVAL and gives you metadata for whatever channel you use (Slack, email, webhook). Your app dispatches and then completes the decision:
from open_guard import Policy, ApprovalRequirement, DecisionStatus
from datetime import timedelta
Policy(
evaluator_type="rbac",
action_match="data:delete",
requires_approval=ApprovalRequirement(
approvers=["role:manager"], quorum="any", ttl=timedelta(hours=4),
),
)
decision = guard.authorize(agent, Action(name="data:delete"), resource, tenant_id="acme")
if decision.status == DecisionStatus.PENDING_APPROVAL:
# Dispatch via your channel of choice...
# Later, when the manager approves:
guard.approve(decision.approval_request_id, tenant_id="acme", approver="manager-1")
Explainable decisions
Every decision can return a structured trace, JSON-serializable for logs, SIEM, or downstream agent reasoning:
decision, trace = guard.authorize_traced(subject, action, resource, tenant_id="acme")
# trace.evaluator_results -> which evaluators ran, which policies matched / rejected
# trace.actor_chain -> full OBO chain snapshot
# trace.composition -> "all-allow" | "deny-wins" | "pending-approval"
# trace.duration_ms -> total evaluation time
Reverse lookup — "what can this subject do?"
For RAG filtering, UI menus, and policy debugging. Composes RBAC, ABAC, TBAC, and ReBAC across all stores with cursor pagination and deny precedence:
result = guard.list_authorized(
subject=user, action=Action(name="read"), resource_type="document",
tenant_id="acme", limit=100,
)
# result.resource_ids, result.next_cursor
Prompt-injection defense
Two complementary layers:
ReadOnlyPolicyViewis auto-wired in the evaluation path so no evaluator can mutate policy state.- Attribute taint marking:
subject.attributes["_trusted"] = {"role": False}flags an attribute as untrusted (e.g. extracted from user content). Policies opt in withtrusted_only=Trueto refuse evaluation on tainted data.
Integrations
MCP — Model Context Protocol
pip install open-guard-python[mcp]
from open_guard.adapters.mcp import MCPGuard
mcp = MCPGuard(guard=guard, resolver=session_resolver, default_tenant_id="acme")
result = mcp.check_tool_call(session_id="s1", tool_name="search", params={"query": "..."})
tools = mcp.list_authorized_tools(session_id="s1")
SDK-agnostic core, parameter-level ABAC via dot-paths, chain-propagation via MCPSessionResolver, pre-authorization schema hygiene, pending-approval surface for dangerous tools.
FastAPI
pip install open-guard-python[fastapi]
Mount the admin REST surface (policies, roles, relationships, delegations, sessions):
from fastapi import FastAPI
from open_guard import PolicyAdmin, AdminRouter
app = FastAPI()
admin = PolicyAdmin(guard)
app.include_router(AdminRouter(admin=admin), prefix="/admin")
Or wire decision endpoints with middleware:
from open_guard.api.fastapi import OpenGuardMiddleware, RequirePermission, RequireActor
app.add_middleware(OpenGuardMiddleware, guard=guard)
@app.get("/docs/{doc_id}")
def get_doc(doc_id: str, _auth=Depends(RequirePermission("read", "document"))):
...
@app.get("/human-only")
def human_only(_auth=Depends(RequireActor(ActorType.USER))):
...
Standalone HTTP service
Run open-guard as a service that any language can call:
pip install open-guard-python[service]
uvicorn open_guard.service.app:create_app --factory
The service exposes /authorize, /authorize/traced, /list_authorized, /authorize_and_consume, and the full /admin/* CRUD surface. Authentication is pluggable: built-in ApiKeyAuth (sha-256 hashed keys), ShieldAuth (open-shield JWT, optional [shield] extra), or CallableAuth (BYO).
Tenant is read from the auth claim by default; X-Tenant-ID override is gated by the cross_tenant_admin scope.
Python SDK
pip install open-guard-python[client]
from open_guard.client import OpenGuardClient, ApiKey
client = OpenGuardClient(base_url="https://og.example.com", auth=ApiKey("og_..."))
decision = client.decisions.authorize(subject={...}, action={...}, resource={...})
client.admin.policies.create(tenant_id="acme", evaluator_type="rbac", ...)
Sync + async clients, namespaced API (client.decisions.*, client.admin.{policies,roles,relationships,delegations,sessions}.*), retry policy that's idempotent-only by default.
Admin SDK (in-process)
When you embed open-guard directly, the same admin surface is available without the HTTP hop:
from open_guard import PolicyAdmin
admin = PolicyAdmin(guard)
admin.create_policy(tenant_id="acme", evaluator_type="rbac",
subject_match={"roles": ["viewer"]}, action_match="read")
admin.assign_role(tenant_id="acme", subject_id="user-1", role="viewer")
admin.list_policies(tenant_id="acme")
Storage backends
# In-memory — zero config, great for tests and demos
guard = Guard.from_config(backend="memory")
# SQLite — single-node, file-based
guard = Guard.from_config("sqlite:///local.db")
# PostgreSQL — production
guard = Guard.from_config("postgresql://user:pass@host/db")
# Async — native non-blocking stores
guard = await AsyncGuard.from_config(backend="memory")
guard = await AsyncGuard.from_config("sqlite+aiosqlite:///local.db")
guard = await AsyncGuard.from_config("postgresql+asyncpg://user:pass@host/db")
DB tooling: create_all_tables(engine), get_ddl(dialect), and a CLI (open-guard db init, open-guard db schema) when you install the [cli] extra.
Multi-tenancy is built in. Every policy carries a tenant_id, and every authorize call takes one — cross-tenant access is impossible by construction.
Architecture
Clean / hexagonal. The domain is pure Python with zero external dependencies. Adapters are swappable. Adding a new authorization model is "implement EvaluatorPort and register it."
+------------------------------------------+
| API layer (FastAPI, MCP, REST service) | thin framework integration
+------------------------------------------+
| Domain (pure Python — no deps) | entities, ports, services
| Guard → PolicyRouter → Evaluators |
| DelegationManager, SessionManager, |
| ApprovalManager, TaskManager |
+------------------------------------------+
| Adapters (swappable) |
| RBAC, ABAC, TBAC, ReBAC, Trust | 5 evaluators
| InMemory / SQLAlchemy stores | 7 store types, sync + async
+------------------------------------------+
Ports: EvaluatorPort, PolicyStorePort, RelationshipStorePort, DelegationStorePort, SessionStorePort, ApprovalStorePort, TaskStorePort, CandidateResourcePort, RevocationStreamPort — each with an async counterpart.
Status
Beta. open-guard has shipped twelve releases, 1235 tests pass at 93% coverage, the API has been stable since v0.4.0, and it's in real-world use inside the Cerebrio platform. It is appropriate for production workloads where you can pin versions and follow the changelog. The 1.0 stamp is reserved for after a full external security review and a documented breaking-change policy.
Semver: patch (0.12.x) is bug-fix-only and never breaks API. Minor (0.x.0) may add new ports, evaluators, or extras but will avoid breaking existing public APIs. Anything breaking is called out explicitly in the changelog.
Async parity: Guard and AsyncGuard are at full feature parity as of v0.12.0 (delegation, approval, list_authorized, sessions, all five evaluators).
Service Mode wire contract: SERVICE_API_VERSION = "v1" and is stable. Breaking wire changes require a bump.
Release history
| Version | Date | Highlights |
|---|---|---|
| v0.12.3 | 2026-05-25 | First public release under the avinash-singh-io PyPI account; tag-driven auto-publish via Trusted Publisher (OIDC) |
| v0.12.0–v0.12.2 | 2026-05-25 | Unreachable — initial uploads went out under the wrong PyPI account and were deleted. Per PyPI policy these version numbers can never be republished. v0.12.3 contains the same engine as v0.12.2. |
| v0.12.0 (logical) | 2026-05-18 | Async parity — AsyncGuard.list_authorized, AsyncDelegationEvaluator, AsyncApprovalManager; /list_authorized + /authorize_and_consume engine-agnostic |
| v0.11.0 | 2026-05-04 | Service Mode + Python SDK — standalone HTTP service, sync + async Python clients |
| v0.10.0 | 2026-04-28 | Production integration — async SQL backend, resource-scoped RBAC, check_relationship |
| v0.9.0 | 2026-04-20 | AI-era primitives — TrustGateEvaluator, OperationChainConfig, ActorType.ROBOT / EDGE_DEVICE |
| v0.8.0 | 2026-04-19 | Consumer integration — Guard.from_config(), PolicyAdmin, AdminRouter, AsyncGuard, CLI, DDL export |
| v0.7.0 | 2026-04-19 | Production hardening — sessions, policy isolation, push revocation |
| v0.6.0 | 2026-04-19 | Delegation & bounds — push/pull delegation, usage caps, leases, cascade revocation |
| v0.5.0 | 2026-04-18 | Agent integration — Guard.list_authorized, MCP adapter, integration guide |
| v0.4.0 | 2026-04-18 | Agentic core — on-behalf-of chains, tri-state decisions, decision traces |
| v0.3.0 | 2026-04-17 | ReBAC + SQLAlchemy — Zanzibar relation tuples, SQL stores, ABAC enhancements |
| v0.2.0 | 2026-04-16 | TBAC — time windows, recurring schedules, task-scoped permissions |
| v0.1.0 | 2026-04-16 | RBAC + ABAC core — first cut |
Next: gRPC wrapper and TypeScript + Go SDKs (Phase 12). No date — landed when a real non-Python consumer needs it.
Ecosystem
open-guard is standalone, but it pairs cleanly with the Cerebrio identity stack:
Identity Provider (Logto, Keycloak, Auth0, ...)
→ open-shield-python — authentication (who are you?)
→ open-guard — authorization (can you do this?)
→ Your app — agents, robots, services, humans
Development
git clone https://github.com/avinash-singh-io/open-guard.git
cd open-guard
uv sync # Install all dev dependencies
uv run pytest # 1235 tests, 93% coverage
uv run mypy src/ --strict # Strict type check
uv run ruff check . # Lint
Pull requests welcome. The repo follows conventional commits and atomic-commit hygiene; see CLAUDE.md for the project's working rules.
Links
- PyPI: https://pypi.org/project/open-guard-python/
- Source: https://github.com/avinash-singh-io/open-guard
- Issues: https://github.com/avinash-singh-io/open-guard/issues
License
MIT © 2026 Avinash Singh
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file open_guard_python-0.12.4.tar.gz.
File metadata
- Download URL: open_guard_python-0.12.4.tar.gz
- Upload date:
- Size: 649.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4475ba7172bf0b4d61b0f2a0caf1c287f764510b470144a752702a79772f2b14
|
|
| MD5 |
669b171943f3745ff96a953e65ff03a3
|
|
| BLAKE2b-256 |
a8f4122fef920d022ba42890f7c4f1d3072cd39c4122c0a1817bc9c99f0d40b0
|
File details
Details for the file open_guard_python-0.12.4-py3-none-any.whl.
File metadata
- Download URL: open_guard_python-0.12.4-py3-none-any.whl
- Upload date:
- Size: 168.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cfbefa683f984473ec3ebd879a0a4bcfbdfa3c1703f769df6b5ef17a637ffea
|
|
| MD5 |
f32e5c3cd9fe206ff9d9146822183945
|
|
| BLAKE2b-256 |
8e3ebbd22812e743a2f49014ddf4ba85b790118fe7dabf7513e23d278c4fe5d4
|