Sign in
Open navigation

← Home

Keepsake protocol

Open specification for portable process provenance. Anyone can check offline — no account, no API key, no permission.

← Developers · Online verify · Offline HTML verifier

What a Keepsake documents

A Keepsake documents that process events and final delivery fingerprints were sealed through Yubikiri — either a completed commission room (claimType: commission.process) or solo work (claimType: session.process). It is process provenance. The full limits are set out in Disclaimers.

Versions

Canonical JSON

All signed payloads use deep key-sorted JSON: recursively sort object keys, then JSON.stringify with no extra whitespace. Arrays keep order. This is identical to stableStringify in the open-source cores.

Signed Keepsake manifest

Document shape: { claim, alg, signature, keyId?, signatures? }. Primary signature is Ed25519 over the canonical claim. An optional HS256 HMAC may also be attached for platform checks; offline verifiers only need Ed25519.

Public keys: /.well-known/yubikiri-keys.json. Signature encoding is unpadded base64url. Verify with the SPKI public key matching keyId.

claim.claimType = "commission.process"
claim.id          = "yk_ks_…"
claim.tipEventHash / tipChainIndex / eventCount / chainRoot
claim.finalAssets[] = { assetId, sha256, contentCredentialId, fileName }
claim.verifyUrl   = https://yubikiri.art/verify?keepsake=yk_ks_…
claim.disclaimer  = process-provenance disclaimer (verbatim)

Activity hash chain

Each activity event commits to the previous tip. Event hash = SHA-256 of the canonical body:

{
  "v": 1,
  "actorId": string | null,
  "chainIndex": number,
  "commissionId": string,
  "message": string,
  "meta": unknown | null,
  "prevEventHash": string,  // genesis for index 0
  "type": string
}

chainRoot = SHA-256 of the newline-joined ordered list of event hashes (hex). The sealed tip is the chain state at mint time; later KEEPSAKE_SEALED activity may extend the live chain without invalidating the sealed tip.

Transparency log (Merkle)

Daily trees use RFC 6962-style domain separation: leaf nodes hash 0x00 || documentHash; internal nodes hash 0x01 || left || right (raw bytes). Odd levels duplicate the last node. Document hash = SHA-256 of the canonical signed keepsake document (keepsakeDocumentHash).

Directory: /.well-known/yubikiri-log.json. Day records: GET /api/keepsake-log/YYYY-MM-DD and /api/keepsake-log/latest. Entries are { logIndex, keepsakeId, leafHash } — hashes only, no client PII.

Room archive proof bundle

keepsake/manifest.json          # signed keepsake
keepsake/activity-chain.json    # sealed tip + event fields
keepsake/inclusion-proof.json   # anchored path or pending_anchor
keepsake/verify.html            # single-file offline verifier

Online APIs (free)

Keepsake Seal embeds

Artists copy one HTML snippet under any posted final. The SVG lives at /api/embed/seal/yk_cc_… and should link to /verify?credential=yk_cc_…. Every seal on Carrd, X, or VGen is a crawlable backlink. The off-platform name is “Yubikiri Keepsake Seal.” No client names, prices, or file bytes.

<a href="https://yubikiri.art/verify?credential=yk_cc_...">
  <img src="https://yubikiri.art/api/embed/seal/yk_cc_..." width="320" height="72" alt="Yubikiri Keepsake Seal" />
</a>

Keepsake records export

Authenticated GET /api/kept-passport returns yubikiri.kept.v1 JSON with asArtist and asClient sealed rooms (Keepsake ids, final-file ids, making-of URLs when consented). The API name is retained for compatibility; the UI calls this “Keepsake records.”

Offline tools

Open /keepsake-verify.html and drop a room archive ZIP or the keepsake/ JSON files. Or from a checkout of this repo:

npm run keepsake:verify -- ./path/to/room-archive.zip
npm run keepsake:verify -- ./path/to/keepsake-dir --keys https://yubikiri.art/.well-known/yubikiri-keys.json

Claims discipline

Verification being free is permanent product policy. Never put Keepsake lookup behind a paywall. Monetization belongs around the record (workspace, payments, studio tools) — not on checking the math.