Skip to main content

Allem Python SDK

Check AI agent actions before execution. Log actions for compliance.

This is the governance SDK — Tier 1 in Allem's connection-tier model: check_action() before the action happens, log_action() after, a durable spool so nothing is lost, and local enforcement of hard gates when Allem is unreachable. It is a different package from allem-api (a convenience client for the REST admin surface) and from allem-cli (the Tier 2 coding-agent hooks). Only this one blocks locally during an outage.

Install

pip install allem

Quick start

from allem import AllemClient

allem = AllemClient(
    api_key="alm_sk_test_...",
    endpoint="http://localhost:8000",
)

verdict = allem.check_action(
    agent_id="my_agent",
    action_type="send_email",
    parameters={"to": "client@example.com"},
    affects_external=True,
)

if verdict:
    send_email(...)
else:
    print(f"Blocked: {verdict.explanation}")

Verdict is truthy when the action is allowed, so if verdict: works directly. Inspect .severity, .recommended_action, .event_id, and .freeze for richer detail.

Fire-and-forget logging

For events that already happened (webhooks, retroactive logs):

allem.log_action(
    agent_id="my_agent",
    action_type="send_email",
    parameters={"to": "client@example.com"},
)

log_action never raises — logging must never break the agent.

Fail-open design

If the Allem API is unreachable, check_action returns Verdict(allowed=True, ...). An Allem outage will not block the customer's agent. The condition is recorded in verdict.explanation so calling code can log it.

Only two things ever produce allowed=False: a verdict Allem actually returned, and the local hard gate below. An auth failure, a 500, or a response body the client cannot parse is an absence of a verdict, not a denial, and fails open like any other outage.

Hard gates: the one thing that fails closed (Tier 1 only)

A scope rule the customer marks hard_gate: true is their fail-closed exception to the rule above, and setting it means Allem's availability becomes your availability for those actions. On this package the gate holds even when we are unreachable — a gate that only holds while Allem is up opens during exactly the incident it was bought for.

The SDK keeps a cached list of which action types are hard-gated for each agent (GET /agents/{id}/scopes/hard-gates, resolved through GET /agents/me), refreshed off the latency path after a successful check. When the platform does not answer:

Situation Outcome
Action is not hard-gated allowed — fail-open, unchanged
Action is on the cached list denied locally
Cached list is stale hard-gated entries still block
No list has ever been fetched allowed, and warned on every check

A stale cache does not relax the gate. Age makes the list less complete — a rule added during the outage is unknown to the SDK — not less binding on what it already holds.

The never-fetched row is the honest limit: the SDK cannot enforce a list it has never seen, so it proceeds and says so through the allem.client logger, every time, naming the endpoint and the reason. Watch for that warning; it means hard gates are not being enforced locally.

A locally-denied action still reaches the chain. It is spooled like any other event, annotated with which gate blocked it, the scope_version the list came from, and how stale the cache was — so an auditor can tell a gate that worked from a gate that guessed.

Configuration. The cache lives under ALLEM_HOME (default ~/.allem/state/, owner-only), with a 300s staleness bound overridable by ALLEM_HARD_GATE_TTL_S. On a read-only filesystem — a container or a Lambda — it falls back to memory for the process lifetime and logs once; the gate still blocks, it just has to be refetched on each cold start. check_action is never failed because a cache file could not be written.

Tier 2 is different, deliberately. The coding-agent hooks in allem-cli do not block during an outage: a hook that broke a developer's session would be uninstalled within the hour, and the agent could reach the same result through another tool path anyway. If you read "hard gates fail closed", it is true of this package and not of that one.

Authentication

The SDK sends the API key in the X-API-Key header. Use a test-mode key (alm_sk_test_...) when developing against a non-production endpoint.

Completeness: sequences and the durable spool

Every event the SDK emits carries a client-generated sequence block — {run_id, client_seq, signature, signature_method} — numbered 1, 2, 3, … per agent per process. The server can therefore prove not only that stored records were never altered (the hash chain), but that no record is missing: a sequence number that never arrives is a detectable gap.

