Skip to main content

RBACX

CI Docs Coverage

License: MIT

PyPI Python

Universal RBAC/ABAC/ReBAC policy engine for Python with a clean core, policy sets, a compact condition DSL (including time ops), and adapters for common web frameworks.

Features

  • Algorithms: deny-overrides (default), permit-overrides, first-applicable
  • Conditions: ==, !=, <, <=, >, >=, contains, in, hasAll, hasAny, startsWith, endsWith, before, after, between
  • Role shorthand: "roles": ["admin", "editor"] on any rule — sugar for hasAny on subject.roles
  • Explainability: decision, reason, rule_id/last_rule_id, obligations
  • Policy sets: combine multiple policies with the same algorithms
  • Hot reload: file/HTTP/S3 sources with ETag and a polling manager
  • Types & lint: mypy-friendly core, Ruff-ready
  • AI Policy Authoring (rbacx[ai]): generate, refine, and explain policies using any OpenAI-compatible LLM

Installation

pip install rbacx

Optional extras include adapters, metrics, YAML support, and AI policy authoring.

Quickstart

from rbacx import Action, Context, Guard, Subject, Resource

policy = {
    "algorithm": "deny-overrides",
    "rules": [
        {
            "id": "doc_read",
            "effect": "permit",
            "actions": ["read"],
            "resource": {"type": "doc", "attrs": {"visibility": ["public", "internal"]}},
            "roles": ["reader", "admin"],  # shorthand: hasAny on subject.roles
            # Or using the explicit condition syntax:
            # "condition": {"hasAny": [ {"attr": "subject.roles"}, ["reader", "admin"] ]},
            "obligations": [ {"type": "require_mfa"} ]
        },
        {"id": "doc_deny_archived", "effect": "deny", "actions": ["*"],
         "resource": {"type": "doc", "attrs": {"archived": True}}}
    ],
}

g = Guard(policy)

d = g.evaluate_sync(
    subject=Subject(id="u1", roles=["reader"]),
    action=Action("read"),
    resource=Resource(type="doc", id="42", attrs={"visibility": "public"}),
    context=Context(attrs={"mfa": True}),
)

assert d.allowed is True
assert d.effect == "permit"
print(d.reason, d.rule_id)  # "matched", "doc_read"

Decision schema

  • decision: "permit" or "deny"
  • reason: one of "matched", "explicit_deny", "action_mismatch", "condition_mismatch", "condition_type_mismatch", "resource_mismatch", "no_match", "obligation_failed"
  • rule_id and last_rule_id (both included for compatibility; last_rule_id is the matched rule id)
  • policy_id (present for policy sets; None for single policies)
  • obligations: list passed to the obligation checker (if a permit was gated)
  • (optional) challenge: present when an authentication/step-up is required (e.g., for MFA); may be used to return 401 with the appropriate challenge header

Policy sets

Default algorithm is:

from rbacx.core.policyset import decide as decide_policyset

policyset = {"algorithm":"deny-overrides", "policies":[ policy, {"rules":[...]} ]}
result = decide_policyset(policyset, {"subject":..., "action":"read", "resource":...})

If you want to test, try this:

from rbacx.core.policyset import decide as decide_policyset

# example set of policies
policyset = {
    "algorithm": "deny-overrides",
    "policies": [
        {"rules": [
            {"id": "allow_public_read", "effect": "permit", "actions": ["read"],
             "resource": {"type": "doc", "attrs": {"visibility": ["public"]}}}
        ]},
        {"rules": [
            {"id": "deny_archived", "effect": "deny", "actions": ["*"],
             "resource": {"type": "doc", "attrs": {"archived": True}}}
        ]},
    ],
}

# example request
req = {
    "subject": {"id": "u1", "roles": ["reader"]},
    "action": "read",
    "resource": {"type": "doc", "id": "42", "attrs": {"visibility": "public", "archived": False}},  # can try: would be `deny` if archived `True`
    "context": {},
}

res = decide_policyset(policyset, req)
print(res.get("decision", res))  # -> "permit"

Hot reloading

Default algorithm is:

from rbacx import Guard, HotReloader
from rbacx.store import FilePolicySource

guard = Guard(policy={})
mgr = HotReloader(guard, FilePolicySource("policy.json"), initial_load=...)
mgr.check_and_reload()        # initial load
mgr.start(10)  # background polling thread

If you want to test, try this:

⚠️ Important: this example creates a file on disk. You also can rewrite it with TempFile (tempfile.NamedTemporaryFile)

import json
import time
from rbacx import Action, Context, Guard, HotReloader, Resource, Subject
from rbacx.store import FilePolicySource

# create a tiny policy file next to the script
policy_path = "policy.json"
json.dump({
    "algorithm": "deny-overrides",
    "rules": [{
        "id": "allow_public_read", "effect": "permit", "actions": ["read"],
        "resource": {"type": "doc", "attrs": {"visibility": ["public"]}}
    }]
}, open(policy_path, "w", encoding="utf-8"))

guard = Guard({})
mgr = HotReloader(guard, FilePolicySource(policy_path), initial_load=True)
mgr.check_and_reload()  # initial load

print(guard.evaluate_sync(
    subject=Subject(id="u1", roles=["reader"]),
    action=Action("read"),
    resource=Resource(type="doc", id="1", attrs={"visibility": "public"}),
    context=Context(),
).effect)  # -> "permit"

# update policy and wait 3 second for reload
json.dump({
    "algorithm": "deny-overrides",
    "rules": [{"id": "deny_all", "effect": "deny", "actions": ["*"], "resource": {"type": "doc"}}]
}, open(policy_path, "w", encoding="utf-8"))
mgr.start(3)  # starting polling
time.sleep(3)

print(guard.evaluate_sync(
    subject=Subject(id="u1", roles=["reader"]),
    action=Action("read"),
    resource=Resource(type="doc", id="1", attrs={"visibility": "public"}),
    context=Context(),
).effect)  # -> "deny"

Packaging

  • We ship py.typed so type checkers pick up annotations.
  • Standard PyPA flow: python -m build, then twine upload to (Test)PyPI.

License

MIT

Metadata

Release files for rbacx 1.18.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rbacx 1.18.0
File Size Uploaded
rbacx-1.18.0.tar.gz 92.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rbacx 1.18.0
File Interpreter ABI Platform
rbacx-1.18.0-py3-none-any.whl Python 3 none any Details

Total release size: 207.9 kB

Release files / rbacx-1.18.0.tar.gz

Download URL rbacx-1.18.0.tar.gz
Size 92.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e45232ee9b3328a34819b8869c8aeb18e7d9e1d5a93f370a2ba762435832e1aa
BLAKE2b-256 checksum
How to use checksums
b8cc05e7b32cd0d060c40c846577eccb43972b6044ca8f1a1e6239af3608bf64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.

Transparency log

Release files / rbacx-1.18.0-py3-none-any.whl

Download URL rbacx-1.18.0-py3-none-any.whl
Size 115.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9cf251ea3f02f0ae8e3c8337c7ab05d1a2da9be86047f4a3d3eece86357f5ec1
BLAKE2b-256 checksum
How to use checksums
da905b53bd15e7f8dfb8a70add067b949bf6d2611581b07f962bf0190a9ac615
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.18.0 This release

2 release files

1.9.5

2 release files

1.9.4

2 release files

1.9.3

2 release files

1.9.2

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.9.0

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page