The protocol

Protocol overview

Handprint is a decision provenance protocol for AI-assisted work. It captures human decisions from agent conversations, structures them as typed marks, links them to output artifacts, signs them cryptographically, and chains them into a tamper-evident sequence.

The protocol is local-first. All data is created and stored on your machine, and publishing to a hub is opt-in and excludes conversation content.

The design goal is to be agent-agnostic, so that any tool which produces a conversation transcript can be a source. Today the only wired-up source is Claude Code transcripts. Broader agent support is on the roadmap and is not built yet. The hub itself is a thin index over signed objects rather than a data store.

The reference code lives in the handprint repository (the CLI and protocol implementation). This is a living document, and the repos are the source of truth.

The grab and push pipeline

The core flow is two commands. grab reads a transcript, extracts marks and artifacts, sanitizes and encrypts the conversation payload locally, and signs the object automatically. There is no separate sign step, signing happens as grab builds the object. push then publishes the signed index to the hub. The payload is never part of that push.

The diagram below shows where each step runs and exactly what crosses the boundary to the hub. Everything inside the local machine box stays on your filesystem. Only the bottom row of fields is ever transmitted.

            LOCAL MACHINE (~/.handprint)                       HUB
  +--------------------------------------------------+   +----------------+
  |                                                  |   |                |
  |  transcript (Claude Code today)                  |   |                |
  |     |                                            |   |                |
  |     v                                            |   |                |
  |  [ GRAB ]  extract marks + artifacts             |   |                |
  |     |                                            |   |                |
  |     v                                            |   |                |
  |  sanitize regex -> encrypt (XSalsa20-Poly1305)   |   |                |
  |     |                                            |   |                |
  |     v                                            |   |                |
  |  sign  canonical JSON -> Ed25519 signature       |   |                |
  |     |                                            |   |                |
  |     +----- payload (encrypted) stays here -------X   | never leaves   |
  |     +----- seed + keys stay here ---------------X    | never leaves   |
  |     |                                            |   |                |
  |     v                                            |   |                |
  |  [ PUSH ] -- marks, artifacts, source, --------->|-->| thin index of  |
  |             sig, pubkey, parent, ts, v           |   | signed objects |
  |                                                  |   |                |
  +--------------------------------------------------+   +----------------+

The full command set is init, login, sources, grab, push, log, show (with --decrypt), verify, status, keys (add, list, rotate, export), and config (show, get, set). The grab command takes -n/--limit, --source, --extractor (local or host), and --dry-run.

Object model

A handprint is a signed JSON object with exactly nine top-level fields. This schema is intentionally minimal and immutable, so once it is signed no field is added, removed, or modified.

Field Description
marks Array of typed decisions (vision, choice, method) with subtype and note.
artifacts Array of output references (type, URI, content hash).
source Metadata about the agent and extractor used.
payload Encrypted, sanitized conversation text (local-only, never pushed).
sig Ed25519 signature over the canonical JSON of every other field.
pubkey The signer's 32-byte Ed25519 public key (base64url).
parent BLAKE2b-256 hash of the previous handprint in the chain.
ts ISO 8601 creation time.
v Protocol version identifier.

The signature in sig covers a canonical, sorted-key JSON of everything except sig itself. On push the hub receives v, ts, marks, artifacts, source, parent, sig, and pubkey. The payload is never pushed, it stays local and encrypted at rest.

The structure below groups those nine fields by what they carry. The provenance block describes the human decisions, the security block carries the cryptographic proof, and the linkage block positions the object in the chain.

+================ handprint ================+
|                                           |
|  PROVENANCE                               |
|    marks ......... [ vision | choice |    |
|                      method ] + note      |
|    artifacts ..... [ type, uri, hash ]    |
|    source ........ agent + extractor      |
|                                           |
|  PAYLOAD (local-only, never pushed)       |
|    payload ....... encrypted blob ........|
|                    XSalsa20-Poly1305      |
|                                           |
|  SECURITY                                 |
|    sig ........... Ed25519 over canon.     |
|    pubkey ........ 32-byte signer key     |
|                                           |
|  LINKAGE                                  |
|    parent ........ BLAKE2b-256 of prev    |
|    ts ............ ISO 8601               |
|    v ............. protocol id            |
|                                           |
+===========================================+

Marks come in three kinds. A vision mark is a goal, direction, or principle. A choice mark is an approval, override, rejection, constraint, or inquiry. A method mark is a tool, knowledge, or process. Every mark carries a free-text note of 1 to 280 characters. Artifacts are typed as git-commit, git-repo, file, url, deployment, c2pa, or custom, and any URI must use an allowed scheme (http, https, git, ssh, or file).

Cryptography

Handprint uses a small set of audited primitives from the @noble libraries by Paul Miller. There is no custom cryptography, and there is no libsodium, tweetnacl, or node:crypto dependency.

Signatures use Ed25519 through @noble/ed25519, with 32-byte keys and deterministic output. The 32-byte seed is itself the private key. Hashing uses BLAKE2b-256 through @noble/hashes, and it is the only hash in the system: it produces the content-addressable object hashes, the parent hash-chain links, and the digests that get signed. Conversation payloads are sealed at rest with XSalsa20-Poly1305 authenticated encryption through @noble/ciphers, using a fresh 24-byte random nonce per payload.

