Typed, fail-closed authorization engine for Python 3.12+ with optional Strawberry, FastAPI, and SQLAlchemy adapters.
Project description
pyrmit
Status: v0.x — experimental. API may change without notice.
A typed, fail-closed authorization engine for Python 3.12+. Strongly-typed generic policies, two-state decisions, and pluggable audit and entitlement providers. Optional adapters for Strawberry GraphQL, FastAPI, and SQLAlchemy.
Install
pip install pyrmit # core only
pip install "pyrmit[strawberry,fastapi,sqlalchemy]" # with adapters
The adapter extras pull in their respective frameworks; install only the ones you use.
Quickstart
from dataclasses import dataclass
from enum import StrEnum
from pyrmit import ALLOW, Decision, Entitlements, PolicyEngine, Principal, deny
class Action(StrEnum):
READ = "read"
@dataclass(frozen=True)
class Actor:
user_id: int
is_admin: bool
@dataclass(frozen=True)
class Article:
id: int
owner_id: int
is_published: bool
engine: PolicyEngine[Principal[Actor], Action, Article] = PolicyEngine()
@engine.policy(action=Action.READ, subject_type=Article)
def can_read_article(principal: Principal[Actor], article: Article) -> Decision:
if principal.actor.is_admin:
return ALLOW
if article.is_published:
return ALLOW
if article.owner_id == principal.actor.user_id:
return ALLOW
return deny("article_unpublished")
alice = Principal[Actor](
actor=Actor(user_id=42, is_admin=False),
entitlements=Entitlements.empty(),
)
hidden = Article(id=1, owner_id=99, is_published=False)
decision = engine.decide(principal=alice, action=Action.READ, subject=hidden)
assert decision.allowed is False
assert decision.reason == "article_unpublished"
This example is exercised verbatim by
tests/integration/typed_decisions/test_readme_quickstart.py
to make sure the README never drifts from real behavior.
Key properties
- Two-state Decision —
allowed: boolplus a stable machine-readablereason. No third state. - Fail-closed — missing policy denies (
policy_not_registered); a policy raising any exception denies (policy_error); audit-store failure denies (audit_unavailable) whenaudit_failure_mode="deny"(which requiresaudit_allows=Trueat engine construction so that ALLOW decisions are actually covered). - Defensively immutable —
Decision.detailandAuditEntry.metadataare wrapped inMappingProxyTypeat construction and reject non-primitive values at runtime. - Strict typing —
mypy --strictclean, noAnyin core, notype: ignore. The decorator's subject_type binding is enforced by the type checker (see the negative test undertests/typing/).
Adapters
Adapters live under pyrmit.adapters.* and are optional extras:
- Strawberry:
policy_guard(...)— single-extension field guard with pre-resolution / from-source / post-resolution loader phases. NULL denial short-circuits before the resolver runs.post_resolution_policy_guard(...)is the explicit, safer counterpart for redaction-style use; it refuses to run inside a mutation operation by default (read_only=True) because the resolver runs before the authorization decision.PolicyGuardFactory(engine=..., principal_loader=...)is the recommended entry point for real schemas — it captures the engine and principal loader once and exposes.guard(...)/.post_resolution_guard(...)so individual fields don't restate the cross-cutting deps. Seeexamples/strawberry_graphql/example_di.pyfor a DI-style wiring. - FastAPI:
require_policy(...)— dependency factory that translates denials to HTTP responses; the NULL surface requires anull_mapper. - SQLAlchemy:
visibility_scope(...)decorator marks a function as the per-actor row-level visibility predicate for a model;verify_scope_applied(...)is a tripwire test helper that checks the predicate is genuinely present on a compiled query.
Development
uv sync --all-extras
uv run pytest -q
uv run mypy src tests
uv run ruff check
uv run ruff format --check
See CONTRIBUTING.md for project conventions
(PEP 695 generics, assertpy in tests, no Any outside the documented
carve-outs, etc.) and SECURITY.md for the disclosure
policy.
License
Apache-2.0. See LICENSE.
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 pyrmit-0.2.0.tar.gz.
File metadata
- Download URL: pyrmit-0.2.0.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3cabfa95fc3ec7be3a0b6949c384f5cf05d7fcfa6b8dfdb7ed437e851f972ca
|
|
| MD5 |
9d3103472a82178332cc273d7dac3326
|
|
| BLAKE2b-256 |
2f7c6225d9208c4436e6c49c4d021c4cc5f4638ac9bbba209558e173ee88fefb
|
Provenance
The following attestation bundles were made for pyrmit-0.2.0.tar.gz:
Publisher:
release.yml on cahna/pyrmit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrmit-0.2.0.tar.gz -
Subject digest:
b3cabfa95fc3ec7be3a0b6949c384f5cf05d7fcfa6b8dfdb7ed437e851f972ca - Sigstore transparency entry: 1740629669
- Sigstore integration time:
-
Permalink:
cahna/pyrmit@04cd5e7284c3abf5f6c4dce63feb5ddec8827b56 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/cahna
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@04cd5e7284c3abf5f6c4dce63feb5ddec8827b56 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyrmit-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pyrmit-0.2.0-py3-none-any.whl
- Upload date:
- Size: 54.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0439d05cb635863339de49dd78ff8df2a43af9b76850d0bb8923ebe4832b77ac
|
|
| MD5 |
0eee891aa545b8cedaeab469f3456bef
|
|
| BLAKE2b-256 |
137da26712eabbd280e1e9890f5de485e966c0ac398598c23a07e80ec9a289eb
|
Provenance
The following attestation bundles were made for pyrmit-0.2.0-py3-none-any.whl:
Publisher:
release.yml on cahna/pyrmit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyrmit-0.2.0-py3-none-any.whl -
Subject digest:
0439d05cb635863339de49dd78ff8df2a43af9b76850d0bb8923ebe4832b77ac - Sigstore transparency entry: 1740629695
- Sigstore integration time:
-
Permalink:
cahna/pyrmit@04cd5e7284c3abf5f6c4dce63feb5ddec8827b56 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/cahna
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@04cd5e7284c3abf5f6c4dce63feb5ddec8827b56 -
Trigger Event:
push
-
Statement type: