Metadata-Version: 2.5
Name: chitmark
Version: 0.6.0
Summary: Official Python SDK for Chitmark by Open Agent Ledger: trust decisions on agent-mediated actions (verify, feedback, challenge)
Project-URL: Homepage, https://chitmark.com
Project-URL: Documentation, https://chitmark.com/docs
Author-email: Open Agent Ledger <dev@chitmark.com>
License: Proprietary: no open-source grant. Copyright (c) 2026 Open Agent Ledger. All rights reserved.
License-File: LICENSE
Keywords: agent-actions,anti-abuse,chitmark,open-agent-ledger,trust-decisions
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: cryptography>=43.0.0; extra == 'dev'
Requires-Dist: mypy>=1.14.0; extra == 'dev'
Requires-Dist: pyjwt>=2.9.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: respx>=0.22.0; extra == 'dev'
Requires-Dist: ruff>=0.9.0; extra == 'dev'
Provides-Extra: verdict
Requires-Dist: cryptography>=43.0.0; extra == 'verdict'
Requires-Dist: pyjwt>=2.9.0; extra == 'verdict'
Description-Content-Type: text/markdown

# chitmark (Python)

Official Python SDK for Chitmark: trust decisions on agent-mediated actions, tuned by business outcomes.

AI agents and multi-account farms drain free tiers, trial credits, and API allowances while looking exactly like your best customers. Chitmark scores each action in under 50 ms and returns `allow`, `challenge`, or `deny`. Then your outcomes (conversion, credit burn, chargeback) come back through `feedback` and tune the next decision.

[![PyPI version](https://img.shields.io/pypi/v/chitmark.svg)](https://pypi.org/project/chitmark/)

Requires Python >= 3.10. Fully typed (`py.typed`), synchronous `httpx` under the hood.

Try it without a key: [run the playground](https://chitmark.com/playground). Live service health: [chitmark.com/status](https://chitmark.com/status).

## Install

```bash
pip install chitmark
# or
uv add chitmark
```

## Quick start

```python
from chitmark import Chitmark

with Chitmark(api_key="ck_live_...") as client:
    verdict = client.verify(
        action="signup",
        session="sess_9f3a",
        surface="app.acme.com/signup",
        subject={
            "email": "buyer@acmecorp.com",  # hashed client-side before the wire
            "ip": "203.0.113.7",  # truncated to /24 client-side
            "userAgent": "Mozilla/5.0 ...",
        },
    )

# Persist verdict["eventId"] on the account row: feedback joins only on that id.
```

## The three verbs

| Method                  | Endpoint             | Purpose  |
| :---------------------- | :------------------- | :------- |
| `client.verify(...)`    | `POST /v1/verify`    | Decide   |
| `client.feedback(...)`  | `POST /v1/feedback`  | Learn    |
| `client.challenge(...)` | `POST /v1/challenge` | Escalate |

Timeouts and transport errors return a degraded challenge verdict and never raise on `verify`. HTTP errors raise `ChitmarkApiError` with the status attached. Set `on_degraded` to `challenge`, never allow on degraded.

### Report outcomes

Store the `eventId` from verify on the account row, then report outcomes against that same id. Never guess or derive the id.

```python
# At signup: persist the join key
verdict = client.verify(action="signup", subject={"email": "a@b.com"})
db.accounts.update(user_id, chitmark_event_id=verdict["eventId"])

# Later, when a label matures:
client.feedback(
    event_id=account.chitmark_event_id,  # the stored join key
    outcome="credit_burn",
    value=87.4,  # dollar amount: unit rides along (default "usd")
    observed_at="2026-08-06T04:00:00Z",
)
```

Exact duplicate feedback bodies derive the same warehouse id, so retrying a connector never double-counts a burned value. `unit` ships only alongside `value`.

### Handle a challenge

When verify returns `challenge`, issue one, solve the proof locally, and complete it. Proof-of-work difficulty is server-issued (4 by default, up to 6 at higher risk tiers): about 65k hashes, milliseconds for one real user, costly at farm scale.

```python
import hashlib

issued = client.challenge(event_id=verdict["eventId"], session="sess_9f3a")
instructions = issued["instructions"]

if instructions["type"] == "pow":
    prefix = "0" * instructions["difficulty"]
    nonce = 0
    while True:
        digest = hashlib.sha256(
            f"{issued['challengeId']}:{instructions['seed']}:{nonce}".encode()
        ).hexdigest()
        if digest.startswith(prefix):
            break
        nonce += 1

    client.complete_challenge(
        event_id=verdict["eventId"],
        challenge_id=issued["challengeId"],
        session="sess_9f3a",
        proof={"type": "proof_of_work", "nonce": str(nonce)},
    )
    # Re-verify with context={"challengeId": ...} so the next verdict honors it.
```

### Verify the receipt

Every production verdict ships a `verdictToken`: an ES256 JWT bound to session, origin, and event. Verify it before acting on high-value decisions (install the `verdict` extra for the crypto dependency):

```python
pip install "chitmark[verdict]"
```

```python
from chitmark.verify_token import verify_verdict_token

claims = verify_verdict_token(
    verdict["verdictToken"],
    session="sess_9f3a",  # enforce session binding (recommended)
    aud="api.chitmark.com",  # enforce origin binding
)
# claims: {"eventId", "decision", "actorType", "confidence", "jti", "exp", ...}
```

Rejects expired tokens, unknown keys, bad signatures, and session or origin mismatches with typed error codes.

### PII modes

| Mode               | Behavior                                                  |
| :----------------- | :-------------------------------------------------------- |
| `hashed` (default) | SHA-256 email, /24 IP truncation, allowlisted form fields |
| `none`             | Derived/header-shape signals only                         |
| `raw`              | Tenant opt-in only; higher compliance review              |

### Dependency injection and lifecycle

The client owns its `httpx.Client` by default and closes it on context exit. Pass your own for connection pooling or tests:

```python
import httpx
from chitmark import Chitmark

pool = httpx.Client(base_url="https://api.chitmark.com", timeout=0.8)
client = Chitmark(api_key="ck_live_...", http_client=pool)
```

## Develop

```bash
cd packages/sdk-python
uv sync --extra dev   # or: pip install -e ".[dev]"
pytest
ruff check .
```

## Agent integration

Using Cursor, Claude Code, Codex, or another coding agent? Point it at
[chitmark.com/SKILL.md](https://chitmark.com/SKILL.md), or paste this into your
prompt: `Integrate Chitmark into my app following https://chitmark.com/SKILL.md`.

## Resources

- [Agent integration skill](https://chitmark.com/SKILL.md)
- [API reference](https://chitmark.com/docs)
- [Quickstart](https://chitmark.com/docs/quickstart)
- [Playground](https://chitmark.com/playground)
- [Examples repository](https://github.com/nonameuserd/chitmark-examples)

## License

Proprietary: see [LICENSE](LICENSE).
