Metadata-Version: 2.4
Name: agentoath
Version: 1.1.0
Summary: The open protocol for AI agent identity, trust, and notarization.
Author-email: AgentOath Protocol Team <team@agentoath.ai>
License: Apache-2.0
Project-URL: Homepage, https://agentoath.ai
Project-URL: Hosted Registry, https://registry.agentoath.com
Project-URL: Documentation, https://agentoath.com/integrate
Keywords: ai,agent,trust,identity,notarization,ed25519,protocol
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
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: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=41.0
Requires-Dist: click>=8.0
Requires-Dist: httpx>=0.25.0
Provides-Extra: timestamps
Requires-Dist: rfc3161ng>=2.1; extra == "timestamps"
Requires-Dist: opentimestamps-client>=0.7; extra == "timestamps"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: rfc3161ng>=2.1; extra == "dev"
Requires-Dist: opentimestamps-client>=0.7; extra == "dev"
Requires-Dist: httpx>=0.25.0; extra == "dev"

# AgentOath Python SDK

The open protocol for AI agent identity, trust, and notarization.

## Two layers, one package

**Peer-to-peer** (everything below unless marked otherwise) — identities are
`did:trust:agent:`, receipts live with whoever holds them, and trust is a score
you compute from the receipts you have received. Nothing central to trust or pay
for. Run your own registry with `pip install agentoath-registry` if you want one.

