Skip to main content

AgenticRail SDK — deterministic sequence enforcement for AI agents

Project description

agenticrail

AgenticRail Python SDK — deterministic sequence enforcement for AI agents.

Gate every step of your agent workflow before it executes. Cryptographic receipts. Zero enforcement failures.

pip install agenticrail

What it does

AgenticRail sits beneath your agent and enforces that steps run in the right order, with no skips, no replays, and no re-entry after a sequence is sealed. Every decision — ALLOW, DENY, or HALT — produces a cryptographically signed receipt stored at the edge.

Three verdicts:

  • ALLOW — step is clear, your agent code executes
  • DENY — enforcement violation (wrong order, replay, sealed), execution blocked
  • HALT — hard stop (injection attempt, poison payload detected), execution blocked

Install

# Core client only
pip install agenticrail

# With LangGraph support
pip install "agenticrail[langgraph]"

# With CrewAI support
pip install "agenticrail[crewai]"

# Everything
pip install "agenticrail[all]"

Quick start — any framework

The key below is real. Copy it as it is.

DEMO-AGENTICRAIL-PUBLIC-2026 is a live, public evaluation key. It is not a placeholder and there is nothing to sign up for — no account, no email, no waiting. It is rate limited to 300 requests/minute, prefixes your sequence ids with demo-, and every sequence you run with it is verifiable by anyone at report.agenticrail.nz/report.

Production keys look different — prefix.secret, with a dot. If you pass anything else, including an unset environment variable, the gate replies Malformed API key. For a production key: hello@agenticrail.nz

Give every run a fresh sequence_id. Sealing is permanent and by design: once a sequence reaches its last step it can never be reopened. A fixed id therefore works exactly once — and on the shared demo key that id is shared with every other user, so the second person to run it gets RailDenied: SEALED_SEQUENCE. That is the gate working, not a bug.

import uuid

from agenticrail import RailClient

client = RailClient(api_key="DEMO-AGENTICRAIL-PUBLIC-2026")

# RailSequence sends step_order on every call — the gate reads it from each payload
seq = client.sequence(
    sequence_id=f"my-agent-run-{uuid.uuid4().hex[:8]}",
    step_order=["verify_identity", "assess_risk", "execute_transfer", "audit_ledger"],
)

seq.next("verify_identity")    # → ALLOW
seq.next("assess_risk")        # → ALLOW
seq.next("execute_transfer")   # → ALLOW
seq.next("audit_ledger")       # → ALLOW (seals sequence)

# Any out-of-order, replay, or post-seal call raises RailDenied
seq.next("verify_identity")    # → raises RailDenied: SEALED_SEQUENCE

Get a production API key at agenticrail.nz.


LangGraph integration

Wrap each node at add_node time. Uses thread_id from LangGraph config as the sequence_id — each graph invocation is isolated.

from langgraph.graph import StateGraph, END
from agenticrail import RailClient
from agenticrail.integrations.langgraph import LangGraphRail

client = RailClient(api_key="YOUR_KEY")
rail = LangGraphRail(
    client,
    step_order=["research", "analyze", "write_report", "review"],
)

builder = StateGraph(AgentState)

# Wrap each node — gate fires before node execution
builder.add_node("research",     rail.wrap("research",     research_fn))
builder.add_node("analyze",      rail.wrap("analyze",      analyze_fn))
builder.add_node("write_report", rail.wrap("write_report", write_report_fn))
builder.add_node("review",       rail.wrap("review",       review_fn))

builder.set_entry_point("research")
builder.add_edge("research",     "analyze")
builder.add_edge("analyze",      "write_report")
builder.add_edge("write_report", "review")
builder.add_edge("review",       END)

graph = builder.compile()

# thread_id → sequence_id: each invocation is independently tracked and verifiable
result = graph.invoke(
    {"query": "EU AI Act compliance checklist"},
    config={"configurable": {"thread_id": "run-2026-001"}},
)

What happens on DENY or HALT: rail.wrap() raises RailDenied inside the node. LangGraph catches unhandled node exceptions and halts graph execution. No subsequent nodes run.

Concurrent runs: Each thread_id is a separate sequence. Concurrent graph invocations with different thread IDs do not interfere — the gate tracks them independently via Durable Objects.


CrewAI integration

Use guard.kickoff() instead of crew.kickoff(). Step[0] is gated before the crew starts; subsequent steps are gated via a task callback injected between tasks.

from crewai import Crew
from agenticrail import RailClient
from agenticrail.integrations.crewai import CrewRailGuard

client = RailClient(api_key="YOUR_KEY")
guard = CrewRailGuard(
    client,
    step_order=["research_task", "analysis_task", "write_task"],
)

crew = Crew(
    agents=[researcher, analyst, writer],
    tasks=[research_task, analysis_task, write_task],
)

# Drop-in replacement for crew.kickoff()
# Each call generates a fresh sequence_id — each run is independently tracked
result = guard.kickoff(crew, inputs={"topic": "AI governance"})

Note on blocking: CrewAI's task_callback fires synchronously between tasks, so the gate can block task N before task N starts. However, if CrewAI swallows exceptions in callbacks, a denied task may still execute. In that case, guard.kickoff() raises RailDenied after the crew finishes, preserving the denial in the audit trail.

