Beersy
BRC-145

Registry-Free Typed Content Anchor with On-Chain Code Provenance

Committing to a piece of content along with what of thing it is usually means registering the type somewhere. This derives a single fixed-length from the type and content directly, so no registry is needed.

RexStarBSV20 min read
data+kinddomain prefixanchorik1 code

Summary

Why
A plain SHA-256 hash of content has no notion of type, no domain separation from other hash uses, and no built-in versioning, so two different kinds of data can collide, and there's no way to coordinate new content types without a central registry.
What
BRC-145 defines a registry-free scheme for hashing typed content into a single 32-byte "" (encoded as an ) and for publishing a of running software on chain.
How
You compute SHA-256 over a fixed domain string, a one-byte length prefix, a "kind" string you invent yourself, and the content bytes, then encode the result as an ik1 code or use the same construction with an adopter name to fingerprint a software build for on-chain publication.

What this lets you do

  • Fingerprint any content with a self-chosen type , no registry needed
  • Guarantee two different kinds never collide even over identical data
  • Encode an anchor as a compact, version-tagged ik1 text code
  • Render a deterministic visual seal from an anchor for at-a-glance tamper detection
  • Publish a build's tamper fingerprint on chain so users can verify running code matches the release

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

Reference for an AI

Everything an assistant needs to answer questions about BRC-145 accurately, including what it depends on.

The specification

Abstract

This BRC specifies a registry-free typed content anchor: a deterministic scheme for committing to typed content with a single 32-byte and no central registry. An anchor is SHA-256(INKAN_DOMAIN || u8(len(kind)) || kind || data): a fixed domain string separates these digests from every other SHA-256 usage, a one-byte length prefix makes the kind/data boundary unambiguous for all inputs, and the kind string is inside the digest so two anchors of different types never collide even when the underlying data is identical. Because the type is carried in the digest and the type namespace is content-derived, third parties assign their own and tiers ([namespace:]name[@t1|t2|t3]) without asking anyone, and the same kind renders and verifies identically on every device, forever. Anchors serialize to self-identifying ik1 codes whose version prefix keeps the construction hash-agile. The same primitive produces software/build provenance: a per-adopter over running code, published on-chain as an immutable data record or a 1-of-1 (1Sat) code-provenance ordinal that any client re-verifies against the build it is executing. This BRC defines the anchor construction, the code format, the open kind system, the tamper fingerprint, and the on-chain provenance record, in enough detail for an independent second implementation to interoperate byte-for-byte.

In plain terms: this standard describes a way to take any piece of digital content (an address, a document, a contract, a software release) and produce a short, permanent fingerprint for it that also records what kind of thing it is. Two different kinds of content can never share a fingerprint, even when their raw bytes are identical, and anyone can invent a new kind for their own application without asking a registry, a committee, or this document. The fingerprint writes out as a compact text code, renders as a deterministic visual seal that looks the same on every device forever, and can be published on the BSV chain as a permanent, timestamped record. The same mechanism lets a software project publish the fingerprint of a released build on chain, so any user can check that the code they are running is exactly the code that was released. The scheme is published as the Inkan Standard v1.0 (frozen) by RexStarBSV, with a complete Python reference implementation and JavaScript and Kotlin ports proven byte for byte against a shared vector suite for the layers each implements.

Motivation

Bitcoin SV applications routinely need to commit to a piece of content (an address, an , an account, a contract, a build artifact) in a way that is (a) deterministic across languages and devices, (b) typed, so a to one kind of thing cannot be confused with a commitment to another, and (c) coordinated without a registry, so any party can introduce a new type without a gatekeeper.

The existing BRC stack does not provide this primitive:

  • BRC-42 / BRC-43 (BSV key derivation, invoice numbering / ) namespace keys by protocol and that two parties must already share. They are a coordination convention for derivation, not a general typed content-commitment, and they presuppose the counterparties agree on the protocol string out of band.
  • BRC-100 () and its createAction give an application a way to write arbitrary bytes to the chain, but say nothing about how to canonically commit to typed content before writing it. Two apps that both "hash the thing and put it in an OP_RETURN" produce incompatible, type-blind, domain-unseparated digests.
  • Overlay-network discovery (topic managers / ) solves coordination by introducing exactly the registry this scheme is designed to avoid: a topic string must be recognized by an to be meaningful.
  • Visual-hash / identicon conventions (LifeHash, jdenticon, blockies) are presentation-only. They render a digest but define neither a typed, domain-separated digest nor a self-describing, version-tagged code, and none binds the type of the content into the commitment.

