URMA. GitHub

SPECIFICATION / REVISION 0.11

The normative URMA V0 specification, rendered from the exact published Markdown. The Markdown file is the document of record; its SHA-256 identifies this revision.

Document
URMA V0 — normative protocol specification, revision 0.11, 10 October 2026; wire version byte 00; CC0-1.0
Exact file
PROTOCOL.md (Markdown, UTF-8)
SHA-256
a6ef41624d180bc5eb45983902d6dbf0b8e1309ddfd695e5f1242f21b1ca0bcb
Whitepaper
urma-whitepaper-2026-10-10.pdf (PDF, non-normative, CC0-1.0; Cristian Zmole, ROSINT) · SHA-256 351d3eeffca7ed245758f3e92d9abee76e6474fbfa8ffdc5e88d56ae63c84e5f
Source
github.com/urma-protocol/urma — implementation, readers, test code and vector-generation scripts under 0BSD; serialized vector data declared CC0-1.0 stays CC0-1.0; third-party dependencies keep their own licenses

Website publication (non-normative): . The document and PDF retain their own dates. The approved specification is preserved byte for byte, including its prepublication wording “local and unpublished”; that wording has not been rewritten in the rendered document.

URMA V0 — normative protocol specification

Document revision: 0.11 · 2026-10-10 · SPDX-License-Identifier: CC0-1.0

Author: Cristian Zmole · ROSINT

Contents

1. Authority and status

This is the single normative URMA specification. MUST, MUST NOT and REQUIRED express conformance requirements. Application documents cannot override this document.

V0 is a new wire format. No compatibility, implicit migration, marker alias, fallback decoder or cryptographic equivalence with previous experiments is specified.

Revision 0.9 explicitly amends the kind 06 avatar layout incompatibly while retaining wire version 00 and kind 06 (§7.1.3, §11). It is a local, unpublished revision; the normative revision 0.8 Markdown bytes remain unchanged. An author-only editorial correction of the active revision 0.8 PDF does not apply this amendment or change its protocol content.

Revision 0.10 is local and unpublished. It adds only the optional, explicitly selected recovery mode of §6.1. Wire version 00, every byte layout and cryptographic domain, the strict opening rules of §§5.4–6 and the revision 0.9 avatar amendment remain unchanged. Recovery of an object is not acceptance of its supplied container as valid.

Revision 0.11 is local and unpublished. It incompatibly narrows kind 05 to 0–128 UTF-8 body bytes and 8–136 complete record bytes (§7.1, §11), retaining wire version 00 and kind 05. The limit counts octets, not characters. All other kinds, including kind 0C, the avatar amendment and optional recovery rules, remain unchanged. No implicit migration or update of published artifacts is specified.

Application profiles, products and services are outside this protocol. Profile interoperability is separate from URMA conformance, and the protocol never depends on a product or profile interpreter. A separate non-normative whitepaper presents the protocol scientifically; it adds no rules. The revision history of this document is in §15.

2. Scope and semantic families

URMA defines writing, verification and recovery of:

  1. Encrypted private objects containing arbitrary original bytes. Private text, images, documents, audio and video use the same private-object format. Content type is encrypted metadata.
  2. Public signed text. Its author is the key authorizing the Taproot reveal; a detached application signature MUST NOT be added as a second author proof.
  3. Generic public signed multipart objects containing arbitrary bytes. Their bounded root → leaves → data graph is defined in §7.2–7.8. Payload meaning and application profile validation remain outside the protocol.
  4. Atomic public profile records carrying an eight-byte profile identifier and opaque bytes, defined in §7.1.2. Payload meaning and profile validation remain outside the protocol.

Public text kinds 02/04/05/0A/0B, avatar kind 06 and profile record kind 0C are atomic. They use the single Taproot profile in §8, without fragmentation, reassembly or size-based transport selection. Atomic public records are at most 32,768 bytes including headers. Multipart records 07/08/09 are at most 262,144 bytes (256 KiB) including headers. A multipart object has the separate bound in §7.2. Clients MUST count bytes, not displayed characters.

The protocol accepts arbitrary public bytes and assigns them no meaning; what an application accepts or displays is outside it. A reference to external media preserves the reference, not the media.

Private objects may span their full representable canonical chunk count. Resource policies may refuse work but MUST NOT redefine protocol validity.

3. Roles, secrets and dependencies

An archive root is exactly 32 uniformly random bytes. Every object under that root is recoverable with that root. Compartments use independent roots. V0 defines no recipients, key wrapping, invitations, revocation or root rotation.

The public author is a BIP 340 x-only secp256k1 public key. It identifies a pseudonymous identity in the selected chain context, not a globally registered name. Funding, private recovery and public authorship are different roles. The author key and the funding key MAY be the same key; separate keys are not required. The archive root remains a separate, independently generated secret; nothing in this section permits deriving it from, or using it as, any other key. V0 does not purport to detect or prohibit a third party reusing underlying key material.

A mnemonic, QR or encrypted client backup MUST recover the raw root exactly. None is required for interoperable recovery. A wallet seed, publisher journal, original filename, implementation database or founder service MUST NOT be a private recovery prerequisite.

Data sources, chain consensus and retained history remain dependencies. A central index, username registry, canonical feed or mandatory operator relay MUST NOT be required. Replaceable indexes may supply candidates, not authority.

4. Common byte conventions

Offsets are zero-based. Lengths are bytes. Integers are unsigned little-endian unless explicitly stated otherwise. Addition, multiplication, ceiling division and offsets MUST be checked for overflow before allocation or I/O.

The four marker bytes are ASCII URMA, hex 55 52 4d 41. Version is a separate one-byte unsigned field; V0 encodes 00. No terminator follows a marker or string.

Offset Length Field
0 4 URMA marker
4 1 Version, 00
5 1 Record/container kind
6 2 Flags, zero unless a defined kind explicitly assigns bits

Kind values below are hexadecimal; all V0 flags are zero.

Kind Encoding Carrier
01 Private canonical record Taproot reveal or offline container
02 Public plain post Taproot reveal
03 Private-object container Offline only
04 Public reply Taproot reveal
05 Public profile Taproot reveal
06 Public avatar Taproot reveal
07 Public multipart data part Taproot reveal
08 Public multipart leaf manifest Taproot reveal
09 Public multipart root manifest Taproot reveal
0A Public structured Wire post Taproot reveal
0B Public structured Wire reply Taproot reveal
0C Public atomic profile record Taproot reveal

