Metadata-Version: 2.4
Name: envoys
Version: 0.1.0
Summary: Cryptographic identity for AI agents. Ed25519 keypairs, RFC 9421 HTTP Message Signatures, self-resolving keyids.
Project-URL: Homepage, https://envoys.me
Project-URL: Repository, https://github.com/jschoemaker/Envoys-public
Project-URL: Issues, https://github.com/jschoemaker/Envoys-public/issues
Project-URL: Specification, https://envoys.me/specs/signature/v1
Author-email: jerown <jeroenschoemaker1@gmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: a2a,agent-identity,ai-agents,cryptography,ed25519,envoys,http-signatures,rfc9421
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
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
Requires-Dist: cryptography>=42
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# envoys

Cryptographic identity for AI agents. Ed25519 keypairs, RFC 9421 HTTP Message Signatures, self-resolving keyids.

Python SDK. Mirrors [`@envoys/sdk`](https://www.npmjs.com/package/@envoys/sdk) (Node) — same surface, same wire format, byte-compatible against the [`envoys-rfc9421`](https://github.com/jschoemaker/Envoys-public/tree/main/fixtures/envoys-rfc9421) §14 test vectors.

## Install

```bash
pip install envoys
```

## Quick start

### Register an agent

```python
import asyncio
from envoys import Envoys, RegisterOptions

async def main():
    client, result = await Envoys.register(RegisterOptions(
        account_key = "ak_...",        # from envoys.me dashboard
        name        = "scout",
    ))
    print(result.address)       # scout@your-handle.envoys.me
    print(result.private_key)   # PEM PKCS8 — store securely, shown once

asyncio.run(main())
```

### Sign an outgoing HTTP request

```python
from envoys import Envoys, EnvoysConfig
import httpx

envoys = Envoys.from_env()   # reads ENVOYS_AGENT_KEY, ENVOYS_ADDRESS, ENVOYS_PUBLIC_KEY, ENVOYS_PRIVATE_KEY

body    = {"task": "summarize", "url": "https://example.com/doc"}
headers = envoys.sign_request("POST", "/api/task", body)

with httpx.Client() as http:
    r = http.post("https://other-agent.example/api/task",
                  headers={**headers, "Content-Type": "application/json"},
                  json=body)
```

### Verify an incoming request

```python
import asyncio
from envoys import Envoys, VerifyRequestOptions

async def verify(method, path, headers, body):
    result = await Envoys.verify_request(
        method, path, headers, body,
        VerifyRequestOptions(allowlist=["scout@trusted.envoys.me"]),
    )
    if not result.verified:
        return None, result.error
    return result.address, None
```

## Surface

Mirrors [`@envoys/sdk`](https://github.com/jschoemaker/Envoys-public/tree/main/packages/sdk) one-to-one with Python-idiomatic naming:

| Operation | Method |
| --- | --- |
| Register a new agent | `Envoys.register(opts)` |
| Construct from environment | `Envoys.from_env()` |
| Key rotation sync | `client.sync_keys()` |
| Sign arbitrary payload | `client.sign(payload)` |
| Sign HTTP request (RFC 9421) | `client.sign_request(method, path, body, *, tag=None)` |
| Sign Agent Card (JWS EdDSA) | `client.sign_agent_card(card)` |
| Verify HTTP request | `Envoys.verify_request(method, path, headers, body, options)` |
| Verify Agent Card | `Envoys.verify_agent_card(jws)` |
| Verify payload signature | `Envoys.verify(payload, signature, public_key)` |
| Resolve public key by address | `Envoys.resolve_public_key(address)` |
| Resolve dual-shape keyid | `Envoys.resolve_key_from_keyid(keyid_url)` |
| Resolve did:web | `Envoys.resolve_did_web(domain)` |
| Reset pin | `Envoys.reset_pin(address)` |
| Clear caches (testing) | `Envoys.clear_pins()`, `Envoys.clear_replay_cache()`, `Envoys.clear_key_cache()` |

## Spec

The wire format is published at <https://envoys.me/specs/signature/v1>. All operations in this SDK byte-match the §14 reference vectors at [`envoys-rfc9421`](https://github.com/jschoemaker/Envoys-public/tree/main/fixtures/envoys-rfc9421). Cross-implementation conformance verified against `@envoys/sdk` (Node) and `aim-did-rfc9421` (multi-language).

## License

Apache-2.0
