This release is a pre-release and may not be stable for production use.
AccessGate
Composable, default-deny RBAC, ABAC, and relationship-aware authorization for Python applications.
AccessGate answers one question: may this subject perform this action on this resource in this context? It provides a small framework-neutral kernel, secure decision defaults, and extension interfaces for application-specific identity, policy, audit, and framework integration.
It is complementary to authentication and session-security packages such as SessionArmor. Those establish who the caller is and whether the session can be trusted; AccessGate decides what that caller is allowed to do.
Status: early alpha. Security controls and assurance evidence are being developed rigorously, but the public API may evolve before 1.0. This package has not received an independent audit or any government/NATO accreditation.
Design guarantees
- Default deny: abstention never grants access.
- Deny overrides: any explicit denial wins over grants by default.
- Fail closed: policy and audit failures deny access by default.
- Exact matching: built-in role, action, owner, and tenant comparisons never use substring matching.
- Safe ABAC: missing attributes and operator failures are indeterminate, cannot be negated into access, and deny by default.
- Immutable inputs: request attributes, literal values, and decision metadata are recursively snapshotted before evaluation or audit.
- Bounded evaluation: serialized policies, condition trees, MCP messages, and collection workloads have explicit denial-of-service limits.
- Verifiable policy identity: canonical fingerprints, ordered policy-set fingerprints, externally verified signed envelopes, and engine-owned policy deployment identifiers support change control without embedding key custody.
- No expression evaluation: declarative policies use registered operators
and mapping-only attribute traversal—never Python
evalor object traversal. - Framework-neutral core: no runtime dependencies and no ORM, token, or user model assumptions.
- Custom from day one: policies, decision strategies, identity/resource resolvers, audit sinks, and framework adapters are public extension points.
Subject, Resource, AuthorizationRequest, and Decision expose to_dict()
for JSON-friendly snapshots of their otherwise immutable data.
Installation
pip install AccessGate
For the optional Django adapter:
pip install "AccessGate[django]"
Quick start
from access_gate import (
AuthorizationEngine,
Resource,
RolePolicy,
Subject,
TenantBoundaryPolicy,
)
engine = AuthorizationEngine(
[
TenantBoundaryPolicy(actions={"incident.read"}),
RolePolicy({"incident_manager"}, actions={"incident.read"}),
]
)
decision = engine.authorize(
Subject(
"user-42",
roles=frozenset({"incident_manager"}),
attributes={"tenant_id": "acme"},
),
"incident.read",
Resource("incident", "INC-123", {"tenant_id": "acme"}),
)
assert decision.allowed
Tenant boundaries deliberately do not grant access. They abstain when the tenant matches and deny when it is missing or different; another policy must positively allow the action.
Attribute-based access control
ABAC policies can compare subject, resource, action, and context attributes:
from access_gate import (
ActionAttribute,
AllOf,
AttributePolicy,
ContextAttribute,
ResourceAttribute,
SubjectAttribute,
)
document_access = AttributePolicy(
AllOf(
SubjectAttribute("department").equals(ResourceAttribute("department")),
SubjectAttribute("clearance").greater_than_or_equal(
ResourceAttribute("classification")
),
ActionAttribute("risk").less_than_or_equal(3),
ContextAttribute("device.trusted").equals(True),
),
actions={"document.read"},
name="document_access",
)
Conditions support AllOf, AnyOf, and Not, multi-valued attributes, exact
membership and set relations, numeric comparisons, and explicit existence
checks. Not preserves indeterminate outcomes, so a missing attribute never
becomes a grant merely because a condition was negated.
Policies serialize to the versioned access_gate.policy.v1 format:
from access_gate import dumps_policy, loads_policy
document = dumps_policy(document_access)
restored = loads_policy(document)
For controlled deployments, identify the exact ordered policy set and pass that identifier into the engine so every final and audited decision is attributable:
from access_gate import AuthorizationEngine, policy_set_fingerprint
policy_set_id = policy_set_fingerprint([document_access])
engine = AuthorizationEngine([document_access], policy_set_id=policy_set_id)
dumps_signed_policy() and loads_signed_policy() accept application-supplied
signer/verifier protocols. AccessGate deliberately does not choose algorithms,
store keys, or implement a trust store. See
docs/POLICY_FORMAT.md.
See docs/abac.md for the full data model, operator semantics,
customization rules, JSON format, and rollout guidance.
Application-defined policies
Implement the Policy protocol directly or wrap a function:
from access_gate import Decision, FunctionPolicy
def published_incidents(request):
resource = request.resource
if resource and resource.attributes.get("published") is True:
return Decision.allow("The incident is public.", policy="published")
return None
policy = FunctionPolicy("published", published_incidents)
A policy returns an allow/deny Decision, or None to abstain. Custom decision
strategies can replace deny-overrides where a domain needs different semantics.
Policies exposing a validated frozenset in an actions attribute are indexed
by the engine and skipped for unrelated actions. FunctionPolicy supports this
directly through its actions argument. Use decide_many() for ordered batch
evaluation; every request still receives its own decision and audit event.
For mixed RBAC/ABAC grants, configure alternative grant policies to abstain on a miss. Explicit boundary denials still override every grant.
Django adapter
from access_gate import AuthorizationEngine, RolePolicy
from access_gate.adapters.django import DjangoAuthorizer
authorizer = DjangoAuthorizer(
AuthorizationEngine([RolePolicy({"admin"}, actions={"users.list"})])
)
@authorizer.require("users.list")
def user_list(request):
...
The default Django subject resolver only reads pk, is_authenticated,
access_gate_roles, and access_gate_attributes. Real applications should
normally provide a resolver that validates and maps their exact identity claims.
The adapter also accepts custom context and action-attribute resolvers for ABAC.
Synchronous and asynchronous Django views are supported. Async views may use
awaitable subject, resource, context, action-attribute, and denial resolvers.
MCP server
AccessGate includes a dependency-free stdio MCP server:
accessgate-mcp
From a source checkout, run python mcp/server.py. The server exposes package
capabilities, authorization-model guidance, the ABAC JSON Schema, exact built-in
operator discovery, and read-only policy validation. It does not authenticate
users, evaluate production identity data, or change application policy.
Project boundary
AccessGate does not authenticate users, validate JWTs, secure sessions, query
application permissions, or decide how tenants and resources are represented.
Those are application and adapter responsibilities. See
docs/architecture.md and
docs/customization.md. AccessGate provides its own
policy format but does not claim wire compatibility with XACML, Cedar, Rego, or
other policy languages.
Development
python -m pip install -e ".[dev]"
ruff check src tests
mypy src/access_gate
pytest --cov=access_gate --cov-report=term-missing
Security reviewers should begin with the
threat model,
assurance case, and
audit scope.
License
MIT (c) Tunet Ltd.
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 accessgate-0.2.0a1.tar.gz.
File metadata
- Download URL: accessgate-0.2.0a1.tar.gz
- Upload date:
- Size: 96.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84badd53c280c772e2141b271ae3a8d604e5f7292e65bcf3d9e1d18bd27d5579
|
|
| MD5 |
b273f0723aad1e4a2c7b362732b16a89
|
|
| BLAKE2b-256 |
aa58dd6c76fa189122750da220d8b64e6306882e6b455c8cc490aa75b99bcb39
|
Provenance
The following attestation bundles were made for accessgate-0.2.0a1.tar.gz:
Publisher:
publish.yaml on Tunet-xyz/access_gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
accessgate-0.2.0a1.tar.gz -
Subject digest:
84badd53c280c772e2141b271ae3a8d604e5f7292e65bcf3d9e1d18bd27d5579 - Sigstore transparency entry: 2476228255
- Sigstore integration time:
-
Permalink:
Tunet-xyz/access_gate@0fb6084954372c858056311297e0ac698c785b72 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tunet-xyz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@0fb6084954372c858056311297e0ac698c785b72 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file accessgate-0.2.0a1-py3-none-any.whl.
File metadata
- Download URL: accessgate-0.2.0a1-py3-none-any.whl
- Upload date:
- Size: 37.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d232a72a9f63f4fcefc87b0bdb46597cb54b8452f7cd128c0bf9fa26aa9a7bb
|
|
| MD5 |
b9ddcfcddf4c2c79122b8659ce26c201
|
|
| BLAKE2b-256 |
8fce690571186ca5d8e450e041bea41116f1e6f6034435103cd39beff135a9c8
|
Provenance
The following attestation bundles were made for accessgate-0.2.0a1-py3-none-any.whl:
Publisher:
publish.yaml on Tunet-xyz/access_gate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
accessgate-0.2.0a1-py3-none-any.whl -
Subject digest:
3d232a72a9f63f4fcefc87b0bdb46597cb54b8452f7cd128c0bf9fa26aa9a7bb - Sigstore transparency entry: 2476228277
- Sigstore integration time:
-
Permalink:
Tunet-xyz/access_gate@0fb6084954372c858056311297e0ac698c785b72 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Tunet-xyz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@0fb6084954372c858056311297e0ac698c785b72 -
Trigger Event:
workflow_dispatch
-
Statement type: