Metadata-Version: 2.4
Name: sovest-firewall
Version: 0.1.0
Summary: Stdlib-only Python client for the Sovest Agent Honesty Firewall HTTP surface (claim <= evidence, action <= authority).
Author: Sovest
License: Proprietary
Project-URL: API contract, https://example.invalid/AGENT_FIREWALL_API_v1
Keywords: agent,firewall,honesty,claim-ceiling,guardrail
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# sovest-firewall (Python SDK)

Stdlib-only Python client for the **Sovest Agent Honesty Firewall** HTTP surface.
It calls one pre-action gate before your agent does anything consequential.

> Honest claim ceiling: the firewall enforces a **policy** — claim ≤ evidence, action ≤ authority.
> It does **not** make an agent correct, safe, or aligned, and it is not a guarantee of safe outcomes.
> It blocks unsupported claims from becoming actions and makes admitted ones scoped, verifiable,
> disputable, and correctable.

**Status:** v0.1.0. Not yet published to PyPI. Dependency-free (uses `urllib` from the standard library).
Requires Python >= 3.9.

## Install

From this directory (editable install):

```bash
pip install -e .
```

That exposes the `sovest_firewall` package importably. No third-party packages are pulled in.

## Usage

```python
from sovest_firewall import FirewallClient, NotAdmitted, guard

fw = FirewallClient("http://127.0.0.1:8787")

# 1. Get a verdict (never raises on a refusal — the refusal IS the answer):
v = fw.check(
    claim_text="Refund approved for order 4821",
    claim_type="LIVE_PROCESS",
    scope="refunds:order_4821",
    requested_action="REFUND_APPROVE",
    action_impact="consequential",
    evidence_refs=["sha256:order_record", "sha256:customer_chatlog"],
    authority_refs=["refund_policy_v2"],
)
print(v["verdict"], v["admitted"])

# 2. Admit + record (returns the receipt, raises NotAdmitted on refusal):
try:
    receipt = fw.admit(
        claim_text="Refund approved for order 4821",
        scope="refunds:order_4821",
        requested_action="REFUND_APPROVE",
        action_impact="consequential",
        evidence_refs=["sha256:order_record"],
        authority_refs=["refund_policy_v2"],
    )
    print(receipt["admissibility_id"])
except NotAdmitted as e:
    print("refused:", e.verdict["reason"])

# 3. Guard a callable — runs it ONLY if the firewall admits the action:
def do_refund():
    ...  # the real side effect
result = guard(
    do_refund, fw,
    claim_text="Refund approved for order 4821",
    scope="refunds:order_4821",
    requested_action="REFUND_APPROVE",
    evidence_refs=["sha256:order_record"],
    authority_refs=["refund_policy_v2"],
)
```

## API surface (stable for v0.1.0)

| symbol | purpose |
|---|---|
| `FirewallClient(base_url, timeout)` | client bound to a firewall server |
| `.check(**fields) -> dict` | get a verdict; does not raise on refusal |
| `.admit(**fields) -> dict` | admit + record; raises `NotAdmitted` on refusal |
| `.build_request(path, fields)` | build the `urllib` request without sending (offline tests) |
| `guard(action, client, **fields)` | run `action` only if admitted |
| `NotAdmitted` | exception carrying the full refusal verdict (`.verdict`) |
| `FirewallError` | transport / unexpected-status error |

## Running the examples and checks

- `examples/quickstart.py` — runnable end-to-end when a firewall server is up
  (start `python3 ../../server.py` first), prints the verdict.
- `tests/test_offline.py` — offline check; constructs a client and validates request
  serialization (URL, method, headers, JSON body) with no network call.

See `SDK_PY_CHECK.txt` for captured proof of the import + offline checks.
