Metadata-Version: 2.4
Name: trustrail-sdk
Version: 0.9.0a1
Summary: Alpha Python SDK for TrustRail governed actions and gateway execution
Author: TrustRail Engineering
License: BUSL-1.1
Keywords: ai-agents,authorization,policy,governance,trustrail
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# TrustRail Python SDK (alpha)

The distribution is `trustrail-sdk`; the import is `trustrail`
(`from trustrail import TrustRailClient`). PyPI's `trustrail` is an unrelated project. Needs a
TrustRail control plane: its URL and an agent credential (`tr_agent_…` or a `trb1_` bundle) from
that workspace's console. Source-available under BUSL-1.1; the licence is in the wheel.

The alpha client covers the governed workload path: evaluate an exact canonical action, create a
narrow delegation, open an A2A conversation, exchange signed messages and proposals, obtain a
short-lived execution authorization, and call either the protected MCP or provider gateway. It uses
only Python's standard library and never persists workload credentials, authorization tokens, or
provider credentials.

```python
from trustrail import TrustRailClient

client = TrustRailClient(
    api_url="https://api.trustrail.example",
    gateway_url="https://gateway.trustrail.example",
    agent_credential="tr_agent_...",
)
action = client.evaluate_action({...})
authorization = client.issue_execution_authorization(action["action_id"])
execution = client.execute(authorization["authorization_token"])
```

## Deadlines and retries

Every request carries a ten-second timeout and is retried up to three times with full-jitter
exponential backoff. Both are configurable, and `retry_attempts=1` sends exactly one attempt:

```python
client = TrustRailClient(
    api_url="https://api.trustrail.example",
    gateway_url="https://gateway.trustrail.example",
    agent_credential="tr_agent_...",
    timeout_seconds=5.0,
    retry_attempts=4,
    retry_initial_delay_seconds=0.25,
    retry_max_delay_seconds=4.0,
)
```

**A request is retried only when replaying it is provably safe** — a safe method, or a request
carrying an `Idempotency-Key`, which this client mints once per logical call so a replay collapses
into the original operation. A `500` is retried only for safe methods; a keyed mutation that fails
mid-flight is reconciled by its key rather than resent. `429` and `503` honour `Retry-After`, and a
wait longer than `retry_max_delay_seconds` is surfaced to your scheduler instead of blocking.

`TrustRailError` means the API answered — `.status`, `.problem`, and `.retry_after_seconds` apply.
`TrustRailTransportError` means the request never reached the server, with the original fault on
`__cause__`. The second says nothing about whether the action happened; that ambiguity is what the
platform reconciles, and it is why an unkeyed mutation is never silently resent.

## Claude Agent SDK

`governed_pre_tool_use(agent, specs)` returns a `PreToolUse` hook for the Python Claude Agent SDK.
Name the tools to govern — your own and MCP tools (`mcp__<server>__<tool>`) — each with the keyword
arguments `agent.guard()` takes. A callable field receives the tool's input as one mapping:

```python
from claude_agent_sdk import ClaudeAgentOptions, HookMatcher, query
from trustrail import agent_from_environment, governed_pre_tool_use

agent = agent_from_environment()
hook = governed_pre_tool_use(agent, {
    "mcp__payments__refund": {
        "action_type": "payments.refund.issue",
        "purpose": "Refund a customer",
        "resource": lambda tool_input: {"type": "payment", "id": tool_input["payment_id"]},
    },
})
options = ClaudeAgentOptions(
    hooks={"PreToolUse": [HookMatcher(matcher="^mcp__payments__", hooks=[hook])]},
)
async for message in query(prompt=prompt, options=options):
    ...
```

The same three rules as the JavaScript hook. On ALLOW it returns nothing and the SDK's own
permission rules decide, so TrustRail never approves past your settings. A denial, a pending
approval or an unreachable TrustRail blocks the call with text the model can act on. A hook rather
than `can_use_tool`, because the SDK skips `can_use_tool` for calls a rule or mode already approved.
This is Decision mode: the tool still runs in your process.