No existing BRC covers a registry-free typed content commitment or software/build provenance published on-chain. A bare SHA-256(content) has three defects this BRC fixes: it is not domain-separated (the same digest can mean anything, inviting cross-protocol confusion), it is not type-bound (a hash of an "address" equals a hash of the same bytes labelled "contract"), and it has no in-band version of the hash function (a future migration off SHA-256 is silent and dangerous). The scheme defined here closes all three while remaining a plain SHA-256 that any language computes with its standard library.

NotaryHash (Gregory Ward, SmartLedger; merged as BRC-220) and this proposal overlap in two ways: both anchor a content commitment on chain, and both derive that commitment from a deterministic length-prefixed binary encoding, so any language reproduces identical bytes. They answer different questions. NotaryHash standardizes a notarization : a signer proves, SPV-verifiable against alone, that they signed one specific SHA-256(content) at the block where it was mined, and the document itself never leaves the client. That is the right tool when what matters is who signed a given hash and when. This proposal standardizes a registry-free typed content commitment. The kind of the thing being committed to is folded into the digest by a fixed domain string and a one-byte length prefix, so a commitment to an address and a commitment to a contract over identical bytes produce different anchors that cannot collide, and no central authority allocates or arbitrates the type namespace. Around that anchor it adds a self-versioning code format that keeps the construction hash-agile, plus a per-adopter fingerprint used for running-code and build provenance. The two layers compose cleanly: a typed anchor computed here can itself be the hash a NotaryHash certificate signs. Cite NotaryHash for the bare signed-hash anchoring case; the contribution here is the typed-commitment and type-system layer above it.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as in RFC 2119.

1. Constants

All multi-byte integers below are big-endian unless stated otherwise. u8(n) denotes the single unsigned byte n (0 ≤ n ≤ 255). || denotes byte concatenation. Text is encoded to bytes with UTF-8 (RFC 3629). "NFC" denotes Unicode Normalization Form C (UAX #15). hex(x) denotes lower-case, zero-padded hexadecimal.

NameValueBytes (hex)Role
INKAN_DOMAINASCII "inkan/v1" + 0x0069 6e 6b 61 6e 2f 76 31 00glyph/anchor domain (9 bytes)
CODE_PREFIX"ik1"69 6b 31self-identifying code prefix
SHORT_CODE_LEN16n/ahex chars in a short code body (64 bits)
KIND_DOMAINASCII "inkan/kind/v1" + 0x0069 6e 6b 61 6e 2f 6b 69 6e 64 2f 76 31 00open-kind derivation domain (14 bytes)
FP_SUFFIX(v)"/inkan/v" + dec(v) + 0x00e.g. v=12f 69 6e 6b 61 6e 2f 76 31 00tamper-fingerprint domain suffix
MARKERASCII "INKANC1"49 4e 4b 41 4e 43 31on-chain signed-record marker (7 bytes)

Open-kind resolution constants (§4):

  • TIER_CELLS = { "t1": 72, "t2": 48, "t3": 24 }; default tier is t2.
  • KMAX = { 72: 2, 48: 4, 24: 7 }.
  • ORBIT_REPS = (0, 1, 3, 5, 7, 9, 11, 13, 15, 21, 23, 27, 31, 63), 14 rotation-distinct 6-bit frame representatives.

INKAN_DOMAIN, CODE_PREFIX, and KIND_DOMAIN are permanent. An implementation MUST NOT alter any of them; changing INKAN_DOMAIN or CODE_PREFIX invalidates every anchor ever computed and is a new version namespace, not a revision (§9).

2. The anchor

Given data (an octet string, or text) and kind (a string):

  1. If data is text, set data_bytes = utf8(NFC(data)). If data is raw octets, set data_bytes = data verbatim (raw bytes MUST NOT be normalized, they are not text).
  2. Set kind_bytes = utf8(NFC(kind)). The implementation MUST reject kind with len(kind_bytes) > 255.
  3. The glyph is:
seed = INKAN_DOMAIN || u8(len(kind_bytes)) || kind_bytes || data_bytes
  1. The anchor is SHA-256(seed), exactly 32 octets (FIPS 180-4).

The anchor is the sole basis for trust: it is what a receipt commits to, the input to every presentation, and the value re-derived during verification. Any visual or other presentation of an anchor is a convenience; the anchor, never the presentation, is trusted.

Why the length prefix (normative rationale). Without u8(len(kind_bytes)) between kind_bytes and data_bytes, the concatenation kind_bytes || data_bytes is ambiguous: kind="address", data="X" and kind="", data="addressX" produce the same trailing bytes and therefore the same anchor. The one-byte length prefix makes the boundary unambiguous for every possible (kind, data) pair (see Test Vector 5). An implementation MUST include it exactly as specified.

Why the type is in the digest (registry-free typing). Because kind_bytes is inside seed, an anchor for (data, kindA) and an anchor for the same data under kindB differ whenever kindA ≠ kindB. Type collisions are therefore cryptographically excluded rather than administratively prevented, so no allocation authority or registry is required to keep two adopters' types apart.

3. Codes (ik1)

An anchor serializes to a self-identifying ASCII string:

full = CODE_PREFIX || hex(anchor) # "ik1" + 64 hex chars = 67 chars
short = CODE_PREFIX || hex(anchor)[:16] # "ik1" + 16 hex chars = 19 chars
  • The literal ik marks an anchor code; the trailing digit is the code-format / hash version. 1 denotes SHA-256 over glyph domain inkan/v1.
  • The hex body of a code is a genuine prefix of the full anchor, so any code (including a short code) is auditable by recomputing the anchor and checking the prefix. The short code's 16 hex chars are 64 bits: collision-safe to billions of items and forgery-resistant, while remaining short enough for filenames and display. A short code is a only; trust decisions MUST use the full 32-byte anchor.
  • Version handling (MUST). A reader parsing a code MUST verify the version prefix. If a code begins with ik but not with a version this implementation supports (e.g. a future ik2), the reader MUST reject it rather than assume SHA-256. The reference parse is: reject if the string does not start with ik; reject if it does not start with the supported CODE_PREFIX; otherwise the digest hex is the remainder after CODE_PREFIX. This is what keeps the anchor hash-agile: a hash migration is a new prefix (ik2…), unambiguously distinguishable at parse time.

4. Kinds, open, tier-weighted, third-party assignable

A kind is any UTF-8 string of at most 255 bytes. By convention it is written [namespace:]name[@tier] (e.g. address, acme:invoice@t3, acme:vault@t1) but the anchor (§2) treats it as an opaque byte string, so any string is a valid kind. Namespacing (ns:) lets adopters avoid clashing without coordination; because the whole string is inside the digest, acme:invoice and beta:invoice are distinct anchors even over identical data.

Presentations (the visual seal, §5) require a kind to resolve to geometry parameters (cells, k, motif), where cells ∈ {24, 48, 72} is the tier's cell count, k ∈ 1..7 is a rotational-symmetry order, and motif is a 6-bit frame code. Resolution is:

4.1 Reserved kinds. If kind is a key of the reserved table below, use its curated entry directly:

kindcellskmotif
"" (empty / default)48263
identity48456
account48336
address72151
vault72161
signer72254
contract72260
code24742
report24644
collection24548
backup24432
custom/2424152
custom/4848152
custom/7272152

4.2 Derivation (any other kind). If kind is not reserved, derive (cells, k, motif) deterministically:

  1. Tier. If kind ends with the literal suffix @t1, @t2, or @t3, that selects the tier; otherwise the tier defaults to t2. (The reference tests the suffixes in the order t1, t2, t3 and takes the first match.) cells = TIER_CELLS[tier].
  2. Kind hash. kh = SHA-256(KIND_DOMAIN || utf8(kind)), where kind is the kind string as provided (see the normalization below).
  3. Symmetry. k = 1 + (kh[0] mod KMAX[cells]).
  4. Frame motif. motif = ORBIT_REPS[ kh[1] mod 14 ].

Because the tier is part of the kind string and the kind is part of the anchor, the same kind renders identically everywhere and namespaced kinds never collide between adopters, no registry is needed for either the anchor or the presentation.

Normalization note (MUST-for-interop). The anchor (§2) NFC-normalizes kind before hashing; the reference presentation-derivation in §4.2 hashes the kind bytes as provided (no explicit NFC step) and performs the reserved-table lookup and suffix test on the string as provided. For an ASCII kind (all reserved kinds are ASCII) the two layers are identical. To keep the anchor and the presentation consistent for non-ASCII kinds, callers SHOULD pass kinds already in NFC. An implementation aiming for byte-exact parity with the reference presentation MUST NOT insert an NFC step into §4.2.

Tiers and exposure. Tiers t1/t2/t3 map to cell profiles 72/48/24. More cells means more visible entropy and a costlier forgery (§ Security Considerations); value-at-risk kinds (e.g. address, vault) SHOULD use t1. The resolution parameters affect only the presentation; the anchor never depends on (cells, k, motif), it depends only on the exact kind string. Two distinct kind strings that happen to resolve to the same (cells, k, motif) still produce distinct anchors.

5. Deterministic presentation (the seal)

An anchor MAY be rendered to a deterministic visual seal so a human notices when a trusted value changes. The seal is optional to this BRC's interoperability core (anchor + code + kind, §2-§4). Its full pixel-level construction is out of scope here; an implementation that renders seals MUST match the reference conformance vectors byte-for-byte at the RGB level. The seal's normative contract is:

  • Deterministic and float-free. Geometry and palette are integer tables, so all implementations agree byte-for-byte by construction, not by luck. Seal renderer version is SEAL_VERSION = 3.
  • The kind selects symmetry and frame. (cells, k, motif) from §4 fix the rotational symmetry order and the 6-bit frame, so a seal of one kind cannot be confused with a seal of another.
  • A visible distance floor. Interior cells carry 2 bits each from bit-interleaved extended Golay [24,12,8] blocks; any two distinct seals of the same profile differ in at least 8 cells (Golay minimum weight), a floor verified tight over all 4096 code words.
  • Byte-deterministic PNG. The canonical seal is an 8-bit indexed PNG whose chunks are exactly IHDR, PLTE, IDAT, IEND, with no ancillary chunks. The cross-language invariant is the decoded RGB pixel content, not the compressed bytes (deflate output is per-implementation). A tagged export additionally embeds iTXt chunks (keyword inkan-code carrying the full ik1 code, keyword inkan-kind carrying the kind) making the image self-describing. The embedded code is an untrusted hint: a verifier MUST recompute the anchor and compare pixels, treating the tag only as a cross-check.

iTXt body layout used by the reference tagged export, for a keyword and UTF-8 text:

keyword(latin-1) || 0x00 || 0x00(comp flag) || 0x00(comp method) || 0x00(empty lang) || 0x00(empty translated keyword) || utf8(text)

Verification of a presented seal. To verify a seal claimed to be (data, kind): recompute the anchor and the canonical seal; decode the presented PNG to native RGB; if it is an exact integer multiple of the canonical size, integer-downscale it, otherwise reject; compare pixel-for-pixel. Result is GENUINE iff equal. Re-encoding and integer upscales are tolerated; a resampled, cropped, or otherwise different image is rejected. Verification MUST NOT trust embedded metadata.

6. Tamper fingerprint (build / running-code provenance digest)

The per-adopter tamper fingerprint of an octet payload (a build's bytes, an asset, a config) is:

domain_prefix = u8(len(adopter_bytes)) || adopter_bytes || FP_SUFFIX(version)
 = u8(len(adopter_bytes)) || adopter_bytes || "/inkan/v" || dec(version) || 0x00
fingerprint = SHA-256(domain_prefix || payload)

where adopter_bytes = utf8(NFC(adopter)), len(adopter_bytes) ≤ 255 (reject otherwise), and version defaults to 1. fingerprint_hex is the lower-case hex of the 32-byte digest.

The length-prefixed, per-adopter domain namespaces each project: identical payloads yield distinct fingerprints across adopters while every adopter shares the same universally verifiable scheme, and no adopter needs to register its name. A one-byte change in payload changes the whole digest (avalanche). This fingerprint is the digest used for software/build provenance: the fingerprint of the running build.

7. On-chain provenance

An anchor or a fingerprint (§2, §6) MAY be committed to the chain to obtain an immutable, timestamped, non-retractable record. This BRC defines two on-chain forms; both are self-verifying (a client re-derives everything offline and trusts the chain only for immutability and time-ordering).

7.1 Signed-record data output (reference OP_RETURN form). A signed statement (the reference case is a warrant/compromise canary, but the container is general) is carried as a data output whose is:

OP_RETURN
push( MARKER) # "INKANC1", 7 bytes
push( canonical_record_bytes) # §7.2
push( der_signature) # secp256k1 ECDSA, DER
push( public_key) # 33-byte compressed secp256k1 point

Each push(blob) is minimal Bitcoin script pushdata: for n = len(blob),

  • n < 0x4Cu8(n) || blob
  • 0x4C ≤ n ≤ 0xFF0x4C || u8(n) || blob
  • 0x100 ≤ n ≤ 0xFFFF0x4D || u16le(n) || blob
  • otherwise → 0x4E || u32le(n) || blob

A reader parses the script by matching OP_RETURN then reading four pushes in order; it MUST reject the output if the first push is not exactly MARKER or if any push is truncated. Everything a verifier needs (the record, the signature, and the public key) is on-chain, so the output is checked entirely offline (§7.3). The reader MUST NOT trust the parsed bytes; it re-verifies them.

7.2 Canonical record and signature. A signed record is a JSON object serialized canonically: keys sorted lexicographically, no insignificant whitespace (separators , and :), UTF-8, non-ASCII left un-escaped. The reference canary record has fields:

fieldtypemeaning
vintrecord version (1)
domainstringoperator/publisher identifier
seqintmonotonic sequence number
datestringYYYY-MM-DDTHH:MM:SSZ (UTC)
block_heightinta recent chain-tip height (anti-backdate proof)
block_hashhex stringthat tip's block hash
statementstringthe human-readable assertion

The message to sign/verify is SHA-256(canonical_record_bytes). The signature scheme is ECDSA, DER-encoded, deterministic per RFC 6979, low-S. A signature-algorithm identifier is reserved for a future post-quantum migration. The block_height/block_hash pair binds the record to a chain tip so it cannot have been pre-signed before that block existed.

7.3 Status. A verifier reports exactly one of:

  • INVALID, the signature does not verify against the published key over SHA-256(canonical_record_bytes).
  • STALE, signature valid but the record is outside the freshness window: with now, max_age_days (reference default 35), and date parsed as UTC, the record is fresh iff -86400 ≤ (now − date_seconds) ≤ max_age_days · 86400 (i.e. not expired and not implausibly future-dated by more than one day).
  • OK, signature valid and fresh.

For a canary, absence, STALE, or INVALID is itself the warning.

7.4 1-of-1 ordinal form (code-provenance ordinal). A build's provenance MAY instead be published as a 1-of-1 (1Sat) ordinal whose inscription content is the canonical tagged seal PNG (§5) of the build's fingerprint (§6). The iTXt inkan-code / inkan-kind tags make the inscription self-describing; the ordinal's single-satoshi custody model conveys and transfers the mark. The inscription envelope and transfer follow the 1Sat Ordinals convention (referenced, not redefined here). A client verifies a running build by recomputing the build's fingerprint, forming its ik1 code, and comparing against the on-chain code-provenance ordinal for that release; the displayed seal is the on-chain-committed mark, so a tampered build cannot reproduce it.

7.5 Broadcast interoperability (BRC-100, informative). The reference publishes §7.1 outputs through a BRC-100 wallet: it runs getVersion → getNetwork → isAuthenticated → getPublicKey → createAction, funding a single OP_RETURN output (satoshis: 0) carrying the §7.1 script. The wallet only funds and broadcasts; it never signs the record (the operator signs offline with the bare secp256k1 key), so verification is never coupled to a wallet. An implementation MAY use any broadcast path; the on-chain bytes (§7.1) are what matters. Implementations that broadcast SHOULD default to a test network and require explicit operator opt-in for mainnet, so real funds are the last step, never the first.

8. Verification requirements (summary)

  • An anchor is re-derived, never received: a verifier presented with (data, kind) and a claimed code/seal MUST recompute SHA-256(seed) per §2 and compare, and MUST NOT trust any embedded code or metadata.
  • A code MUST be version-checked before its body is interpreted as a SHA-256 digest (§3).
  • An on-chain record MUST be re-verified offline (signature + freshness) after parsing; the chain is trusted only for immutability and timestamp ordering (§7).

9. Versioning and parity

  • Anchor (inkan/v1, SHA-256, code ik1): permanent. A new hash is a new version namespace (ik2…), not a revision. Changing the anchor domain invalidates every anchor ever computed.
  • Presentation (SEAL_VERSION): swappable without touching any anchor. Swapping a presentation never invalidates an anchor, the property that lets an on-chain-anchored digest outlive any renderer. Two surfaces that compare seals MUST share SEAL_VERSION.
  • Fingerprint domain (FP_SUFFIX version): per-adopter and independently versioned.

Test Vectors

All values below are produced by the reference implementation and are reproducible with a SHA-256 and UTF-8 alone (except the ECDSA signature in TV7).

TV1, anchor of text, default kind

data = "Hello" (text) kind = ""
seed (hex) = 696e6b616e2f7631000048656c6c6f
 = INKAN_DOMAIN || 00 (len kind = 0) || "Hello"
anchor = ae63a28a7b3fbae640f24ab4003d285a453e5493b89741326a7fbcc36a2a6a44
code = ik1ae63a28a7b3fbae640f24ab4003d285a453e5493b89741326a7fbcc36a2a6a44
short code = ik1ae63a28a7b3fbae6
resolve_kind("") = (cells=48, k=2, motif=63) [reserved]

TV2, anchor of a typed address (reserved kind address)

data = "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2" kind = "address"
seed (hex) = 696e6b616e2f7631000761646472657373314276424d53455973745765747154466e354175346d3447466737784a614e564e32
 = INKAN_DOMAIN || 07 (len "address") || "address" || "1BvB…VN2"
anchor = 12cf1aaca7ef50793d990e94c9ae889af6f9e8fdfc28b2f9a72d4028bde05dcd
code = ik112cf1aaca7ef50793d990e94c9ae889af6f9e8fdfc28b2f9a72d4028bde05dcd
resolve_kind("address") = (cells=72, k=1, motif=51) [reserved, tier t1]

TV3, open-kind derivation (no registry): acme:invoice@t3

kind = "acme:invoice@t3" (not reserved)
tier = t3 (suffix "@t3") -> cells = 24
kh = SHA-256( "inkan/kind/v1\0" || "acme:invoice@t3")
kh[0] = 137, kh[1] = 93
k = 1 + (137 mod KMAX[24]=7) = 1 + 4 = 5
motif = ORBIT_REPS[ 93 mod 14 = 9 ] = 21
resolve_kind("acme:invoice@t3") = (cells=24, k=5, motif=21)

This resolves identically on any device with no allocation authority, demonstrating the registry-free kind system. (myapp:thing(48, 4, 11) and acme:vault@t1(72, 2, 27) resolve the same way.)

TV4, NFC normalization stability

kind entered as "café" with é = U+00E9 (NFC)
kind entered as "café" with e + U+0301 (NFD)
data = "x"
Both anchors = ik188858a890fa01bf5a463e0e4ca0181d18fe261f34799fe5c5d0ab76e0647f6af (equal)

Derived kind "café@t2" (NFC): utf8 = 63 61 66 c3 a9 40 74 32 (8 bytes)
resolve_kind("café@t2") = (cells=48, k=3, motif=1)

TV5, length-prefix collision resistance

kind = "address", data = "X" -> ik1b49a32c4d99c6b80b331223166711da0a91105d1aea989430b8dc22e073c97fd
kind = "", data = "addressX" -> ik13780ad6190697cc754df1159766f9c9781aa610529d0654a197a9a31c691a7f7
distinct: TRUE

Without the u8(len(kind)) prefix both would hash … "addressX" and collide. The length prefix (07 vs 00) makes the boundary unambiguous.

TV6, tamper fingerprint / build provenance (per-adopter namespacing)

domain_prefix("acme", v=1) = 04 61636d65 2f696e6b616e2f7631 00
 = 0461636d652f696e6b616e2f763100
fingerprint("build-bytes-v1", adopter="acme") = d88657e80e283d5a44bd29b83cf8a93ec60729ec635e642b5454b8829bd3e3c6
fingerprint("build-bytes-v1", adopter="otherapp") = 1847961553e04828204efb7322f1763b8ade6faa14f6f78e30b43957a0c24ab1

Same payload, different adopter → different digest, with no registration.

TV7, on-chain signed record and OP_RETURN script (§7)

record (fields) = { v:1, domain:"acme", seq:7, date:"2026-06-30T00:00:00Z",
 block_height:870000,
 block_hash:"00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff",
 statement:"No warrant, gag, key compromise or coercion." }

canonical_record_bytes (222 bytes, keys sorted, no whitespace) =
 {"block_hash":"00ff...00ff","block_height":870000,"date":"2026-06-30T00:00:00Z",
 "domain":"acme","seq":7,"statement":"No warrant, gag, key compromise or coercion.","v":1}
message_hash = SHA-256(canonical_record_bytes)
 = 7b0991a1ab13b4b17384e1c6acd0017edb77bd563cdea59cdc776993d8c99362

pubkey = 031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f
der_sig = 3045022100d336085483c2dea86794ef3404d7dc7beef56368fd380000e4d0d8f3ac3fe50c
 02200a4fff49ba1173d1b66f8d4a335cb587c221db79280df23fc2b495b081c65e11
verify(record, der_sig, pubkey) = true (secp256k1 ECDSA over message_hash)

OP_RETURN script (339 bytes) begins:
 6a OP_RETURN
 07 494e4b414e4331 push "INKANC1"
 4c de 7b22626c6f636b5f68617368... push canonical_record_bytes (0xde = 222 bytes)
... push der_sig, push pubkey

Round-tripping the script recovers record, der_sig, and pubkey for offline re-verification. The keys in this vector are deterministic test values; production issuer keys and the first live record are minted at launch and never appear in test material.

Implementations

The reference implementation is Inkan, an offering by RexStarBSV: a frozen v1.0 standard carrying three reference implementations and a pinned conformance-vector suite, from which every value in the Test Vectors above is reproduced.

  • Reference implementation (Python): seed/anchor and code construction (inkan_digest, inkan_code, inkan_short_code, code_to_digest_hex), open-kind resolution and the Seal v3 renderer (resolve_kind, seal_rgb, seal_png, reserved KINDS table and derivation constants), the tamper fingerprint (domain_prefix, fingerprint_digest, fingerprint_hex), the signed-record/canary module (canonical_bytes, message_hash, sign, verify, status), and the on-chain anchoring module (build_canary_script, parse_canary_script, and a BRC-100 createAction broadcast path with a testnet default guard). Stdlib-only for everything except secp256k1 signing/verification.
  • JavaScript port: a dependency-free, synchronous, browser-and-Node port of the anchor, code, kind resolution, seal renderer, and fingerprint, proven byte-for-byte against the Python reference by the shared vectors.
  • Kotlin port: the mobile/desktop rendering path, likewise held to the shared vectors.
  • Conformance vectors: a fingerprint/canary/glyph vector set and a Seal v3 vector set (sampled fields, RGB SHA-256s, and constructed minimum-distance pairs) let any implementation prove parity. An implementation conforms iff it reproduces both.
  • Standalone verifier and creator, offered by RexStarBSV: a command-line verifier with seal, fingerprint, canary, and selftest subcommands (the verifier reproduces the pinned vectors before it trusts itself), plus creation of anchors, codes, and seals from any (data, kind) pair through the same package.

The reference sources and the full conformance-vector suite are maintained by RexStarBSV, with public release of the Inkan Standard forthcoming; this BRC is written so that a second, independent implementation can be built from the Specification and Test Vectors above without access to them. The optional seal presentation layer (§5) additionally requires the released conformance vectors for byte-level parity; the interoperability core (§2-§4, §6-§7) is fully specified here.

Mathematical and cryptographic basis

The commitment folds the type into the digest. The anchor is SHA-256 over a fixed, versioned domain string terminated by a zero byte, then the length-prefixed kind, then the content (§2). Because the kind sits inside the preimage, two records of different kinds coincide only if SHA-256 itself collides. The length prefix makes the concatenation injective: given the fixed domain, there is exactly one way to split a preimage back into (kind, content), so no two distinct field assignments produce the same bytes. This is what removes the need for a central type registry, since type distinctness is a property of the hash rather than of a lookup table. Authenticity of the anchoring party, where a use requires it, is a separate signature over the same digest and does not change the commitment.

Security Considerations

  • The anchor is the trust root; the image is not. Every verifier MUST re-derive the anchor from (data, kind) and MUST NOT trust a presented code, seal, or embedded metadata. A phishing lookalike fails because it is not the byte-exact render of the recomputed anchor.
  • Domain separation and length prefixing. The fixed INKAN_DOMAIN prevents an anchor digest from being mistaken for, or manufactured from, any other SHA-256 usage. The u8(len(kind)) prefix (§2) and the u8(len(adopter)) prefix (§6) each close a boundary-ambiguity collision class; both MUST be implemented exactly, as an implementation that drops either re-opens a real collision (TV5).
  • Type binding without a registry. Because kind is inside the digest, cross-type confusion is cryptographically excluded, not administratively prevented. This removes the registry as both a coordination burden and a single point of failure/censorship, but it also means there is no authority to revoke or arbitrate a namespace; adopters MUST choose namespaces they control (e.g. a domain-like ns:).
  • Hash agility. The ik1 version prefix makes a future migration off SHA-256 explicit and non-silent. A reader that assumes SHA-256 for an unrecognized version (e.g. ik2) would mis-verify; readers MUST reject unknown versions (§3).
  • Seal forgery economics. Visible entropy scales with tier: a perfect visual collision costs on the order of 2^16 / 2^28 / 2^40 renders (birthday) for t3 / t2 / t1. Low-tier seals therefore have findable perfect collisions by design (value-at-risk kinds SHOULD use t1, and in all cases the 32-byte anchor, not the image, is the trust root). The 6-bit frame yields 14 distinct frames per configuration; beyond that, open kinds may share a frame. This is defence-in-depth, not a break, since different kinds over the same data always differ in the interior and always differ in the anchor.
  • Honest perceptual limits. All perceptual-distance figures are model outputs, not measurements of people; no human study has been run, so no accessibility or "phishing-proof" claim is made. Implementers MUST NOT market a seal as human-proof.
  • On-chain records are self-verifying and non-retractable. A signed record (§7) carries its own record, signature, and public key, so it is checked entirely offline; the chain is trusted only for immutability and timestamp ordering. The block_height/block_hash binding is an anti-backdate proof. A verifier MUST treat absence, STALE, or INVALID as the warning (fail-closed), and MUST NOT trust the parsed on-chain bytes without re-verifying the signature and freshness. INVALID means a signature check was performed and failed; a verifier whose signature backend is unavailable MUST raise an explicit error and MUST NOT report INVALID or OK, since a fabricated is a silent false alarm in one direction and a silent pass in the other.
  • Key custody. The signing key MUST be the operator's own key held offline; the broadcast wallet (§7.5) funds but never signs, so a compromised or coerced broadcast path cannot forge a record. The test-network default (§7.5) keeps real funds as the last step, never the first.
  • Metadata hygiene / privacy. The canonical seal and the fingerprint image writers emit no ancillary PNG chunks (no author, software, timestamp, or physical-dimension metadata). Implementations MUST preserve this and MUST NOT embed identifying information (public key, fingerprint, name, , timestamp) in a seal's filename; a seal image should reveal nothing about the machine, build, time, or subject beyond the anchor itself. The optional iTXt tags carry only the (already public) ik1 code and kind, and remain untrusted hints.
  • Presentation/anchor consistency for non-ASCII kinds. The anchor NFC-normalizes kind while the reference presentation derivation does not (§4.2 note); callers SHOULD pass NFC kinds so both layers agree.

References

  • FIPS 180-4, Secure Hash Standard (SHA-256).
  • RFC 2119, Key words for requirement levels.
  • RFC 3629, UTF-8.
  • Unicode Standard Annex #15, Unicode Normalization Forms (NFC).
  • RFC 6979, Deterministic ECDSA.
  • SEC 2 / secp256k1, the ECDSA curve used for on-chain signed records.
  • BRC-42, (protocol/key-ID namespacing contrast).
  • BRC-43, , Protocol IDs, Key IDs, and Counterparties.
  • BRC-100, Wallet-to-Application interface (createAction broadcast path used by the optional on-chain anchoring).
  • BRC-220, NotaryHash, privacy-preserving signed-hash notarization; the signed-hash counterpart this typed commitment composes with.
  • 1Sat Ordinals, 1-of-1 ordinal inscription convention (used, not redefined, by §7.4).
  • Golay, M. J. E., Notes on digital coding (extended Golay [24,12,8], the seal's distance floor).
  • The Inkan Standard, version 1.0 (frozen), by RexStarBSV, and its conformance vectors, from which this BRC is drawn.
Was this helpful?

Search Beersy

Search standards by number, title, author or topic