Hallmarks

Spot it
before you read it.

Hallmarks turn long, opaque identifiers — crypto addresses, key fingerprints, commit SHAs, anything you'd otherwise read character by character — into small, distinct visual marks. A mismatch jumps out before you've finished reading the string. The careful read is still yours when it matters.

brush swarm always
No match
oak enable before

For anything you compare character by character

Hallmarks aren't avatars and they're not memory aids — you don't need to remember what your hallmark looked like yesterday. You only need to compare what's on the screen with what's somewhere else: another screen, a printed receipt, a hardware wallet, a teammate's terminal.

bc1qar0srrr7xfkv…wf5mdq

Crypto addresses

Sender's screen vs. recipient's screen. Wallet vs. hardware-wallet display. The two should match before you sign.

SHA256:nThbg6kXUpJWG…

Key fingerprints

SSH host keys, PGP fingerprints, certificate hashes — the things you're supposed to verify out-of-band but rarely do.

9d2c5e85e9b9…

Commit SHAs

What CI is building, what's deployed in prod, what the reviewer is looking at. Visual confirmation across systems.

Where else this fits

Crypto popularized the need because the cost of getting it wrong is immediate and irreversible. But the same comparison problem shows up in any industry where humans are the bridge between two systems that need to agree on a long opaque string — banking, logistics, healthcare, software supply chains, legal, scientific records.

Banking

An invoice arrives by email with the supplier's IBAN. The clerk types it into the banking portal. Both the invoice and the portal display the hallmark next to the IBAN — a glance confirms the destination before the wire is signed off.

Invoice (PDF)
DE89 3704 0044 0532 0130 00
=
Banking app
DE89 3704 0044 0532 0130 00

Logistics & supply chain

A truck arrives at the warehouse. The dock worker compares the manifest on the tablet against the printed container label. If the hallmarks match, sign for receipt; if not, hold the shipment.

Shipping manifest
MSCU 567 812 3
=
Container label
MSCU 567 812 3

Software supply chain

CI builds a Docker image, pushes it, records the digest. Production pulls the image and surfaces the digest on the deploy dashboard. Operators glance: hallmarks match → the right image shipped to the right cluster.

CI build
sha256:5e2c4ab8…aa31e7d8
=
Production deploy
sha256:5e2c4ab8…aa31e7d8

A short history of visual fingerprints

Long before crypto wallets, people needed a quick way to compare long opaque strings without reading every character. The tools changed across eras, but the goal stayed the same: reduce human error when two systems must agree.

+-----------------+
|.o #o^&o% o.o.X@*|
|&^+ *+^X#*o&B. *=|
|OXB=^* = XO.+=^%.|
|oBoO /=#^B+B%@#OB|
|B^O%+O*&@oOo/%/.+|
|XX%^B@B^OX=B#^@&#|
|oO^/O&&.^/@ &/=+O|
|+B#O#X=&.+BB=OXB |
|&^/#B/B/O=%%*@OO#|
+-----------------+
SSH randomart
Key fingerprints in terminal UIs (2008)
Web identicons
Deterministic account visuals for avatars
Wallet-era marks
Address checks where mismatch costs are immediate
Hallmarks
Standard-first: cross-platform, accessible, low-res

Three styles, one algorithm

The same pattern, rendered for different display environments. Monochrome uses shape (circle vs. rounded square) to carry the third value when color isn't available.

standard
OKLCH-tuned color pair.
high contrast
For sunlight, low-quality displays, vision impairment.
monochrome
Shape replaces color for the third value.

Light and dark mode

Mode is orthogonal to style — every style renders in both light and dark. The pattern and hues are identical; only the palette changes. The three-word companion is fully mode-invariant, making it the clearest anchor when comparing across a light-mode phone and a dark hardware wallet.

standard
high contrast
monochrome

Light and dark side by side, each on its own background:

light
dark

Borders for busy backgrounds

An optional 1-unit stroke in the primary color. Useful when the hallmark sits against a tinted surface, a photograph, or other hallmarks in a dense list — anywhere its own light background would otherwise blur into the surroundings. bordered: true.

Tinted card
No border
With border
Photo / pattern
No border
With border

Visual variety

Twenty-four arbitrary inputs. The point of the grid isn't beauty — it's that no two look the same at a glance. Hover any tile to see its source string.

Designed to work small

Hallmarks were tuned for the 22–34 px range — small enough to sit inline next to an address line, large enough to distinguish without squinting. Below 18 px, switch to the 14×20 pixel-art variant.

bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
bc1q5shngj24323nsrmxv99st02na6srekfctt30ch
bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh
bc1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3

14×20 pixel-art mode

For hardware wallets, e-ink readers, embedded LCDs, and any display where sub-pixel rendering isn't available. Each cell becomes a 2×2 block; accent cells take a diagonal two-pixel form so the three-value distinction survives. Render once, upscale with nearest-neighbor — never anti-aliasing.

standardOKLCH color pair
Native 14×20
high contrastFor sunlight & e-ink
Native 14×20
monochrome1-bit displays
Native 14×20

Three words for ears, screen readers, and phone calls

Every hallmark has a three-word companion drawn from the BIP-39 English wordlist, derived from the same hash. It's the natural aria-label for the image, the natural thing to read aloud over a phone call ("does your screen say violin orbit tangerine?"), and the natural thing to show below the visual when you want a second channel.

brush swarm always
From: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq

Use it

Two reference implementations ship today: TypeScript for the web and Node, Swift for iOS and macOS. Both produce byte-identical output for the same input — you can mix and match across platforms.

TypeScript / JavaScript

npm install hallmarks
import { hallmark, hallmarkWords } from "hallmarks";

const node = hallmark("bc1qar0srrr7xfkvy...");
document.querySelector("#avatar").appendChild(node);

const aria = hallmarkWords("bc1qar0srrr7xfkvy...");
// → ["brush", "swarm", "always"]

Swift

// Package.swift
.package(url: "https://github.com/GBKS/hallmarks", from: "1.0.0")
import Hallmarks

Hallmark(input: "bc1qar0srrr7xfkvy...")
    .frame(width: 64)

Both implementations are reference implementations of the Hallmarks v1.0 specification. Conformance test vectors live in test-vectors.json — any new implementation that reproduces every vector exactly is conformant.

Background

Hallmarks grew out of Arké, an experimental Bitcoin and Ark wallet I've been building, where sending money to long, look-alike addresses needed a visual second channel. The first sketch lived inside the app; after a post arguing for a standard, it became its own thing — open spec, two reference implementations, and the site you're looking at.

Arké is in TestFlight if you'd like to see Hallmarks in real use, or to nudge the rough edges with me.

— Christoph Ono