No other kind or flag is allocated.

Unknown versions, unallocated kinds and nonzero reserved flags MUST be rejected as unsupported encodings; they MUST NOT be parsed under a guessed layout. Malformed instances of an allocated encoding are invalid. Neither result is a complete authenticated object.

5. Private canonical record

5.1 Fragmentation and representational bound

Let C = 32,768 original bytes per chunk and L be the original byte length. Nonempty objects have N = ceil(L / C), with 1 <= N <= 2^32 - 1. An index i satisfies 0 <= i < N. Chunk i contains n = min(C, L - i*C) original bytes. All but the last contain C bytes.

The count and index are u32. Length is u64, additionally bounded by N*C and the count relation. The maximum representable original length is

140,737,488,322,560 bytes = (2^32 - 1) * 32,768 = 2^47 - 32,768.

There is no smaller normative private-object policy ceiling. Empty private objects are not encoded by this layout. A client limit, insufficient funds or local capacity refusal MUST be reported separately from invalid object data.

Every new encryption generates an independent OS-random 32-byte object ID. Each index is encrypted once under that ID. Resume MUST retain the original encrypted bytes. Reencrypting, including after snapshot restoration, requires a fresh object ID. RNG failure is outside the construction's security guarantee.

The public count is the real count. Dummy chunks, size classes, cover modes and power-of-two object padding are absent. Only the last record's unused plaintext capacity is randomly padded.

5.2 Layout

Every canonical private record is exactly 32,928 bytes.

Offset Length Field
0 8 Common header; kind 01, flags 0000
8 32 Random object ID
40 16 Keyed discovery tag
56 4 Index i, u32
60 4 Real total count N, u32
64 16 Canonical AES counter IV
80 32,816 Ciphertext
32,896 32 HMAC-SHA256 tag

The first 64 bytes are the public authenticated header. The decrypted body is:

Body offset Length Field
0 32 SHA-256 of the complete original bytes
32 8 Original length L, u64
40 4 Private content-type hint, u32
44 4 Reserved metadata flags, zero
48 n Original chunk bytes
48 + n 32,768 - n OS-random padding

Content-type hints: 0 opaque bytes, 1 text, 2 image, 3 audio, 4 video. They describe the original bytes; they neither transform them nor authorize execution. Text encoding or codec inference by a client MUST NOT alter recovery. Other hint values are unsupported in V0. All chunks of an object MUST agree on the authenticated digest, length, count, content type and metadata flags.

Original embedded metadata remains within the original encrypted bytes. Filename, identity, GPS and capture time are not additional public fields. The count leaks (N-1)*C < L <= N*C.

5.3 Cryptographic construction

Use SHA-256, HMAC-SHA256, HKDF-SHA256 and AES-256 in CTR mode. The HKDF labels below are exact ASCII bytes, without NUL or newline. Keys are raw bytes, not passwords, hexadecimal text or mnemonic strings.

PRK = HKDF-Extract-SHA256(salt = object_id, IKM = archive_root)
Kenc = HKDF-Expand-SHA256(PRK, "URMA/V0/private/content", 32)
Kmac = HKDF-Expand-SHA256(PRK, "URMA/V0/private/authentication", 32)
tag  = HKDF-Expand-SHA256(PRK, "URMA/V0/private/discovery", 16)
IV   = big_endian_u64(i) || zero[8]
ciphertext = AES-256-CTR(Kenc, IV, body)
MAC = HMAC-SHA256(Kmac, public_header || IV || ciphertext)

HKDF-Extract is HMAC with salt as HMAC key and the root as message. CTR encrypts the initial 128-bit big-endian counter, XORs the resulting keystream with the body, and increments that entire counter by one per block. No cipher padding is used. Internal random record padding is plaintext data covered by encryption and authentication. The 32,816-byte body consumes 2,051 AES blocks; adjacent index ranges start 2^64 blocks apart.

Every implementation uses this construction; portable recovery and negative vectors are mandatory conformance evidence (§12).

5.4 Validation and reconstruction

A reader MUST:

  1. Check exact record length, common header, nonzero count and index bounds.
  2. Derive the object's keys and tag locally and compare the derived tag with the stored discovery tag. A record whose stored tag differs is unrelated to this root: it MUST NOT be accepted for the archive even if its MAC would verify. A matching tag selects a candidate and authenticates nothing by itself; the MAC in step 3 decides.
  3. Verify the full HMAC over exactly bytes 0 through 32,895, and require the canonical IV, before decrypting or trusting encrypted metadata.
  4. Check decrypted length bounds, N = ceil(L/C), type, reserved flags and consistency with the object's other authenticated records.
  5. Accept identical authenticated duplicates idempotently. A different authenticated record for the same object/index is a conflict; arbitrary first-wins or last-wins resolution is forbidden.
  6. Gather every distinct index, remove only padding beyond the derived n, concatenate original chunks by index and verify both L and SHA-256.

Individual validation and inter-record consistency are distinct. An individually valid authenticated record has passed the structural checks, matching discovery tag, full MAC, canonical IV and all decrypted semantic checks of its own in steps 1–4. This does not yet establish agreement with another record. A valid MAC with invalid IV, L/count relation, type or flags is an invalid record, not an authenticated conflict. In contrast, incompatible object metadata between two records that each passed every individual check is an authenticated conflict, as are different complete authenticated record bytes at one object/index. Strict opening still rejects invalid records; discovery and the explicit recovery mode (§6.1) omit them with their respective reporting rules.

A truncated, malformed or authentication-failing record is invalid and MUST NOT be accepted as an authentic chunk. A set of valid records missing a required index is an incomplete object and MUST NOT be represented as a successfully recovered canonical object. Discovery or explicit recovery (§6.1) may nevertheless recover a complete validated object from retained records despite omitted invalid or unrelated candidates; this does not certify the supplied set as canonical or as a valid container. A conflict invalidates that object's reconstruction; other objects remain independent.

Discovery skips unrelated records and SHOULD reject/count forged candidates; in both cases it continues, and neither outcome blocks other objects. A strict offline open (§6) rejects a supplied set that contains an unrelated or invalid record. Zero authentic chunks reveal no discoverable object. A wrong root and absence of matching objects are not distinguishable from this data alone. Omission of a whole object by a provider cannot be disproved without additional inventory.