**Hosted Registry** (`agentoath.hosted`) — a client for
[registry.agentoath.com](https://registry.agentoath.com), a notarisation service
where you publish signed receipts about your own actions and any third party can
verify them without trusting the platform. Identities are `did:agentoath:` and
the wire format is different.

They are separate protocols. A receipt built for one is rejected by the other,
by design. The same Ed25519 keypair can hold an identity in both.

### Stability

The peer-to-peer layer has been in use since 0.2 and follows semantic
versioning from 1.0 on: no breaking change without a major bump.

**`agentoath.hosted` is provisional.** The *wire format* it speaks is fixed —
it is pinned to the Registry's published spec at `GET /api/v1/registry`, and
the cross-language canonical fixtures in `tests/` fail if either side drifts.
What may still move is the shape of this client: argument names, helper
functions, where things live. That is a deliberate exception to the guarantee
above, stated here rather than implied by the version number, because the
module is new and has had one real integration so far.

If you need it frozen, pin an exact version. If you build on it, the stable
thing to build on is the wire format, not this client's signatures.

## Installation

```bash
pip install agentoath
```

## Quick Start

```python
from agentoath import TrustAgent, TrustReceipt

# Create a new Agent identity
agent = TrustAgent.create(
    name="My AI Assistant",
    capabilities=["chat", "search"],
    platform="custom",
)

# Save identity to disk
agent.save("my_agent.json")

# Load identity from disk
agent = TrustAgent.load("my_agent.json")

# Sign a trust receipt after an interaction
receipt = agent.sign_receipt(
    to_agent="did:trust:agent:bbb...",
    action="collaboration",
    rating=9,
    description="Built a great report",
)

# Verify a receipt
is_valid = receipt.verify_from_signature(other_agent.public_key)

# Calculate trust score
score = agent.calculate_trust_score(receipts)
print(f"Trust Score: {score.overall}")
```

## CLI

The SDK includes a command-line tool:

```bash
# Generate a new Agent keypair
agentoath init --name "My Agent" --capabilities "chat,search"

# Display Agent info
agentoath info

# Sign a receipt
agentoath sign-receipt --to did:trust:agent:bbb... --rating 9

# Verify a receipt
agentoath verify-receipt receipt.json --from-key-file agent_key.json

# Compute trust score
agentoath trust-score --receipts receipts.json --target-did did:trust:agent:bbb...
```

## Registry Client

Connect to a registry **you run yourself** (`pip install agentoath-registry`).
For the hosted Registry at registry.agentoath.com see *Hosted Registry* below —
it is a different protocol and this client cannot talk to it.

```python
from agentoath import TrustAgent
from agentoath.registry_client import RegistryClient

agent = TrustAgent.create(name="My Agent")
client = RegistryClient("http://localhost:8500")  # your own server

# Register with the Registry
resp = client.register(agent)
print(resp.data["identity_certificate"])

# Publish a receipt
receipt = agent.sign_receipt(to_agent="did:trust:agent:bbb...", rating=9)
client.publish_receipt(receipt)

# Query trust score
score = client.query_trust_score("did:trust:agent:bbb...")
print(score.data)
```

The client supports offline mode -- when the Registry is unreachable, it returns graceful fallback responses instead of crashing.

## Hosted Registry

A client for [registry.agentoath.com](https://registry.agentoath.com). Disabled
by default — it sends nothing until you turn it on.

```python
from agentoath.hosted import (
    AgentOathIdentity, AgentOathClientConfig, AgentOathRegistryClient,
    build_signed_receipt, verify_receipt_locally, sha256_json,
)

identity = AgentOathIdentity.generate()
print(identity.did)          # did:agentoath:<sha256 of the raw public key>
print(identity.private_key)  # keep this out of version control

client = AgentOathRegistryClient(AgentOathClientConfig(
    base_url="https://registry.agentoath.com",
    api_key="...",
    enabled=True,
))
client.register_identity(identity, name="my-service")

receipt = build_signed_receipt(
    identity,
    receipt_id="verdict-1234",
    action="compliance.verdict.red",
    metadata={"schema": "v1", "content_digest": sha256_json(text)},
)
client.publish_receipt(receipt)
```

Three rules the Registry enforces. `build_signed_receipt` checks all three
locally, so you get a Python exception at the call site instead of an HTTP 422
somewhere else:

1. `rating` must be a JSON **integer** 0–10. Python prints `7.0` where
   JavaScript prints `7`, so a float makes the signature unverifiable.
2. Empty optional fields must be **omitted entirely**, never sent as `""`.
3. `metadata` must always be present, and filled in **before** signing.

Receipts are public and permanent, so `metadata` keys are checked against a
28-key blocklist — matched per underscore-separated segment, which means
`prompt_hash` and `user_email` are rejected. Use `content_digest`, `actor_ref`
and similar instead. Full guide: <https://agentoath.com/integrate>.

## Proving *when* — and surviving deletion

A signed receipt proves what. It does not prove when: the `timestamp` field is
whatever the signer put there, so anyone can sign a receipt dated three years
ago. And it does not prove the receipt still exists — it is a row in a database.

```bash
pip install "agentoath[timestamps]"
```

```python
from agentoath.timestamps import stamp_receipt, verify, upgrade

proof = stamp_receipt(receipt)      # only the hash is sent — never the receipt
report = verify(proof)
report["any_valid"]         # a timestamp authority signed this hash and its own clock
report["bitcoin_confirmed"] # ...and it is committed into the Bitcoin blockchain

proof = upgrade(proof)      # hours later: pending -> confirmed
```

Two mechanisms, because they fail differently:

- **RFC 3161** — a Time-Stamping Authority signs your hash with its clock.
  Instant, recognised by courts and eIDAS. You trust the TSA, not us.
- **OpenTimestamps** — your hash is committed into Bitcoin. No trusted party at
  all, at the cost of a few hours until it lands in a block.

Once you hold an OpenTimestamps proof and your own copy of the receipt, you can
prove what happened and when **even if every receipt in the Registry is
deleted**. It does not make the database append-only; it makes the database not
matter.

`stamp` never raises. A timestamp authority being down must not break the thing
that was only trying to record a receipt — whatever failed lands in
`proof["errors"]` where you can see it.

## Attesting a document

There is no document receipt type. A document is an ordinary receipt with one
metadata convention, so signing, the canonical form shared with JavaScript, the
privacy blocklist and the Registry's validation all apply unchanged.

```python
from agentoath.documents import build_document_receipt

receipt = build_document_receipt(
    identity, "scans/book-0417.pdf",
    refs={"source_ref": "isbn:9780000000001", "acquired_ref": "po-2026-0817"},
)
receipt["receipt_id"]   # doc-<sha256 of the file> — attesting it twice is idempotent
```

**The file never leaves your machine**; only its sha256 goes into the receipt. A
digest proves "this is the same file" to whoever has the file and proves nothing
to anyone who does not — which is what you want when the thing you are attesting
is a scan you may not redistribute. Files are streamed, so a multi-gigabyte scan
does not have to fit in memory. Reference fields must end in `_ref` and stay
under 128 characters: receipts are public and permanent, and a pasted sentence
is not recoverable.

## API Reference

### TrustAgent

| Method | Description |
|--------|-------------|
| `TrustAgent.create(name, capabilities, platform)` | Create a new Agent with a fresh keypair |
| `TrustAgent.load(path, password)` | Load an Agent from a key file |
| `agent.save(path, password)` | Save the Agent's identity to disk |
| `agent.sign_receipt(to_agent, action, rating)` | Sign a Trust Receipt |
| `agent.counter_sign_receipt(receipt)` | Add a counter-signature |
| `agent.verify_receipt(receipt, from_public_key)` | Verify a receipt's signature |
| `agent.calculate_trust_score(receipts)` | Compute trust score |
| `agent.did` | The Agent's DID |
| `agent.public_key_formatted` | Public key as `ed25519:{base64}` |

### TrustReceipt

| Method | Description |
|--------|-------------|
| `TrustReceipt.create(from_agent_did, from_private_key, to_agent_did)` | Create and sign a receipt |
| `TrustReceipt.from_dict(data)` | Create from a dictionary |
| `receipt.counter_sign(to_private_key)` | Add counter-signature |
| `receipt.verify_from_signature(public_key)` | Verify initiator's signature |
| `receipt.verify_counter_signature(public_key)` | Verify counter-signature |
| `TrustReceipt.verify(receipt_dict, from_public_key)` | Static verification |

### RegistryClient

| Method | Description |
|--------|-------------|
| `client.register(agent)` | Register an Agent |
| `client.get_agent(did)` | Look up an Agent by DID |
| `client.get_agent_card(did)` | Get Agent's public profile |
| `client.search_agents(name, platform, capability)` | Search for Agents |
| `client.publish_receipt(receipt)` | Publish a signed receipt |
| `client.get_receipts(agent_did)` | Get receipts for an Agent |
| `client.verify_receipt(receipt, from_public_key)` | Online verification |
| `client.query_trust_score(agent_did)` | Query trust score |

## Development

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=agentoath --cov-report=term-missing
```

## Links

- Protocol website: [agentoath.ai](https://agentoath.ai)
- Hosted Registry: [agentoath.com](https://agentoath.com)
- Integration guide: [agentoath.com/integrate](https://agentoath.com/integrate)

## License

Apache 2.0
