Docs

Handprint is a local-first command-line tool that captures the human decisions behind AI-assisted work, signs them cryptographically, and lets you publish a verifiable record. This page teaches you how to install and use the CLI day to day. For the object model, cryptography, and on-the-wire details, read the full protocol spec.

Handprint reads Claude Code transcripts today. The design is agent-agnostic, but Claude Code is the only agent wired up right now. Broader agent support is on the roadmap.

Install

Install the CLI globally from npm.

npm i -g handprint-sh

You need Node.js 20 or newer. Everything runs locally, so there is nothing to host and no account required to start.

Confirm the install worked.

handprint --version

Quickstart

The core loop is two verbs. You grab decisions out of a conversation, which signs them automatically, and you push the ones you choose to share. There is no separate sign step; signing happens inside grab when it builds the object.

First set up your global signing identity once on this machine.

handprint init --global
✓ Created ~/.handprint (config, seed, fingerprint)
Your fingerprint: 8f3a2c1d4b5e6f70

Then, inside any project directory, initialize the project. This creates a .handprint/ directory, and nothing leaves your machine until you choose to push.

handprint init
✓ Created .handprint/ in this project
✓ Wrote .handprint/AGENTS.md so agents know handprint is active

Now pull the decisions out of a recent AI conversation. grab scans your Claude Code transcripts, extracts marks and artifacts, sanitizes and encrypts the conversation payload locally, then signs the object and appends it to your local chain.

handprint grab
Scanning Claude Code transcripts… 1 new session
Extracted 3 marks:
  vision   set the goal: privacy is architectural, not policy
  choice   approved: Ed25519 over RSA for compact signatures
  method   chose tool: @noble libraries for all crypto primitives
Linked 2 artifacts (1 git-commit, 1 file)
✓ Sanitized and encrypted conversation payload (local only)
✓ Signed handprint a1b2…  parent f0e9…
✓ Appended to chain (HEAD → a1b2…)

Review what landed with log, show, and verify.

handprint log
handprint verify

To publish, authenticate to the hub once, then push. Your handprints stay private to your account; you share specific slices later with a scoped share link from the dashboard.

handprint login
handprint push
Pushed 3 handprints to the hub.

That is the full loop. The rest of this page explains what each piece means.

Concepts

Marks and their three types

A mark is a single structured decision lifted out of your conversation. Every mark carries a type, a subtype, and a short note of 1 to 280 characters describing what you decided. There are exactly three types, and together they answer what you wanted, what you decided, and how you decided it.

A vision mark records a goal, a direction, or a principle you set. A choice mark records a moment of judgment such as an approval, an override, a rejection, a constraint, or an inquiry. A method mark records a tool, a piece of domain knowledge, or a process you brought to bear. You do not write marks by hand. The extractor identifies them when you run grab, and you review them in the log.

Artifacts

Artifacts are the outputs a decision shaped. Each one is a pointer rather than a copy, storing a type, a URI, and an optional content hash. The artifact types are git-commit, git-repo, file, url, deployment, c2pa, and custom, and URIs are restricted to an allowlist of the http, https, git, ssh, and file schemes. The link from a mark to an artifact is the provenance, the verifiable connection between what you decided and what resulted.

Signing and your key

Everything derives from one 32-byte seed on your machine, stored at ~/.handprint/keys/seed as base64url with owner-only 0600 permissions. The crypto stack is the @noble libraries by Paul Miller, with no libsodium, tweetnacl, or node:crypto involved.

When you grab, the handprint is canonicalized into deterministic JSON with sorted keys, hashed with BLAKE2b-256 via @noble/hashes, and signed with your Ed25519 private key via @noble/ed25519. In noble, the 32-byte seed is the private key. The signature digest is BLAKE2b-256, the same hash used for object hashes and the parent hash-chain; there is no SHA-256 anywhere in handprint. The signature covers a canonical JSON of every field except sig, and your public key travels with the object, so anyone holding your public key can confirm you authored it and that nobody altered it afterward.

The same seed also derives the key that encrypts your conversation text before it is ever written to disk. The encryption key is BLAKE2b(seed, context "payload-encryption-v1", 32 bytes), and the payload is sealed with XSalsa20-Poly1305 via @noble/ciphers using a fresh 24-byte random nonce. Your key fingerprint is the first 16 bytes of BLAKE2b(pubkey) rendered as base64url. Your raw conversation never leaves your machine, and the seed never does either.

