Metadata-Version: 2.4
Name: auth51-checksum
Version: 0.1.1
Summary: The agent-identity checksum algorithm — the shared, byte-for-byte source of truth used by the auth51 authority (at registration) and the auth51 clients (at runtime recompute).
Project-URL: Homepage, https://auth51.com
Project-URL: Repository, https://github.com/unforge-io/auth51-checksum
Author-email: unforge <dev-admin@unforge.io>
License: Apache-2.0
Keywords: agent-identity,ai-agents,auth51,checksum
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# auth51-checksum

The agent-identity **checksum algorithm**, extracted as the single, shared
source of truth. The same bytes hash the same way on both sides of the trust
boundary:

- the **authority** computes it at registration → the *registered checksum*;
- the **clients** (embed/wrap) recompute it at runtime from the *live* agent and
  compare.

Because both sides run the identical algorithm, a client can verify "this is the
registered, unmodified agent" **without the agent self-declaring its identity** —
and the authority re-verifies at mint. This package is the engine behind that.

> ⚠️ This is a wire/registration **contract**. Changing the algorithm is a
> breaking change to every already-registered agent. Don't refactor in ways that
> change a single output byte — bump a version suffix instead. The conformance
> test (`tests/`) pins this to golden vectors generated from the authority's own
> `crypto/agent_checksum.py`; a failure there is a release blocker.

## Two algorithms (the authority accepts EITHER at mint)

**v1 — patchet-compat.** `SHA-256`, bare 64-char hex.
```
canon = {
  "id":     agent_id,
  "prompt": normalize_prompt_v1(prompt_template),   # whitespace-collapse, NO Unicode NFC
  "tools":  sorted([{name, signature, description, source_code?}], by name),  # is_agent dropped
  "config": configuration,
}
sha256(json.dumps(canon, sort_keys=True)).hexdigest()
```

**v2 — auth51 native.** `SHA3-512`, self-describing `sha3-512:v1:<128 hex>`.
```
canonicalize(model_dump(components))  →  json.dumps(sort_keys=True)  →  sha3-512
```
where `canonicalize` recursively sorts dict keys and runs `normalize_text` on
**every** string (strip, `\r\n`→`\n`, collapse blank lines, strip each line, drop
empty lines, **Unicode NFC**). The component dict carries every tool field —
`name, signature, description, source_code, is_agent` — defaults included; that
exact shape is part of the hash.

**v3 — auth51 native, IDENTITY-ONLY.** `SHA3-512`, self-describing
`sha3-512:id1:<128 hex>`.
```
canonicalize({agent_id, prompt_template, configuration})  →  json.dumps(sort_keys=True)  →  sha3-512
```
Same canonicalization as v2, but over **prompt + config only — no tools.** Tools
are the *grant* (authorization), not *identity* (DESIGN §13.1): adding/swapping a
tool re-derives the grant, it doesn't change who the agent is. This is what the
modern clients recompute at runtime — purely from the live prompt + config, both
**observable on the wire to the model**, so no tool material is needed. The
authority stores all three; **mint accepts any.**

The behavioural differences: **v2 NFC-folds Unicode; v1 does not** (composed vs
decomposed `é` collide under v2, differ under v1). And **two agents with the same
{id, prompt, config} but different tools share a v3 but differ in v2** — that's
the whole point of v3.

## Use
```python
from auth51_checksum import AgentComponents, Tool, compute_both, matches

comp = AgentComponents(agent_id="ReviewBot", prompt_template="You are…", tools=[], configuration={})
v2, v1 = compute_both(comp)              # both checksums
matches(registered_checksum, comp)       # True if either matches (the client-side gate)
```
`matches()` mirrors the authority's mint-time acceptance: recompute from the live
agent, and the registered checksum is valid if it equals *either* algorithm.

## Regenerating golden vectors
From the authority repo (its venv has pydantic):
```python
from auth51_authority.schemas.intent import AgentComponents
from auth51_authority.crypto.agent_checksum import compute_both
# run compute_both over the fixtures, dump {name, components, v2, v1} → tests/golden.json
```

## Ports
- **Python** — this package (reference implementation).
- **TypeScript** — for `@auth51/client` (node), to be ported from the spec above
  and pinned against the *same* `golden.json`. The golden vectors are the
  cross-language contract: any port must reproduce them exactly.