For hard blocking (guaranteed pre-execution), use guard.gate() in a custom orchestration loop:

guard.reset()
for step, task_fn in zip(STEP_ORDER, task_functions):
    guard.gate(step)   # raises RailDenied immediately on DENY
    task_fn()

API reference

RailClient

client = RailClient(
    api_key="...",       # Required. Bearer token for the wrapper API.
    model_id="agent",    # Optional. Identifies your agent in receipts.
    base_url=None,       # Optional. Defaults to api.agenticrail.nz/v1/evaluate.
    timeout=10,          # Optional. Request timeout in seconds.
)

client.evaluate(sequence_id, step, *, ...)

decision = client.evaluate(
    sequence_id="my-run-001",
    step="verify_identity",
    action_type="CHECK_STATE",     # Optional. Default: CHECK_STATE.
    action="verify user identity", # Optional. Human-readable label.
    inputs={"user_id": "u123"},    # Optional. Metadata attached to receipt.
    step_order=[...],              # Required on every call — gate reads it from each payload.
    raise_on_deny=True,            # Optional. Default True.
)
# decision.decision  → "ALLOW" | "DENY" | "HALT"
# decision.allowed   → True if ALLOW
# decision.pack_id   → SHA-256 receipt hash
# decision.receipt   → full signed receipt dict
# decision.reasons   → list of reason codes on DENY/HALT

client.sequence(sequence_id, step_order)

Returns a RailSequence that tracks step_order state — call .next(step) for each step without managing step_order yourself.

RailDenied

try:
    seq.next("execute_transfer")
except RailDenied as e:
    print(e.decision)   # "DENY" or "HALT"
    print(e.reasons)    # ["SEQUENCE_VIOLATION"]
    print(e.pack_id)    # receipt hash for audit
    print(e.step)       # "execute_transfer"

LangGraphRail

rail = LangGraphRail(
    client,
    step_order=["step_a", "step_b", "step_c"],
    fallback_sequence_id=None,  # Used when thread_id not in config
)
wrapped_fn = rail.wrap("step_a", original_fn, action_type="CHECK_STATE")

CrewRailGuard

guard = CrewRailGuard(
    client,
    step_order=["task_1", "task_2", "task_3"],
    sequence_id=None,    # Optional. Auto-generated per kickoff() call if None.
    action_type="CHECK_STATE",
)
result = guard.kickoff(crew, inputs={...})
guard.gate("task_1")     # Explicit gate for custom loops
guard.reset()            # Reset state for a new run

Compliance reports

Every pack_id in a RailDecision is a verifiable receipt. Paste your sequence ID into the report generator or call it programmatically:

curl -X POST https://api.agenticrail.nz/v1/report \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sequence_id": "my-run-001", "format": "json"}'

Returns: chain proof (Ed25519 signature verification, offline-reproducible; legacy HMAC k1 verified server-side for pre-cutover receipts), enforcement log, AI-written compliance narrative.

Demo sequences (using DEMO-AGENTICRAIL-PUBLIC-2026) are verifiable at report.agenticrail.nz — no auth needed.


Action types

Type Use when
CHECK_STATE Reading or observing state (default)
VALIDATE_INPUT Verifying inputs before acting
RECORD_RESULT Writing or committing an outcome
CLARIFY_NEXT_STEP Asking for clarification
SELECT_NEXT_STEP Choosing a path
WAIT_FOR_SIGNAL Pausing for an external trigger
PAUSE_CYCLE Deliberate suspension
REDUCE_STIMULUS Backing off

Links


TUARA KURI LIMITED — trading as AgenticRail. Hokianga, New Zealand.

Project details


Download files

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

Source Distribution

agenticrail-0.2.5.tar.gz (15.3 kB view details)

Uploaded Source

Built Distribution

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

agenticrail-0.2.5-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

Details for the file agenticrail-0.2.5.tar.gz.

File metadata

  • Download URL: agenticrail-0.2.5.tar.gz
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agenticrail-0.2.5.tar.gz
Algorithm Hash digest
SHA256 041041375b1a4634ea7115f646f5b3a490e6a773c7376d516eeb4839075c7507
MD5 171f48d2134155a04be2f845af4236b8
BLAKE2b-256 03d8cfab7e77110882570a1d41200fe0636dc71ba06a8f774c2a404e734184b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for agenticrail-0.2.5.tar.gz:

Publisher: publish-python-sdk.yml on MSMD-RUA/AgenticRail

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

File details

Details for the file agenticrail-0.2.5-py3-none-any.whl.

File metadata

  • Download URL: agenticrail-0.2.5-py3-none-any.whl
  • Upload date:
  • Size: 12.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agenticrail-0.2.5-py3-none-any.whl
Algorithm Hash digest
SHA256 d206497eacdce218eb8bcd8fb8367fffac9aeac2b4b870f1f3c0ee1e931f8472
MD5 1535098aedf57ea75f4643d03c2154ce
BLAKE2b-256 3b50f5ea540336b6b44650ef86392a7192742d8675fffaf65141088fb294b4e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for agenticrail-0.2.5-py3-none-any.whl:

Publisher: publish-python-sdk.yml on MSMD-RUA/AgenticRail

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 Pingdom Monitoring Sentry Error logging StatusPage Status page