BRC-216 Applications

Dead-Man Timed Reveal of a Sealed Secret

This BRC defines a dead-man switch: a way for an owner to seal a document so that it stays private while the owner is active and becomes public if the owner stops checking in. The owner encrypts the document under a data key, splits that key into n shares with a threshold m ([SSS]), and commits the shares in a Merkle tree whose root is root . The owner arms

RexStarBSV (RexStarBSV@protonmail.com)

How it connects

Relationships

Authoritative content mirror

Full upstream text

View source ↗
Rendered from apps/0216.md at the indexed upstream commit

Abstract

This BRC defines a dead-man switch: a way for an owner to seal a document so that it stays private while the owner is active and becomes public if the owner stops checking in. The owner encrypts the document under a data key, splits that key into n shares with a threshold m ([SSS]), and commits the shares in a Merkle tree whose root is root. The owner arms the switch on-chain by publishing the encrypted document, a set of k check-in outputs at addresses derived from one base key P, and one watched outpoint F. Each check-in output has a cutoff height; the owner stays alive by spending it before that height. If the owner misses a cutoff, the holders of the shares reconstruct the data key and publish a release: an output carrying one share, its Merkle path, and a sealed pointer to the document, under a 137-byte header that binds root to P with a signature only the owner could make. A reader that collects m releases reconstructs the data key, decrypts the document, and reads it. A single reserved marker makes releases discoverable, and a reader ranks them by the capital locked toward them, treated as advisory because the marker is permissionless. The disclosure is gated by the holders, not by any script: the firing rule, the quorum that decides a cutoff has passed, the authentication a reader performs, and the bounds on everything a reader parses from the chain are all specified here, because each is a place where a careless implementation discloses too early, fails to disclose at all, or trusts a value an attacker chose.

Motivation

A person who holds information that should come out if they are silenced, an estate that should open if its owner dies, a document that should survive the person who wrote it: each needs a secret that stays sealed while a hand is on it and opens when the hand is lifted. A plain encrypted file does not do this, because someone has to hold the key, and whoever holds the key can open it at any time or lose it. Splitting the key among several holders with a threshold spreads that trust, and tying the opening to an on-chain signal that the owner has stopped acting turns "someone decided to open it" into "the owner stopped saying not to".

Building this correctly is harder than it looks, and every error is silent until the day it matters. There are five classes of error this BRC exists to rule out.

  1. Disclosing too early. The opening is a decision the holders make by watching the chain. If a holder reads a provider that is only caught up to last week and treats "no spend seen" as "the owner missed the cutoff", it releases a live person's secret. The quorum that decides a cutoff has passed has to measure how far each source's address history is actually complete, and compare that against the holder's own proof-of-work tip, before it may ever conclude a check-in was missed (section 8).

  2. Never disclosing. The opposite failure is a release that the holder believes it published but that no node ever accepted, or a firing rule that waits for the final deadline before it will act on an earlier missed check-in. A holder has to tell apart a release it has merely recorded from one a node has acknowledged from one a block has confirmed, and it has to judge each cutoff on its own, not behind the last one (sections 7 and 9).

  3. Opening the wrong thing. The data key becomes public the moment a reveal is meant to happen, so the Merkle root over the shares, the shares themselves, their paths, and the key can all be re-packaged by a stranger into a different header pointing at different content. "The first release of a root wins" is not an authenticity rule. A reader has to bind the released document to a signature the owner made over root, under the key the owner used to arm the switch, and has to treat a root that appears under two different commitments as disputed rather than silently electing a winner by block height (section 10).

  4. Trusting attacker-supplied bytes. Everything a reader pulls off the chain here, the clear metadata, the release outputs, the discovery feed, was written by whoever paid the fee, which is anyone. A title of unbounded length, a category outside the fixed set, a transaction listed in the feed that no server will serve: each is an input a reader has to bound and validate on read, not at creation (sections 11 and 12).

  5. Linking the owner to the switch. The switch's whole privacy rests on its on-chain footprint being indistinguishable from ordinary activity before a reveal, and on the owner's own coins never being spent into the switch's machinery in a way that links them. That has to hold on every coin-selection path, and the check-in keys have to be derived so that their addresses are unlinkable to the base key and so that one nonce reuse cannot compromise every key at once (sections 5 and 7).

A single specified format, with the firing rule and the reader's authentication fixed to the byte, lets two implementations agree on when a switch has fired, on whether a release is genuine, and on what a reader may show, so that a holder in one program and a reader in another reach the same verdict about the same switch.

Scope and conformance

Section 1 defines terms. Section 2 states the trust model and what each party learns. Sections 3 to 6 specify the sealed secret and the arming transaction. Sections 7 to 9 specify the owner's upkeep, the firing rule, and the release. Sections 10 and 11 specify how a reader reconstructs, authenticates, discovers, and ranks. Sections 12 to 14 specify the clear metadata bounds, the owner-to-holder message encoding, and the reserved markers. Section 15 lists every requirement by role.

Out of scope are how the owner recruits and reaches the holders, how a holder is told to watch a switch, the transport that carries a share to a holder or a release instruction back, the fee rate, and the user interface. The wire bytes, the firing rule, and the reader's checks are in scope, because those are what two implementations must share.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in capitals. Four roles are used. An owner seals a document and arms, maintains, cancels or withdraws a switch. A holder keeps one share and, after a cutoff is missed, publishes a release. A reader collects releases, reconstructs, authenticates and displays a document, and reads the discovery feed; a reader is a wallet's registry view or a standalone page, and the two MUST reach the same verdict. A wallet is any implementation that builds transactions on behalf of third-party applications or agents.

Relationship to existing standards

The companion deadline-gate covenant

The companion BRC-215 (block-height deadline covenant templates, OP_PUSH_TX) gates a spend so that it cannot be mined before a height. This BRC gates a disclosure, and it does so off-chain: its secret is released by the holders when they decide a cutoff has passed, rather than unlocked by a script. The two are independent.

Key derivation and signatures

Check-in keys are derived from a base key P by additive tweaks on secp256k1, in the manner of child-key derivation ([BRC-42] uses the same group operation with a different tweak input). Signatures are ECDSA over secp256k1, DER-encoded, in canonical low-S form, and a signer MUST produce the nonce deterministically ([RFC6979]); section 5 states why a randomized nonce is catastrophic here and not merely discouraged.

Hashing, encryption and encoding

Hashes are SHA-256 ([FIPS180-4]), single unless stated. Documents and the sealed pointer are encrypted with AES-256-GCM ([SP800-38D]). Owner-to-holder messages are JSON ([RFC8259]). The secret is split with Shamir's scheme over GF(2^8) ([SSS]).

Discovery

A release is found by the script hash of a fixed marker output, the same mechanism an [ElectrumX]-style index exposes for any output script. The marker is permissionless; section 11 states what a reader may and may not conclude from it.

Specification

1. Terminology

  • Data key: a 32-byte key. The document is encrypted under it; the shares reconstruct it.
  • Share: one Shamir share, a 33-byte string x || y, where x is one byte in 1..255 and y is 32 bytes. With threshold m = 1 the single share's y is the data key itself (section 3).
  • root: the 32-byte Merkle root over the shares (section 3).
  • key_commitment: a 32-byte hash binding root, the data key and the sealed pointer (section 3).
  • P: the 33-byte compressed base public key for the check-in outputs; p its private half, the fuse master key.
  • Fuse: one check-in output, at a P2PKH address derived from P and an index k (section 5). "Fuse" and "check-in output" are the same thing.
  • F: the watched outpoint. Spending F re-encodes or cancels the switch; the owner watches it for re-encoding and so does a reader (sections 7 and 10).
  • Cutoff cutoff(j): the block height by which the owner must have spent fuse j. cutoff(j) = first_cutoff + j * interval.
  • Release: the on-chain output that carries one share, its Merkle path, the header, and the sealed pointer, so a reader can reconstruct (section 9).
  • CONFIRM_DEPTH: 3. The number of blocks a height-dependent fact must be buried under before any party acts on it.
  • TIP_AHEAD: 6. How far past a cutoff a source's history must reach before it counts as a witness that a fuse is unspent (section 8).
  • MIN_SOURCES: 2. The least number of independent archival sources an unspent verdict needs (section 8).

Hex strings below are byte strings in the order they appear on the wire. ASCII strings in b"..." are their UTF-8 bytes.

2. The trust model

The disclosure is not gated by any script or key held back from the holders. Any coalition of m holders can reconstruct the data key and read the document at any moment, before any cutoff, because the shares are the only secret and the holders already have them. The on-chain schedule tells an honest holder when it is meant to release; it does not stop a dishonest coalition of m from releasing early, and it cannot. An owner MUST choose m and the holders accordingly, and an implementation MUST present this plainly rather than implying the chain enforces the timing.

The case m = 1 is the sharp edge: a single holder can open the secret alone, and that holder's share is the data key verbatim, so even passive leakage of that one share discloses the document. An implementation MUST restrict m = 1 to a holder the owner controls, such as the owner's own second device, and SHOULD require an explicit, logged acknowledgement from the owner before arming with m = 1, and SHOULD warn for low m generally.

What a holder learns is bounded on purpose. A holder is given its share and a watch spec (section 4): the outpoint F, the base key P, the fuse index k, and the schedule. A holder is not told m, n, the number of other holders, the document length, or the owner's identity. The sealed pointer a holder carries in a release stays sealed until the data key is reconstructed, so a holder cannot read the document, or even locate it, from what it holds.

3. The sealed secret

3.1 Shares

The data key is split with Shamir's scheme over GF(2^8) with the AES field polynomial 0x11b, threshold m, count n, with 1 <= m <= n <= 32. Each share is x || y: x a byte in 1..255, y 32 bytes, one GF(2^8) interpolation per key byte. Reconstruction interpolates m shares at x = 0.

Shamir reconstruction over this field has no integrity of its own: a wrong, foreign, or insufficient set of shares interpolates to some 32-byte value with no error raised, and a single crafted share lets an attacker steer the result to a key of their choosing. Authentication of a reconstructed key is therefore never the reconstruction itself; it is key_commitment and the Merkle path together (section 10). With m = 1 the interpolation is the share's y unchanged, so the one share is the data key.

3.2 The Merkle root

The shares are the leaves of a binary Merkle tree with a fixed 32 leaves. The n real shares are placed among 32 - n random 33-byte leaves and the 32 leaves are shuffled, so the tree's shape does not reveal n. A leaf hash is

leaf = SHA256(b"TKDM2/leaf" || share)            # share is 33 bytes

and an internal node is

node = SHA256(b"TKDM2/node" || left || right)

single SHA-256, domain-separated, not double SHA-256. root is the 32-byte root. A share's Merkle path is its 1-byte leaf index (0..31) and the 5 sibling hashes from leaf to root, 160 bytes, enough for a 32-leaf tree.

3.3 The key commitment

key_commitment = SHA256( b"TKDM2/key" || root || data_key || SHA256(sealed_pointer) )

where sealed_pointer is the 540-byte sealed pointer of section 4. The commitment binds the root, the key the shares reconstruct, and the pointer to the document, so a reader that holds a reconstructed key and a decrypted pointer can check that all three belong together (section 10).

4. The sealed pointer and the watch spec

4.1 The pointer

The pointer locates the document and records the schedule. Its plaintext is laid out as follows and zero-padded to POINTER_PLAIN = 512 bytes:

Field Width Notes
version 1 2
doc_txid 32 the document transaction id, display byte order
len(doc_vouts) 1 1..24
doc_vouts 2 each output indices carrying the document chunks, little-endian
doc_length 4 sealed document length, little-endian; 28 < doc_length <= 1000028
f_txid 32 the outpoint F transaction id
f_vout 4 little-endian
P 33 base key, compressed
k 2 fuse count, little-endian
first_cutoff 4 little-endian
interval 4 little-endian
len(token) 2 little-endian, <= 300
token len(token) an opaque holder-supplied tag, MAY be empty
zero padding to 512

The pointer is sealed with AES-256-GCM under the key SHA256(b"TKDM2/pointer" || data_key), with associated data AD_POINTER (section 1 of Test Vectors), giving POINTER_SEALED = 512 + 28 = 540 bytes (a 12-byte nonce and a 16-byte tag). The pointer carries neither m, nor n, nor the document hash, nor the encryption parameters: a holder who decrypts nothing, and an attacker who steals a sealed pointer, learn only its length.

4.2 The watch spec

A holder is given an 80-byte watch spec, which carries only what the holder needs to judge the switch:

version(1)=2 || f_txid(32) || f_vout(4 LE) || P(33) || k(2 LE) || first_cutoff(4 LE) || interval(4 LE)

The renewal fuse is index k - 1.

5. Fuse-key derivation

Fuse j (for j in 0 .. k-1, with k <= 1000) is a P2PKH output whose key is derived from the base key by an additive tweak:

t = SHA256( b"TKDM2/fuse" || P || uint16_le(j) )
fuse_pubkey(j)  = P + t * G
fuse_privkey(j) = p + t   (mod the curve order)
fuse_script(j)  = P2PKH( HASH160( compressed fuse_pubkey(j) ) )

The tweak preimage MUST be exactly b"TKDM2/fuse", the 33-byte compressed P, and j as a 2-byte little-endian integer, in that order. Including P in the preimage is a privacy requirement and not a free choice: it keeps the fuse addresses unlinkable to the base key, so an observer who does not hold P cannot tie a switch's check-ins to a common key (the Mathematical and cryptographic basis gives the boundary, including what a release later discloses). A derivation MUST reject a tweak t that is 0 or greater than or equal to the curve order, so that every fuse key is well defined and the derivation fails closed.

A signer that spends a fuse (a check-in, section 7) MUST produce its ECDSA nonce deterministically ([RFC6979]) and MUST NOT use a randomized nonce. This is stronger than the usual advice. The fuse keys share the base secret p through additive tweaks, so a single nonce reused across any two fuse signatures reveals a fuse key, and from it the base secret p to anyone holding P, and with p every fuse key and the release-authorisation key P fall at once. A conforming-looking implementation that signed with a random nonce would produce valid signatures and be catastrophically breakable.

6. The arming transaction

Arming publishes three things, none of which carries a reserved marker, so that an armed switch is indistinguishable on-chain from ordinary wallet activity before it fires.

  1. The document. The plaintext document, at most MAX_DOCUMENT = 1000000 bytes, is placed in an envelope

    b"TKDE2" || root(32) || uint32_le(len(metadata)) || metadata || uint32_le(len(document)) || document
    

    encrypted with AES-256-GCM under the data key with associated data AD_DOCUMENT, which adds 28 bytes. The envelope embeds root, which is what section 10 anchors against. The sealed envelope is split across 1..24 outputs, each an untagged OP_FALSE OP_RETURN <push> carrying roughly 60,000 to 90,000 bytes of envelope with up to a quarter of random padding, and the pointer records which outputs and in what order (doc_vouts, doc_length).

  2. The fuses. The k fuse outputs, at the scripts of section 5, each of value at least MIN_FUSE_VALUE = 1000 satoshis and all distinct in value, so that spends of them are individually identifiable to the owner.

  3. The outpoint F. A pre-existing outpoint that the arming spends as an input; a transaction is this switch's arming by virtue of spending F. The owner re-encodes or cancels the switch by spending F in a competing transaction, and since only one spend of F can be mined, the owner and a reader follow the switch by watching which spend of F is block-proven (section 7).

The arming's inputs, F among them, are the owner's and are subject to the coin segregation of section 7.

6.1 The arming-validity contract

Two readers must agree on which armings exist, so the checks divide into what an owner enforces when it builds a switch and what a reader can verify from the chain.

An owner building a switch MUST enforce the following, which are fixed at creation and are not carried on chain (the sealed pointer and the watch spec hold none of m, n, window or grace), so a reader cannot check them:

  • 1 <= m <= n <= 32;
  • MIN_WINDOW <= window < interval, where MIN_WINDOW = 7;
  • 0 <= grace < 65536;
  • first_cutoff > window (the first cutoff lies beyond the check-in window).

A reader MUST reject an arming that fails any of the following, each derivable from the sealed pointer and the arming transaction:

  • exactly one document envelope, whose embedded root the release's header matches (section 10);
  • the sealed document length is consistent with the pointer's doc_length, and 28 < doc_length <= 1000028;
  • the k fuse outputs are present, at the derived scripts of section 5, each of value at least MIN_FUSE_VALUE and all distinct;
  • 1 <= k <= 1000;
  • 1 <= len(doc_vouts) <= 24;
  • len(token) <= 300;
  • first_cutoff + (k - 1) * interval + CONFIRM_DEPTH < 500000000 (every cutoff is a block height, not a timestamp).

7. Check-in, renewal, cancellation and the owner's block discipline

The owner stays alive by spending fuse j before cutoff(j), in order. Spending the renewal fuse (index k - 1) renews or cancels the switch. The owner MAY withdraw the fuse coins to end the switch. A check-in and a cancellation are version-1 spends, a check-in carrying sequence number 0xFFFFFFFE and a cancellation 0xFFFFFFFF, so that only the owner's own spend re-encodes or ends a switch.

Two disciplines bind the owner, and both are about not acting on a chain fact that a reorganisation can undo.

Block proof gates every state change. Every transition the owner records that depends on a block, the acceptance of an arming, the arm-proven state, a withdrawal, and a release coin seen spent, MUST be gated on CONFIRM_DEPTH confirmations, not only the firing decision. A shallower reorganisation MUST NOT be able to flip the owner's bookkeeping from armed to not, or the reverse.

Re-encoding is detected on the outpoint, not the outputs. An owner that re-encodes a switch spends F. The owner (and a reader, section 10) MUST detect this by observing F's outpoint being spent, by any transaction, independent of what that transaction pays to, and MUST fetch and block-verify the full spending transaction even though it credits none of the owner's addresses. On every pass the owner MUST drop any recorded re-encoding whose transaction is no longer block-proven and re-run detection against whatever spend of F is block-proven now, and MUST NOT re-sign a check-in over, or treat as armed, a transaction that is not presently block-proven.

Coin segregation. The switch's own coins, the change from a check-in, the coin a cancellation sweeps to, and a withdrawal's output, MUST be segregated from the owner's ordinary wallet on every coin-selection path, not only the default send. A wallet MUST refuse to spend a switch output in any transaction other than the switch's own machinery, enforced at the coin store or the signing gate rather than at one chokepoint, so that no application spend, agent spend, manual coin control, or split can link the switch to the owner's other coins.

8. The firing rule

A holder fires (releases) when a cutoff has been missed. Deciding that a cutoff was missed is the most dangerous judgement in the system, because a false positive discloses a live owner's secret and cannot be undone. The determination has three states, and a holder MUST act only on the first two and MUST treat everything else as the third.

  • SPENT: a block proves a transaction that spends the fuse. A spender that is merely seen, in a mempool or over a mesh, but not block-proven, is not SPENT; it yields UNKNOWN.
  • UNSPENT: the fuse is proven unspent by a quorum that is caught up past the cutoff. This holds only when the holder's own proof-of-work header tip is at least cutoff + TIP_AHEAD, and at least MIN_SOURCES independent archival sources each report an address history that is complete to a height at least cutoff + TIP_AHEAD.
  • UNKNOWN: anything else. A holder MUST NOT treat UNKNOWN as UNSPENT and MUST NOT fire on it. An implementation MUST raise UNKNOWN explicitly rather than return a value that defaults toward firing.

Several rules make UNSPENT mean what it says.

  • Absence MUST be measured by how far a source's address history is known complete (its address-index cursor), not by the source's reported block-header tip. A source whose history is only recent, and so cannot be complete back to the cutoff, MUST NOT count as an unspent witness. Each source's reported height MUST be cross-checked against the holder's own proof-of-work tip.
  • The quorum floor for judging fuse j is cutoff(j). No fuse's verdict may depend on a quorum caught up past a later fuse's cutoff. A holder judges fuse j once the chain reaches cutoff(j) + CONFIRM_DEPTH, against an unspent quorum floored at cutoff(j), so that an earlier missed check-in fires at its own cutoff and not behind the final one.
  • Presence (reading a transaction to confirm it spends the fuse) needs the transaction itself. A holder SHOULD have at least two independent providers that can serve a raw transaction, and the spec's "any one source that reveals a spend wins" holds only when an independent fetch can also serve that transaction; its integrity is checked by its transaction-id hash, which a holder MUST verify.
  • A single source MUST NOT be able to wedge the determination to UNKNOWN forever by listing one transaction that no provider will serve. A candidate spender MUST be corroborated by at least two sources before it can force UNKNOWN, or the lone source that listed an unservable transaction is dropped and the quorum re-evaluated without it. Liveness, the eventual ability to fire, is only as strong as the most adversarial single source, and an implementation MUST provide a recovery or timeout path so that one source cannot suppress a fire indefinitely.

9. The release

A holder that has decided a switch fired publishes a release: a transaction with two zero-value outputs.

  1. The bundle output, OP_FALSE OP_RETURN b"TKSH2" OP_PUSHDATA2 <len LE> <bundle>, where bundle is 872 bytes:

    version(1)=2 || header(137) || index(1) || path(160) || share(33) || sealed_pointer(540)
    

    index is the share's leaf index (0..31) and path its 5 sibling hashes. The header is

    root(32) || key_commitment(32) || len(sig)(1) || sig(<=72) || zero-pad to 137
    

    where the padding MUST be all zero. sig is the release-authorisation signature:

    e   = SHA256( b"TKDM2/relauth" || root || key_commitment )
    sig = ECDSA_sign(e)  under the fuse master key p,  DER, canonical low-S
    

    so the header carries a signature, under the key that armed the switch, over the root and the commitment together. This is what binds a release to its owner (section 10).

  2. The discovery marker, OP_FALSE OP_RETURN b"TKSHREG1", 11 bytes, which makes the release discoverable (section 11).

A holder's view of its own release has three states, and a holder MUST keep them apart and MUST NOT collapse them:

  • RECORDED: signed and persisted, not yet acknowledged by any node.
  • SENT: a node returned the exact transaction id.
  • RELEASED: a block confirms it.

A holder MUST NOT report a release as published before it is at least SENT; "recorded" is not "published". A holder's resend loop SHOULD surface a persistent "not accepted by the network" condition to the owner-facing side rather than present an unbounded "releasing" state. A holder MUST serialise releases per switch, one in flight per fuse, and MUST pin a release to a single, deterministically chosen funding outpoint; a holder MAY legitimately be funded more than once by the owner, and pinning keeps "the holder publishes once" true under that.

10. Reconstruction and authentication

A reader collects releases, reconstructs the data key from m shares, decrypts the document, and shows it. Because every byte it collects was written by whoever paid the fee, the reader authenticates before it shows anything.

Group by header. A reader MUST group released bundles by their exact header and reconstruct per group, never over a mixed set of shares drawn from different headers.

Reconstruction is not authentication. The Shamir step returns a key with no integrity (section 3). The only authentication of a reconstructed key is both of:

  • a Merkle path from each used share's leaf to the header root (section 3.2); and
  • key_commitment equal to SHA256(b"TKDM2/key" || root || data_key || SHA256(sealed_pointer)), which ties the reconstructed key and the decrypted pointer to root.

A reader MUST treat a reconstruction that fails either check as unauthenticated and MUST NOT show its result.

Anchor to an on-chain document. A reader MUST anchor every opened switch to an on-chain document whose envelope embeds a root equal to the header root (section 6), and MUST require that an on-chain arming pays fuse_script(P, 0) for the P the header's signature verifies against. A bare reconstruction, not anchored to an arming and a document, is unauthenticated.

Authenticity is the signature, not the timing. The first on-chain release of a root is not a proof of authorship: once the data key is public, the root, the shares, the paths and the key can all be re-packaged into a different header pointing at different content. A reader MUST verify the release-authorisation signature in the header under the base key P, and MUST bind P to the on-chain arming (the fuse output the arming pays and the owner spends), rather than to which release was seen first. Block height, and any tie-break on header bytes, are influenceable by an attacker (an unmined release has no height, and a same-block tie is grindable) and MUST NOT be used as an ordering or authenticity oracle.