Erasure coding is not a normative recovery step. Optional application artifacts can supply candidate reconstructed bytes, which MUST pass ordinary validation.

6. Offline private-object container and independent recovery

The optional offline container contains exactly one private object:

common_header(kind = 03, flags = 0)
record_count_u32
(record_length_u32 || canonical_record_bytes) repeated record_count times

Record count is 1 through 2^32-1, and every record length is exactly 32,928. Total container length is 12 + record_count * 32,932. Readers MUST detect truncation, overflow and trailing bytes. The wrapper has no separate MAC; the records authenticate their identity, ordering, metadata and content.

In strict offline open, mixed objects are rejected. Identical duplicates do not satisfy missing positions. A strict offline open rejects any invalid or unrelated supplied record and outputs only the complete validated original. Implementations may stream records to bounded storage rather than allocating from an untrusted count.

Independent recovery requires the raw root, canonical records or accessible source bytes, and the source coordinates necessary to retrieve them. For a chain scan: select the exact chain/network, a start height no later than the first relevant reveal, and an accessible history source. No final manifest is necessary for canonical private recovery.

A system-tool implementation must be possible from §§4–6 and the referenced standard primitives alone; no particular tool defines correctness. Scripts MUST authenticate before decrypting and avoid exporting partial plaintext. Derived-key exposure in process arguments, temporary plaintext handling and constant-time limitations must be disclosed for each actual script; they are not protocol security guarantees.

An offline public-text export cannot discard the transaction proof and still claim authenticated authorship; §8 applies.

6.1 Optional explicit recovery from intact packed framing

An implementation MAY provide a separately and explicitly selected recovery mode for supplied bytes using the kind 03 packed framing. Strict offline open remains as specified above and in §5.4; it MUST NOT silently fall back to this mode. This mode recovers one object, not a valid container.

  1. The entire packed input MUST satisfy the existing common header (kind 03, flags 0000), count, per-record length and total-length rules of §6. Every advertised record slot is exactly 32,928 bytes, and the complete input is exactly 12 + record_count * 32,932 bytes. Overflow, truncation and trailing bytes MUST cause refusal. Record counts and lengths MUST NOT be rewritten after filtering. No search for a marker, resynchronization, framing repair or fallback decoder is permitted.
  2. Candidate classification applies only at an intact framed slot boundary. A slot that is not a recognized private candidate, such as bytes without the URMA marker or a well-formed common header of a recognized public kind, is omitted and counted as skipped_unrelated. Malformed URMA versions, unknown kinds or invalid flags are rejected records. Recognizing a public kind does not validate its public payload or proof. No marker search, autodetection or resynchronization is performed. Each private candidate MUST pass all individual checks of §5.4 before it can establish object identity or participate in a conflict: exact layout and bounds; locally derived matching discovery tag; full MAC and canonical IV before decryption; valid decrypted L/count relation, supported type and zero metadata flags. Invalid records and candidates classified as unrelated MUST be omitted and counted separately. A valid MAC alone is insufficient: invalid IV, length, count relation, type or flags still makes a record invalid and omitted. An unauthenticated header or object ID MUST NOT select the object, introduce a mixed-object result or poison retained authenticated records. Reading an untrusted O as the HKDF salt for validation does not establish identity; the strict-open mixed-object rejection above MUST NOT be used to reject an invalid candidate solely for its unauthenticated O in this mode.
  3. Exactly one distinct object ID O among the individually valid authenticated records is required. More than one such O MUST cause mixed-object refusal, even when one object is complete and another is partial. A record with a differing discovery tag is unrelated even if its MAC would verify; it cannot establish O. With no individually valid authenticated records, no object is recovered; the result MUST NOT claim success or establish that the root was correct or that matching data does not exist.
  4. For the one authenticated O, all retained records MUST agree on the digest, L, N, content type and metadata flags. A disagreement between individually valid records is an authenticated conflict. Different complete authenticated record bytes at the same index are also a conflict, including differences only in encrypted padding. Either conflict MUST refuse the object's recovery; first-wins, last-wins or selection of a hash-matching subset is forbidden. Identical authenticated duplicates remain idempotent and cannot supply a missing index. Invalid records from step 2 are omitted before these comparisons.
  5. Before any successful output, the entire input MUST have been consumed and every advertised record examined. A complete prefix MUST NOT cause early success: a later conflict, second authenticated O, framing failure or source failure still refuses recovery. All distinct indexes 0..N-1 MUST be present; only derived padding is removed, and the ordered original bytes MUST match both authenticated L and SHA-256. Missing indexes mean incomplete recovery, not partial success. A partial recovery output file MUST NOT be created or exposed; bounded internal staging MUST NOT be presented as recovered output.
  6. Source/I/O errors and declared capacity failures MUST cause refusal, not be counted as skippable records or reported as successful/empty recovery. On success the result MUST be identified as recovered-object, with rejected_records counting omitted invalid records and skipped_unrelated counting omitted unrelated or non-private-candidate slots. These counters are diagnostic classifications, not proof of origin, corruption cause or a foreign root: missing marker bytes or a differing discovery tag may also be damage to records originally belonging to the supplied root. It MUST NOT assert that the supplied container passed strict validation. These counts are result metadata, not new fields in the serialized format.

7. Public record layouts

7.1 Atomic text, reply, identity profile and avatar

All public kinds use the common 8-byte prefix, flags zero, and the single Taproot publication profile in §8. Each record occupies one separate reveal transaction. The complete serialized record MUST be at most 32,768 bytes. The prefix determines one fixed body layout; no JSON, CBOR, TLV, extension map, lengthless intermediate field, separator or terminator is permitted.

Kind Body starting at offset 8 Complete length
02 plain post Exact UTF-8 text consuming all remaining bytes 8–32,768
04 reply Raw target TXID[32], then exact UTF-8 text consuming the remainder 40–32,768
05 profile Exact UTF-8 pseudonym of 0–128 bytes consuming all remaining bytes 8–136
06 avatar Exactly 128 raw indexed-pixel bytes; §7.1.3 Exactly 136
0C profile record Profile identifier[8], then opaque payload consuming all remaining bytes; §7.1.2 16–32,768