The seed is 32 random bytes stored as base64url at ~/.handprint/keys/seed with mode 0600. The encryption key is derived as BLAKE2b(seed) keyed under the context payload-encryption-v1 to 32 bytes. The key fingerprint is the first 16 bytes of BLAKE2b(pubkey), encoded base64url. The same seed produces the signing keypair, the encryption key, and the fingerprint.

Hash-chain and parent linkage

Each handprint stores the BLAKE2b-256 hash of the handprint before it in the parent field. This forms a tamper-evident chain, because changing any field of an earlier object changes its hash and breaks every link that follows. The genesis handprint has a null parent.

  genesis              h1                   h2                   h3
+----------+        +----------+        +----------+        +----------+
| parent:  |        | parent:  |        | parent:  |        | parent:  |
|   null   |        | H(gen.)  |        |  H(h1)   |        |  H(h2)   |
|          |        |          |        |          |        |          |
| marks    |        | marks    |        | marks    |        | marks    |
| sig (Ed) |        | sig (Ed) |        | sig (Ed) |        | sig (Ed) |
+----------+        +----------+        +----------+        +----------+
      |                  ^  |                ^  |                ^
      |  BLAKE2b-256     |  |  BLAKE2b-256   |  |  BLAKE2b-256   |
      +------------------+  +----------------+  +----------------+

  Edit any past object -> its BLAKE2b-256 changes -> the next parent
  pointer no longer matches -> the whole chain fails verification.

Sanitization pipeline

Before encryption, conversation text passes through an aggressive, regex-based sanitization pipeline that runs locally on the plaintext. Over-redaction is intentionally preferred over under-redaction. Sanitization is defense-in-depth behind encryption, not a primary security boundary.

The pipeline redacts email addresses, PEM blocks, and known token prefixes such as Slack (xox*), Stripe (sk_live_ and sk_test_), and GitHub (gh[pousr]_). It redacts ALL_CAPS KEY=VALUE secrets whose name contains KEY, SECRET, TOKEN, PASSWORD, or API, removing both the name and the value, and it redacts bare ALL_CAPS identifiers that contain those same words. It catches --key and --api-token style command flags, URL auth parameters such as token= and key=, and mixed alphanumeric strings of eight or more characters that contain at least two letters and two digits. Every rule runs through compiled regex, with no network calls and no ML inference.

Multi-device keys

Each device generates its own independent seed and keypair. There is no seed synchronization between devices. The hub maintains a registry of public keys associated with a user identity, and verifies signatures against any registered key.

handprint keys add --label 'device-name'   # register a new device key
handprint keys list                        # show registered keys + fingerprints
handprint keys rotate                      # new seed, retire the old key
handprint keys export                      # export key material

Old handprints remain verifiable because the hub retains retired public keys. Encrypted payloads written under an old seed still require that old seed to decrypt.

Storage layout

Global state lives under ~/.handprint/. It holds config.json (identity and hub url), keys/seed, a sources/ directory, and credentials.json (the hub token written by login). Each project gets a .handprint/ directory with its own config.json, an AGENTS.md, a .gitignore, an objects/ store (content-addressable, git-like, keyed by BLAKE2b-256 hashes), refs/HEAD, and an append-only log of object hashes.

Privacy model

Privacy in handprint is architectural, not policy-based. The protocol is designed so that sensitive data cannot reach the hub even if the implementation has bugs.

Conversation text is never sent to the hub in any form, and the seed and encryption key never leave the local filesystem or memory. Publishing is opt-in: grab is fully local, and only push sends anything to the hub. Pushed handprints are private to you by default, visible only on your own dashboard. Making a slice of your profile public is a separate, deliberate act: you mint a share, a point-in-time snapshot served at a token URL that anyone with the link can view, and that is revoked the moment you delete it. Two independent layers, sanitization and then encryption, run before anything is written to local storage. The hub receives only the marks, artifact references, source metadata, signature, public key, parent hash, and timestamp.

Shipped today versus roadmap

What ships today is the CLI, Ed25519 signing, BLAKE2b-256 hashing, XSalsa20-Poly1305 payload encryption at rest, the local content-addressable store, and the hub. The hub supports push, profiles, revocable share snapshots (the token URLs that make a slice of a profile public), a contribution heatmap, and search across your own handprints. All of this works with Claude Code transcripts right now.

Several things are on the roadmap and are not built yet. Support for any agent or tool beyond Claude Code is planned but not wired up, even though the design goal is to be agent-agnostic. A social and people layer (following, connecting, sharing, discovery beyond your own work, any network effects, and a marketplace) is planned and not present. Interoperability across multiple hubs, MCP support, and team or enterprise features are also planned and not yet built. There is no post-quantum work of any kind, no ML-DSA, and no FIPS-204 signatures, present or planned in this document.

The full protocol specification, JSON schemas, and reference implementation are available in the open-source the handprint repository.