Disputed roots. A reader MUST treat a root that appears under two or more distinct key_commitment values as disputed: it MUST surface both, or neither, and MUST NOT silently elect one as canonical by height or by byte order. The reader states OPEN, SEALED, COPY, UNREADABLE or DISPUTED for a switch, and DISPUTED is a terminal display state, not a resolved one.

11. Discovery and ranking

Releases are discovered through the TKSHREG1 marker, found by its output script's hash in an index. The marker is permissionless: anyone can publish one, over any transaction. A reader MUST NOT treat the discovery feed as an access-controlled or complete list of switches, and MUST NOT present it as authoritative.

Version dispatch. The discovery marker TKSHREG1 does not encode a version; the release output's first push is the version tag (TKSH2). A conforming reader MUST dispatch on that tag, and MUST NOT silently drop a well-formed release whose version it does not implement: it reports such a release instead.

Partial reads are named, not hidden. A transaction listed in the feed that a reader cannot fetch MUST be surfaced as a named gap, and MUST NOT suppress the releases that were read. A reader MUST NOT present "I could read only part of the feed" as "there is nothing here". A reader SHOULD draw from more than one source so that one server's refusal is not authoritative, and SHOULD bound the size it will read per listed transaction.

Saturation is reported. A window limit truncates the feed by height, and the newest entries can be flooded by an attacker to push older ones out of the window. A reader MUST read newest-first under a stated window policy, and MUST distinguish "read the whole marker history and found nothing" from "read only the newest N", reporting when the window was saturated. On-chain releases remain recoverable by transaction id or a private index, which bounds the harm, but the default view MUST NOT read as empty when it was merely truncated.

Ranking is advisory. A reader MAY rank switches by importance, the sum of the satoshis currently locked toward a switch and still unspent. This weight is sound only when each lock's spent state is resolved against a block proof: only block-proven-unspent weight may rank or hide. A lock's weight MUST decay as it is spent, and a reader MUST NOT accept an attacker-chosen expiry height as a way to hold weight indefinitely. Until spent-state resolution is in place, a reader MUST mark importance ranking as unverified and advisory. A listing fee, if any, is advisory and MUST NOT be presented as a spam floor for discovery, because it does not raise the cost of flooding the window.

Denial-of-service caps. A reader MUST bound releases read per transaction (MAX_RELEASES_PER_TX = 64), switches held per read (MAX_SWITCHES = 200), and shares per header group, so that a flooded feed cannot exhaust it.

12. Clear metadata bounds

An arming MAY carry clear metadata so a reader can list a switch before it is opened. This metadata is attacker-controlled bytes and MUST be validated on read, against fixed bounds, not only at creation. A reader MUST parse it defensively: metadata that is not a small JSON object shows nothing.

For this format a reader reads three fields and bounds each:

  • title: coerced to a string and truncated to TITLE_MAX_CHARS = 200 characters.
  • categories: a list, each entry coerced to a string, at most 8 kept; anything that is not a list yields none.
  • explicit: a boolean.

A reader MUST bound the serialised metadata it will parse, so that one arming cannot inject unbounded bytes into every reader's feed, and MUST NOT emit a value whose type or range a consumer could not have anticipated. A reader parsing a release output MUST treat any script too short for its declared pushes as "not a release" and skip it, at per-output granularity, so that one malformed output never discards a transaction's valid release.

13. Owner-to-holder messages

The messages an owner sends a holder, and a holder's instructions back, are JSON and MUST be strict [RFC8259]: NaN, Infinity and -Infinity MUST be rejected, because they are not valid JSON and two parsers disagree on whether a message containing them is valid. The judging side (the holder) MUST reject such a message rather than risk acting on it. A message is at most MESSAGE_CHARS = 1500 characters.

14. Reserved markers and footprint

The markers this switch uses are reserved protocol identifiers. A wallet that builds transactions on behalf of third-party applications or agents MUST refuse any supplied output whose first OP_RETURN push is one of the switch's reserved markers, TKSH2 or TKSHREG1, and SHOULD refuse any reserved marker of the wider protocol it implements, so that an application cannot plant a marker that makes an ordinary wallet transaction look like a switch's and defeat the indistinguishability of section 6. The refusal applies when the reserved tag is the first push of an OP_FALSE OP_RETURN output.

The footprint of a switch, before it fires, is untagged document chunks shaped like any other data store, an untagged arming that pays P2PKH fuse addresses, and the watched outpoint F. The only deliberate on-chain mark a switch ever makes is the TKSH2 release beside the global TKSHREG1 marker, after it fires. An implementation MUST NOT promise a per-switch opt-in stealth tag that it does not provide; there is no such tag.

15. Summary of requirements