The reply target occupies offsets 8–39. Its bytes use transaction-hash wire order, as in a serialized transaction input's previous-outpoint hash. Convert a usual displayed TXID hex string by decoding hex and reversing the 32 bytes; reverse again for display. It refers to the selected chain/network, which is not duplicated in the record. Reply text starts at offset 40. There is exactly one target and no separate quote kind.

Text and pseudonyms MUST be valid UTF-8. Their bytes, including embedded U+0000 if present, are data, never terminators. Readers MUST NOT normalize Unicode, trim whitespace, change line endings, case-fold or reserialize before verification or recovery. Empty UTF-8 is structurally valid; client composition/display policy is separate. The text budget is 32,760 bytes for a plain post and 32,728 for a reply. The avatar body is binary and MUST NOT undergo UTF-8 decoding.

The kind 05 pseudonym body MUST contain at most 128 octets of valid UTF-8, measured after UTF-8 encoding and excluding the common eight-byte header. Complete kind 05 lengths are 8 through 136 bytes, inclusive. Empty UTF-8 remains valid, as does embedded U+0000 within the byte budget. The limit is not a count of Unicode scalar values, UTF-16 code units or grapheme clusters: for example, 64 copies of U+03B1 occupy 128 UTF-8 bytes and fit, while 65 occupy 130 bytes and MUST be rejected. Readers and writers MUST reject a longer kind 05 body; they MUST NOT normalize or truncate it to make it fit. No separator, terminator, timestamp, address or additional field is appended to the pseudonym. This ceiling applies only to kind 05, not to kind 0C's opaque application profile payload or to any other public text kind.

A plain post contains only text after its prefix. A reply contains only its fixed target and text. Profiles and avatars are independent author-authenticated transactions; neither is embedded in a post or reply. Identity-state resolution, caching and presentation are outside this protocol. These atomic kinds do not carry multipart content; kinds 07–09 have separate layouts and reuse the same transport.

Each public record is identified by its reveal TXID within the configured chain/network. Identical text in different transactions creates different posts. An unresolved target does not invalidate a reply's own bytes or author proof.

A different layout requires a newly assigned kind or protocol version, except for the explicit revision 0.9 amendment to kind 06 in §7.1.3 and the revision 0.11 kind 05 ceiling amendment above. A future kind containing multiple variable-width fields MUST use explicit lengths; parsers MUST NOT infer boundaries from contents or append fields to a V0 kind.

7.1.1 Structured wires and hashtags (kinds 0A and 0B)

Kinds 0A and 0B are structured forms of kinds 02 and 04, which remain valid. The same 8-byte header, zero flags, one reveal, author proof and 32,768-byte complete record cap apply.

A kind 0A body contains the following fields in order. A kind 0B body first contains one raw 32-byte target TXID in the same wire order and selected-network context as kind 04, followed by these same fields:

Field Encoding
Wire length u8, 0–32
Wire Exactly wire-length ASCII bytes; zero length means no wire
Hashtag count u8, 0–8
Each hashtag u8 length (1–32), followed by exactly that many ASCII bytes
Text length u16 little-endian, number of UTF-8 bytes
Text Exactly text-length bytes, valid UTF-8, preserved without normalization

After text, the parser MUST be at the exact end of the record. Truncation, trailing bytes, excess lengths/counts and complete records above the cap are invalid. Empty text is structurally valid. Minimum complete lengths are 12 bytes for 0A and 44 for 0B. Metadata consumes part of the total byte budget; the u16 text field does not authorize text exceeding the record cap.

Each nonempty label MUST match [a-z0-9][a-z0-9_-]{0,31}. Wire names are single flat labels; there are no subchannels. / and # are display prefixes, never encoded label bytes. Hashtags MUST be strictly ascending in ASCII byte order, which also excludes duplicates. Decoders MUST reject noncanonical bytes, not normalize them.

Each record has zero or one wire and independently zero to eight hashtags. A hashtag denotes the same subject across wires within the selected network. Wire and hashtag labels are entirely open: no registration, owner, privilege, authorization or protocol moderation is implied. A label proves no affiliation.

A reply may target a post or reply (02, 04, 0A or 0B) on the selected network. Its wire and hashtags are its own declared metadata, not implicitly inherited from the target. An unresolved target does not invalidate the reply's own bytes or proof. Existing 02/04 text containing /labels or #words remains literal text, with no structured metadata inferred. Records without metadata may use 02/04; empty metadata in 0A/0B is also structurally valid. A reader that does not support 0A/0B rejects them as unsupported, rather than misreading their text.

Structured fields support deterministic grouping; they do not remove the need to discover, verify and index chain records.

7.1.2 Atomic profile record (kind 0C)

Kind 0C carries an application profile's bytes in one atomic record. The same 8-byte header, zero flags, one reveal, author proof and 32,768-byte complete record cap apply. Its body is:

Offset Length Field
8 8 Profile identifier
16 0–32,752 Payload consuming all remaining bytes

Complete lengths are 16 through 32,768 bytes. The profile identifier is authenticated but uninterpreted by the core; eight zero bytes mean unspecified profile, and an unknown identifier does not make an otherwise valid record invalid. The payload is opaque to the core: it undergoes no UTF-8 decoding, and bytes resembling another URMA record, manifest or archive trigger no parsing. Payload layout, meaning and validation belong to the profile's own specification; conformance to a profile is separate from URMA conformance, and the core never depends on a profile interpreter. A reader that does not support kind 0C rejects it as unsupported.

7.1.3 Fixed indexed-pixel avatar (kind 06)

A kind 06 avatar MUST contain exactly 128 body bytes at offsets 8–135 after the common eight-byte header 55 52 4d 41 00 06 00 00: exactly 136 bytes for the complete record. Its image is exactly 16×16 pixels, four bits per pixel. There is no additional avatar header, dimension field, length field, palette field, alpha channel, padding, row-stride field or trailing data.

