Skip to main content

archytan-lite (Python)

Fail-closed Python client for the Archytan Lite authorization gate — see high-archy.tech.

pip install archytan-lite

Run the gate

This package is the client; the gate runs as a Docker image on your own infrastructure. The quickstart starts one and walks through a signed ALLOW, a BLOCK and a single-use capability in about a minute, checking every answer as it goes:

curl -fsSLO https://raw.githubusercontent.com/High-ArchyTech-Solutions/archytan-lite-community/main/quickstart/quickstart.sh
bash quickstart.sh

Examples, JSON Schemas, release notes and issues live in archytan-lite-community.

The rule this client enforces

Only an HTTP 200 whose body has decision == "ALLOW", and a receipt whose Ed25519 signature verifies against the gate's public key, and a receipt that describes this request counts as authorized.

Everything else is denied: a non-200, a timeout, a refused connection, a malformed body, a signature that doesn't verify, a receipt for some other action, the gate process being dead.

Nothing here raises for a denial. A denial arrives as allowed=False, never as an exception — an exception is something a caller can forget to catch, and a forgotten except around an authorization check fails open.

Use

from uuid import uuid4
from archytan_lite import authorize, redeem_capability

result = authorize(
    url="http://archytan-lite:8421",
    token=CALLER_TOKEN,
    gate_public_key_hex=GATE_PUBLIC_KEY,   # the hex `keygen` printed
    action="invoice.delete",
    actor={"uid": "user_1"},               # see "Roles" below
    resource={"type": "invoice", "id": "inv_7"},
    idempotency_key=str(uuid4()),
)
if not result.allowed:
    raise PermissionError(result.reason)

Pass traceparent= (the W3C header value of the current trace) to any of the four calls and the gate logs its trace and span ids beside the decision, so the decision shows up next to your trace in your APM. A value that isn't a valid version-00 traceparent isn't sent, so it can't cause a refusal.

authorize_async and redeem_capability_async are the async forms. They share their decision logic with the sync versions rather than reimplementing it — two copies is how a sync path and an async path end up disagreeing about what "authorized" means, with only one of them audited.

Roles

Omit actor["role"] when the gate runs credential-bound roles. In that mode your role is a property of your credential, resolved server-side, and a role asserted in the request that disagrees with it is treated as an escalation attempt and refused. The receipt comes back carrying the role the gate resolved.

Send actor["role"] only against a gate configured with a single shared token, which has no other way to learn one. When you do send it, this client checks the receipt agrees with it.

Capabilities

When the gate has capabilities enabled, an ALLOW carries a single-use grant scoped to exactly one action on one resource, expiring in seconds. Holding it is not permission to act — spend it immediately before acting:

spent = redeem_capability(
    url="http://archytan-lite:8421",
    token=CALLER_TOKEN,
    capability_token=result.capability.token,
    action="invoice.delete",
    resource={"type": "invoice", "id": "inv_7"},
)
if not spent.redeemed:
    raise PermissionError(spent.reason)   # expired, out of scope, or already spent

delete_invoice("inv_7")

A second redemption of the same capability is refused, so an actuator cannot be driven to act twice on one authorization.

AI agent frameworks

pip install "archytan-lite[langchain]"

ArchytanGuardedTool wraps a function so an agent cannot reach it without the gate authorizing the call and the capability being spent first:

from archytan_lite.integrations.langchain import ArchytanGuardedTool

delete_invoice_tool = ArchytanGuardedTool(
    name="delete_invoice",
    description="Permanently delete an invoice by id.",
    action="invoice.delete",
    resource_type="invoice",
    func=really_delete_invoice,          # reached only after ALLOW + redemption
    gate_url="http://archytan-lite:8421",
    caller_token=AGENT_TOKEN,
    gate_public_key_hex=GATE_PUBLIC_KEY,
    actor_uid="agent-invoices",
)

It is a LangChain BaseTool, which LangGraph and CrewAI both accept, so one integration covers all three.

A denial returns an explanation to the agent rather than raising, because a raised exception inside an agent loop is usually swallowed and retried — the agent should be told it was refused, in text it can reason about, so it stops rather than loops.

What this cannot do for you: nothing stops your code from importing the underlying function and calling it directly. The wrapper makes the guarded path the easy one and the unguarded path a deliberate act; it is not a sandbox.

Version 2 receipts

Gates since Archytan Lite 2.4 mint version 2 receipts, which also sign what each decision was made under: the SHA-256 of the gate's policy and callers files, the credential that asked (caller_id), the resource's tenant, the gate's mode and whether observe mode turned a refusal into an ALLOW, the caller's W3C trace id, and the receipt's id, instance, trace id and time. This client verifies both versions, each under its own signature tag, and refuses a receipt that claims a version it doesn't know, a v1 receipt that carries any v2 field, and a v2 receipt with any field changed. Clients older than 2.1 refuse v2 receipts, so upgrade them before the gate (or run the gate with ARCHYTAN_LITE_RECEIPT_VERSION=1 until you have).

Pass expected_policy_sha256= (the hex SHA-256 of the policy file you expect the gate to run) and an ALLOW is accepted only if the receipt says the gate decided under exactly that policy; a v1 receipt, which records no policy, is refused. Name the tenant on the request (resource={"type": ..., "id": ..., "tenant_id": ...}) and a v2 receipt for any other tenant is refused too.

Contract siblings

This client, clients/node/index.js, and the authorize() helper in testing/failclosed/failclosed_test.go are the same contract in three languages. If you change the decision logic in one, change it in the others.

How it checks a receipt

The gate signs intent_hash, not the fields printed beside it. So before trusting the signature, this client recomputes intent_hash from the receipt's fields plus the idempotency key it sent, reproducing Go's encoding/json output exactly (HTML escapes, U+2028/U+2029, control characters, raw UTF-8 for everything else), and pinned to clients/testdata/intent_vectors.json, which is generated from the Go code. Without that step an intermediary could pair a genuine signature with rewritten fields. verify_receipt(receipt, gate_public_key_hex, idempotency_key) exposes the same check for a stored receipt; verify_receipt_signature alone does not prove the fields were signed.

Metadata

Release files for archytan-lite 2.1.1

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

Source distribution (sdist)

Source distribution for archytan-lite 2.1.1
File Size Uploaded
archytan_lite-2.1.1.tar.gz 22.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for archytan-lite 2.1.1
File Interpreter ABI Platform
archytan_lite-2.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 39.2 kB

Release files / archytan_lite-2.1.1.tar.gz

Download URL archytan_lite-2.1.1.tar.gz
Size 22.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3d459ad3aa32d3ef66638a886d37d1b082a4b6d045cf6679b45b90fbf4d8c9bf
BLAKE2b-256 checksum
How to use checksums
856168294f15356e0b317f95cd8ac72ab4433e73580527973f5940dab63387ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / archytan_lite-2.1.1-py3-none-any.whl

Download URL archytan_lite-2.1.1-py3-none-any.whl
Size 16.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
79b95b3bebe8603600c81482619d17e4976d8c766e15261cf5c6f77d2e3c0aa9
BLAKE2b-256 checksum
How to use checksums
68be55849fc0048f1f69118068ba48e059c410f15433b5a133e9b6fd40dd5e89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

2.1.1 This release

2 release files

2.1.0

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.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