Skip to main content

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.

PyPI Python 3.12+ License: MIT Tests Coverage Status: Beta

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

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 is open_guard (the bare open-guard PyPI 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 actor
  • root — check the human at the top of the chain
  • all — 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:

  • ReadOnlyPolicyView is 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 with trusted_only=True to 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 parityAsyncGuard.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 primitivesTrustGateEvaluator, OperationChainConfig, ActorType.ROBOT / EDGE_DEVICE
v0.8.0 2026-04-19 Consumer integrationGuard.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 integrationGuard.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


License

MIT © 2026 Avinash Singh

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

open_guard_python-0.12.4.tar.gz (649.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

open_guard_python-0.12.4-py3-none-any.whl (168.4 kB view details)

Uploaded Python 3

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

Hashes for open_guard_python-0.12.4.tar.gz
Algorithm Hash digest
SHA256 4475ba7172bf0b4d61b0f2a0caf1c287f764510b470144a752702a79772f2b14
MD5 669b171943f3745ff96a953e65ff03a3
BLAKE2b-256 a8f4122fef920d022ba42890f7c4f1d3072cd39c4122c0a1817bc9c99f0d40b0

See more details on using hashes here.

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

Hashes for open_guard_python-0.12.4-py3-none-any.whl
Algorithm Hash digest
SHA256 4cfbefa683f984473ec3ebd879a0a4bcfbdfa3c1703f769df6b5ef17a637ffea
MD5 f32e5c3cd9fe206ff9d9146822183945
BLAKE2b-256 8e3ebbd22812e743a2f49014ddf4ba85b790118fe7dabf7513e23d278c4fe5d4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page