# Role Requirement
R1 owner MUST present that m holders can disclose at any time; the chain does not gate the timing.
R2 owner MUST restrict m = 1 to a holder the owner controls, and SHOULD log an explicit acknowledgement for m = 1.
R3 owner MUST build shares, the 32-leaf Merkle tree, key_commitment, the sealed pointer and the arming exactly as sections 3, 4 and 6 specify.
R4 owner MUST derive fuse keys with the exact tweak preimage of section 5 and MUST reject a tweak of 0 or at least the curve order.
R5 owner A fuse signer MUST use a deterministic nonce ([RFC6979]) and MUST NOT use a randomized nonce.
R6 owner MUST gate every block-dependent state change on CONFIRM_DEPTH confirmations.
R7 owner MUST detect re-encoding by F's outpoint being spent, MUST block-verify the full spending transaction, and MUST drop and re-detect any re-encoding no longer block-proven.
R8 owner MUST segregate the switch's coins on every coin-selection path and MUST refuse to spend a switch output outside the switch's own machinery.
R9 owner MUST build a valid arming per the contract of section 6.1 and MUST refuse one that fails any clause.
R10 holder MUST act only on SPENT or UNSPENT and MUST raise UNKNOWN explicitly; MUST NOT fire on UNKNOWN.
R11 holder MUST measure absence by address-history completeness to at least cutoff + TIP_AHEAD, across at least MIN_SOURCES archival sources, cross-checked against its own proof-of-work tip.
R12 holder MUST judge fuse j against a quorum floored at cutoff(j), never past a later fuse's cutoff.
R13 holder MUST verify a fetched spender by its transaction-id hash and MUST NOT let a single unservable listing wedge the determination; MUST provide a recovery path.
R14 holder MUST keep RECORDED, SENT and RELEASED apart and MUST NOT report published before SENT.
R15 holder MUST serialise releases per switch and pin each to one deterministically chosen funding outpoint.
R16 holder MUST build the release outputs exactly as section 9 specifies, header padding all zero, sig the release-authorisation signature.
R17 reader MUST group bundles by exact header and reconstruct per group.
R18 reader MUST authenticate a reconstruction by both a Merkle path to the header root and key_commitment, and MUST treat a bare reconstruction as unauthenticated.
R19 reader MUST anchor an opened switch to an on-chain document whose envelope root equals the header root and to an arming paying fuse_script(P, 0).
R20 reader MUST verify the release-authorisation signature under P and bind P to the arming; MUST NOT order or elect by height or header bytes.
R21 reader MUST treat a root under two or more distinct commitments as DISPUTED and MUST NOT elect a winner.
R22 reader MUST dispatch on the release tag and MUST handle or report every defined version; MUST NOT silently drop an unimplemented version.
R23 reader MUST surface an unfetchable listed transaction as a named gap without suppressing read entries, and MUST NOT treat the feed as complete or access-controlled.
R24 reader MUST read newest-first and MUST report a saturated window rather than show it as empty.
R25 reader MUST rank only by block-proven-unspent weight, MUST decay weight by spend, MUST reject an attacker-chosen expiry, and MUST mark unresolved ranking advisory.
R26 reader MUST bound releases per transaction, switches per read, and shares per group.
R27 reader MUST validate clear metadata on read against the bounds of section 12 (title coerced and capped, categories a bounded list of strings, serialised length bounded) and MUST skip an output too short for its declared pushes.
R28 owner, holder Owner-to-holder messages MUST be strict [RFC8259] JSON; NaN, Infinity and -Infinity MUST be rejected.
R29 wallet MUST refuse an application- or agent-supplied output whose first OP_RETURN push is a reserved marker (section 14).
R30 owner, reader MUST NOT advertise a per-switch stealth tag; the only deliberate mark is the post-fire release beside the discovery marker.

Test Vectors

The vectors below are reproducible: every input is the SHA-256 of the fixed UTF-8 label shown, so a reader can rebuild each value from this document. They were produced by the reference implementation, and the primitives were checked against a second implementation that shares no code with it. Hex is in wire byte order.

The pointer and document are sealed with AES-256-GCM under two fixed associated-data strings:

AD_POINTER  = b"TKDM2/pointer-ad"
AD_DOCUMENT = b"TKDM2/document-ad"

Fixed keys

The fuse master private key is p = SHA256("dead-man timed reveal test vector: fuse master key"), and P is its compressed public key, the key the header's release authorisation verifies against:

p = a9f5588dc17c2d9431fc0c23083a37276eaf7a442d3ad14fbaa2e9db692ff005
P = 0263ec1bce2a536b062f970f9824888e0fa5b32d186e85943dfc9e1cfc8dbccd67

1. Fuse derivation

tweak(k) = SHA256(b"TKDM2/fuse" || P || uint16_le(k)); the fuse public key is P + tweak(k) on the curve, compressed; the fuse script is OP_DUP OP_HASH160 <hash160(fuse public key)> OP_EQUALVERIFY OP_CHECKSIG.

k tweak fuse public key (compressed) fuse script
0 2c95be339bc468e8d319168ec1d43595812c623c6f73cce3de4bb007f0ef8451 02dd7e2f0d4656d774fdd9ccf372507bf13fb709c0e999414826f51bb37fd24a73 76a91476ce359004a1c59eb9bd05007fdc1e52b2383dd688ac
1 2b0d137fc5ba2db59380ecc1c37eb4899db3b045cb6b324e5dd83afc909dbeab 02828e60100c0ebb46529421e807443a18ca34e4411d2cf04430be6620e59c78c4 76a9149e857cca0d5bbbb8f69fddadda5e7b9d24c7a65e88ac
999 2810ed21b252ac6d05e7200c733964cba988256e24460b0271ac14fe4e80a015 035e4c3cbd06b96cc7866191e64514d7958df19ee2e4d384fea6d13231cdc97518 76a914bb6c6ee423a465a79958347108f58d2abd6b94bf88ac

The private half of fuse 0 is (p + tweak(0)) mod n = d68b16c15d40967d051522b1ca0e6cbcefdbdc809cae9e3398ee99e35a1f7456.

2. The Merkle tree

The 32 leaves are the leaf hashes of 32 shares, where share i = byte(i+1) || SHA256("dead-man timed reveal test vector: leaf " || decimal i) for i = 0..31. A leaf is SHA256(b"TKDM2/leaf" || share), and a parent is SHA256(b"TKDM2/node" || left || right).

share 0 = 012b0f391bd2c9364ad6c4b5e880c3f8d16253bfd635a47967774f715bc32cdee8
leaf  0 = 2643546ba02c4d57d55ece2681e04d31131a1e9f03c8dd20bba8a9205b46cc22
root    = 4cd0102926c4d162e63336d1633967d99227ac4967f22e77b9b8ba302a32c0f9

The path from leaf 0 to the root is the five sibling hashes

8837f18e0c6cd25209d0880a2485157a85db87475d9c2d1502904531a0446381
c9587bf46efb00084c72f7ca47735a0c6c82c0a077466aa4c554e74c77f2bfe1
ad8246c99c4462dc386e076ec52d4b5876e5133c68a20229af74e03d7c5f1a9b
b7f0841503671d76465d75791d1236768ecb1671ef50faf3568aacee8569265b
1aec0b1146de7e33a6a73296bd8643c249656a4b467f5879d829bb40369fa22c

and folding them upward from leaf 0 reproduces root.

3. The key commitment

With root from section 2, data_key = SHA256("dead-man timed reveal test vector: data key"), and SHA256(sealed_pointer) the hash of the 540-byte sealed pointer:

data_key           = 71377245532a48c16c88348804a28d48f09689e8bba4a750977946097fcf22a6
SHA256(sealed_ptr) = b2b46e4ec38053dc84e3d448441a4694ed76b3f4656551e00a790f0b1d127efc
key_commitment     = 2c2d0e1ee68b123a1b25885541ca9fa7a8dd4aca9d606b6794f09b4cfbf30005

where key_commitment = SHA256(b"TKDM2/key" || root || data_key || SHA256(sealed_pointer)).

4. The release authorisation

With root from section 2 and key_commitment from section 3, e = SHA256(b"TKDM2/relauth" || root || key_commitment):

e   = a44c8fe9c884241c2a93797cff9a1924aeb65beb48cd29477df7cf95f41f255b
sig = 3045022100ff0cdf9512f736e24cc161a7b88cd4d68480d0e903b2a702cb5a81ea2eacf7cc02203b80a7f69bc01b80b2a4ae36d34b8aadf2c00f1057c454b9a104d4ec26c0d93b

sig is 71 bytes, DER, canonical low-S, over e under p ([RFC6979]). It verifies under P. The same sig does not verify against a root with one byte changed, nor under any key other than P.

The shared fixture snapshot

The full sealed pointer (540 bytes), the 872-byte bundle, the release output and the document envelope each carry an AES-GCM nonce and a shuffled 32-leaf layout, so they are not reproducible from fixed inputs. The two reference implementations pin one byte-exact snapshot of them in a shared fixture file generated from the reference, and a reader checks invariants rather than exact bytes: the 137-byte header decodes to root, key_commitment and a sig that verifies under P (sections 2 to 4); the bundle's share lies on the tree named by root, through its path; key_commitment binds the reconstructed key and the decrypted pointer; and the document envelope embeds a root equal to the header root. The snapshot also carries two odd-message cases: one owner-to-holder message containing NaN and one containing Infinity, each rejected by both implementations (section 13).

Implementations

A reference implementation exists in a wallet that arms, maintains, cancels and withdraws switches, holds shares and publishes releases, and reads and displays releases; and a second implementation, in a different language and sharing no code, judges switches and publishes releases from the holder's side. The two share a fixture file that pins every constant and every byte layout of sections 1, 4, 5, 9 and the firing rule, and they agree on it byte for byte. The firing rule's three-state determination, the quorum, and the per-cutoff judgement were exercised against multiple independent chain sources, including a source that serves only recent history, which the quorum correctly refuses to count as an unspent witness.

Mathematical and cryptographic basis

This section states each property the format rests on and the assumption it reduces to: the information-theoretic secrecy of Shamir's scheme, the semantic security of AES-256-GCM, the collision and preimage resistance of SHA-256, the existential unforgeability of ECDSA over secp256k1, and the determinism of the signing nonce ([RFC6979]).

Confidentiality below the threshold

The document is sealed with AES-256-GCM under the data key, and the data key is split with Shamir's scheme over GF(2^8) at threshold m. Any set of fewer than m shares is, by the secrecy of Shamir's scheme, statistically independent of the data key: it excludes no value and gives no advantage in recovering it. A coalition of fewer than m holders learns nothing of the data key, and the document ciphertext is protected under that key by the semantic security of AES-256-GCM, so an observer of the on-chain ciphertext learns nothing of the document beyond its length. This is a bound on the shares, not on the holders: section 2 states that any m holders can open the document whenever they choose.

Authentication of a reconstructed key

Shamir reconstruction carries no integrity of its own. A wrong, foreign or insufficient set of shares interpolates to some 32-byte value with no error, and a single crafted share steers the result to a value the attacker picks (section 3.1). A reconstructed data_key' is authenticated only by two checks together, each reducing to the collision resistance of SHA-256:

  • a Merkle path from each used share's leaf to the header root, where a leaf is SHA256("TKDM2/leaf" || share) and a node is SHA256("TKDM2/node" || left || right), so a share that lies on a given root but was not placed there is a SHA-256 collision; and
  • key_commitment = SHA256("TKDM2/key" || root || data_key' || SHA256(sealed_pointer')), so a reconstruction with a data_key' or a pointer other than the committed ones that still matches the header's key_commitment is a SHA-256 collision.

A reader that performs both accepts data_key' only as the key the switch's author committed to, with the pointer the author sealed. An interpolation that skips either check is unauthenticated.

Authorship of a release

Once a switch fires its data key is public, so the root, the shares, their paths and the key can be re-packaged by anyone under a header of their own. The header's release authorisation distinguishes the author's release from such a re-packaging. sig is an ECDSA signature over e = SHA256("TKDM2/relauth" || root || key_commitment) under the fuse master key p, whose public half P the arming pays through fuse_script(P, 0). Producing a valid sig for a (root, key_commitment) under a given P is an existential forgery against ECDSA under P; a party that does not hold p can sign only under a key P' of its own, and a reader binds P to the on-chain arming, so a release under any key but the arming's P is unauthorised. A root that is presented under two distinct key_commitment values is surfaced as disputed and is never resolved by ordering (section 10), because a block height and a header-byte tie-break are influenceable by an attacker and are not an authorship oracle.

Unlinkability and the nonce

Fuse k's key is P + t_k * G with t_k = SHA256("TKDM2/fuse" || P || uint16_le(k)), and the arming pays each fuse's P2PKH script. The tweaks are a function of P, so tying a switch's fuse addresses to their common key needs P. Putting P inside the tweak preimage is what denies P to an observer of a spent fuse: a check-in reveals the fuse public key P + t_k * G, and recovering P from it would need t_k, which needs P, a circular dependency behind a hash with no algebraic inversion. The k fuses are paid in one arming transaction, so they are grouped by that co-occurrence as any batch payment of distinct-value outputs is; what P hides is their derivation from one key and the switch's meaning, not their co-location. A release then discloses P to everyone: its header carries root and key_commitment, so e is computable, and ECDSA public-key recovery on the header's sig yields P (two candidates, resolved by matching fuse_script(P, 0) on chain). So the moment one release is mined, anyone can recover P, compute every t_k, and tie the switch's whole fuse cluster and check-in history to it. The derivation is unlinkable to a common key only until the first release is mined.