The object on the wire

A handprint object has the fields v, ts, marks, artifacts, source, payload, parent, sig, and pubkey. The payload is the encrypted conversation, and it is never pushed; it stays local, encrypted at rest. Only v, ts, marks, artifacts, source, parent, sig, and pubkey are sent to the hub.

Sanitization

Before the payload is encrypted, a regex sanitizer runs locally on the plaintext and prefers false positives over leaks. It redacts email addresses, PEM blocks, and known token prefixes such as Slack xox*, Stripe sk_live_ and sk_test_, and GitHub ghp_, gho_, ghu_, ghs_, and ghr_. It redacts ALL_CAPS KEY=VALUE secrets where the name contains KEY, SECRET, TOKEN, PASSWORD, or API (both name and value are removed), bare ALL_CAPS identifiers matching those same words, --key and --api-token style flags, and URL auth parameters like token= and key=. It also redacts mixed alphanumeric strings of 8 or more characters that contain at least two letters and two digits.

Sharing

Everything you push stays private to your account. To show a specific slice of your work — a project, a mark type — you generate a share link from the dashboard: a public, read-only snapshot scoped to the filters you set. Sharing is a hub action, not part of the signed record, so it never requires re-signing. Either way, the encrypted conversation payload is never part of a push; the hub only ever receives the structured, signed decision metadata.

The hub

The hub at handprint.sh is a thin index over signed objects, not a place your data lives by default. It stores the marks, artifact references, source metadata, signatures, and chain links for the projects you choose to publish, and it verifies every signature against your registered public keys on the way in. Today the hub supports pushing, profiles, scoped share links, a contribution heatmap, and search. Authentication uses handprint login, which runs a device-code flow: you confirm a code in the browser once, and the CLI stores the resulting hub token at ~/.handprint/credentials.json.

The social and people layer (following, connecting, sharing or discovery beyond your own work), interoperability across multiple hubs, MCP, and team or enterprise features are on the roadmap and are not built yet.

Extraction engines

grab extracts marks entirely on your machine. Three engines are available.

ollama (recommended)

Runs extraction through a local OpenAI-compatible server. Ollama is the default target; LM Studio, llama.cpp server, and vLLM work the same way. Nothing leaves your computer.

Setup:

ollama pull qwen2.5:3b
handprint config set extraction.provider ollama --global

Power-user config keys: extraction.baseUrl, extraction.model, extraction.apiKey. The provider name openai is an accepted alias.

local (node-llama-cpp)

An embedded engine with no external server required. Handprint downloads a model on first use (about 2 GB). Self-contained and private.

host

Routes extraction through an AI tool already installed on your machine: Claude Code, opencode, or codex. Convenient and uses your existing quota, but it uses that tool's cloud model.

If the chosen engine is not ready when you run grab, the command stops with a one-line explanation before processing anything.

The grab workflow

grab follows a scan-then-confirm pattern:

  1. Scan: reads transcript sources and builds a plan grouped by project, showing what it found.
  2. Confirm: presents the plan and waits for your approval before processing anything. A bare grab with no terminal to accept input stops after the scan. Pass -y to skip the prompt for agents and scripts.
  3. Process: for each session, extracts marks and artifacts, sanitizes and encrypts the payload locally, signs the object, and appends it to the chain.

Incremental and idempotent

Each run processes only sessions, and only the messages, that are new since the last grab. Overlapping runs never redo work. Pass --redo to force a re-grab of already-processed sessions.

Targeting

  • --days N: sessions from the last N days
  • --since / --until: explicit date range
  • --project <name>: restrict to one project
  • --min-messages N: skip sessions with fewer than N messages
  • -n N: cap the number of sessions processed

Progress and safe stop

grab shows live progress and an ETA as it works. You can stop it at any time; the chain remains consistent because each session is appended atomically before the next one starts.

Visibility on the hub

Everything you push stays private — it is not shown on a public profile or indexed. You share by generating a share link from the dashboard, which mints a public, read-only snapshot at a hard-to-guess URL. Share links are scoped to whatever filters you have set (a project, a mark type, a subtype), so you share exactly the slice you intend and nothing more. The encrypted conversation payload is never sent to the hub, share link or not.

