Skip to content

Draft. This text can change at any time, and there's no version history until the draft is frozen. Comments stay attached to the passage they were made on; if it changes, they move to its section, marked as on an earlier version.

Test vectors (v1 core)

This page answers one question: what do the Tieroute test vectors cover, and how do you regenerate and check them? They're byte-exact examples that any Tieroute implementation can check itself against. Section references (§) are to the Tieroute protocol specification.

Warning: every key in these files is public. Never use them for anything real.

File What it covers
signed.json The signed envelope (§4.3): the body as text in the JSON Canonicalization Scheme (JCS), the signature input JCS(["tieroute/v1", t, b]), the signature, the whole envelope as JCS, and its hash. The example is a POST auth/session body, plus the same envelope with an extra member, which must be rejected.
recovery.json A recovery code (16 bytes and 12 BIP-39 (Bitcoin Improvement Proposal 39) words), HMAC-based key derivation (HKDF) of recovery keys 1 and 2, their pre-rotation commitments, and recovery_wrap (§6.1, §6.3)
chain.json A valid four-entry identity chain: genesis, add device, a recovery-signed removal with re-commitment, and a self-rotation. Includes the identifier derivation and the expected final state (§6.2 to §6.4).
codes.json A contact verification-code round (§6.7)
ed25519.json Ed25519 verification rules (§4.3): a valid signature, an unreduced S, and small-order keys and R points, including the all-zero signature that a plain cofactorless check accepts for any message
fork.json The fork rule (§6.4.1, §6.5): a stolen device moves the identity to another homeserver, the recovery key forks it back, and seven cached-versus-candidate cases with the required outcome, including both orders of arrival for a late recovery entry
vouch.json Who vouches for chain entries (§6.5): 11 cases covering previous-home confirmation, unreachable and 404 homes (a first fetch rejects while the previous home can't be reached), and recovery entries vouching for earlier ones
s2s.json A server-to-server (S2S) request signed with a server key named by kid (§8.3)
formatting.json Message formatting (§12.1): 30 bodies and the block trees they parse to, covering every span, escapes, links, quotes, code blocks, and the edge cases. A Go reference parser generates them and an independent JavaScript parser in check.mjs must agree
sframe.json SFrame (Secure Frame) for calls (§15.2, RFC 9605): RFC 9605's own case for AES_128_GCM_SHA256_128, header encodings, and Tieroute's key IDs ((leaf_index << 4) + (epoch % 16)) with their keys and an encrypted frame. The Messaging Layer Security (MLS) exporter output is a stand-in, since it needs a real group. The reference client and check.mjs check it
link.json Signing in with another device (§7.2, and the device-linking endpoints in the client API): the link code for a device key, accepted spellings, confirmation-code normalization, and a signed link proof
account.json Account data (§7.4): the account key wrapped under recovery_wrap from recovery.json, and an account-data blob, each AES-128-GCM with its 12-byte nonce first and the associated data that binds it to its identity and purpose; and a recovery-code rotation's staged wrap (recovery-next), bound to its code by the commitment to the code's key 1. The reference client and check.mjs check it

Raw bytes are hex, in fields ending in _hex. Signed objects and other signed or hashed inputs are given as their exact JCS text (body, sig_input, signed, input): sign and hash the UTF-8 bytes of that text. Inside it, byte strings are unpadded base64url, as on the wire (§4).

Generate the vectors

The vectors are produced by a Go program with fixed inputs, so the output is reproducible. From the spec/vectors/ directory:

cd gen
go run . -out ..

Regenerate them whenever the spec changes something they cover, and commit the result.

Check the vectors

check/check.mjs re-derives everything it can from the JSON using only the built-in crypto of Node.js and its own JCS: every signed object from its type and body, signatures, hashes, HKDF, identifiers, and codes. It's a second, independent implementation of the encoding and the crypto parts. From the spec/vectors/ directory:

node check/check.mjs .

Not covered yet

  • MLS objects: key packages, commits, app-message inside MLS. These depend on the MLS library and suite (§3.1). Use the IETF MLS interop vectors for the MLS layer itself.
  • Link bundles, which use the hybrid post-quantum form of Hybrid Public Key Encryption (HPKE)
  • Contact requests

© 2026 Reroute Holdings LLC

A public draft of Tieroute v1, open for comments.