Metadata-Version: 2.4
Name: provenant-sdk
Version: 0.8.3
Summary: Provenant client SDK — authorize, simulate, and complete agent actions through the control plane.
Author: IdentiQube LLC
License: Apache-2.0
Project-URL: Homepage, https://provenant.identiqube.com
Project-URL: Repository, https://github.com/IdentiQube/Provenant
Project-URL: Issues, https://github.com/IdentiQube/Provenant/issues
Keywords: provenant,ai-agents,authorization,control-plane,governance
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# provenant-sdk (Python SDK)

First-party Python client for the **Provenant** control-plane API — the few lines an
autonomous agent adds to become governable.

- **Zero dependencies.** Standard library only (`urllib`, `json`, `dataclasses`,
  `typing`). Offline-friendly and trivial to vendor.
- Mirrors the TypeScript SDK (`@identiqube/provenant-sdk`) surface, paths, headers, and accepted
  status codes — using Python `snake_case` method names.

## Install

```bash
pip install provenant-sdk
```

Or vendor the `provenant_sdk/` package directly — it has no third-party deps.

## Quickstart — authorize → act → complete

```python
import os
from provenant_sdk import ProvenantClient

provenant = ProvenantClient(
    "https://provenant.identiqube.com",
    api_key=os.environ["PROVENANT_KEY"],
)

decision = provenant.authorize({
    "type": "payment.send",
    "resource": "vendor:acme",
    "valueCents": 25_00,
})

if decision["status"] == "authorized":
    ref = pay_vendor(...)  # your real-world side effect
    provenant.complete(decision["id"], {
        "success": True,
        "externalRef": ref,
        "warrant": decision["authorizationToken"],  # proves possession
    })
else:
    # A `deny` decision is returned (not raised) — branch on it.
    print("blocked:", decision["decision"]["reason"])
```

A `deny` decision is returned, **not raised**, so you can branch on
`decision["status"]`. Auth/validation failures raise `ProvenantError`.

## Binding enforcement — `invoke`

Act on a resource *through* Provenant. The control plane evaluates policy and only
performs the downstream call (using the connector's server-held credential) when
allowed — your agent never holds the credential and cannot bypass the decision.

```python
res = provenant.invoke({
    "type": "payment.send",
    "resource": "vendor:acme",
    "valueCents": 25_00,
    "request": {
        "method": "POST",
        "path": "/v1/charges",
        "body": '{"amount": 2500, "currency": "usd"}',
    },
})

if res["status"] == "executed":
    print(res["output"])   # { "status", "headers", "body" } from the connector
else:
    print("blocked/held:", res["action"]["status"])
```

## Convenience — `guard`

Authorize, run your work if allowed, then report completion automatically. If your
callback raises, the action is completed with `success=False` and the error re-raised.

```python
def execute(decision):
    ref = pay_vendor(...)
    return {"result": ref, "externalRef": ref}

outcome = provenant.guard(
    {"type": "payment.send", "resource": "vendor:acme", "valueCents": 25_00},
    execute,
)

if outcome["authorized"]:
    print("done:", outcome["result"])
else:
    print("blocked:", outcome["decision"]["decision"]["reason"])
```

## Methods

| Method | HTTP |
| --- | --- |
| `authorize(input)` | `POST /v1/actions/authorize` (ok: 200, 202, 403) |
| `get_action(action_id)` | `GET /v1/agent/actions/{id}` |
| `simulate(input)` | `POST /v1/actions/simulate` (ok: 200) |
| `complete(action_id, result)` | `POST /v1/actions/{id}/complete` (ok: 200) |
| `invoke(input)` | `POST /v1/gateway/invoke` (ok: 200, 202, 403, 502) |
| `mint_credential(input)` | `POST /v1/gateway/credential` (ok: 200, 202, 403) |
| `connectors()` | `GET /v1/connectors/available` → `connectors` list |
| `spawn(input)` | `POST /v1/agents/delegate` (ok: 201) |
| `guard(input, execute)` | convenience: authorize → execute → complete |

Auth header: `x-provenant-key: <api_key>`. Request bodies are JSON
(`content-type: application/json`).

## Errors

On a non-ok status, methods raise `ProvenantError(code, message, status)`, parsed from
the JSON `{"error": {"code", "message"}}` envelope (falling back to `request_failed` /
`HTTP {status}`).

```python
from provenant_sdk import ProvenantError

try:
    provenant.authorize({"type": "payment.send", "resource": "vendor:acme"})
except ProvenantError as e:
    print(e.code, e.status, e.message)
```

## Testing

`ProvenantClient` accepts an injectable `opener` so you can test offline without a
network. See `tests/test_client.py`.

```python
client = ProvenantClient("https://api.test", "key", opener=my_stub_opener)
```

## License

Apache-2.0 (see the LICENSE file in this package). This SDK is open source; the Provenant
server product it talks to is separately licensed (proprietary).