Rows run from top to bottom and pixels within each row from left to right. For coordinates 0 <= x < 16, 0 <= y < 16, let i = 16*y + x. Pixel i is in body byte floor(i/2), at complete-record offset 8 + floor(i/2) = 8 + 8*y + floor(x/2). The left/even-indexed pixel MUST use the high nibble (bits 7–4); the right/odd-indexed pixel MUST use the low nibble (bits 3–0). Every row occupies exactly eight consecutive bytes. For example, the top row with palette indexes 0 1 2 3 4 5 6 7 8 9 A B C D E F is encoded as 01 23 45 67 89 AB CD EF at record offsets 8–15. Offset 8 contains pixels (0,0) and (1,0); offset 15 contains (14,0) and (15,0). Row 1 begins at offset 16 with (0,1) and (1,1); the last byte, offset 135, contains (14,15) and (15,15). Each pair is high nibble first, then low nibble.

The following exact 16-entry palette defines every nibble value. Each hex triplet is eight-bit R, G, B in standard sRGB, in that order. All colors are fully opaque; an RGBA renderer uses alpha 255, but no alpha bytes are encoded. Implementations MUST use these values when interpreting the indexed pixels; they MUST NOT substitute a palette or infer color values from the body.

Index RGB hex Index RGB hex
0 000000 8 555555
1 0000AA 9 5555FF
2 00AA00 A 55FF55
3 00AAAA B 55FFFF
4 AA0000 C FF5555
5 AA00AA D FF55FF
6 AA5500 E FFFF55
7 AAAAAA F FFFFFF

Every possible 128-byte body after a valid kind 06 prefix is valid indexed-pixel data: every nibble is an index 0–F. Bytes that happen to resemble PNG, GIF or another file signature remain pixel indexes and MUST NOT be blacklisted for that resemblance. Common record validation and the author proof still apply. The body MUST be interpreted solely as these raw indexed pixels, never as UTF-8, a compressed image, a file format or executable content. Readers MUST NOT sniff a codec, autodetect dimensions, accept a supplied palette or metadata, or try a fallback decoder. Any different body or complete-record length is invalid, including the former 512-byte body and 520-byte complete record. No implicit conversion, crop, resampling or migration from the former 32×32 layout is specified. Existing published records are not rewritten; former-format avatars are not accepted as kind 06 under this amended V0. The normative revision 0.8 Markdown bytes remain unchanged; its active PDF may receive an author-only editorial correction without adopting this amendment.

The constrained resolution and palette reduce representable visual detail; they do not make abusive content impossible. Symbols and drawings, including harmful ones, remain representable. These bytes define an inert pixel format, not a guarantee that every drawing is lawful or harmless. Local display and identity-hiding policy remain application choices.

7.2 Constants, byte conventions and geometry

All offsets below are absolute within a canonical record. The common eight-byte URMA header is unchanged. Flags and explicitly reserved fields are zero. Integers in these records are unsigned little-endian. SHA-256 fields contain the 32 raw digest bytes, without display-order reversal. TXIDs contain the existing transaction-hash wire order from §7.1; displayed hexadecimal is converted by reversing the decoded 32-byte value.

Symbol Value / definition
R 262,144 maximum bytes per complete multipart record
C R - 12 = 262,132 original object bytes per data part
F 511 data references per leaf
G 511 leaf references per root
L Original public object payload length
N max(1, ceil(L / C)) data parts
M ceil(N / F) leaves

The explicit object ceiling remains 8,553,279,476 bytes, including profile framing. It is not enlarged by the larger record capacity. Readers MUST require 0 <= L <= 8,553,279,476, derive N and M as above, and require 1 <= N <= 32,630 and 1 <= M <= 64. Fanout remains 511; the largest permitted object has 32,630 parts, 64 leaves and one root (32,695 records). The final part at that ceiling has 174,448 payload bytes. The structural 511-by-511 tree capacity does not authorize larger objects.

Readers MUST NOT fall back to 32,756-byte slices or accept a root whose counts encode that rejected geometry; there is no legacy interpretation.

For L > 0, part i has exactly min(C, L - i*C) payload bytes. For L = 0, there is exactly part 0 with zero payload. There are no dummy parts, variable chunk sizes, padding records or direct root-to-data shortcuts.

Leaf j covers indices j*F through j*F + min(F, N-j*F)-1. Every nonfinal leaf therefore has exactly 511 entries. The root has exactly M entries in increasing leaf-index order. A one-part object still uses all three levels. Use checked arithmetic for every count, offset and multiplication before I/O or allocation.

7.3 Kind 07: public data part

Offset Bytes Field
0 8 Common header: version 00, kind 07, flags zero
8 4 Global part index i, u32
12 0–262,132 Exact consecutive object payload bytes

Record length is 12 through 262,144 bytes. The zero-payload case is legal only when the root declares the empty object above. Index and exact payload length are validated against the root/leaf context; reject i >= 32630 even before that context is available. A structurally valid detached part does not by itself establish a complete object.

The payload is opaque. Bytes resembling another URMA record, archive, executable or manifest do not trigger parsing or recursive traversal.

7.4 Shared child-reference entry

An entry is exactly 64 bytes:

child_reveal_txid[32] || SHA256(exact_complete_child_record)[32]

Hash the complete child record including its common header, not a decoded object, application payload, script or transaction. The TXID locates the reveal; the record hash binds expected content independently of witness-excluding TXID semantics. No URLs, block heights, filenames, inferred ordering or index-service IDs occur in these references.

7.5 Kind 08: public leaf manifest

Offset Bytes Field
0 8 Common header: version 00, kind 08, flags zero
8 2 Leaf index j, u16
10 2 Data entry count k, u16
12 4 First global part index, u32
16 64*k Ordered references to kind 07 reveals

Readers MUST require j < 511, 1 <= k <= 511, first_index = j*511, exact length 16 + 64*k, and no trailing bytes. The maximum leaf length remains 32,720 bytes; unused capacity under the 262,144-byte multipart record ceiling MUST NOT be padded. In object context also require j < M and k = min(511, N-first_index). Entry r requires part index first_index+r. Leaves cannot reference leaves or roots.

7.6 Kind 09: public root manifest

Offset Bytes Field
0 8 Common header: version 00, kind 09, flags zero
8 8 Original object length L, u64
16 32 SHA-256 of the complete original object payload
48 4 Total data part count N, u32
52 2 Total leaf count M, u16
54 2 Reserved, zero
56 8 Opaque application profile identifier
64 64*M Ordered references to kind 08 reveals

Readers MUST require the geometry of §7.2 and exact length 64 + 64*M, from 128 through 4,160 bytes under the explicit object ceiling. Unused capacity under the 262,144-byte record ceiling MUST NOT be padded. Root entry j requires leaf index j. The profile identifier is authenticated but uninterpreted by the core. Eight zero bytes mean unspecified application; unknown identifiers do not make an otherwise valid URMA object invalid. Profile-specific interpretation is outside core validity.

