TreeTop Client
Dataclass-based HTTPX client for the Treetop REST API. Python ≥ 3.12, zero runtime deps beyond HTTPX.
Features
- Unified Batch Authorization Endpoint: Process multiple authorization requests in a single API call
- Detail Levels: Control response verbosity (brief vs. detailed with policy information)
- Backward Compatible: Existing code using
check()andcheck_detailed()continues to work seamlessly - Full Async Support: Async/await support for all API methods
- Type Safe: Fully type-hinted dataclasses for requests and responses
- Version Tracking: Access policy version information (hash and loaded_at timestamp)
- Treetop REST v0.0.12: Operational probes, generated OpenAPI, metrics, status, policy, and schema endpoints
- Request Context: Pass request-scoped Cedar context attributes during authorization
Basic Usage (Single Request)
from treetop_client.client import TreeTopClient
from treetop_client.models import (
Action,
Decision,
Request,
Resource,
User,
ResourceAttribute,
ResourceAttributeType,
)
client = TreeTopClient(base_url=f"http://localhost:{PORT}")
attrs = {}
attrs["ip"] = ResourceAttribute.new("10.0.0.1", ResourceAttributeType.IP)
attrs["name"] = ResourceAttribute.new("myhost.example.com", ResourceAttributeType.STRING)
req = Request(
principal=User.new("myuser", ["mynamespace"], ["mygroup"]),
action=Action.new("myaction", ["mynamespace"]),
resource=Resource.new("Host", id="myhost", attrs=attrs)
)
# Use the check method (wraps batch API internally)
resp = client.check(req)
# Use is_allowed() / is_denied() methods
assert resp.is_allowed()
# Or compare with the Decision enum
assert resp.decision == Decision.ALLOW
Batch Authorization
Send multiple authorization requests in a single API call for better performance:
from treetop_client.client import TreeTopClient
from treetop_client.models import (
Action,
Request,
Resource,
User,
ResourceAttribute,
ResourceAttributeType,
)
client = TreeTopClient(base_url=f"http://localhost:{PORT}")
# Create multiple requests
requests = []
for i in range(3):
attrs = {"ip": ResourceAttribute.new(f"10.0.0.{i}", ResourceAttributeType.IP)}
req = Request(
id=f"request-{i}", # Optional client-provided correlation ID
principal=User.new(f"user{i}", ["mynamespace"]),
action=Action.new("view", ["mynamespace"]),
resource=Resource.new("Host", id=f"host{i}", attrs=attrs)
)
requests.append(req)
# Process all requests in one call (brief detail level)
response = client.authorize(requests)
# Access results
print(f"Successful: {response.successful}, Failed: {response.failed}")
for result in response:
print(f"Request {result.id}: {result.get_decision()}")
# Look up specific result by ID
result = response.get_by_id("request-0")
if result and result.is_allowed():
print("Request 0 was allowed!")
Detailed Responses (With Policy Information)
Retrieve matching policy information in your responses:
from treetop_client.client import TreeTopClient
from treetop_client.models import (
Action,
Decision,
Request,
Resource,
User,
ResourceAttribute,
ResourceAttributeType,
)
client = TreeTopClient(base_url=f"http://localhost:{PORT}")
attrs = {}
attrs["ip"] = ResourceAttribute.new("10.0.0.1", ResourceAttributeType.IP)
attrs["name"] = ResourceAttribute.new("myhost.example.com", ResourceAttributeType.STRING)
req = Request(
principal=User.new("myuser", ["mynamespace"], ["mygroup"]),
action=Action.new("myaction", ["mynamespace"]),
resource=Resource.new("Host", id="myhost", attrs=attrs)
)
# Get detailed response with policy information
resp = client.check_detailed(req)
assert resp.is_allowed()
assert resp.decision == Decision.ALLOW
# Access policy information (if allowed)
# The server returns all matching policies as PermitPolicy objects
policies = list(resp) # or resp.policies
if policies:
print(f"First matching policy: {policies[0].literal}")
print(f"Total matching policies: {len(policies)}")
print(f"Annotation IDs: {[p.annotation_id for p in policies if p.annotation_id]}")
print(f"Cedar IDs: {[p.cedar_id for p in policies if p.cedar_id]}")
# Access version information
hash = resp.version_hash() # SHA-256 hash or None
loaded_at = resp.version_loaded_at() # datetime or None
Batch Detailed Responses
Combine batch processing with detailed responses:
# Create multiple requests
requests = [req1, req2, req3]
# Get batch response with detailed policy information
response = client.authorize_detailed(requests)
for result in response:
if result.is_success() and result.is_allowed():
print(f"Decision: {result.get_decision()}")
# Access all matching policies for this result
policies = result.policies
if policies:
print(f"First matching policy: {policies[0].literal}")
print(f"Total matching policies: {len(policies)}")
print(f"Version hash: {result.version_hash()}")
Async API
All methods have async versions:
# Single request (async)
resp = await client.acheck(req)
# Batch requests (async)
response = await client.aauthorize(requests)
# Detailed batch requests (async)
response = await client.aauthorize_detailed(requests)
Correlation ID
Track requests across services using correlation IDs:
from treetop_client.client import TreeTopClient
from treetop_client.models import (
Action,
Request,
Resource,
User,
ResourceAttribute,
ResourceAttributeType,
)
client = TreeTopClient(base_url=f"http://localhost:{PORT}")
attrs = {}
attrs["ip"] = ResourceAttribute.new("10.0.0.1", ResourceAttributeType.IP)
attrs["name"] = ResourceAttribute.new("myhost.example.com", ResourceAttributeType.STRING)
req = Request(
principal=User.new("myuser", ["mynamespace"], ["mygroup"]),
action=Action.new("myaction", ["mynamespace"]),
resource=Resource.new("Host", id="myhost", attrs=attrs)
)
# Pass correlation ID for tracing
resp = client.check(req, correlation_id="my-correlation-id")
response = client.authorize([req1, req2], correlation_id="batch-trace-id")
Request Context
Pass request-scoped Cedar context values with the same attribute encoding used for resources:
req = Request(
id="prod-check",
principal=User.new("alice", ["DNS"], ["admins"]),
action=Action.new("create_host", ["DNS"]),
resource=Resource.new(
"Host",
id="hostname.example.com",
attrs={"name": ResourceAttribute.new("hostname.example.com")}
),
context={
"env": "prod",
"approved": True,
"retry_count": 3,
"roles": ["operator", "reviewer"],
"ticket": {"type": "String", "value": "CHG-123"},
},
)
Strings, booleans, integers, and lists are encoded as Cedar String, Bool,
Long, and Set values. Typed dictionaries may use String, Bool, Long,
Ip, or Set; invalid tags, nulls, and floating-point numbers raise
ValueError before a request is sent.
Server Metadata and Uploads
assert client.health()
version = client.version()
print(version.version, version.core.version, version.policies.hash)
status = client.status()
print(status.request_context.supported)
print(status.request_limits.max_batch_size)
# v0.0.12-compatible operational and discovery endpoints
assert client.livez()
assert client.readyz()
openapi = client.openapi()
print(openapi["info"])
print(client.metrics())
policies = client.get_policies()
raw_policies = client.get_policies(raw=True)
user_policies = client.list_policies(
"alice", groups=["admins"], namespaces=["DNS"]
)
raw_user_policies = client.list_policies("alice", raw=True)
schema = client.get_schema()
raw_schema = client.get_schema(raw=True)
client.upload_policies("permit (...);", upload_token="server-token")
client.upload_schema('{"": {}}', upload_token="server-token", as_json=True)
Notes
Usernamespace and groups are optional; they default to the root namespace if not providedActionnamespace is optional; it defaults to the root namespace if not provided- Each
Requestcan optionally have anidfield for client-provided correlation IDs in batch operations - Each
Requestcan optionally include acontextobject for request-context evaluation - Resource attributes are optional, and namespaced resource kinds such as
Database::Tableare supported
Development
This project uses uv for dependency management.
# Install dependencies (including dev dependencies)
uv sync --extra dev
# Run tests
uv run pytest
# Run integration tests (requires Docker & Docker Compose)
uv run pytest -m integration
# Or test a server already listening on http://localhost:10101
TREETOP_INTEGRATION_EXTERNAL_SERVER=1 uv run pytest -m integration
# Exercise the performance benchmarks locally
uv run pytest benchmarks --codspeed -m benchmark
# Add a new dependency
uv add package-name
# Add a dev dependency
uv add --dev package-name
CPU-sensitive request construction, serialization, response parsing, policy lookup,
and mocked end-to-end client paths are tracked in CI with
CodSpeed. Its simulated-CPU mode is the closest Python
equivalent to instruction-counted iai-callgrind: pull requests get stable
regression comparisons, history, and profiles without relying on noisy hosted-runner
wall time. Import the repository into CodSpeed once to enable result uploads; the
workflow authenticates with GitHub OIDC and does not require a long-lived token.
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 treetop_client-0.0.12.tar.gz.
File metadata
- Download URL: treetop_client-0.0.12.tar.gz
- Upload date:
- Size: 17.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e43e4d182e2840440fbe50707a819f7e9e9c93b9fe837e8728e50b8b9b38eb15
|
|
| MD5 |
e37afac6d077fd947a1d49e044dac37d
|
|
| BLAKE2b-256 |
eb58e36d2bf847b29a9cc37f4fa7849124c170ffdc8fde98072cb5d519936066
|
Provenance
The following attestation bundles were made for treetop_client-0.0.12.tar.gz:
Publisher:
publish.yml on treetop-policy-engine/treetop-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
treetop_client-0.0.12.tar.gz -
Subject digest:
e43e4d182e2840440fbe50707a819f7e9e9c93b9fe837e8728e50b8b9b38eb15 - Sigstore transparency entry: 2469952226
- Sigstore integration time:
-
Permalink:
treetop-policy-engine/treetop-client-python@3c53d38e2b5f291fea415ca3dd5e34df805e001c -
Branch / Tag:
refs/tags/v0.0.12 - Owner: https://github.com/treetop-policy-engine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c53d38e2b5f291fea415ca3dd5e34df805e001c -
Trigger Event:
push
-
Statement type:
File details
Details for the file treetop_client-0.0.12-py3-none-any.whl.
File metadata
- Download URL: treetop_client-0.0.12-py3-none-any.whl
- Upload date:
- Size: 15.9 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 |
df9cee9c261a2128e241611103d7dac116194e517f9f73169260dd9ae735b173
|
|
| MD5 |
1b99ce3e08a854ca008634acaeeb131f
|
|
| BLAKE2b-256 |
523b75abc17eabf28fea4c4730f29ac1e6e168381d4e27425f0ca158051d46e8
|
Provenance
The following attestation bundles were made for treetop_client-0.0.12-py3-none-any.whl:
Publisher:
publish.yml on treetop-policy-engine/treetop-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
treetop_client-0.0.12-py3-none-any.whl -
Subject digest:
df9cee9c261a2128e241611103d7dac116194e517f9f73169260dd9ae735b173 - Sigstore transparency entry: 2469952418
- Sigstore integration time:
-
Permalink:
treetop-policy-engine/treetop-client-python@3c53d38e2b5f291fea415ca3dd5e34df805e001c -
Branch / Tag:
refs/tags/v0.0.12 - Owner: https://github.com/treetop-policy-engine
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c53d38e2b5f291fea415ca3dd5e34df805e001c -
Trigger Event:
push
-
Statement type: