Metadata-Version: 2.4
Name: transient-trace
Version: 0.1.0a2
Summary: Python SDK for transient trace ATP reference implementation
Author: Transient Intelligence
Keywords: atp,trace,ed25519,rfc8785,governance
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: PyNaCl>=1.5.0
Requires-Dist: rfc8785>=0.1.2
Requires-Dist: typing_extensions>=4.6.0
Requires-Dist: python-ulid>=3.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"

# transient-trace

Governance SDK for AI agents. Intercepts, evaluates, and audits every
action an agent takes — with policy enforcement, tamper-evident receipts,
and a self-learning rule engine.

## Install

```bash
pip install transient-trace
```

For development (editable install — picks up source changes immediately):

```bash
pip install -e /path/to/transient-trace-py
```

## Quickstart

The fastest path to governed agents is the `wrap` command. It installs a
persistent shell shim so every invocation of the binary goes through
governance automatically — no prefix required.

```bash
# Wrap Claude Code and add shims to your shell RC file
transient-trace wrap install claude --auto-rc

# Restart your shell (or source the RC file)
source ~/.zshrc
```

That's it. Every `claude` invocation is now governed with a full receipt trail.

```bash
# Check governance is active
transient-trace wrap status

# View recent receipts
transient-trace receipts list --since 30m

# Summary with deny rate
transient-trace receipts summary --since 1h
```

---

## Documentation

| Document | Description |
|---|---|
| [Quickstart](docs/quickstart.md) | Get governance running in under five minutes |
| [CLI Reference](docs/cli-reference.md) | All commands, flags, and environment variables |
| [Policy Format](docs/policy.md) | Writing and testing governance policies |
| [How It Works](docs/how-it-works.md) | Architecture, interception model, trust boundaries |
| [Receipts](docs/receipts.md) | Audit trail, tamper detection, agent self-check |

---

## Enforce a policy

By default, transient-trace runs in **audit mode** — records everything,
blocks nothing. To enforce a policy, switch to **strict mode**:

```bash
cat > my-policy.json << 'EOF'
{
  "version": 1,
  "defaultAction": "deny",
  "rules": [
    { "id": "allow-git",       "action": "allow", "actionClasses": ["read", "write_low"] },
    { "id": "allow-anthropic", "action": "allow", "actionClasses": ["network"],
      "hosts": ["api.anthropic.com"] }
  ]
}
EOF

transient-trace run --mode strict --policy "$(cat my-policy.json)" claude -p "..."
```

Or set strict mode as the permanent default:

```bash
transient-trace config set mode strict
```

---

## How transient-trace intercepts agent actions

transient-trace uses three complementary interception layers:

1. **PATH shims** — thin bash scripts for `git`, `curl`, and other monitored
   binaries are prepended to PATH. Shell-resolved calls are caught here.

2. **Popen hook** — `sitecustomize.py` is injected via `PYTHONPATH` into every
   Python subprocess. It monkey-patches `subprocess.Popen` to catch calls that
   use absolute binary paths, bypassing PATH. This is why transient-trace works
   inside Claude Code without any changes to Claude Code itself.

3. **Inherited environment** — both mechanisms are inherited by child processes,
   giving coverage across nested agents and subprocesses.

See [How It Works](docs/how-it-works.md) for the full architecture.

---

## Python SDK (low-level)

For direct integration into Python agents:

```python
from transient_trace import Client_init

policy = {
    "version": 1,
    "defaultAction": "allow",
    "rules": [{"id": "allow-all", "action": "allow"}]
}

client = Client_init({"agentId": "my-agent", "policy": policy})

result = client.executeActionWithReceipt(
    lambda: {"ok": True},
    {"target": "resource-1", "action_class": "write_low"}
)

print(result["receipt"]["receipt_id"])       # TR-...
print(result["receipt"]["outcome"])          # allow
print(result["receipt"]["signature"]["alg"]) # Ed25519
```

If policy returns `deny`, raises `RuntimeError: Denied: <reason_code>`.

### Key differences from the TypeScript SDK

- **Synchronous API** — no `await`, no `asyncio`
- **Config is a dict** — `Client_init({"agentId": "..."})`, not keyword args
- **Default policy is deny-all** — pass a policy or set `ATP_POLICY_PATH`

---

## Building sdist / wheel (macOS, external drives)

Setuptools writes a staging directory under the current working directory. On
exFAT/FAT32 volumes (many external SSDs), macOS creates AppleDouble `._*`
metadata files during staging — cleanup then fails and `python -m build`
errors out.

**Fix:** build from an APFS temp path:

```bash
./scripts/build_release.sh
```

Or copy the repo to `~/Developer` (APFS) and run `python -m build` there.

To remove stray `._*` files: `./scripts/clean_macos_artifacts.sh`

---

## Interoperability with TypeScript SDK

Receipt signatures are cross-verifiable — a receipt signed by the Python SDK
can be verified by the TypeScript SDK and vice versa.

- Canonicalization: RFC 8785 JCS (both SDKs, action receipts)
- Signing: Ed25519 via PyNaCl (Python) / @noble/curves (TypeScript)
- Session receipts: legacy deep-sort canonicalization (both SDKs, phase 1 parity)