CLI reference

Every command reads and writes locally unless it explicitly talks to the hub. Run handprint <command> --help for the full flag list.

handprint init

With --global, sets up your global signing identity on first run. Without it, prepares the current project with a .handprint/ directory and an AGENTS.md pointer.

handprint init --global   # one-time machine identity
handprint init            # set up the current project

handprint login

Authenticates to the hub through a device-code flow and stores the hub token at ~/.handprint/credentials.json. Required before push.

handprint login

handprint sources

Lists the available transcript source adapters and their status.

handprint sources

handprint grab

Scans your transcript sources for new conversation, extracts marks and artifacts, sanitizes and encrypts the payload locally, signs the object, and appends it to the chain. Signing is automatic; there is no separate sign command.

handprint grab -n 5 --source claude-code --extractor local --dry-run

The flags are -n / --limit to cap how many sessions are processed, --source to target one source, --extractor to pick local or host extraction, and --dry-run to preview without writing.

handprint push

Publishes your signed handprints to the hub in batches. The encrypted payload is never sent; you share specific slices later with a scoped share link.

handprint push

handprint log

Lists your local handprints in chain order.

handprint log

handprint show

Shows a single handprint by hash or prefix. Pass --decrypt to reveal the locally stored conversation payload.

handprint show a1b2 --decrypt

handprint verify

Recomputes every BLAKE2b-256 hash, checks every Ed25519 signature, and confirms each entry was signed by one of your own keys, rejecting a chain that was forged or spliced with a foreign key.

handprint verify

handprint status

Reports chain state, auth status, and your key fingerprint.

handprint status

handprint keys

Manages your signing identity across devices. Each device has its own independent seed, and the hub recognizes every key you register.

handprint keys list                      # show registered keys and fingerprints
handprint keys add --label 'laptop'      # register a new device key
handprint keys export                    # print the seed for secure backup
handprint keys rotate                    # new seed, retire the old key

handprint config

Reads and writes config values with a dotted path.

handprint config show
handprint config get extraction.provider
handprint config set extraction.provider ollama

Using it with your agent

Handprint is built to be driven by AI agents, not just by you at a prompt. An agent uses the exact same CLI a human does. The important command for an agent to run is handprint grab, ideally right after a meaningful decision lands. Agents discover that handprint is active by reading the tiny .handprint/AGENTS.md pointer file that project init checks into your project, which keeps the footprint in their context window small.

Handprint works with Claude Code today. The extractor and source model are agent-agnostic by design, but Claude Code is the only agent wired up right now, and broader agent support is on the roadmap rather than shipped. The agent never touches your key material directly. The CLI reads the seed, derives what it needs, uses it, and clears it internally.

The fastest way to get going inside Claude Code is to paste a short setup prompt that installs the CLI and runs the first steps for you.

Set up handprint.sh in this project: install the CLI with `npm i -g handprint-sh`,
run `handprint init --global` to make my signing key, `handprint init` to set up this
project, then `handprint grab` to capture and sign my first handprint.

Configuration

Your global state lives in ~/.handprint. It holds config.json (your identity and the hub URL), the 32-byte seed at ~/.handprint/keys/seed with owner-only 0600 permissions, the registered transcript sources under ~/.handprint/sources/, and credentials.json with the hub token written by handprint login. Your signing keypair, encryption key, and fingerprint are all derived from the seed.

Each project keeps its own .handprint/ directory. It contains config.json, the AGENTS.md pointer, a .gitignore, a content-addressable objects/ store keyed by BLAKE2b-256 hashes in a git-like layout, refs/HEAD pointing at the tip of the chain, and an append-only log of object hashes. The config and AGENTS.md are meant to be committed, while objects/, refs/, and log are git-ignored for you.

Back up your seed somewhere safe such as a password manager, because losing it means losing the ability to sign as that identity or to decrypt old conversation payloads. The seed never leaves your machine.

Going deeper

The name comes from the Cueva de las Manos in Patagonia, where stenciled hands say "I was here, I made this." That is what a handprint is for: proof a human was present and deciding. When you want the object schema, the canonicalization rules, the sanitization pipeline, and the cryptographic guarantees in full, read the protocol spec.