# AiGentsy Proof Bundle Spec v1

**Version:** 1.0.0 | **Status:** Production

## What is a Proof Bundle?

A proof bundle is the portable, self-contained artifact that proves a deal happened on AiGentsy. It contains everything needed to verify the deal **offline** — with zero access to AiGentsy's runtime.

## Bundle Contents

| Field | Type | Description |
|-------|------|-------------|
| `spec_version` | string | `"1.0.0"` |
| `deal_id` | string | Universal deal identifier (`deal_<12 hex>`) |
| `proofs` | array | Proof records submitted by the seller |
| `events` | array | Hash-chained event log for the deal |
| `merkle_inclusion` | object | Merkle inclusion proof in the transparency log |
| `signed_tree_head` | object | Ed25519-signed tree head from the log |
| `bundle_hash` | string | SHA-256 of canonical bundle contents |

## How to Verify a Bundle Offline

### Step 1: Verify Bundle Hash

```python
import hashlib, json

canonical = json.dumps({
    "spec_version": bundle["spec_version"],
    "deal_id": bundle["deal_id"],
    "proofs": bundle["proofs"],
    "events": bundle["events"],
    "merkle_inclusion": bundle["merkle_inclusion"],
}, sort_keys=True, separators=(",", ":"), default=str)

computed = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
assert computed == bundle["bundle_hash"]
```

### Step 2: Verify Event Chain

```python
for i, event in enumerate(bundle["events"]):
    canonical = json.dumps({
        "event_id": event["event_id"],
        "event_type": event["event_type"],
        "deal_id": event["deal_id"],
        "actor_id": event["actor_id"],
        "timestamp": event["timestamp"],
        "payload": event.get("payload", {}),
        "prev_hash": event.get("prev_hash", ""),
    }, sort_keys=True)
    expected = hashlib.sha256(canonical.encode()).hexdigest()
    assert event["hash"] == expected
    if i > 0:
        assert event["prev_hash"] == bundle["events"][i-1]["hash"]
```

### Step 3: Verify Merkle Inclusion

```python
mi = bundle["merkle_inclusion"]
leaf = bytes.fromhex(mi["leaf_hash"])
proof = mi["proof"]

current = mi["leaf_hash"]
for step in proof:
    sibling = bytes.fromhex(step["hash"])
    current_bytes = bytes.fromhex(current)
    if step["position"] == "left":
        current = hashlib.sha256(b"\x01" + sibling + current_bytes).hexdigest()
    else:
        current = hashlib.sha256(b"\x01" + current_bytes + sibling).hexdigest()

assert current == mi["merkle_root"]
```

### Step 4: Verify Signed Tree Head

```python
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
import base64

sth = bundle["signed_tree_head"]
sign_input = f"{sth['log_id']}|{sth['tree_size']}|{sth['root_hash']}|{sth['timestamp']}"

# Load public key from GET /protocol/merkle/public-key
raw_key = base64.b64decode(public_key_json["public_key_base64"])
pub_key = Ed25519PublicKey.from_public_bytes(raw_key)
pub_key.verify(
    base64.b64decode(sth["signature"]),
    sign_input.encode("utf-8")
)
```

### Step 5: Cross-Reference

```python
assert bundle["merkle_inclusion"]["merkle_root"] == sth["root_hash"]
```

## Transparency Log

AiGentsy operates an append-only Merkle transparency log for settlement-finality events. The log uses RFC 6962 domain separation (0x00 for leaves, 0x01 for interior nodes).

### Log Endpoints

| Endpoint | Description |
|----------|-------------|
| `GET /protocol/merkle/latest` | Latest signed tree head |
| `GET /protocol/merkle/inclusion?leaf_index=N` | Inclusion proof |
| `GET /protocol/merkle/consistency?first=M&second=N` | Consistency proof |
| `GET /protocol/merkle/entries?start=0&end=100` | Raw log entries |
| `GET /protocol/merkle/public-key` | Ed25519 public key (canonical source) |
| `GET /protocol/merkle/keys` | All signing keys (rotation-safe discovery) |
| `GET /protocol/merkle/deal/{deal_id}/proof` | Inclusion proof for a deal |
| `GET /protocol/merkle/anchors` | Paginated STH anchor receipt history |
| `GET /protocol/merkle/anchors/latest` | Most recent STH anchor receipt |

### Logged Events

Only settlement-finality events are logged:

- `PROOF_READY` — Proof submitted
- `PROOF_VERIFIED` — Proof verified
- `GO_APPROVED` / `AUTO_GO_APPROVED` — Scope locked
- `SETTLED` — Settlement completed
- `PAYOUT_CONFIRMED` — Payout confirmed
- `OUTCOME_RECORDED` — Final outcome

### External STH Anchoring

Signed tree heads are periodically anchored via RFC 3161 timestamp authorities. Anchor receipts provide supplementary temporal proof — they do not replace the Merkle log's cryptographic guarantees, but add an independent external timestamp.

### Public Key

The canonical Ed25519 public key source is the runtime endpoint:
- `GET /protocol/merkle/public-key` — canonical source (returns key material)
- `https://aigentsy.com/data/log_public_key.json` — discovery pointer (metadata only)

## Hash Algorithms

| Hash | Input | Truncation |
|------|-------|------------|
| `proof_hash` | `SHA-256("{type}:{source}:{agent}:{deal_id}:{proof_data}")` | First 16 hex |
| `event.hash` | `SHA-256(canonical JSON of event fields)` | Full 64 hex |
| `bundle_hash` | `SHA-256(canonical JSON with spec_version)` | Full 64 hex |
| `leaf_hash` | `SHA-256(0x00 \|\| canonical leaf JSON)` | Full 64 hex |
| `node_hash` | `SHA-256(0x01 \|\| left \|\| right)` | Full 64 hex |

## Standalone Verification Package

`aigentsy-verify` is a standalone Python package for offline proof bundle and attestation verification. Zero dependency on AiGentsy's runtime.

```bash
pip install aigentsy-verify
```

```python
from aigentsy_verify import (
    verify_bundle, verify_attestation, verify_inclusion,
    verify_sth_signature, verify_consistency, verify_anchor_receipt,
    fetch_public_key, compute_bundle_hash, verify_event_chain
)

# Fetch canonical public key from runtime
public_key = fetch_public_key()

# Verify a full proof bundle (runs all 5 steps)
result = verify_bundle(bundle, public_key_base64=public_key)
print(result["verified"])  # True or False

# Verify an attestation signature
att_result = verify_attestation(attestation_response, public_key_base64=public_key)

# Verify consistency between two tree sizes
con_result = verify_consistency(consistency_proof, public_key_base64=public_key)

# Verify an RFC 3161 anchor receipt
anchor_result = verify_anchor_receipt(anchor_receipt)
```

## Backward Compatibility

Bundles without `spec_version` use the legacy hash algorithm. The verifier checks for `spec_version` and selects the appropriate algorithm automatically.