The root reveal TXID is the locator. A root does not contain its own TXID. There is no content-derived substitute for this locator and no global object-ID registry. Re-publication in another root transaction produces another locator.

7.7 Author proof, membership and rejection

Every root, leaf and data part uses the existing canonical Taproot commit/reveal profile in §8. All three kinds are allowed on-chain. A commit may fund multiple public reveals as a transport-client choice; each reveal still carries exactly one complete record and retains the current transaction shape.

Let A be the root's verified BIP 340 x-only author. Readers MUST require every referenced leaf and data reveal to verify under that same A. No detached signature is added. Funding keys do not establish authorship. Signatures inside an application payload are preserved content, not a second URMA author proof.

For each node, verify the full reveal, actual commit outpoint/amount/script, canonical script, single-leaf commitment, control block and SIGHASH_DEFAULT signature exactly as in §8. Parsing a key or comparing a TXID alone is insufficient. Preserve witnesses for offline verification.

Maintain a graph-wide set initialized with the root TXID. Reject any reference to an already referenced reveal TXID, including the root itself, repeated leaves, repeated data within/across leaves and ancestor references. Also enforce the kind required at each edge. Together these rules reject cycles and aliasing; no generic recursive graph walker is needed. Identical payload bytes at two positions are allowed, but their records have different indices and distinct reveal TXIDs. Different roots may independently reference the same otherwise valid records; duplicate prohibition is per root graph.

At a source boundary, a reveal with the wrong TXID, or one whose proof does not verify, is an invalid candidate: discard it and try another source. Such a candidate does not poison authentic data already retained. Verify the proof before judging the record hash. Once the reveal at the requested TXID has a fully verified proof, the record it carries is fixed by the commitment; if that record's SHA-256 differs from the reference in the signed manifest, the object structure is invalid, and no other source is tried for that object. A signed manifest containing duplicate slots, wrong geometry or an impossible reference type is likewise invalid object structure. Never resolve disagreements by first-wins or last-wins selection.

7.8 Completeness and independent recovery

Recovery from a known (chain, root_txid) is:

  1. Fetch and verify the root and its commit context; validate bounded geometry.
  2. Fetch every named leaf; verify its proof and author, then check its record hash against the reference and its position.
  3. Fetch every named part; verify its proof and author, then check its record hash against the reference, its index and its length.
  4. Concatenate payloads by global index. Check exact L and root SHA-256.
  5. Only then export the complete generic payload or claim full application-profile validation. A bounded diagnostic inspection of already authenticated segments MAY precede completeness, but MUST be labelled partial and MUST NOT establish whole-object integrity, profile acceptance or successful recovery.

The public manifest hierarchy is mandatory inventory for this family. The existing rule that private bytes can be recovered without a final manifest remains true for private objects; it must not be generalized to this family. Missing a leaf or part means incomplete recovery, even if an application could use some available bytes. Source failure is distinct from missing data. No verified partial payload is labelled a complete snapshot.

Cryptographic validity, generic completeness, application-profile validity, chain inclusion and confirmations are separately reported. A reorg does not corrupt retained bytes. URMA has no all-or-nothing chain transaction spanning this graph.

Implementations MUST bound I/O, memory and storage before allocation. They MAY refuse a valid graph exceeding declared local capacity; such a refusal is not invalidity. Reconstruction MUST use bounded staging and MUST NOT expose a partial payload as a completed object. Partial diagnostic inspection is not a complete payload handoff. Source adapters must bound raw responses and retries before decoding. No profile parser is a prerequisite to generic recovery.

8. Canonical Taproot publication profile

8.1 Transaction and key roles

For public records, A is the stable x-only public author key. For a private record, A is a transport signing key: it authorizes the transaction and is not private-content authorship. It MUST NOT be derived from the archive root. It MAY be reused across private records and MAY be the writer's author or funding key; V0 requires no per-record separation.

One reveal carries exactly one complete record of kind 01, 02, 04, 05, 06, 07, 08, 09, 0A, 0B or 0C. The offline container kind 03 MUST NOT be accepted as an on-chain record. A commit creates the native P2TR output that this reveal spends. A commit may fund several private or public reveals; it does not make their object complete until all required canonical records are recoverable.

The reveal has transaction version 2, exactly one input spending its commit outpoint, an empty scriptSig, and one native SegWit return output (P2WPKH or P2TR). Output value, fee, nLockTime and nSequence are chosen before signing and must satisfy the selected chain's rules; they are not content metadata. The commit's other funding/change details are transport-client concerns. A funding signature alone never establishes public author identity.

8.2 Script and commitment

There is one tapscript leaf, leaf version c0, and no additional Merkle path. The internal key is A. The leaf is precisely:

PUSH32(A) OP_CHECKSIG
OP_0 OP_IF
  PUSH4("URMA")
  PUSH(record segment 0) ... PUSH(record segment k)
OP_ENDIF

Split the canonical record into consecutive 520-byte segments plus one final nonempty segment if needed. No empty trailing segment is added. Use minimal push encodings: a one-byte segment 01..10 uses OP_1..OP_16 and byte 81 uses OP_1NEGATE; all other segments of length 1–75 use direct length (including a one-byte 00 segment, encoded as 01 00). Lengths 76–255 use PUSHDATA1; 256–520 use PUSHDATA2 with a little-endian length. The parser reconstructs number-opcode segments as their one-byte values, requires canonical segmentation and rejects extra opcodes/data. The separate OP_0 before OP_IF is structural and contributes no record bytes.

The native commit output is OP_1 PUSH32(Q.x), where the Taproot output key Q is the BIP 341 tweak of internal key A with this exact single-leaf script. The control block is 33 bytes: c0 OR Q parity, followed by A. Reject a different internal key, leaf version, path length or output commitment.

8.3 Author proof

The reveal witness is exactly:

[64-byte Schnorr signature, exact tapscript, 33-byte control block]

There is no annex, detached application signature or extra witness item. Only SIGHASH_DEFAULT is accepted: a 64-byte BIP 340 signature with no appended sighash byte. The signature message is the BIP 341 transaction message with the BIP 342 script-path extension: ext_flag=1, key_version=0, this tapleaf hash, and codesep_pos=0xffffffff because the script has no code separator.

