Metadata-Version: 2.5
Name: actauth
Version: 0.0.3
Summary: A self-hosted policy gate for AI agent tool calls.
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# ActAuth (Python)

Reference implementation of the rule engine, scoped resolution, conditions,
and audit log. See the [root README](../README.md) for the product pitch and
rule format; this file only covers what's specific to the Python package.

## Install

```bash
pip install -e ".[dev]"
```

## Quickstart

```bash
python examples/quickstart.py
```

```python
import asyncio
from actauth import AuditLog, Gate, Scope

async def main():
    gate = Gate.from_config(
        "examples/actauth.yml",
        audit_log=AuditLog("actauth-audit.jsonl"),
    )
    scope = Scope(tenant="beta-fintech", environment="production", agent="payments-agent")
    result = await gate.evaluate("send_refund", {"amount": 5000}, scope)
    print(result.decision, result.reason)

asyncio.run(main())
```

## Run the tests

```bash
pytest
```

## Project layout

```
src/actauth/
  models.py       Decision, Scope, Rule, EvaluationResult
  conditions.py   safe {field, op, value} evaluator, no eval()
  rules.py        RuleSet — loads YAML, resolves scope + conditions
  audit.py        AuditLog — append-only JSONL
  approvers.py    Approver interface, ConsoleApprover, SlackApprover
  gate.py         Gate — ties the above together
examples/
  actauth.yml     example rule set
  quickstart.py   runnable end-to-end demo
tests/
```

## Status

Rule engine, scoping, conditions, audit log, and `SlackApprover` are real
and tested. No agent-SDK adapter yet.

## ConsoleApprover

The default `Approver` — a blocking terminal prompt, useful for local dev
and for exercising the full `ask` pipeline without standing up Slack:

```python
from actauth import Gate
from actauth.approvers import ConsoleApprover

gate = Gate.from_config("actauth.yml", approver=ConsoleApprover())
# this is also the default if you omit `approver`

result = await gate.evaluate("send_refund", {"amount": 900}, scope)
# prints scope/tool/args/reason, then blocks on `approve? [y/N]`
```

## SlackApprover

```python
from actauth.approvers import SlackApprover

approver = SlackApprover(
    bot_token=os.environ["SLACK_BOT_TOKEN"],
    channel="#approvals",
    signing_secret=os.environ["SLACK_SIGNING_SECRET"],
)

# wire your own route for the Slack app's Interactivity Request URL:
@app.post("/slack/interactions")
async def slack_interactions(request):
    raw_body = await request.body()
    await approver.handle_interaction(
        raw_body.decode(),
        request.headers["x-slack-request-timestamp"],
        request.headers["x-slack-signature"],
    )
    return Response(status_code=200)
```

`request_approval()` posts an interactive Approve/Deny message and resolves
when `handle_interaction()` is called with the matching click — verified
against Slack's request signature, timing out (deny) after
`timeout_seconds` (default 5 minutes) if nobody responds.
