Skip to main content

Actrail

Runtime policies for AI agents — the open-source edge SDK

Govern what your agents do, not just what they say. Actrail captures every agent action as metadata-only telemetry and enforces deterministic policies before a risky action runs — a human-readable rule, never an LLM, in the binding decision.

PyPI Python License Ruff mypy: strict


Why Actrail

AI agents don't just generate text — they take actions: refund a customer, delete a row, deploy to prod, send data to an external LLM. Content guardrails don't see any of that. Actrail governs the actions:

  • Deterministic enforcement. Policies are plain rules (refund > $500 without approval → require_approval) evaluated on a bounded request, fail-open by default. No model participates in the verdict.
  • Metadata-only by construction. The SDK never sends raw arguments or results. It emits a redacted, per-field skeleton — keys kept, values stripped or transformed — built from scratch, never copied.
  • An always-on secrets floor. API keys, tokens, private keys, passwords, connection strings are never emitted, and it can't be turned off.

Install

pip install actrail

Requirements

  • Python 3.11+. The system Python on macOS is 3.9 and will not work — use a 3.11/3.12 environment.
  • macOS 12+ (Apple Silicon or Intel) or Linux x86_64. Prebuilt wheels bundle the native shim — no toolchain needed.

Optional extras for deeper local scanning:

pip install "actrail[ner]"    # + Presidio / spaCy for names & unstructured PII
pip install "actrail[full]"   # + transformers

Verify:

actrail --help                                        # bundled CLI on PATH
python -c "import actrail; print(actrail.__version__)"

not a supported wheel on this platform? Almost always your Python isn't 3.11/3.12 (macOS ships 3.9). Create a 3.11/3.12 env — e.g. uv venv --python 3.12 — and reinstall.

Quickstart

Claude Code (zero-code onboarding)

actrail init --key ak_live_...     # registers the SDK, wires hooks, starts the daemon (shadow)
actrail doctor                     # verify config · hooks · daemon

init also registers this SDK release with the backend (metadata only — integration/version/contract, no customer data), so Actrail can install the managed policies this release is certified to enforce before the first trail arrives.

init wires the pre-tool policy check immediately. Policies still begin in shadow, so nothing blocks until an administrator graduates one to enforce in the Actrail console. actrail enforce off is an explicit local emergency opt-out; enforce on restores the hook if it was removed.

Two managed catalog rules intentionally need organization-specific named sets before they can govern: production_destructive_action needs prod_hosts, and customer_data_unapproved_destination needs approved_destinations. Configure those in the console during onboarding; the SDK never guesses tenant trust boundaries.

Any agent (the library)

import actrail

actrail.init(key="ak_live_...", agent="support-bot")

# Before a risky action — get a deterministic, bounded verdict.
verdict = actrail.check(
    trail_id="sess_42",
    tool="pay.api",
    action="refund",
    payload={"order": {"amount": 2500, "currency": "USD"}, "table": "prod.customers"},
)
if not verdict.allow:
    raise PermissionError(verdict.reason)   # require_approval / deny

# After it runs — capture the action as metadata-only telemetry (fire-and-forget).
actrail.capture(
    trail_id="sess_42",
    tool="pay.api",
    action="refund",
    payload={"order": {"amount": 2500}, "customer": {"email": "alice@example.com"}},
)

payload is the structured tool-call args. Actrail walks it into a governed fields[] envelope — it never leaves your machine as raw data.

The telemetry contract

Every field passes through a transform ladder, least → most revealing, and the customer governs it per field (default-deny):

Rung What's emitted Example
drop nothing (skeleton only) unknown fields
type_only just the type customer.email(type: email)
derived a safe derivation emaildomain:example.com
bucket a coarse bucket amount: 25001000_10000
token a one-way HMAC pseudonym join without revealing
raw the value verbatim only for non-secret, opted-in fields

Three guarantees stack, in order:

  1. Default-deny — an un-contracted field gets a conservative per-type default.
  2. Secrets floor — a secret-typed field, or any value that looks secret, is forced to drop. Non-overridable — a contract can never lift a secret onto the wire.
  3. Outbound validator (fail-closed) — every emitted value is re-scanned and scrubbed if it still looks secret, the last line before anything leaves.

A seeded fuzz test asserts the invariant under the worst case — every field forced to raw: no secret ever appears in an emitted value.

What leaves the machine

{
  "tool": "pay.api", "action": "refund", "destination": "internal",
  "data_classes": ["financial", "pii"],          // derived from the envelope
  "fields": [                                     // the redacted skeleton
    {"path": "order.amount", "semantic_type": "amount", "handling": "bucket", "value": "1000_10000"},
    {"path": "customer.email", "semantic_type": "email", "handling": "type_only"},
    {"path": "auth.api_key", "semantic_type": "api_key", "handling": "drop"}   // floored — no value
  ],
  "amount": 2500,                                 // a first-class grammar fact
  "taint_sources": [{"table": "prod.customers"}], // enables "data from prod.customers" policies
  "taint": ["email:v1:9f3a…"],                    // one-way fingerprints (flow, not values)
  "sdk_version": "1.1.0", "contract_version": "v1"
}

No args. No result. No raw values. Ever.

Architecture

tool call ──► capture()/check() ──► enrich (taint) ──► contract pipeline ──────► backend
              payload · content       HMAC fingerprints  build ▸ govern ▸ validate  /spans · /check
                                                         fields[] · amount · sources
  • Deterministic outside, fail-open at /check, fail-safe at ingest.
  • BYO scanner — the enrichment scanners implement a simple Scanner protocol.
  • Optional NER (pip install actrail[ner]) for richer PII detection; the default path stays lightweight (regex + entropy + detect-secrets).

