Metadata-Version: 2.4
Name: open-guard-python
Version: 0.12.4
Summary: Vendor-neutral authorization library — unified RBAC, ABAC, TBAC, ReBAC for all actor types
Project-URL: Homepage, https://github.com/avinash-singh-io/open-guard
Project-URL: Repository, https://github.com/avinash-singh-io/open-guard
Project-URL: Bug Tracker, https://github.com/avinash-singh-io/open-guard/issues
Author-email: Avinash Singh <avinashsingh539+og@gmail.com>
License: MIT
License-File: LICENSE
Keywords: abac,access-control,ai-agents,authorization,policy,rbac,rebac,security,tbac
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: cli
Requires-Dist: click>=8.0; extra == 'cli'
Provides-Extra: client
Requires-Dist: httpx>=0.27.0; extra == 'client'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.129.0; extra == 'fastapi'
Provides-Extra: mcp
Requires-Dist: mcp>=0.3; extra == 'mcp'
Provides-Extra: service
Requires-Dist: fastapi>=0.129.0; extra == 'service'
Requires-Dist: uvicorn[standard]>=0.40.0; extra == 'service'
Provides-Extra: shield
Requires-Dist: open-shield-python>=0.1.0; extra == 'shield'
Provides-Extra: sql
Requires-Dist: sqlalchemy>=2.0; extra == 'sql'
Provides-Extra: sql-async
Requires-Dist: aiosqlite>=0.20.0; extra == 'sql-async'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'sql-async'
Description-Content-Type: text/markdown

# open-guard

> A unified Python authorization library — RBAC, ABAC, time, relationships, delegation, and AI-agent identity chains, in one engine.

[![PyPI](https://img.shields.io/pypi/v/open-guard-python.svg)](https://pypi.org/project/open-guard-python/)
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-1235%20passing-brightgreen.svg)](#development)
[![Coverage](https://img.shields.io/badge/coverage-93%25-brightgreen.svg)](#development)
[![Status: Beta](https://img.shields.io/badge/status-beta-yellow.svg)](#status)

`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](#install)
- [30-second quick start](#30-second-quick-start)
- [What you get](#what-you-get)
- [Authorization models](#authorization-models)
- [AI-era features](#ai-era-features)
- [Integrations](#integrations)
- [Storage backends](#storage-backends)
- [Architecture](#architecture)
- [Status](#status)
- [Release history](#release-history)
- [Development](#development)
- [License](#license)

---

## Install

```bash
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

```python
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`:

```python
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.

```python
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.

```python
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.

```python
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.

```python
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

```python
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

```python
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:

```python
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):

```python
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:

```python
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:

```python
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:

```python
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:

```python
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

```bash
pip install open-guard-python[mcp]
```

```python
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

```bash
pip install open-guard-python[fastapi]
```

Mount the admin REST surface (policies, roles, relationships, delegations, sessions):

```python
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:

```python
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:

```bash
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

```bash
pip install open-guard-python[client]
```

```python
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:

```python
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

```python
# 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

```bash
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](LICENSE) © 2026 Avinash Singh