The derivation shares one secret p across every fuse through tweaks that anyone holding P can compute, and this is where the nonce rule earns its MUST (section 5). From two ECDSA signatures that reuse a nonce, which share the value r, an observer recovers the nonce and then the signing key by the standard same-key relations (kk = (z1 - z2) * (s1 - s2)^-1 and d = (s1 * kk - z1) * r^-1, both reduced modulo the curve order). Every fuse key is p + t_k, so a party that holds P (every holder does, and P is recoverable from any release) turns a recovered fuse key into p, and with p every fuse key and the release-authorisation key fall together. A deterministic nonce ([RFC6979]) makes the nonce a function of the key and the message, so two signatures reuse a nonce only when they sign the same message under the same key, and this recovery cannot arise. A randomized-nonce signer would still produce valid signatures and would be breakable this way, which is why the rule is normative.

Domain separation

The six hashing contexts are separated by the prefixes TKDM2/leaf, TKDM2/node, TKDM2/key, TKDM2/pointer, TKDM2/fuse and TKDM2/relauth, and the two sealing contexts by the associated-data strings TKDM2/pointer-ad and TKDM2/document-ad. No value produced in one context is a valid value in another, so a leaf is not read as a node, a commitment is not read as a relauth digest, and a sealed pointer is not read as a sealed document, under the collision resistance of SHA-256.

What is not cryptographic

The decision that a cutoff has passed (section 8) rests on an observation of the chain, not on a proof. Its safety is the quorum and freshness discipline of section 8, and the firing rule is written so that every outcome short of a proven, caught-up quorum is UNKNOWN, on which a holder never fires.

Security Considerations

The timing is social, not cryptographic. The chain says when an honest holder should release; it cannot stop m holders releasing early. The owner's protection is the choice of m and of trustworthy holders, and, for m = 1, control of the one holder. An implementation that implies otherwise misleads the owner about the one thing that matters.

A false fire is unrecoverable. Section 8 is written so that the only way to conclude a cutoff was missed is a quorum that is provably caught up past that cutoff, measured by how complete each source's address history is and checked against the holder's own proof of work. Every weaker signal (a recent-only source, a mempool sighting, a single provider) yields UNKNOWN, and a holder never fires on UNKNOWN. The cost of this is liveness: a hostile source can delay a fire, which section 8 bounds with corroboration and a recovery path, and which is the safe direction to fail.

Authenticity rests on one signature. Once a switch fires, its data key is public, so anyone can re-wrap its root into a new header. The release-authorisation signature under the base key P, bound to the on-chain arming, is the only thing that tells a genuine release from a re-wrapping. A reader that ranked by block height instead would show an attacker's copy as the original. A root seen under two commitments is disputed, and disputed is where it stays.

One nonce reuse breaks everything. The fuse keys and the release-authorisation key all derive from the base secret p through additive tweaks, so a single reused ECDSA nonce reveals a fuse key, and to anyone holding P the base secret p and with it every key the switch has. This is why the deterministic nonce is a MUST and not advice (section 5).

Everything read is attacker-controlled. The discovery feed, the clear metadata and the release outputs are all permissionless bytes. The feed is not complete and can be flooded, so a reader reports gaps and saturation rather than showing a truncated view as empty. The metadata is bounded and validated on read. A short or malformed output is skipped, per output, so one bad output does not discard a transaction's valid release.

Linkage. An armed switch looks like ordinary activity: untagged chunks, P2PKH fuses, a watched outpoint. The deliberate mark is made only on firing. The owner's own coins stay unlinked to the switch only if segregation holds on every spend path, which is why it is enforced at the coin store and not at one send path (section 7).

The holders learn little. A holder is given its share and a watch spec, and no more: not m, not n, not the document's length or location, which stay inside the sealed pointer until a reconstruction. A stolen share is one of m needed; a stolen sealed pointer discloses only its length.

Privacy Considerations

Before a switch fires, its on-chain footprint carries no mark that identifies it: the document rides in untagged OP_FALSE OP_RETURN outputs shaped like any other data store, the arming is a transaction that pays P2PKH fuse addresses and spends the watched outpoint F, and none of these carries a reserved marker (section 14). An observer who does not hold the base key P cannot tell an arming or a check-in from ordinary wallet activity, and cannot tie a switch's fuse addresses to a common key, though the fuses do co-occur in the one arming transaction like an ordinary batch payment (the Mathematical and cryptographic basis gives the argument). What the chain leaks before a release, to anyone, is the size of the sealed document, from the number and length of its chunk outputs; and, to a party that holds P, which is every holder, the cadence of the owner's check-ins, since that party can watch each fuse address for its spend.

A holder is given only its share and the watch spec, and never m, n, the document's length or location (Security Considerations). Because the watch spec carries P, a holder can watch the switch's fuse addresses, so the owner's check-in cadence is visible to every holder. A holder is not anonymous to that cadence, and an owner who needs it hidden should weigh which holders learn P.

After a switch fires the release is deliberately public: the TKSH2 output beside the TKSHREG1 marker announces the switch, the clear metadata (section 12) is legible to anyone, and the reconstructed document is readable by all. The release also discloses the base key P to anyone, through public-key recovery on its header signature (the Mathematical and cryptographic basis), so the switch's whole fuse cluster and the owner's full check-in cadence become publicly linkable at the first release, not only to the prior holders. The format has no setting that keeps a fired switch private, because disclosure is its purpose. The clear metadata an owner writes is published verbatim within the bounds of section 12, so an owner controls how much a listing reveals before a reader opens the document.

References

  • [SSS] A. Shamir, How to Share a Secret, Communications of the ACM, 1979.
  • [FIPS180-4] NIST, Secure Hash Standard (SHS), FIPS 180-4.
  • [SP800-38D] NIST, Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC, SP 800-38D.
  • [RFC6979] T. Pornin, Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA). https://www.rfc-editor.org/rfc/rfc6979
  • [RFC8259] T. Bray, The JavaScript Object Notation (JSON) Data Interchange Format. https://www.rfc-editor.org/rfc/rfc8259
  • [BRC-42] BSV BRCs, BRC-42: BSV Key Derivation Scheme.
  • [ElectrumX] An index that exposes an output script's history by the hash of the script.