A verifier needs the full reveal, the exact commit prevout amount/script, the tapscript and control block. It MUST validate the commitment and Schnorr signature against A and the actual transaction/prevout context. Merely parsing a public key from a script, validating a commit funding signature or looking up a TXID is insufficient.

This binds the public record through its script-path commitment and author authorization. The TXID identifies the post, but does not itself commit to witness bytes. An offline public proof therefore retains both transactions/witness and the required prevout context; chain inclusion evidence is a separate claim.

8.4 Networks and discovery

Bitcoin and Litecoin use distinct chain contexts; network identity MUST be checked before fetching or submitting. Mainnet support cannot be inferred from test-network software. Feed identities, TXID references and observations are scoped to the configured chain/network. Cross-chain replies and a merged canonical feed are absent.

A scanner searches input witnesses for the exact envelope and validates the record and its proof. It MUST NOT treat an arbitrary occurrence of the marker as a valid record. Chain sources must preserve inclusion-evidence semantics.

Full consensus/chain selection may be supplied by a validating node. Bitcoin raw-block verification includes the witness commitment, not only the transaction Merkle root. A reader that takes Litecoin's scrypt proof of work, MWEB or inclusion validation from a validating node has delegated them; that delegation is not independent consensus validation by a URMA parser.

Record structural/cryptographic validity, chain inclusion, confirmation count, provider provenance and client lifecycle remain distinct. The canonical record does not contain a broad publication-state machine.

9. Private-object invariants and implementation boundaries

Each object is an independent reconstruction boundary. V0 defines no atomic archive/scan/account transaction. Implementations may export complete authentic objects while reporting other incomplete objects separately.

Source failures and reorg observations are not cryptographic record failures. An implementation MUST NOT claim a scan completed or a historical placement remains confirmed when its source/evidence does not establish that claim. Retained authentic private bytes do not become corrupt merely because their observed placement was orphaned.

Resource caps MUST be declared as local implementation capacity. Parsers must bound their work and storage without trusting unauthenticated lengths. Normative integer bounds, exact byte layouts and application capacity refusals must not be conflated.

Publication persistence, scheduling, quotes, fee budgets, retry/timeout handling, partial-success exit status, parity artifacts, UI labels and caches are outside this protocol.

10. Security model

Encryption does not hide marker use, chunk grouping/count, publication timing or funding relationships. Root compromise exposes that compartment's objects. An authorized holder can retain or redistribute recovered bytes.

Public author proof establishes authorization by a pseudonymous key, not the person's civil identity, uniqueness of display name, truth of a statement or capture time. Private MAC authentication likewise does not prove a photographed scene was genuine.

Recovery requires bytes that left the lost device, retained accessible copies and surviving secrets for private objects. Chain publication supplies no unconditional eternity, connectivity or censorship-resistance guarantee. External media linked by public text is mutable and not canonically recovered.

The originating application and operating system can see the original plaintext; nothing in the protocol protects plaintext held on that device.

11. Versioning and licensing

A change to accepted bytes, key derivation domains, mandatory validation or meaning requires an explicit versioned specification update and new vectors. Unknown versions are never guessed. Compatibility follows the serialized marker, version and kind bytes; a project name confers none.

The explicit revision 0.9 amendment retains version 00 and kind 06 but changes avatar length and interpretation (§7.1.3). Thus the common header alone does not distinguish that amended layout from the former one. Implementations of this revision MUST reject former 512-byte avatar bodies and 520-byte complete records; they MUST NOT infer a historical layout, convert it implicitly or claim compatibility with those avatars. Retaining the wire byte does not update immutable published artifacts, stored records or old software.

The explicit revision 0.11 amendment retains version 00 and kind 05 but reduces the valid UTF-8 pseudonym ceiling from 32,760 to 128 body bytes (§7.1). Otherwise-valid former kind 05 bodies of 129–32,760 bytes, complete lengths 137–32,768, are invalid under this amendment. Their unchanged 00/05 header does not select the former ceiling. Implementations MUST NOT accept them through a historical-limit fallback or silently truncate, normalize or migrate them. Former kind 05 records whose bodies already meet the new byte limit remain valid if they satisfy every other rule. Kind 0C remains 16–32,768 complete bytes; no other kind's layout, limit or acceptance rule changes.

The specification, scientific whitepaper, public vectors and protocol data are CC0-1.0. Project-authored source and reference applications are 0BSD. Dependencies retain their own licenses.

12. Conformance and completion

URMA V0 conformance requires a public deterministic corpus derived from this specification, with exact input/output bytes and expected outcomes. Self round-trips alone are insufficient.

An implementation claiming optional recovery under §6.1 MUST additionally provide vectors distinguishing strict opening from recovery: invalid/unrelated records before and after a complete object; MAC-valid but individually invalid IV/length/count/type/flags; unauthenticated object-ID changes; no authentic records; incomplete objects; identical duplicates; full-record and metadata conflicts; a second authentic O even if partial; framing failures and forbidden trailing bytes. Source/capacity refusal and the absence of early/partial output MUST also be verified. No recovery vector changes strict acceptance outcomes.

The corpus must cover private key derivation, encryption/MAC, padding and count, all integer and size boundaries, invalid records versus incomplete objects, unrelated records including a differing discovery tag with a valid MAC, duplicates/conflicts, unknown versions/flags/kinds, public UTF-8 preservation, atomic caps, author proof, script/commitment/sighash rejection, and every other normative rejection. Include every public kind from §7, exact reply TXID byte order, truncated targets, profile/UTF-8 handling, avatar lengths and forbidden appended fields.

For kind 05, the corpus MUST cover an empty body, exact 127/128/129-byte body boundaries (135/136/137 complete bytes), the former 32,760-byte body maximum, valid multibyte UTF-8 that fits or exceeds the octet limit, invalid UTF-8 within the limit, embedded U+0000 and preservation without normalization or truncation. It MUST distinguish kind 05's 128-byte body ceiling from the unchanged limits of other text kinds and kind 0C. Passing historical vectors under the former profile ceiling does not establish conformance to this amendment.

