Metadata-Version: 2.4
Name: ardyn
Version: 0.3.0
Summary: Official Python client for the Ardyn Liability Verification Substrate API
Project-URL: Homepage, https://ardyn.ai
Project-URL: Documentation, https://docs.ardyn.ai
Project-URL: Repository, https://github.com/Kylewilson04/ardyn
Author-email: Ardyn Intelligence <sdk@ardyn.ai>
License-Expression: MIT
Keywords: ai,attestation,autonomous-systems,certification,liability,mcp,verification
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# Ardyn Python SDK

Official Python client for the [Ardyn Liability Verification Substrate](https://ardyn.ai) API.

## Installation

```bash
pip install ardyn
```

## Quick Start

> **New customer?** Create your account self-service — no Ardyn involvement required.
> See [Self-Service Onboarding](#self-service-onboarding) below to obtain your API key,
> then use it here.

```python
import asyncio
from ardyn import ArdynClient

async def main():
    async with ArdynClient(
        gateway_url="https://api.ardyn.ai",
        api_key="your-api-key",   # obtained from self-service onboarding (see below)
    ) as client:
        # Register an agent on AMD SEV-SNP hardware (production path)
        agent = await client.register_agent(
            platform="amd_sev_snp",
            capabilities=["sev_snp_report"],
            public_key="your-ed25519-public-key-hex",
        )
        print(f"Agent ID: {agent.agent_id}")

        # Activate the agent — self-service, no Ardyn approval required
        activated = await client.activate_agent(agent.agent_id)
        print(f"Agent status: {activated.status}")  # -> Active

        # Create an attestation (issues a DDC)
        attestation = await client.attest(
            tool_name="web_search",
            input_hash="sha256:abc123...",
            output_hash="sha256:def456...",
        )
        print(f"Attestation ID: {attestation.attestation_id}")
        print(f"AEA ID: {attestation.aea_id}")

        # Verify an AEA (convenience — trusts the gateway)
        result = await client.verify(attestation.aea_id)
        print(f"Valid: {result.valid}")

        # Trustless verify: re-derive the verdict from raw evidence
        # Requires the raw hardware report bytes — no trust in Ardyn.
        # Use the standalone ardyn-verify CLI:
        #   ardyn-verify --input ddc_response.json --allowlist allowlist.json

asyncio.run(main())
```

## Trustless Verification

Three call paths — one convenience, two trustless. Zero trust in Ardyn:

```python
# 1. Fetch by ID: downloads the bundle, re-derives locally
#    One server contact for the bytes; zero trust in Ardyn's verdict
result = await client.verify_offline(aea_id="...")
print(f"Verified: {result['verified']}")
for check in result["details"]:
    print(f"  {check}")

# 2. Auditor path: caller already holds the bundle — ZERO Ardyn contact
result = await client.verify_offline(bundle={...})

# 3. File on disk: bundle stored offline — ZERO Ardyn contact
result = await client.verify_offline(bundle_path="bundle.json")

# Synchronous convenience for scripts
result = client.verify_offline_file("bundle.json")
```

**How it works:** All three paths run the standalone `ardyn-verify` CLI, which re-derives
every claim — ARK→ASK→VCEK chain, ECDSA P-384 report signature, report_data binding,
issuer Ed25519 signature — from raw hardware evidence. The verifier is **not** bundled
with the SDK. The trust anchor is a verifier you build and inspect from source:

```bash
cargo install ardyn-verify         # build from source
cargo install --git https://github.com/ardyn/ardyn-verify
```

If `ardyn-verify` is not installed, `verify_offline()` raises `FileNotFoundError`
with explicit build-from-source instructions.

**Where verification happens:**
| Path | Trust Model | Ardyn Contact | Use Case |
|------|------------|---------------|----------|
| `client.verify(aea_id)` | Trusts gateway | Yes (verdict) | Quick check |
| `client.verify_offline(aea_id="...")` | Zero-trust | Yes (bytes only) | Audit |
| `client.verify_offline(bundle={...})` | Trustless | **None** | Court-admissible |
| `client.verify_offline(bundle_path="...")` | Trustless | **None** | Offline auditor |
| `ardyn-verify --input bundle.json` | Trustless | **None** | Raw CLI |

```

## Self-Service Onboarding

Create your account and obtain an API key without contacting Ardyn. The gateway
exposes public self-service onboarding endpoints:

```bash
# 1. Register — starts onboarding, sends a verification email + 2FA setup
curl -X POST https://api.ardyn.ai/onboarding/register \
  -H "Content-Type: application/json" \
  -d '{"email": "you@company.com", "organization": "My Company", "plan": "pro"}'

# 2. Verify email (link in the email) and verify 2FA to activate the tenant
curl -X POST https://api.ardyn.ai/onboarding/verify-2fa \
  -H "Content-Type: application/json" \
  -d '{"tenant_id": "<from step 1>", "code": "<TOTP code>"}'
# -> returns your api_key (unless payment is required, in which case next_step="payment")

# 3. Check status any time
curl https://api.ardyn.ai/onboarding/status/<tenant_id>
```

The returned `api_key` is what you pass to `ArdynClient(api_key=...)`. The
admin-only `register_tenant` method exists for internal/operator use only and is
**not** part of the customer onboarding path.

## Features

- **Async-first**: Built on `httpx` for modern async Python
- **Type-safe**: Full type annotations and dataclass models
- **Auto-retry**: Exponential backoff on transient failures (3 attempts: 1s, 2s, 4s)
- **Connection pooling**: Efficient HTTP/2 connection reuse
- **Comprehensive errors**: Typed exceptions for auth, not-found, validation, and server errors

## API Reference

### `ArdynClient`

```python
ArdynClient(
    gateway_url: str,        # Base URL of the Ardyn gateway
    api_key: str,            # API key for authentication
    timeout: float = 30.0,   # Request timeout in seconds
    max_retries: int = 3,    # Max retry attempts
)
```

### Methods

| Method | Description |
|--------|-------------|
| `register_tenant(name, config?)` | **Internal/operator use only** (admin route) — customers onboard via the self-service flow above |
| `register_agent(platform, capabilities, public_key, manifest_signature?, cert_fingerprint?)` | Register an agent |
| `activate_agent(agent_id)` | Activate your own agent (tenant self-service — no Ardyn approval) |
| `heartbeat(agent_id, status, uptime_secs, attestations_performed, agent_instance_id?)` | Send agent heartbeat |
| `attest(tool_name, input_hash, output_hash, metadata?)` | Create attestation |
| `submit_proof(request_id, agent_id, proof_type, proof_data, timestamp, signature, input_hash?, output_hash?)` | Submit proof |
| `verify(ddc_id)` | Verify a DDC |
| `get_usage(tenant_id)` | Get usage statistics |
| `get_settlement(tenant_id, period)` | Get settlement details |
| `get_dashboard(tenant_id)` | Get dashboard data |
| `health()` | Check gateway health |

### Error Types

- `ArdynError` - Base exception
- `AuthError` - Authentication failed (401/403)
- `NotFoundError` - Resource not found (404)
- `ValidationError` - Request validation failed (400/422)
- `ServerError` - Server error (5xx)

## Development

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

# Run tests
pytest tests/
```

## License

MIT
