# Agentkind Settlement Protocol v0.1 — Deterministic Fingerprint Specification

**Status:** v0.1 (terminal spec for Issue #54)
**Canonical asset:** `$AKIND` on Solana — `6ab7HysqYKjzeyDRqTRS73jt8UKsK6HfdRfmXEVcpump` (case-sensitive Base58; never normalize)
**Model:** thin, non-custodial, read-mostly. The VPS never signs, never broadcasts, never custodies, never sends `$AKIND`.

---

## 1. Purpose

Every `payment-request` has a **stable, reproducible identity** independent of when it was created,
when it was last viewed, or what lifecycle state it is in. The fingerprint lets any party
(payer, payee, agent, auditor) recompute and compare the exact same hash from the same immutable
agreement — without trusting a central server.

The fingerprint is the bridge between **LAYER 1 (PAYMENT REQUEST)** and the rest of the
separation model. It is *not* authorization, *not* a signature, *not* settlement, and *not*
proof of a business outcome.

---

## 2. Canonical serialization

Given a payment-request object `obj`:

1. **Strip mutable / per-run state.** Recursively drop any key in the `MUTABLE_FIELDS` set
   (see §4). This makes the fingerprint stable across drafts, authorizations, settlements and
   status changes.
2. **Preserve array order.** Lists (`metadata`, `evidence_artifacts`, …) are copied in order.
   They are **never** sorted or reordered.
3. **Sort object keys.** All dictionary keys are sorted lexicographically for a stable byte layout.
4. **Serialize.** `UTF-8` + `json.dumps(core, ensure_ascii=False, sort_keys=True, separators=(",", ":"))`.
5. **Hash.** `SHA-256` over the serialized bytes.
6. **Encode.** `fingerprint = "sha256:" + <64 lowercase hex>`.

Reference implementation: [`fingerprint.py`](./fingerprint.py). Golden vectors:
[`golden-vectors.json`](./golden-vectors.json).

---

## 3. Golden test vectors

| Vector | Direction | `request_id` | Fingerprint |
|--------|-----------|--------------|-------------|
| A | `human_to_human` | `akind-req-h2h-0001` | `sha256:df1809ed172f89379086ad2444db7b77d15229b2c66eb322af91f532c05f6b30` |
| B | `human_to_agent` | `akind-req-h2a-0001` | `sha256:4b05e7f272aef035b3b9382950d7c58e961487bbd2f186b6e2a5854efd0747a2` |
| C | `agent_to_agent` | `akind-req-a2a-0001` | `sha256:e7922f1e3cc70f4badbf4bb96087b97e82865346dcae442b1614e8257b1535fd` |

### Properties proven by the vectors

| Mutation | Result | Meaning |
|----------|--------|---------|
| Change `amount` 500000 → 999999 | fingerprint changes | Tamper-evident on immutable terms. |
| Reverse `metadata` array | fingerprint changes | **Array order is preserved** and significant. |
| Change `created_at` → 2099-… | fingerprint **unchanged** | Mutable state is excluded as designed. |

---

## 4. Mutable fields excluded

The following keys are dropped (recursively, at any nesting depth) before hashing:

```
created_at, updated_at, expires_at, authorized_at, settled_at, performed_at,
accepted_at, revoked_at, status, live, fail_closed, live_status,
authorization, settlement_receipt, execution_evidence, receipt, evidence
```

Rationale: these describe *when* and *what state* something is in. They must not alter the
stable identity of the underlying agreement.

---

## 5. Security notes

- **Replay protection** is provided by `request_id` + `nonce`, not by the fingerprint. The
  fingerprint is content identity; the nonce is uniqueness.
- **No key, no signature** is ever part of the fingerprint input. The VPS stores neither.
- **Case sensitivity:** the canonical mint is matched by exact string. Normalizing case would
  create a *different* (and wrong) canonical object and must be rejected.
- The fingerprint is **self-verifying**, not authority-dependent. If a server is unreachable,
  the payer and payee can still independently verify agreement identity offline.