Before any network attempt, each event is written to a local SQLite spool (default ~/.allem/spool.db, override with ALLEM_SPOOL_DIR or the spool_path argument). A background thread delivers spooled events and retries forever — backoff 1s → 32s, then every 60s. If the process exits with events undelivered, the next process replays them. Nothing is ever silently lost, and fail-open behaviour is unchanged: an Allem outage never blocks your agent.

The sequence number is a claim your agent makes. Signing it lets Allem check that claim — and, because your agent holds the private key and Allem holds only the public half, Allem cannot manufacture a claim on your behalf.

pip install 'allem[signing]'
python -m allem.sequence --out ~/.allem/agent.key   # private key, written 0600
# register the PUBLIC key printed by that command:
#   POST /v1/agents/{agent_id}/signing-keys
export ALLEM_SIGNING_KEY_PATH=~/.allem/agent.key

Or AllemClient(api_key=..., signing_key_path="~/.allem/agent.key").

The signature covers a digest of the event body, not just its position, so a signature cannot be lifted from one event onto another. Your private key is never sent to Allem, and the registration endpoint refuses anything that looks like one.

If a signature fails to verify, Allem records the failure and raises a critical finding — it still accepts the event. Enforcement fails open; that does not change.

Full details: docs/completeness-signing.md.

ALLEM_SIGNING_SECRET (HMAC-SHA256) is still accepted so existing deployments keep running, but Allem has never held that secret and therefore cannot verify those signatures: they are recorded as unverified. The SDK warns once at construction. Use a key path instead.

Run lifecycle and heartbeats

client.start_run("billing-bot")   # run_start (client_seq 1), then a beat every 60s
...
client.close()                     # run_end

Heartbeats make silence mean something specific. Without them, "the agent did nothing" and "the agent stopped reporting" are the same observation. With them, the server opens a blackout window when an agent goes quiet past three missed beats, and closes it when your spool replays — so an outage becomes a bounded, recorded fact instead of a hole in the log.

A heartbeat consumes a client_seq like any other event, which is the point: a missing heartbeat is itself a detectable gap.

start_run is an explicit call because run_start must take client_seq = 1 — any log_action for that agent would consume that number first. run_end is also emitted on atexit and SIGTERM (chained onto your own handler, never replacing it).

Call client.flush(timeout=...) before process exit to drain the spool if you want delivery confirmation; it is never required for correctness.

v1 limitations (stated openly)

  • Spool encryption: the spool file is not encrypted at rest in v1. It is created with restrictive permissions (0700 directory, 0600 file), but it may contain sensitive action parameters. If you operate under regulatory requirements (healthcare, financial services), place ALLEM_SPOOL_DIR on an encrypted volume until spool encryption ships.
  • One run per process: sequence state lives in memory, so serverless / one-shot agents produce many short runs (seq 1–2 each). Completeness still holds within each run, but gap detection is nearly vacuous for such workloads. Cross-process sequence persistence is deliberately not attempted in v1.
  • Completeness covers instrumented call sites only. Code paths that never call the SDK reserve no sequence numbers and are outside the guarantee.

Metadata

Release files for allem 0.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 allem 0.1.1
File Size Uploaded Provenance
allem-0.1.1.tar.gz 65.6 kB With provenance Details

Built distribution (wheel)

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

Total release size: 110.4 kB

Release files / allem-0.1.1.tar.gz

Download URL allem-0.1.1.tar.gz
Size 65.6 kB
Tags Source
SHA-256 checksum
How to use checksums
75da69883f0f2ef16f92d8220b21257fa3374d73ca36853ebe62e343c6e0bdce
BLAKE2b-256 checksum
How to use checksums
85fb883b3cddd6f202a5591e95b5c082110207c12565ca07f003398e315aab0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 13, 2026.

Transparency log

Release files / allem-0.1.1-py3-none-any.whl

Download URL allem-0.1.1-py3-none-any.whl
Size 44.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
76dae3c1d56562177483ffc3b5a40bf177513c0a12a748e5aa7478a37d08e5d7
BLAKE2b-256 checksum
How to use checksums
5080982b822c7b44c36c0c10375efe05dee0bd88737a8376a851ea690d543e40
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 13, 2026.

Transparency log

Security for allem 0.1.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for allem-0.1.1.tar.gz

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 Oct 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

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