File and script provenance (v1.1)

Claude Code frequently reads sensitive data, writes a local helper script, and then runs python send.py or uv run python send.py. The final command does not contain the payload, so command scanning alone cannot govern it. v1.1 keeps a small local, metadata-only provenance index:

  1. Read records a one-way resource token, classifications, record count, and taint.
  2. Before a file-upload command, Python script, or Git push runs, Actrail performs bounded local analysis of only the referenced regular files. Python uses a small deterministic data-flow pass, so a local read counts only when its value reaches a supported network call; simple co-occurrence is not treated as a flow.
  3. The /check candidate carries the inherited classifications and the external destination, while earlier source spans are flushed first for ordered evaluation.

Paths, session ids, file contents, and customer values are never persisted in this index. Resource identities and file generations are HMACed, and the state file is written with mode 0600. Script reads are limited to 1 MiB, data classification and record counting to 8 MiB/10,000 records, Git config to 256 KiB, and symlinks, FIFOs, devices, sockets, or files over those limits are never read.

This closes ordinary file-backed curl, supported Python data flows, and Git push paths; it is not an OS network sandbox. Node, Ruby, arbitrary shell scripts, deliberately obfuscated or runtime-downloaded code, native processes that conceal their file/network relationships, and traffic outside a Claude Code tool hook require richer runtime telemetry or host/network controls in addition to Actrail.

After upgrading, run:

actrail init --key ak_live_...     # idempotently upgrades owned hook matchers
actrail restart                    # loads the new SDK into the warm daemon
actrail doctor

Security & privacy

The metadata-only guarantee is by construction, not detection — see SECURITY.md. Short version: raw payloads never leave the machine, secrets are floored non-overridably, taint is one-way HMAC, and the wire is fail-closed validated.

Development

uv venv && uv pip install -e ".[dev]"
uv run pytest          # tests (+ safety-invariant fuzz)
uv run ruff check .    # lint
uv run mypy            # strict types

License

Apache 2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

actrail-1.2.0.tar.gz (2.3 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

actrail-1.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

actrail-1.2.0-cp312-cp312-macosx_12_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.12macOS 12.0+ ARM64

actrail-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

actrail-1.2.0-cp311-cp311-macosx_12_0_arm64.whl (2.4 MB view details)

Uploaded CPython 3.11macOS 12.0+ ARM64

File details

Details for the file actrail-1.2.0.tar.gz.

File metadata

  • Download URL: actrail-1.2.0.tar.gz
  • Upload date:
  • Size: 2.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for actrail-1.2.0.tar.gz
Algorithm Hash digest
SHA256 9839c1873bc9d344d2d4c95dd6a7d26cadb99650cd69b6c37ed307dbe0749546
MD5 6711a359c7c54737264b81a4efb04c6b
BLAKE2b-256 61ecdeb22bdf45b75e0a16194813bdb7a77e505112d0dc88b053287fbf336369

See more details on using hashes here.

Provenance

The following attestation bundles were made for actrail-1.2.0.tar.gz:

Publisher: release.yml on actrailhq/actrail-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file actrail-1.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for actrail-1.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 da5feb49be81582083efa8d767d086e1dd4ae2ff70cf8ef8f3e3b06d767f0d33
MD5 39fa19763151b465ee62a96baf5fb938
BLAKE2b-256 4b5cf4a3292d00c40c22eeaadd24169f2a2673ea4a7bf49f4875f905328c5878

See more details on using hashes here.

Provenance

The following attestation bundles were made for actrail-1.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on actrailhq/actrail-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file actrail-1.2.0-cp312-cp312-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for actrail-1.2.0-cp312-cp312-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 8135c0b519462ab0240e33bff4df57b86c708b263507624c1a86ac0b7bc95310
MD5 79f9f2ba3a425b34d8234137db831292
BLAKE2b-256 e9ec3a33d3d7c21025472aaa6aa0e49d12514f8abe5e1ec7e3dd8c0eb1982a20

See more details on using hashes here.

Provenance

The following attestation bundles were made for actrail-1.2.0-cp312-cp312-macosx_12_0_arm64.whl:

Publisher: release.yml on actrailhq/actrail-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file actrail-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for actrail-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4e2b10282bd6b8382058f2fc09422f639d6e6476ce0e273ed1a05cf97a2f3e90
MD5 9a6cac338c70e8dc905810a81232accd
BLAKE2b-256 58370cbc6bf134a23dac7196e6b1ea5a607e1d43c475337acbd5c69edd2122dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for actrail-1.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on actrailhq/actrail-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file actrail-1.2.0-cp311-cp311-macosx_12_0_arm64.whl.

File metadata

File hashes

Hashes for actrail-1.2.0-cp311-cp311-macosx_12_0_arm64.whl
Algorithm Hash digest
SHA256 a425c5940d0363490a895ad17bce00e73435ece09ee911a491d3b294119a78a7
MD5 a5b4ce3ff310ef8d4a63dd32e3488fd4
BLAKE2b-256 3837f70ec5d1496592b783bc560df32b94dedcda5614a8379677bbe7441248a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for actrail-1.2.0-cp311-cp311-macosx_12_0_arm64.whl:

Publisher: release.yml on actrailhq/actrail-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page