For kind 06, the corpus MUST cover a 128-byte body and 136-byte complete record, the 16×16 coordinate-to-byte and nibble mapping, every exact palette index, opaque RGB8/sRGB interpretation and rejection of shorter, longer, padded and former 512/520-byte encodings. It MUST include a correctly sized body with bytes resembling a file signature, accepted strictly as indexed pixels.

For 07–09, the corpus MUST additionally cover empty objects, every record/count boundary, 511-entry leaves, multiple leaves, exact two-level geometry, order, duplicates/cycles/wrong edge kinds, missing nodes, candidate TXID and proof failures versus invalid signed structure, including a fully verified child whose record hash differs from its signed reference, author mismatch, unknown opaque profiles, reserved/trailing bytes, checked overflow and bounded exact recovery. For 0C, the corpus MUST cover the minimum and maximum complete lengths, an empty payload, zero and unknown profile identifiers, truncation below 16 bytes and the absence of any inferred payload structure.

An independent reader MUST verify public graph proofs and reconstruct payloads without the URMA binary or SDK. Existing 01–06 results remain regression gates. Maximum-count arithmetic vectors MUST be labelled sparse; claiming the full 8,553,279,476-byte capacity requires an actual streamed maximum reconstruction with measured memory, disk and time. Public-chain acceptance requires separate evidence.

At least one independent implementation must recover private original bytes and reject invalid authentication without an URMA binary, SDK, service or implementation database. Each supported chain transport needs published evidence under its declared validation model. Scripts, code and vectors must agree before the format revision is complete.

Conformance, stability and external audit are separate claims; an audit claim requires an attributable report. A public corpus and readers are published in the project repository (https://github.com/urma-protocol/urma); implementation status and evidence are recorded outside this document.

13. Primary specifications

14. Decision traceability

The following internal decision identifiers record provenance. The requirements are fully stated in the sections cited; no internal document is needed to implement them.

Owner decisions Location
D-001–D-003 §§1, 4, 11
D-004–D-005 §§2–3; Capture, Archive and Wire application contracts
D-006–D-007 §§5–6, 12
D-008–D-010 §§3, 7–8; Wire profile rules
D-011–D-012 §§5.4, 9; shared export services and Archive Redundancy Profile
D-013–D-014 §7; Wire reply/profile/avatar application rules
D-015 §§2, 5, 7
D-016–D-018 Wire rollback/visibility and shared publication persistence
D-019 §§3, 6
D-020–D-021 Wire external-image behavior
D-022–D-023 §§5.1, 9, 12
D-024–D-025 §§2, 8, 11–12
D-026 §§4, 7; Wire separate profile/avatar transactions and cache
D-027–D-029 §§1–3, 9; architecture, profiles, applications and funding documents
D-030 §§2, 4, 7.2–7.8, 8, 12; generic public multipart allocation
D-031–D-032 Git profile/application and shared-services contracts; no Git semantics in core
D-033 §3; one public identity may both sign author proofs and fund transactions
D-036 §§2, 4, 7.1.2, 8.1, 12; atomic profile record kind 0C; registry profiles outside the core

D-035 governs the multipart record capacity and the separately retained object ceiling in §7.2. D-036 governs kind 0C; the names registry profile that uses it is a separate interoperable specification, not a core rule. The owner decisions of 2026-10-07 govern key roles (§3, §8.1), discovery-tag acceptance (§5.4, §6) and the classification of a record-hash mismatch after a verified proof (§7.7, §7.8). The owner amendment of 2026-10-09 (D-040) governs the fixed avatar layout and palette (§7.1.3) and its incompatible interpretation under retained 00/06 (§11). The owner decision of 2026-10-09 adds the explicit optional recovery mode (§6.1) under document revision 0.10, preserving strict opening and serialized bytes. The owner amendment of 2026-10-10 narrows only kind 05 to 128 UTF-8 body octets under local document revision 0.11 (§7.1, §11); wire version 00 and kind 05 are retained, with no implicit migration.

15. Document revisions

This list is history, not a rule; every rule is stated in the sections cited.

  • 0.4: public multipart kinds 07–09 under wire version 00; kinds 01–06, their byte layouts, private cryptographic domains and Taproot validation unchanged.
  • 0.5: the multipart geometry of §7.2 retained.
  • 0.6: atomic profile record kind 0C (§7.1.2); kinds 01–0B unchanged.
  • 0.7: no byte layout, key derivation or kind changed; discovery-tag acceptance (§5.4, §6), record-hash mismatch after a fully verified proof (§7.7, §7.8), shared author and funding key (§3), reusable private transport key (§8.1).
  • 0.8: no rule changed; implementation state, historical evidence and dependencies on auxiliary documents removed, so that this document stands alone.
  • 0.9 (2026-10-09, local and unpublished): incompatible amendment of kind 06 to 16×16 four-bit indexed pixels, exactly 128 body bytes and 136 complete bytes, with a normative fixed RGB8/sRGB opaque palette and explicit pixel ordering (§7.1.3). Wire version 00 and kind 06 are retained; former 512-byte bodies and 520-byte records are invalid under this amendment, with no autodetection or implicit migration (§11). Other kinds, private cryptographic domains and Taproot validation are unchanged. The normative revision 0.8 Markdown bytes and historical snapshots remain unchanged. An author-only editorial correction of the active revision 0.8 PDF is separate from this amendment; it retains revision 0.8 protocol content.
  • 0.10 (2026-10-09, local and unpublished): explicit optional recovery from intact kind 03 packed framing (§6.1). Invalid/unrelated records are omitted and counted; one fully validated authenticated O is required, mixed objects and authenticated conflicts refuse recovery, and the entire input is examined before complete L/SHA-256-verified object output. Strict opening, framing, source/capacity refusals, all serialized bytes, wire version 00 and the avatar amendment of revision 0.9 remain unchanged.
  • 0.11 (2026-10-10, local and unpublished): incompatible kind 05 ceiling amendment (§7.1, §11): valid UTF-8 body lengths 0–128 octets, complete record lengths 8–136 bytes. Empty profiles remain valid; byte preservation and UTF-8 validation remain mandatory. Wire version 00 and kind 05 are retained; otherwise-valid former bodies of 129–32,760 bytes are rejected, without implicit truncation or migration. All other kinds, including kind 0C, cryptographic domains, Taproot validation, the avatar amendment and optional recovery rules remain unchanged. No publication or update of historical artifacts is claimed.