Network Working Group K. Wildfeuer Internet-Draft Wildhaven Homes LLC Intended status: Informational 17 October 2026 Expires: 20 April 2027 RAPP/1: The RAPP Protocol Suite draft-wildfeuer-rapp-1-00 Abstract RAPP/1 is a profile over existing standards that lets independent programs exchange verifiable, content-addressed records about agents: one canonicalization (JCS), one domain-separated hash, one mint-once identity, one eleven-key event envelope, one wire, and one package format. Two independent implementations that follow this document produce byte-identical artifacts with no out-of-band agreement. The normative text of record is the append-only specification chain published by the author; this document is a stable, archival rendering of it: revision rev-18, chain frame 83bec7762a04959dc91e252055d79f67690c605383018d23b26b99982916dd6a, normative SHA-256 e81e20ba9402ee96422f73e9ef737eba100264155c8f77521600766be8fb21e9. Any later revision supersedes this rendering; the chain, not this document, says which is current. Status of This Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at https://datatracker.ietf.org/drafts/current/. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on 20 April 2027. Copyright Notice Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved. Wildfeuer Expires 20 April 2027 [Page 1] Internet-Draft RAPP/1 October 2026 This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/ license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1. The layered model . . . . . . . . . . . . . . . . . . . . 3 2. Requirements language . . . . . . . . . . . . . . . . . . . . 4 3. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4 4. Canonicalization (L1) . . . . . . . . . . . . . . . . . . . . 4 5. Content addressing (L1) — domain-separated . . . . . . . . . 5 6. Identity — the rappid (L2) . . . . . . . . . . . . . . . . . 6 6.1. stream_id and kind grammar . . . . . . . . . . . . . . . 6 6.2. Minting (mint-once) . . . . . . . . . . . . . . . . . . . 6 6.3. Canonicalization on read; provisional identifiers . . . . 7 7. The Frame (L4) . . . . . . . . . . . . . . . . . . . . . . . 8 7.1. Kind families (one envelope, registry-bound families) . . 8 7.2. Particle and wave (the unification) . . . . . . . . . . . 8 7.3. Chaining, time, and merge order . . . . . . . . . . . . . 9 7.4. Verification (the complete consumer checklist) . . . . . 9 7.5. Heads and forks . . . . . . . . . . . . . . . . . . . . . 10 8. The Wire (L3) . . . . . . . . . . . . . . . . . . . . . . . . 11 9. The Egg (L5) — the single egg spec of record . . . . . . . . 11 9.1. Container, manifest, and egg address . . . . . . . . . . 12 9.2. Variants (the ratified set — closes EGG-01) . . . . . . . 13 9.3. Sealed artifact profile . . . . . . . . . . . . . . . . . 14 9.4. Conformance . . . . . . . . . . . . . . . . . . . . . . . 17 9.5. Instantiation — instance identity and grown_from lineage (rev-6; closes the hatch seam) . . . . . . . . . . . . . 17 10. Trust and signatures (L2) . . . . . . . . . . . . . . . . . . 18 11. Conformance classes . . . . . . . . . . . . . . . . . . . . . 19 11.1. Immutable Grail kernel conformance . . . . . . . . . . . 20 11.2. Operational conformance profiles . . . . . . . . . . . . 22 12. Versioning, evolution, no-legacy . . . . . . . . . . . . . . 23 12.1. Re-genesis (converging an immutable chain — one owner-authorized operation) . . . . . . . . . . . . . . 24 12.2. DOGG specification revision chain . . . . . . . . . . . 25 12.2.1. Immutable bootstrap boundary . . . . . . . . . . . . 25 12.2.2. Revision payloads and historical exception . . . . . 26 12.2.3. Content-addressed publication and resolution . . . . 27 12.2.4. Rev-14 transition and draft replacement . . . . . . 28 13. The registry — an estate's signed root of trust (append-only) . . . . . . . . . . . . . . . . . . . . . . 29 13.1. Trust anchor and registry authentication . . . . . . . . 29 13.2. Owner succession (time-scoped authority) . . . . . . . . 30 Wildfeuer Expires 20 April 2027 [Page 2] Internet-Draft RAPP/1 October 2026 13.3. Entry types (each a §4 value; document schema:"rapp/1-registry") . . . . . . . . . . . . . . . 30 14. Security considerations . . . . . . . . . . . . . . . . . . . 31 15. References . . . . . . . . . . . . . . . . . . . . . . . . . 33 15.1. Normative References . . . . . . . . . . . . . . . . . . 33 15.2. Informative References . . . . . . . . . . . . . . . . . 34 Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 35 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 35 1. Introduction RAPP is a content-addressed distributed organism. Its integrity rests on one invariant: *the same concept has the same bytes everywhere.* This document specifies, normatively and completely, five load-bearing primitives so any two independent implementations interoperate *byte-for-byte with no out-of-band agreement*: canonicalization (§4), content addressing (§5), identity (§6), the frame (§7), the egg (§9) — all riding one wire (§8): POST /chat, or a signed append-only frame. Implementations add agents, cartridges, and registered kinds — never new endpoints, never new envelopes. *Repository scope.* kody-w/RAPP is the canonical public RAPP foundation, product home, reference implementation, organism model, and philosophy. kody-w/rapp-1 is the canonical interoperable protocol authority. This specification governs wire-compatible bytes and conformance; it does not absorb the RAPP product or any downstream Rappter/RapterBox LLC product. *Specification authority.* The append-only DOGG chain at anchor/ chain.jsonl carries the normative revision content. Its hashes prove integrity, not authority or ratification. Until an authenticated RAPP registry/checkpoint exists, the chain accepted by the owner onto protected canonical kody-w/rapp-1 main is authoritative; an immutable accepted commit plus head frame hash is the portable checkpoint. SPEC.md is the byte-exact materialized human view, and orient.json is only a discovery beacon. The selected chain-frame hash, not a mutable path or rev-N label, is the durable identity of a protocol revision (§12.2). 1.1. The layered model L5 EGG cartridge packaging (§9) — MIME-multipart analogue L4 FRAME universal event envelope (§7) — the IP packet of RAPP L3 WIRE transport: /chat + frames (§8) — HTTP-analogue single method L2 IDENTITY rappid namespace + trust (§6,§10) — URI + PKI analogue L1 ADDRESS canonicalization + hash (§4,§5) — the git object model A higher layer *MUST NOT* redefine a lower one. Every layer names exactly one canonical form. Wildfeuer Expires 20 April 2027 [Page 3] Internet-Draft RAPP/1 October 2026 2. Requirements language The key words *MUST*, *MUST NOT*, *REQUIRED*, *SHALL*, *SHOULD*, *MAY* are as in [RFC2119]/ [RFC8174] when in *bold caps*. (The Federal Constitution adopts the same clause.) 3. Terminology *organism* — a running brainstem with persistent identity. *rappid* — the eternal, mint-once name of an organism/door/object (§6): minted once from a UUIDv4 or a public key, never derived from content or name. *frame* — one immutable content-addressed event (§7). *stream* — an append-only hash-chained frame sequence sharing one stream_id. *particle / wave* — a frame's two domain-separated addresses: payload-hash / whole-frame-hash (§7.3). *canonical form* — the one [RFC8785] byte string for a value (§4). *legacy form* — any other historical encoding; legacy is drift and *MUST* be migrated out (Fed. Const. Art. III), except sealed re-genesis history (§12.1). *Grail kernel* — an estate-declared, byte-pinned runtime substrate. Its grail_id is "grail:" || Hb("rapp/1:grail", kernel_bytes); repository, immutable ref, commit, path, raw SHA-256, and byte length are provenance and verification data, not alternate identities. Once activated through §13.3, those bytes are permanent (Protocol Constitution Art. 15); capabilities evolve outside them. *release capsule* — the canonical rapp-cicd/1-release payload identifying one immutable candidate. *serving lineage* — the sequence of qualified release identities that have received user traffic; the currently served release is immutable even while a separate candidate lineage grows. *deployment cell* — an independently observable and isolatable runtime failure domain governed by rapp-deploy/1. 4. Canonicalization (L1) canonical(v) is the UTF-8 byte string produced by *[RFC8785] JCS* for the value v, defined *only* over I-JSON [RFC7493]. JCS fixes member- name ordering (UTF-16 code-unit), string escaping, and number serialization (ECMAScript Number::toString, ECMA-262); there is no insignificant whitespace and no byte-order mark. Input JSON text *MUST* be well-formed UTF-8 [RFC7493] §2.1; an implementation *MUST* refuse (not strip) a leading byte-order mark and *MUST NOT* transcode UTF-16 or UTF-32 input. (Insignificant whitespace in input text is accepted; it is absent only from the canonical form.) *RAPP input-domain profile (parse-side interoperability — this is a RAPP rule, not a JCS mandate).* An implementation *MUST* refuse (never repair) any JSON value that, at any depth, contains: (a) duplicate member names in one object; (b) an unpaired UTF-16 surrogate, or a Unicode noncharacter (U+FDD0-U+FDEF, or any code Wildfeuer Expires 20 April 2027 [Page 4] Internet-Draft RAPP/1 October 2026 point whose low 16 bits are FFFE or FFFF) [RFC7493] §2.1, in any string or member name; (c) a number token that does not survive the binary64 round-trip — let d be the token's nearest binary64 value under roundTiesToEven (the IEEE-754 default; ±∞ are admissible results); the token is refused iff d is not finite, or the [RFC8785] serialization of d (ECMA-262 Number::toString) denotes a different mathematical value than the token (so 0.1 is accepted — it round- trips — while 9007199254740993 and 1e999 (→ +∞) are refused; field range and type rules, such as uint53, are not part of this domain: the rule that types the field applies them); (d) canonical form exceeding 1 MiB or JSON nesting depth exceeding 64 (the root value is depth 1; each nested object/array adds 1). Refusal is whole (§7.5-style), never partial. *No normalization.* RAPP applies *no* Unicode normalization when hashing, storing, or re-emitting an existing value; strings are code- point sequences preserved verbatim and equality everywhere is code- point equality (no canonical-equivalence matching). A producer creating a *new* human-or-identifier string (slug, kind, label, payload object key) *MUST* emit it in Unicode NFC. A producer that builds a frame or packs an egg from given fields treats every payload object member name, at any depth, as new: it *MUST* refuse (never normalize) a name that is not in NFC. Consumers do not test frame contents for NFC (egg paths excepted, §9.1). NFC is Unicode Normalization Form C [UAX #15] in the Unicode version the producer implements. A producer *MUST* also refuse a name containing a code point that is unassigned (General_Category Cn) in that version. By the Unicode normalization stability policy, a name that passes this test for one producer then passes it for every producer on the same or a later Unicode version. *Migration note (drift C4):* the twin/rapp-body _frame.mjs::canonicalize() (sorted-key JSON.stringify) coincides with JCS only for string-only payloads; it *MUST* be replaced by the JCS implementation of record, imported (not re-typed) by every repo that content-addresses. 5. Content addressing (L1) — domain-separated Every hash is *domain-separated* (git's type\0, Nix's tagged store — collisions across address spaces are made unconstructible): H(space, v) = lowercase_hex( SHA-256( utf8(space) || 0x0A || canonical(v) ) ) ; v a value (§4) Hb(space, b) = lowercase_hex( SHA-256( utf8(space) || 0x0A || b ) ) ; b raw octets space is an exact ASCII tag, none containing 0x0A: "rapp/1:particle", "rapp/1:wave", "rapp/1:egg", "rapp/1:egg-manifest", "rapp/1:rappid", "rapp/1:grail", "rapp/1:seal", "rapp/1:sealed-aad", "rapp/1:sealed-key-request". A tag is used by either H or Hb, never both: H uses rapp/1:particle, Wildfeuer Expires 20 April 2027 [Page 5] Internet-Draft RAPP/1 October 2026 rapp/1:wave, rapp/1:egg-manifest, rapp/1:sealed-aad and rapp/1:sealed-key-request; Hb uses rapp/1:egg, rapp/1:rappid, rapp/1:grail and rapp/1:seal. An implementation *MUST* refuse to compute a hash under any other tag or with the other function. Output is always exactly 64 lowercase hex, *never truncated or uppercased*. Two values are treated as the same object iff their same-space hashes are equal; SHA-256 collision resistance FIPS 180-4 is a security assumption of this standard (§14). A name/X.Y label is never identity — only a hash is. A bare 64-hex is meaningful *only* within its space; an implementation *MUST NOT* dereference a hash from one space as an object of another, and content-addressed stores *MUST* key by (space, hash). 6. Identity — the rappid (L2) ## Grammar (case-sensitive, [RFC7405]) abnf rappid = %s"rappid:@" owner "/" slug ":" hash owner = lclabel ; the lowercase GitHub login (1-39 chars) slug = lclabel ; 1-100 chars lclabel = lcalnum *( ["-"] lcalnum ) ; no leading/trailing/adjacent hyphen lcalnum = LCALPHA / DIGIT LCALPHA = %x61-7A ; a-z hash = 64HEXDIGLC HEXDIGLC = DIGIT / %x61-66 ; 0-9 a-f owner *MUST* be the lowercase form of the GitHub login (logins are case-insensitive; display casing is presentation, never identity). Lengths are normative: owner 1–39, slug 1–100; an implementation *MUST* refuse longer. This self-locating form is the *only* conformant rappid; rappid::, rappid:v2:…, bare UUIDs, moment:/name-hash derivations are legacy and *MUST* be migrated out (Art. III), not read forever. 6.1. stream_id and kind grammar abnf stream_id = memory-stream / body-stream / swarm-stream memory- stream = rappid ":" instance ; one organism instance's memory body- stream = rappid ; an organism's biography swarm-stream = %s"net:" lclabel ; a planetary-wire stream instance = lclabel ; 1-64 chars kind = lclabel "." lclabel ; each label 1-64 chars A kind string carries *no intrinsic family*; the §13 registry binds each registered kind to exactly one family. Membership is tested by exact-match against the registry — never prefix inference, never wildcards. 6.2. Minting (mint-once) The 64-hex tail is minted *exactly once* per identity, then immutable: - *keyless:* tail = Hb("rapp/1:rappid", uuid4_octets), where uuid4_octets is the 16-octet binary UUIDv4 [RFC9562] §5.4 (field/byte order per §4 of that RFC). - *keyed:* tail = Hb("rapp/1:rappid", SPKI_DER), the DER SubjectPublicKeyInfo [RFC5280] of the master key. A minter *MUST* refuse octets that are not the DER SPKI of a §10 key (an id-Ed25519 key [RFC8410], or an id- Wildfeuer Expires 20 April 2027 [Page 6] Internet-Draft RAPP/1 October 2026 ecPublicKey prime256v1 key with an uncompressed point); likewise a keyless mint *MUST* refuse 16 octets whose [RFC9562] version is not 4 or whose variant is not 0b10. A producer *MUST NOT* derive the tail from owner/slug or any name (sha256("owner/slug") is prohibited — drift ID-01/C3). On read of an existing rappid.json an implementation *MUST* reuse the stored tail (canonicalize-on-read) and *MUST NOT* re-mint — with exactly one mechanism: the owner-authorized *re-anchor* (§6.3), which mints a fresh 64-hex tail once per authorization and records the superseded id in _migrated_from. Re-anchor is lawful in exactly three cases: (a) a 128→256-bit provisional upgrade (§6.3); (b) §10 key rotation or compromise; (c) migrating a pre-rev-3 keyed tail minted with the un- tagged sha256(SPKI) formula (which fails §10 discovery and *MUST* be re-anchored like a provisional identity). 6.3. Canonicalization on read; provisional identifiers canonicalize_rappid(s) restructures any legacy form into §6.1, *preserving the existing hash* (never inventing one). A restructured identifier whose tail is not exactly 64 lowercase hex (e.g. a legacy 32-hex tail) is *provisional*: it exists only inside the reading process and *MUST NOT* appear in any emitted frame, stream_id, egg, or registry entry. The one-time owner-authorized 128→256-bit re- anchor mints a fresh 64-hex, records the old id in _migrated_from, and is the only way a provisional identity becomes usable. A provisional identifier found in a stored artifact is a drift finding (Art. III). Re-anchor is the single re-mint mechanism (§6.2), lawful only in the enumerated cases: provisional 128→256-bit upgrade, §10 key rotation/compromise, and pre-rev-3 un-tagged-sha256(SPKI) keyed- tail migration. *A re-anchor is valid only with a verifiable authorization* — a self- asserted _migrated_from is insufficient (it would let anyone hijack an identity). A re-anchor *MUST* be recorded as an owner-signed §13.3 *re-anchor record* {old_rappid, new_rappid, case, utc, sig, old_key_sig?}; a consumer *MUST* refuse a new_rappid (and treat _migrated_from as drift) unless that record is present and: - case:"rotation" (uncompromised): old_key_sig (a §10 JWS by the *old* key) verifies — proof of continuity; - case:"compromise": old_key_sig is waived but a §10 *tombstone* for old_rappid is registered in the same append; - case:"tag-migrate" (pre-rev-3 keyed tail): the verifier checks lowercase_hex(SHA-256(SPKI_DER_old)) == the old tail; - case:"upgrade" (provisional 128→256): the old provisional id resolved to this owner at read time. Each mints one fresh tail. The *estate_owner's own* re-anchor record *MUST* be signed by the outgoing estate_owner key (§13.2); root-key compromise is recovered only by out-of-band re-anchoring (§13.1). Wildfeuer Expires 20 April 2027 [Page 7] Internet-Draft RAPP/1 October 2026 7. The Frame (L4) ## The envelope — exactly eleven keys json { "spec": "rapp/1", "kind": "", "stream_id": "", "seq": , "utc": "YYYY-MM-DDTHH:MM:SS.mmmZ", "payload": { }, "payload_hash": "<64hex>", "frame_hash": "<64hex>", "prev": "<64hex|null>", "prev_wave": "<64hex|null>", "sig": "" } - *spec MUST be the exact string "rapp/1"* in every frame. rapp- frame/2.0/2.1 are legacy tokens and *MUST NOT* be emitted. Any revision changing the key set, any field's grammar, or either hash rule *MUST* change this token and land an Art. III total migration; revisions adding only new registered kinds/registry entries keep the token (Fed. Const. Art. II). - *Exactly these eleven keys, always present*, none missing, none extra. A field that does not apply is present with value null (prev/prev_wave at genesis and on non-swarm streams; sig when unsigned) — never omitted, because [RFC8785] hashes null and an absent key differently. Extra or missing keys are refused (§7.5). - *payload MUST be a JSON object* (possibly empty {}); never null, array, string, number, or bool. - seq is uint53 (§7.4). A producer *MUST NOT* emit a frame whose canonical form exceeds 1 MiB or nesting depth 64 (§4). 7.1. Kind families (one envelope, registry-bound families) | family | example registered kinds | stream_id form | logs | |---|---|---|---| | memory | memory.chat-turn, memory.tool- call, memory.save, memory.reconstructed | memory-stream | one organism's life | | swarm | swarm.guidance, swarm.echo, swarm.telemetry, swarm.reconstructed | swarm-stream | the planetary wire | | body | body.pulse, body.twin-pulse, body.reconstructed, body.re-genesis | body-stream | an organism's biography | Each family also has a *.re-genesis kind (memory.re-genesis, swarm.re-genesis, body.re-genesis) used only by §12.1. The family is *not* the kind's prefix — it is the §13 registry binding (so body.twin-pulse is family body). Adding a family or event is a new registered kind on the *same* envelope (Art. IV), never a new frame type. A frame's kind family *MUST* be compatible with its stream_id form (table column 3). 7.2. Particle and wave (the unification) A frame carries *both* of its domain-separated addresses; a reader collapses it to whichever it needs. Computed in order: - *particle* — payload_hash = H("rapp/1:particle", payload). The *worldline identity and chain link*. - *wave* — frame_hash = H("rapp/1:wave", frame \ {frame_hash, sig}) — the frame with *exactly* the frame_hash and sig keys removed, all nine remaining keys (including payload_hash) present. Because payload_hash is in the wave pre- image, frame_hash attests the particle; because only frame_hash Wildfeuer Expires 20 April 2027 [Page 8] Internet-Draft RAPP/1 October 2026 (cannot hash itself) and sig (signs the result) are removed, the pre- image is unambiguous and non-circular. Both hashes are always present (never null). 7.3. Chaining, time, and merge order * *utc* *MUST* be exactly the 24-byte form YYYY-MM-DDTHH:MM:SS.mmmZ — every Y, M, D, H, S and m is an ASCII digit (%x30-39; Unicode digits such as U+0660-0669 or U+FF10-FF19 are refused), YYYY is any year 0000-9999, uppercase T/Z, exactly three fractional digits, no numeric offset; the seconds field *MUST NOT* be 60 (a leap second clamps to 59.999). All utc comparisons are *bytewise* over this fixed form (identical to chronological order). * *Worldline chain (particle):* the *genesis* frame has seq=0 and prev=null; every later frame has seq = predecessor's seq+1 (contiguous) and prev = predecessor's payload_hash. seq is uint53: a number token matching 0 / %x31-39 *DIGIT (no sign, fraction or exponent, so -0 is refused) whose value is ≤ 2^53−1; a stream nearing 2^53−1 converges by re-genesis. * *Wire chain (wave):* prev_wave *MUST* be non-null *iff* stream_id is a swarm-stream *and* seq > 0, in which case it equals the predecessor's frame_hash; in every other frame (all memory/body streams, every genesis) it *MUST* be null. (Presence is a function of stream family, not transport.) * A frame is *immutable*: a new state is a new frame at a new hash; the head pointer (§7.6) re-points. * *Cross-stream merge order* (Dream-Catcher) is the total order: ascending utc (bytewise), ties broken by ascending frame_hash (bytewise); no further ties are possible (§5). 7.4. Verification (the complete consumer checklist) Before accepting a frame, a consumer *MUST*, in order, *refuse* (never repair/reparent) on any failure: 1. *Shape & types:* exactly the eleven §7.1 keys; spec=="rapp/1"; kind a string matching §6.1.1 ABNF, registered (§13), and bound to a family whose §7.2 column-3 form is the form of stream_id (a family outside the §7.2 table fits no form); stream_id a string matching §6.1.1; seq a uint53 (§7.4); utc matching the §7.4 fixed form *and* a calendar-valid [RFC3339] date-time (so 2026-13-45T25:61:61.999Z is refused); payload a JSON object; payload_hash/frame_hash 64HEXDIGLC; prev/prev_wave each null or 64HEXDIGLC; sig null or a §10 JWS string: the detached compact form with strict unpadded base64url parts, a protected header whose octets are canonical(header) and whose members are exactly the §10 Wildfeuer Expires 20 April 2027 [Page 9] Internet-Draft RAPP/1 October 2026 alg, b64, crit and kid (a §6.1 rappid) values, and a signature of exactly 64 octets. A number token that passes §4 (c) but is not a uint53 — 9007199254740992 (2^53), 0.0, 1e0, a negative value — leaves the frame a §4 value, so it is refused here, at step 1, never before the checklist; only a token that §4 (c) itself refuses (9007199254740993, 1e999) makes the octets fail before step 1. 1a. *Stream binding:* frame.stream_id *MUST* byte-equal the identifier of the stream being extended or read (its declared stream_id at the head pointer / repo path of record). A genesis frame is accepted only for the stream it names. (Defeats cross-stream genesis/segment replay — §14.) 2. *Particle:* payload_hash == H("rapp/1:particle", payload). 3. *Wave:* frame_hash == H("rapp/1:wave", frame \ {frame_hash, sig}). 4. *Chain:* seq==head.seq+1 and prev==head.payload_hash (or seq==0 ∧ prev==null at genesis); utc ≥ head.utc (bytewise). With no head, a consumer holding the §13 registry *MUST* refuse a seq=0 frame whose frame_hash differs from the stream's registered genesis (its sole non-deprecated genesis entry) when the registry has one; a registry without a genesis entry for the stream does not refuse on this ground. The comparison opens a chain: a later re-genesis resets the head (§7.6) and does not revoke frames already verified. 5. *Wire:* if stream_id is a swarm-stream and seq>0, prev_wave==head.frame_hash; else prev_wave==null. 6. *Signature:* if sig≠null, discover the key and verify per §10 (registry key discovery, tail check, key lifecycle, the signature equation; present-but-invalid is refusal); a swarm-stream frame with sig==null is refused (§8/§10). Steps 1–5 are *time-independent* (kind lookups only append, §13), so a frame passing them passes forever; step 6 alone may flip pass→fail when a §10 tombstone with revoked_utc ≤ the frame's utc is later registered — the one time-dependent check. 7.5. Heads and forks A stream's *head* is the frame with the greatest verified seq descending from its registered genesis (§13). A consumer *MUST* persist, per stream, the highest (seq, frame_hash) it verified and *MUST* refuse any presented head with lower seq, or a different frame_hash at an already-verified seq (no rollback, no silent reorg). Two distinct valid frames with equal stream_id, seq, and prev are a *fork*: a consumer *MUST* refuse both branches past the fork point and surface a drift finding; only the owner resolves a fork (Art. X), by re-genesis (§12.1) if needed. Wildfeuer Expires 20 April 2027 [Page 10] Internet-Draft RAPP/1 October 2026 *Exception (re-genesis head reset).* The no-rollback rule above refuses a lower-seq head — but a legitimate §12.1 re-genesis presents a new genesis at seq=0. When (and only when) the §13 registry maps a stream_id to a new genesis frame_hash (§12.1 step 3), a consumer *MUST* verify that registered genesis (§7.5) and then *reset* its persisted head for that stream_id to it. Only a registry-published genesis authorizes a reset; any other lower-seq head remains a refused rollback. 8. The Wire (L3) All interaction rides one of exactly two forms: 1. *Synchronous — POST /chat, application/json both ways. The Grail is the reference:* an endpoint conforms when it answers every scenario of the Grail conformance suite (conformance/grail/) the way the Grail pinned in that suite does — same HTTP status and members, the same response and session_id values, and for a refusal the same status and members (the error text is compared only against the same Grail version). - Request: a JSON object. user_input (string, REQUIRED; empty or whitespace-only counts as missing). conversation_history (OPTIONAL array; each item an object with role one of "user", "assistant", "tool" and content a string). session_id (string, OPTIONAL). Unrecognized members *MUST* be ignored, never refused. - Success: HTTP 200, a JSON object with at least response (string), agent_logs (string: one line per agent call, newline-separated, empty when no agent ran), and session_id (string). An endpoint *MAY* add other members (the Grail adds model, requested_model, voice_mode); a consumer *MUST* ignore members it does not recognize. - Refusal of a malformed request: HTTP 400 with exactly {"error": string}, a human- readable message. The status and the shape are fixed; the message text is not part of the frozen form (a Grail bug-fix release may reword it). - New capability is a new agent behind /chat, never a sibling REST route. GET /health is the liveness probe (HTTP 200 JSON object with status); it carries no RAPP semantics. 2. *Asynchronous — an append-only frame (§7) published to a stream* (a repo path, an events/ log). A frame on a *swarm-stream MUST* carry sig≠null (§10); memory/body-stream frames *MAY* be unsigned. Any _history_ is safe to read given a trusted head (§14); the hash chain (§5) makes tampering detectable. 9. The Egg (L5) — the single egg spec of record An *egg* is a cartridge packing a unit of the estate. *RAPP §9 is the one egg spec of record* (it subsumes and retires EGG_FAMILY.md, NEIGHBORHOOD_EGG_SPEC.md, ESTATE_SPEC.md, rappterbook/EGG_SPEC.md, and the rest — drift C7). No other document may re-specify eggs; they cite this section. Wildfeuer Expires 20 April 2027 [Page 11] Internet-Draft RAPP/1 October 2026 9.1. Container, manifest, and egg address An egg is either a JSON object (invite/session variants) or a ZIP whose root is manifest.json (tree variants). The manifest is a §4 value with exactly these members: json { "schema": "rapp/1-egg", "variant": "", "rappid": "<§6.1 rappid>", "created_utc": "<§7.4 utc>", "contents": [ {"path":"","hash":"<64hex>"}, … ], "payload": { }, "sig": "" } - contents *MUST* list every packed file *except manifest.json itself*, exactly once each, with hash = Hb("rapp/1:egg", file_octets) (§5) over the raw stored octets. contents is *always present*; for JSON (pointer/session) variants it *MUST* be exactly []. - path *MUST* be a relative POSIX path: /-separated non-empty NFC UTF-8 segments, no ./.. segment, no leading /, no backslash, no Windows drive-qualified first segment (ALPHA ":"), not the root path manifest.json, and no two path values in one manifest equal as code-point sequences. Paths that differ only in case or normalization, or where one path is a directory prefix of another (docs, docs/a.md), are distinct and valid; a consumer that extracts to a filesystem where they would collide refuses the extraction, not the egg. A segment *MUST NOT* end in a period/space, contain :, contain a C0 control (U+0000-U+001F), or have a basename (the segment up to its first .), compared ASCII case- insensitively, equal to CON, PRN, AUX, NUL, COM1–COM9, or LPT1–LPT9. §9.3's 'no ..' means no .. segment. contents *MUST* be sorted ascending by the UTF-8 bytes of path. - payload is a §4 object (variant-specific). sig is a §10 JWS over canonical(manifest \ {sig}), or null. For the invite variant sig is REQUIRED and *MUST* verify with kid in the §13.2 estate-owner succession (invites are estate-issued; a sig by any other key, even a validly registered one, is refused — otherwise an attacker mints a fresh rappid and forges invites). For other variants a non-null sig *MUST* verify per §10 with kid == a keyed rappid the consumer resolves via §13; a consumer presented a signed egg *MUST* verify it. - *The egg's one §5 address is* egg_hash = H("rapp/1:egg-manifest", manifest \ {sig}) — the manifest with exactly the sig key removed (mirroring §7.3's wave rule: sig authenticates the egg, egg_hash names it, so re-signing never changes identity). Stores key eggs by ("rapp/1:egg-manifest", egg_hash). ("rapp/1:egg" addresses file octets; "rapp/1:egg- manifest" addresses the egg as a whole.) - *Container determinism.* A ZIP variant *MUST* use compression method stored (0) for *every* entry — no deflate in any variant (deflate is library-dependent, so it cannot be byte-reproducible; transport compression, if any, wraps the egg and is not the egg). Entries appear in contents order with manifest.json first; the manifest.json entry's octets *MUST* be exactly canonical(manifest). Every local and central header carries: version needed to extract 20 (0x0014); general-purpose flags exactly 0x0800 (UTF-8 filename; no data descriptor, no encryption); DOS time 0x0000 and date 0x0021 (1980-01-01 00:00:00); no extra field. Every Wildfeuer Expires 20 April 2027 [Page 12] Internet-Draft RAPP/1 October 2026 central header also carries version made by 0x0014, no file comment, disk number start 0, internal and external attributes 0. Every field present in both the local and the central header holds the same value in both. The archive is the local records in entry order with no gap, then the central directory, then an end-of-central-directory record with disk numbers 0, equal entry counts and no comment; no ZIP64 record, and no octet precedes the first local header or follows the end record. contents[].hash is over the file octets (identical to the archive octets under method 0). A JSON-variant egg's serialized form *MUST* be exactly canonical(manifest). A producer *MUST* write exactly these values, so two conformant packers of the same manifest value emit byte-identical eggs. A consumer *MUST* refuse any other violation of this paragraph, but *MUST* accept any value of version made by, version needed (when both headers agree) and the internal and external attributes, and *MUST NOT* interpret them. 9.2. Variants (the ratified set — closes EGG-01) | variant | container | packs | required members | |---|---|---|---| | organism | ZIP | a full brainstem instance | contents (sorted) MUST include the root paths rappid.json and soul.md; MAY include agents/*, organs/*, memory files | | rapplication | ZIP | one rapp | contents MUST include rappid.json and exactly one agent.py at the root (the agent of record); MAY include one ui.html, files under state/, and other files: this MAY list, like the organism row's, is not exhaustive | | session | JSON | one runtime + transcript | payload = {runtime:, transcript:[]}; contents [] | | invite | JSON | a QR-sized pointer (*no packed files*) | payload = {target_rappid:, target_url:, target_kind:("neighborhood" / "estate")}; contents []; sig REQUIRED | | neighborhood | ZIP | several organisms meant to live together | payload = {members:[,…]}; contents = one sub-egg per member, named --.egg at the root, matched by the sub-egg manifest's rappid == the payload.members[] entry | | estate | ZIP | several neighborhoods | payload = {neighborhoods:[,…]}; contents = one sub-egg per neighborhood, named --.egg at root, matched by sub-egg rappid | | sealed | ZIP | publicly mirrorable ciphertext with scoped key release | contents MUST be exactly ciphertext.bin; payload is the closed rapp-sealed-artifact/1 profile in §9.2.1; sig REQUIRED | Wildfeuer Expires 20 April 2027 [Page 13] Internet-Draft RAPP/1 October 2026 For organism and rapplication eggs, rappid.json *MUST* be a §4 object whose rappid member equals manifest.rappid (code points) and whose schema, when present, is "rapp/1"; a consumer refuses the egg at §9.3 step (2) otherwise (this is the §9.3 no-reparent rule applied to the packed identity). payload.members (neighborhood) and payload.neighborhoods (estate) are arrays of distinct §6.1 rappids, possibly empty; an empty list packs no files. "Several" describes use, not a minimum. The QR-sized invite that caused EGG-01 is the *invite* variant: a signed pointer object, not a member-packing neighborhood egg. The banned legacy stamps (brainstem-egg/2.3-neighborhood, neighborhood- egg/1.0) migrate to {schema:"rapp/1-egg", variant:"invite" / "neighborhood"} (Art. III). 9.3. Sealed artifact profile The sealed variant lets any unauthenticated mirror, including a commit-pinned raw.githubusercontent.com URL, distribute an encrypted artifact globally without distributing its decryption key. The public bytes are *ciphertext, not access control*. Bytecode or obfuscation alone provides no confidentiality. A conformant producer *MUST* use a random 256-bit data-encryption key (DEK) per artifact, *MUST NOT* embed that DEK or a shared master key in the egg/client/ URL, and *MUST NOT* place a password in a URL. A password, passkey, device assertion, or account credential may authenticate to the key service; it is not the artifact encryption key. The ZIP contents are exactly one file: text ciphertext.bin = AES-256-GCM ciphertext || 16-byte authentication tag The manifest payload has exactly: json { "schema": "rapp-sealed-artifact/1", "cipher": "A256GCM", "nonce": "<12 bytes, unpadded base64url>", "plaintext_commitment": "<64hex>", "plaintext_bytes": 1234, "media_type": "application/wasm", "key_id": "<64hex>", "key_service_rappid": "rappid:@owner/key- service:64hex", "key_service_url": "https://keys.example.com/chat", "access": "scoped-key-release", "aad_hash": "<64hex>" } Wildfeuer Expires 20 April 2027 [Page 14] Internet-Draft RAPP/1 October 2026 In this template schema, cipher and access are literals that a consumer *MUST* match exactly (access is always scoped-key-release); a <…> value is a placeholder of the stated form; the values shown for plaintext_bytes, media_type, key_service_rappid and key_service_url are examples, and only the member's type is fixed by the template (the rules below still bound each value). plaintext_commitment is a keyed commitment that cannot be tested without the DEK: text prk = HMAC-SHA-256(32 zero octets, DEK) commitment_key = HMAC- SHA-256(prk, utf8("rapp/1:sealed-commitment") || 0x01) plaintext_commitment = lowercase_hex(HMAC-SHA-256(commitment_key, plaintext_octets)) This is the [RFC5869] HKDF-Extract/HKDF-Expand construction with an empty salt, one 32-byte output block, and the exact info string rapp/1:sealed-commitment. The commitment is public but does not permit an offline dictionary attack without the random 256-bit DEK. The exact authenticated-data descriptor is: json { "schema": "rapp-sealed-artifact/1", "artifact_rappid": "", "created_utc": "", "key_id": "", "plaintext_commitment": "", "plaintext_bytes": 1234, "media_type": "" } aad_hash = H("rapp/1:sealed-aad", descriptor), and canonical(descriptor) is supplied as AES-GCM additional authenticated data. nonce decodes to exactly 12 bytes. plaintext_bytes is an integer from zero through 2^30 (1 GiB), a RAPP safety ceiling below both the NIST SP 800-38D per-invocation limit and the deterministic non-ZIP64 container boundary. ciphertext.bin length is exactly plaintext_bytes + 16. The sealed egg's manifest signature is REQUIRED and verifies per §10 with kid exactly equal to manifest.rappid. A sealed artifact therefore uses a keyed §6.2 rappid controlled by its authorized publisher; another valid registry key cannot sign on its behalf. key_service_rappid identifies the RAPP organism authorizing key release. key_service_url is its absolute HTTPS §8 POST /chat URL and *MUST* end in /chat. The URL is location, not authority: the service response is accepted only when its signatures and identifiers bind to key_service_rappid. The caller sends the following canonical JSON object as the user_input string: Wildfeuer Expires 20 April 2027 [Page 15] Internet-Draft RAPP/1 October 2026 json { "schema": "rapp-sealed-key-request/1", "egg_hash": "<64hex>", "key_id": "<64hex>", "recipient_rappid": "rappid:@owner/ device:64hex", "recipient_spki_der_b64": "", "request_nonce": "<64hex>", "expires_utc": "YYYY-MM- DDTHH:MM:SS.mmmZ", "sig": "" } sig is over canonical(request \ {sig}) and verifies under recipient_rappid; egg_hash is the §9.1 sealed egg address. The key service verifies entitlement, exact (egg_hash,key_id), request expiry, recipient key binding, and nonce non-reuse. The §8 response string is canonical JSON: json { "schema": "rapp-sealed-key-release/1", "egg_hash": "", "key_id": "", "recipient_rappid": "", "request_hash": "", "wrap_alg": "ECDH-ES+A256KW", "wrapped_key_jwe": "", "issued_utc": "YYYY-MM-DDTHH:MM:SS.mmmZ", "expires_utc": "YYYY-MM- DDTHH:MM:SS.mmmZ", "service_sig": "" } service_sig is over canonical(release \ {service_sig}) and verifies under key_service_rappid. wrapped_key_jwe uses [RFC7516] with alg:"ECDH-ES+A256KW" and enc:"A256GCM" for the declared recipient SPKI. The service *MUST NOT* return a global raw DEK. Authorization, expiry, device scope, and key rotation are service policy recorded in signed frames, not fields that alter the sealed egg's content address. A mirror URL *SHOULD* be immutable (for GitHub Raw, pin a commit SHA rather than a moving branch). URL secrecy is never assumed: integrity comes from the egg address and signature, confidentiality from AES-GCM, and use authorization from scoped key release. Revocation prevents future key release. It cannot erase ciphertext or plaintext already downloaded and decrypted by an authorized recipient. A conformant product *MUST* state this limit plainly. It also *MUST NOT* claim that encrypted bytecode cannot be inspected after decryption; legal/license controls and bounded server authority remain separate from cryptographic confidentiality. Wildfeuer Expires 20 April 2027 [Page 16] Internet-Draft RAPP/1 October 2026 9.4. Conformance * *Producer* *MUST* emit only schema:"rapp/1-egg" with a variant from §9.2, a §6.1 rappid, and, for ZIP variants, a contents list whose every hash verifies. Every egg it emits, and every sub-egg it packs into one, *MUST* pass consumer steps (0)-(2) below; a REQUIRED sig *MUST* be present and be a §10 JWS (a producer without the signer's key context need not verify it). Given input that cannot meet these rules, a producer *MUST* refuse rather than emit or repair. It *MUST NOT* emit any legacy egg schema. * *Consumer* *MUST* read every §9.2 variant, dispatch on variant, verify *integrity then viability* — (0) the manifest is a §4 value satisfying *every §9.1 rule* — exact member set, path grammar (no .., no leading /, no backslash), no duplicate paths, sort order, and for ZIP variants the archive entry set equals contents ∪ {manifest.json} in the §9.1 deterministic order (this is the zip- slip defense; an unenforced path grammar defends nothing); (1) every contents[].hash recomputes per §5; (2) the variant's §9.2 structural requirements hold — and refuse whole on any failure; it *MUST NOT* reparent on transport. 9.5. Instantiation — instance identity and grown_from lineage (rev-6; closes the hatch seam) An egg names an *artifact*; a hatch creates an *instance*. Rev-5 specified packing and reading but left instantiation identity unwritten, so every implementation had to invent its own — this section is that missing word. * The rappid in a packed egg's rappid.json (§9.2) names the *artifact*. Every hatch of the same egg reads the *same* artifact identity (§6.2 canonicalize-on-read); re-minting it on hatch is prohibited (§6.2). * A consumer that instantiates an egg into a live installation *MUST* mint a *fresh §6.2 identity for the instance*, exactly once, at first boot — from entropy, never derived from the artifact identity, host name, or path. Two instances of one egg share the artifact identity and *MUST NOT* share an instance identity. Wildfeuer Expires 20 April 2027 [Page 17] Internet-Draft RAPP/1 October 2026 * The instance's identity document *SHOULD* record *grown_from*: the §9.1 egg address (64 lowercase hex) of the egg it was instantiated from. grown_from is written at mint time and is *immutable* thereafter; it is null when the source is unknown; a producer *MUST NOT* fabricate it — lineage is a fact about a birth, never a claim to invent. An instance that cannot say where it came from says null. * grown_from is *lineage, not inheritance*: it confers no authority, no trust, and no §10 key material. A reader *MUST* treat it as an unverified assertion unless the named egg is available and its address recomputes per §9.1/§5. * A newly authored artifact produced from one or more existing RAPP objects *MAY* record crossed_from in its packaged rappid.json. The value is a sorted, duplicate-free array of typed addresses {space, hash}, where space is "rapp/1:particle" or "rapp/1:egg- manifest" and hash is 64 lowercase hex. One parent denotes an *offspring*; two or more denote a *cross*. The new artifact *MUST* mint its own §6.2 rappid; parent artifacts and identities remain unchanged. crossed_from is lineage only: it transfers no signature authority, ownership, entitlement, secret, or trust. A verifier treats each parent as unverified until the addressed object is available and recomputes in its declared space. 10. Trust and signatures (L2) sig is OPTIONAL on memory/body streams and REQUIRED on swarm streams (§8). Chain integrity comes from the hash links (§5), not signatures. When present, sig *MUST* be JWS Compact Serialization with *detached, unencoded payload* ([RFC7515] App. F + [RFC7797]): - protected header members are *exactly* alg ("EdDSA" or "ES256"), b64 (false), crit (["b64"]), kid (signer's §6.1 rappid), no others; the header octets *MUST* be canonical(header) (§4 — JCS orders them alg, b64, crit, kid, no whitespace); - the sig string is the detached compact form BASE64URL(canonical(header)) || ".." || BASE64URL(signature); - JWS signing input = BASE64URL(canonical(header)) || "." || canonical(frame \ {sig}); - alg: EdDSA/Ed25519 [RFC8037] or ES256 [RFC7518]; Ed25519 verification is [RFC8032] §5.1.7 with S < L required and the cofactorless check encode([S]B - [k]A) == R, where k is SHA-512(R || A || message) read as a little-endian integer and reduced mod L (as in [RFC8032]'s §6 reference code); public keys that decode (including small-order points) are not refused for their order; ES256 signers *SHOULD* sign deterministically [RFC6979] (Ed25519 is deterministic by construction) so signed frame files stay byte-reproducible. Wildfeuer Expires 20 April 2027 [Page 18] Internet-Draft RAPP/1 October 2026 *Key discovery.* A keyed rappid's tail is one-way (Hb("rapp/1:rappid",SPKI)). A verifier resolves the signer's SPKI (DER) *from the §13 registry* entry (rappid → spki_der_b64); the door-of-record rappid.json is the publication venue the registry entry is generated from, not itself a verification source. The verifier *MUST* check Hb("rapp/1:rappid", SPKI_DER) == the rappid's tail and refuse on mismatch or registry absence. *Key lifecycle.* Rotation is an identity re-anchor (§6.3, case rotation) with new tail = Hb("rapp/1:rappid", newSPKI) and a §13.3 re-anchor record. A re-anchor *deprecates* the superseded rappid's §13 spki entry: a verifier *MUST* refuse a sig whose kid is a superseded rappid on any frame with utc ≥ the re-anchor record's utc (rotation gives forward security; earlier frames verify as before). The deprecated flag of an spki entry does not by itself refuse a sig: the entry still resolves the key (entries are append-only, §13.3), and a sig by it is refused only by the re-anchor rule above or by a tombstone. To stop a key without rotating, the owner registers a tombstone. Compromise is declared by an owner-signed *tombstone* in the §13 registry {rappid, revoked_utc}; a verifier *MUST* refuse any sig by a tombstoned key on a frame whose utc ≥ revoked_utc, checking tombstones at verification time (§7.5 step 6). "Owner-signed" means a sig verifying with kid == the registry's designated estate_owner rappid (§13). A consumer *MUST NOT* infer authorship from an unsigned frame (keyless rappids assert location, not authorship). 11. Conformance classes * *Producer:* emits only §4 JCS/I-JSON bytes, §5 domain-separated full-SHA-256 addresses, §6 rappids minted per §6.2, §7.1 eleven- key frames, §9 rapp/1-egg variants — and *no legacy form*. * *Consumer:* runs the full §7.5 checklist (incl. 1a binding), §9.3 egg verification, §10 signature + key-discovery + tombstone checks, canonicalizes legacy ids on read (§6.3), refuses on any failure, never repairs/reparents/rolls back (§7.6). * *Router/Mirror:* invents no endpoints (§8), declares subordination to kody-w/rapp-1 for protocol semantics, and serves only provenance-stamped hash-matching mirrors. Estate-specific product scope is resolved separately through that estate's signed registry and master plan. Wildfeuer Expires 20 April 2027 [Page 19] Internet-Draft RAPP/1 October 2026 11.1. Immutable Grail kernel conformance An estate declares a Grail kernel through the exact §13.3 grail- kernel entry. Its release_scope selects the release family to which the pin applies; a registry *MUST* contain at most one grail-kernel entry for any release scope. Its entry-level sig *MUST* verify under declared_by, which *MUST* be the estate owner in effect at activated_utc (§13.2); activated_utc *MUST NOT* be more than 300 seconds after the verifier's first-seen time for that entry. The pin activates locally when a consumer first accepts an authenticated, non-rollback registry containing that valid declaration. Until then it is structural evidence only. The Grail is identified by grail_id, never by a mutable branch, tag, repository name, or product label. Once activated, the pin is a permanent compatibility anchor, not a moving release channel: 1. Every conformance evaluation *MUST* receive release_scope from an authenticated owner-controlled release policy, never from candidate bytes or candidate-controlled configuration, then resolve its one grail_id exclusively from the activated, persisted §13.3 binding. If the policy repeats grail_id, that value is only a consistency assertion and *MUST* byte-equal the registry result; it cannot select or rebind the pin. Every ring and release/deployment stage, including development, qualification, Preprod, installation, and production verification, *MUST* compare the candidate's Grail path to that exact binding, SHA-256, and byte length. 2. A missing, changed, substituted, or unmeasured Grail byte is kernel-drift and *MUST* fail closed. Approval, urgency, compatibility claims, and semantic equivalence cannot waive byte inequality. 3. A pipeline *MUST NOT* silently restore the pinned file after testing a different file. The exact release-shaped artifact containing the immutable Grail bytes *MUST* itself pass the release gates. 4. Every gate *MUST* resolve every archive, container, wrapper, environment override, symlink, and filesystem indirection to one regular runtime entry-point file under the immutable release root; refuse links and ambiguous alternatives; verify its bytes; and bind the release digest, resolved entry-point path, release_scope, grail_id, raw SHA-256, and byte length into its evidence. Wildfeuer Expires 20 April 2027 [Page 20] Internet-Draft RAPP/1 October 2026 5. Every gate that executes the kernel *MUST* bind verification atomically to execution from a sealed immutable object or snapshot. An already-open descriptor is sufficient only when its backing bytes are under platform-enforced write exclusion for the entire verification-through-consumption interval and the interpreter/loader consumes that exact descriptor without reopening a pathname. An immutable content-addressed release root is sufficient only when no concurrent principal can modify it. A hash-then-path-launch without one of those guarantees is nonconformant. At least one execution gate for every supported production platform *MUST* perform this proof before release approval. Merely carrying an unused matching file is insufficient. A stored transpiled, bundled, generated, or patched derivative is different kernel bytes and requires a new grail_id; ordinary interpreter/JIT compilation directly from the pinned bytes is permitted when no alternate stored kernel is selected. 6. New implementation behavior *MUST* live outside the Grail kernel and *MUST NOT* alter any RAPP-visible canonical form or wire semantic except through the registration and evolution rules of §§7, 9, 12, and 13. 7. If required behavior cannot be expressed outside the pin, the implementation *MUST* report an incompatibility. It *MUST NOT* edit the Grail and continue under the same identity. 8. Different kernel bytes produce a different grail_id and therefore are a different Grail. A new grail-kernel entry may coexist and may name the old grail_id as predecessor, but it *MUST NOT* overwrite, retag, alias, or redefine the prior Grail. A successor uses a new release_scope; an existing scope is never rebound. A non-null predecessor *MUST* name an earlier accepted grail-kernel entry and *MUST NOT* form a cycle. A registry *MUST* contain at most one grail-kernel entry for each grail_id. 9. On first activation a consumer *MUST* persist the full canonical grail-kernel entry. Every later accepted registry *MUST* retain that entry byte-for-byte. Removal, mutation, or a second entry for the same grail_id is a permanent kernel-drift refusal even when registry_seq increased. This rule governs release topology rather than the §8 wire shape, so it does not add a frame member, endpoint, compatibility shim, or alternate rapp/1 encoding. Wildfeuer Expires 20 April 2027 [Page 21] Internet-Draft RAPP/1 October 2026 11.2. Operational conformance profiles RAPP/1 defines the substrate. Two subordinate operational profiles define how a production AI changes without mutating that substrate or the serving system underneath users: * *RAPP CI/CD* (rapp-cicd/1) — protocols/rapp-cicd/1/SPEC.md; immutable release capsules, ordered qualification evidence, exact- candidate promotion, Preprod, and rollback/restore proof. * *RAPP Deploy* (rapp-deploy/1) — protocols/rapp-deploy/1/SPEC.md; isolated serving/candidate lineages, cellular rollout, progressive exposure, expiring AI-health evidence, quarantine, and exact rollback. The profiles are RAPP/1 applications, not alternate wire versions: 1. Their payloads *MUST* be §4 canonical I-JSON and are identified by H("rapp/1:particle", payload). 2. Authoritative profile payloads *MUST* travel in signed RAPP/1 frames using a registered compatible kind. Local unsigned files are drafts, fixtures, or caches and cannot authorize promotion or traffic. 3. They *MUST NOT* add a transport endpoint beside §8 POST /chat, redefine any RAPP/1 primitive, or weaken §11.1. 4. An estate activates a profile through an authenticated §13.3 protocol entry pinning its exact repository, path, and SHA-256. A moving branch is discovery, never authority. 5. A claim of *RAPP production conformance* requires both profiles. Core Producer, Consumer, and Router/Mirror implementations remain conformant without implementing production operations. 6. Fixed envelope keys carry the safety invariants. Policy-defined check identifiers, component kinds, health objectives, and resilience controls are extension points; adding one does not change the profile token or the RAPP/1 wire. 7. A consumer *MUST* refuse unknown required policy semantics. It may preserve and relay unknown optional evidence, but may not treat it as satisfying a requirement it does not implement. Wildfeuer Expires 20 April 2027 [Page 22] Internet-Draft RAPP/1 October 2026 The executable reference validators are rapp_cicd.py and rapp_deploy.py; controlled profile vectors are in operations_conformance.py. JSON Schemas define structural shape, while the reference validators enforce ordering, identity, temporal, and cross-document rules that JSON Schema cannot express. 12. Versioning, evolution, no-legacy RAPP is a *living standard* (WHATWG): revised in place, never forked into parallel versions; a name/X.Y label *MUST NOT* ever denote two shapes (Art. II) — a shape change moves the token (§7.1). Published content-addressed artifacts are *immutable* (SemVer/crates). Within an estate there is *no perpetual backward compatibility* for the estate's own artifacts and retired legacy encodings (Art. III): a change to such a form is a *total migration* of every instance + *deletion* of the old form. Sealed re-genesis history (§12.1) is the retained live-stream exception and is not "legacy compatibility." *The rapp/1 wire is frozen (rev-15, Art. 18).* No revision of this standard may change a form a rapp/1 artifact is verified by: canonicalization (§4), the hash function and its tags (§5), the rappid grammar and mint (§6.1, §6.2), the eleven-key envelope and its two addresses (§7.1, §7.3), the consumer checklist (§7.5), the two wire forms and the /chat shapes (§8), or the egg container and address (§9.1). A change to any of those is not a revision; it moves the token to rapp/2, specified beside this document, and rapp/1 artifacts keep verifying under this document forever. A consumer *MUST NOT* refuse a rapp/1 artifact because a later token exists. Everything Art. IV names still grows under rapp/1: registered kinds, egg variants, error codes, registry entry types, vocabulary, subordinate profiles, and the registry itself. The frozen forms are what a stranger's implementation, written once and never updated, relies on; they are the reason an independent implementation can be finished. Immutable specification-governance history is a separate narrow exception: rev-5 through rev-13 anchor frames retain interpretable immutable pointer payloads so their ratified normative bytes remain resolvable. They are historical authority records, not accepted live legacy protocol forms. A producer *MUST NOT* emit the pointer-only revision profile after rev-13. Wildfeuer Expires 20 April 2027 [Page 23] Internet-Draft RAPP/1 October 2026 12.1. Re-genesis (converging an immutable chain — one owner-authorized operation) 1. *Terminal seal:* seal = Hb("rapp/1:seal", head_octets). head_octets is the exact octets of the old head's record *as retained under legacy/* (step 4): for a one-frame-per-file store, the retired file's full octets; for a line-oriented log, the head's line *excluding* its trailing terminator. The retained legacy/ artifact is the verification reference for the seal, and retirement *MUST* preserve those octets bit-exact. The step-3 genesis registry entry *SHOULD* record the legacy/ artifact's repo+path so a consumer *MAY* verify the seal against it. (Defined for every legacy shape, including ones that cannot be §7.3-hashed.) 2. *New genesis:* emit seq=0, prev=null, kind = the registered re- genesis kind *of the stream's family* — memory.re-genesis, swarm.re-genesis, or body.re-genesis (three §13 kinds, used only here) so the frame satisfies §7.2 family↔stream compatibility for any stream — sig≠null owner-signed (§10, §13 estate_owner), payload = {"migrated_from":{"stream_id":,"terminal_seal":, "terminal_seq":}} and no other members. A consumer *MUST* treat any *.re-genesis kind as the sole re-genesis marker for its family and refuse an unsigned/non-owner one. A consumer *MUST* also refuse, at §7.5 step 1, a *.re-genesis frame whose payload is not exactly this object: migrated_from with exactly stream_id (a §6.1.1 stream_id), terminal_seal (64HEXDIGLC) and terminal_seq (a uint53); and, at step 4, one with seq≠0 or prev≠null. 3. *Register (the linearization point):* append a §13.3 genesis entry mapping the stream_id to the new genesis's frame_hash, *and flag every prior genesis entry for that stream_id deprecated* — the first convergence included (it deprecates the creation-time genesis), so exactly one non-deprecated entry always remains. A consumer resolves the current genesis *only* via that sole non- deprecated entry. A *concurrent* second registration fails closed (the append is the linearization point, Art. IX); a later fork/brick (§7.6/§14) is a fresh owner-authorized convergence that appends again and re-deprecates. 4. *Retire:* move old frames under legacy/ — retained as immutable sealed history, never served as current, never extended, never read as live chain. No live frame may set prev/prev_wave to a retired hash (a dangling ref is a drift finding). Wildfeuer Expires 20 April 2027 [Page 24] Internet-Draft RAPP/1 October 2026 5. Keep the old stream_id unless the identity itself re-anchored (§6.3), in which case the registry entry also records old_stream_id → new_stream_id. 6. Two frames with equal stream_id+seq from different eras are disambiguated *solely* by descent from the current registered genesis. Re-genesis is one-time per convergence; a repeat _of the same convergence_ is the concurrent case (step 3, fails closed). 12.2. DOGG specification revision chain The RAPP/1 revision content is the append-only JSONL stream at anchor/chain.jsonl. The chain's particle, wave, and predecessor hashes prove byte integrity and linear history; they do *not* authenticate who selected a head or ratified an amendment. Until a separately authenticated RAPP registry/checkpoint is ratified, authority is selected by owner-ratified acceptance of a chain snapshot onto protected canonical refs/heads/main at https://github.com/kody-w/rapp-1. A consumer starting from an out- of-band pinned accepted commit and head frame hash has the same immutable selection evidence. An internally valid fork is not authoritative merely because its hashes verify. Every protocol adjustment *MUST* append exactly one valid successor frame: * spec remains "rapp/1", the envelope remains exactly eleven keys, and the allowed registered anchor kind is body.pulse; this profile does not create a new kind or alter canonicalization, hashing, or registered-kind semantics; * stream_id remains the bootstrap-pinned anchor stream, seq is contiguous, prev names the predecessor's payload_hash, prev_wave and sig are null, and the applicable §7.5 integrity checks hold; * the frame's frame_hash is the durable protocol-revision identity. Human names such as rev-14 are lookup labels/views, never identities. 12.2.1. Immutable bootstrap boundary Verification begins from the frozen rapp-anchor-bootstrap/1 profile published through anchor/bootstrap/index.json. The index names a anchor/bootstrap/sha256-.json object and pins the exact stdlib verifier at anchor/bootstrap_verify.py by raw SHA-256 and byte length. The profile fixes the exact-integer JCS subset and limits, particle/wave domains, eleven frame keys, timestamp/sequence/ Wildfeuer Expires 20 April 2027 [Page 25] Internet-Draft RAPP/1 October 2026 predecessor rules, canonical repository and protected ref, anchor stream ID, genesis frame and payload hashes, body.pulse anchor profile, null signature/wave fields, and byte/depth limits needed to verify this chain. The content-addressed bootstrap profile is immutable. Changing it requires a new bootstrap schema/version and a new external ratification; mutating rapp-anchor-bootstrap/1 in place is refusal. Its initial authenticity is necessarily external — owner-ratified protected canonical main or an out-of-band pinned profile hash/ immutable commit — because neither a chain nor code fetched with that chain can circularly authenticate its own parser. rapp.py remains the complete reference implementation and is cross-checked, but it is not the mutable bootstrap trust pin. 12.2.2. Revision payloads and historical exception Frames through rev-13 retain their immutable legacy pointer fields: canonical_repo, a full 40-lowercase-hex commit, safe relative normative_path, raw-file normative_sha256, and decimal-string normative_bytes. A resolver *MUST* construct only an immutable commit-pinned GitHub Raw URL from those fields, fetch the bytes, and verify length and raw SHA-256 before use. A branch or tag name in commit, an unsafe path, malformed UTF-8, a byte-order mark, or a hash/length mismatch is refusal. Path validation occurs on the original string before path-library normalization: empty, absolute, ./.., empty components, repeated slash, leading ./, trailing slash, backslash, percent-encoded ambiguity, or any value whose reconstructed POSIX form differs is refused. These pointer payloads are immutable governance history under Article 3's narrow exception; they *MUST NOT* be emitted for rev-14 or later. Rev-14 and each successor using this profile carries all normative text inside the chain frame. Its payload *MUST* include schema:"rapp-spec-revision/1", a unique revision, and both previous_revision and previous_normative_sha256 matching the immediate predecessor. It preserves the useful legacy fields above, carries the publication/ratification metadata defined below, and adds exactly this normative object: json { "media_type": "text/markdown; charset=utf-8", "text": "", "sha256": "", "bytes": 123 } normative.text *MUST* encode as UTF-8 without a byte-order mark; normative.sha256 and normative.bytes *MUST* match those exact octets and the corresponding legacy normative_sha256 and normative_bytes. The complete canonical frame remains subject to §4's 1 MiB limit. Wildfeuer Expires 20 April 2027 [Page 26] Internet-Draft RAPP/1 October 2026 Inline revisions require only the selected, verified chain bytes once fetched; their commit/path fields are immutable provenance, not a second source for normative text. The payload's publication object, repeated in the beacon and revision index, records: canonical repository https://github.com/kody-w/rapp- 1; protected ref refs/heads/main; selection by owner-ratified acceptance; the accepted canonical-main commit as linearization point; prohibition of history replacement; mandatory rebase/ regeneration for a competing append; rev-14 ratification under rev-13 Article 14; application of this chain-append process from rev-15 onward; and authenticated_registry_checkpoint:null until one actually exists. This metadata *MUST NOT* be interpreted as a signature or authenticated registry. 12.2.3. Content-addressed publication and resolution Every chain frame *MUST* also be published, without a trailing line terminator, at: text anchor/frames/.json A fetch through mutable main is discovery only. Given frame hash F, a stranger may fetch https://raw.githubusercontent.com/kody-w/rapp- 1/main/anchor/frames/F.json, parse it under the pinned bootstrap, and accept the object only if its computed wave equals F. For accepted snapshot commit C, the immutable URL is https://raw.githubusercontent.com/kody-w/rapp-1/C/anchor/frames/ F.json. Wrong content at the correct hash-derived path is refusal. anchor/index.json is a deterministic generated index from seq, revision label, frame hash, and payload hash to those objects. It carries no independent authority: a consumer *MUST* verify each selected object and match the index and beacon to the fully verified chain. Resolution by frame hash uses the path algorithm directly; resolution by payload hash or sequence uses the verified index; resolution by revision label is a view over it. The fixed pre- profile rev-5 pulses share one historical label, so the rev-5 view resolves to the greatest matching seq, while every pulse remains directly resolvable by sequence and hashes. A consumer *MUST* verify the chain from the bootstrap-pinned genesis before using a revision as selected history. It *MUST* refuse an invalid frame shape, particle, wave, or prev; a fork or duplicate seq; duplicate frame/payload hashes; a duplicate profiled revision; an unsupported schema; a legacy pointer-only frame after rev-13; malformed UTF-8; normative hash/length drift; bootstrap/index/object drift; or an object absent from the selected chain. Wildfeuer Expires 20 April 2027 [Page 27] Internet-Draft RAPP/1 October 2026 orient.json is only a beacon to the head. Its sequence, frame hash, payload hash, bootstrap/index pins, authority-selection metadata, and every retained head mirror — including registered_kinds, vocabulary, operational profiles, foundation, philosophy, and Constitution metadata — *MUST* be regenerated from and match the verified head. While the beacon schema remains rapp/1-anchor, spec.normative_path *MUST* remain as a compatibility alias equal to spec.materialized_path. SPEC.md *MUST* reproduce the selected head byte-for-byte. The Atom feed and mutable main URLs are discovery only. Materialization and immutable-pointer caching *MUST* use bounded reads and same-directory atomic replacement without following a symlink in the destination leaf or path. A symlink, unsafe directory component, oversized cache entry, content change during read/write, or compare-and-swap mismatch is refusal; the requested leaf is replaced, never the symlink target. After acceptance, let C be the full 40-hex canonical-main commit containing the accepted snapshot. The immutable checkpoint URLs are: text https://raw.githubusercontent.com/kody-w/rapp-1/C/anchor/ chain.jsonl https://raw.githubusercontent.com/kody-w/rapp-1/C/anchor/ orient.json https://raw.githubusercontent.com/kody-w/rapp-1/C/anchor/ index.json The authority linearization point is the owner-ratified acceptance of C onto protected canonical main, not creation of a local frame, a feed event, or a hash alone. A stale competing append *MUST* rebase onto the accepted head and regenerate. Force-push or history replacement of accepted authority is prohibited. 12.2.4. Rev-14 transition and draft replacement Rev-14 is ratified under rev-13 Article 14. Owner acceptance of the prepared change onto canonical protected main makes the final rev-14 frame effective; the new chain-append process governs rev-15 onward. Before that acceptance, rev-14 artifacts are unpublished drafts. A later commit in the same owner-ratified change may deterministically replace the one unpublished rev-14 draft line/object and update dependent hashes, but *MUST* preserve every rev-5 through rev-13 line byte-for-byte and *MUST NOT* represent the replaced draft as accepted history. The publication generator *MUST* hold one cross-process exclusive lock across read, verification, generation, comparison, and publication. Immediately before replacing chain.jsonl it *MUST* re- read and verify the on-disk head and compare the complete expected Wildfeuer Expires 20 April 2027 [Page 28] Internet-Draft RAPP/1 October 2026 prefix; a stale writer or changed prefix is refused. Supporting content-addressed artifacts are published first, the authority chain is compare-and-swap replaced next, and the beacon is replaced last, with file and parent-directory synchronization. Because the chain is authority content and the beacon is only a derived view, interruption after a valid chain replacement may leave a stale or missing beacon; the next locked run *MUST* deterministically regenerate it from the verified chain rather than refusing the valid head forever. This chain and beacon are unsigned. They provide integrity after authority selection, not authorship, and implementations *MUST NOT* fabricate a signature or authenticated registry/checkpoint state for them. 13. The registry — an estate's signed root of trust (append-only) Each estate selects an owner-controlled canonical_source for its schema:"rapp/1-registry" document. The kody-w reference estate may publish its registry through kody-w/RAPP or another owner-authorized location, but that estate instance cannot alter this protocol. Because §7.6 head resets, §10 key discovery, tombstone revocation, and ownership all resolve through the selected registry, it is the *root of trust for that estate* and is itself authenticated (an unsigned mutable file at the root of the trust graph would forge that estate). 13.1. Trust anchor and registry authentication * The one bootstrap axiom is the *estate_owner rappid string* itself: since a keyed tail is Hb("rapp/1:rappid", SPKI_DER), the rappid *is* a self-certifying key fingerprint, distributed out-of- band exactly once (QR, invite, docs) the way a root-CA certificate is. * The registry document *MUST* carry a top-level registry_seq (uint53) and a detached §10 JWS sig over canonical(registry \ {sig}) with kid = the estate_owner rappid. A consumer *MUST* verify this signature against an SPKI whose Hb("rapp/1:rappid", SPKI_DER) equals the anchor rappid's tail (the SPKI may travel alongside the registry — the tail check authenticates it) and *MUST* refuse an unsigned or non-verifying registry. Wildfeuer Expires 20 April 2027 [Page 29] Internet-Draft RAPP/1 October 2026 * *No rollback:* a consumer persists the highest registry_seq it verified and refuses any registry with a lower one (mirrors §7.6). *Freshness:* a consumer *MUST* obtain the registry from canonical_source or a provenance-stamped (Art. VIII) mirror of it, *SHOULD* refresh before any §7.5-step-6 or §7.6 head-reset decision, and *MUST* report a verification made against a registry older than its staleness policy as _stale_, not _clean_. 13.2. Owner succession (time-scoped authority) "Owner-signed" means: the sig verifies per §10 *and* kid is the estate-owner *in effect at the artifact's utc* — the current estate_owner or any predecessor reachable through the registry's re- anchor records (§13.3), with the artifact's utc inside that owner's tenure [record.utc, successor.utc). Verification uses the owner in effect at the artifact's time, *never only the current one* (so a routine owner key rotation never invalidates historical re-genesis frames or tombstones). Estate-owner *root-key compromise* is recovered only by re-distributing a new trust anchor out-of-band (§13.1) — it cannot be expressed inside the registry it signs. 13.3. Entry types (each a §4 value; document schema:"rapp/1-registry") The registry is an I-JSON document; every entry is append-only (never removed/renamed; retirement is a deprecated:true flag). Entry types and their exact members: - *protocol* {type:"protocol", name, spec_repo, spec_path, spec_hash, deprecated} — an estate adoption pin, never a power to redefine a protocol. An entry with name:"rapp/1" that is used for a current-conformance claim *MUST* set spec_repo:"https://github.com/kody-w/rapp-1", spec_path:"SPEC.md", and spec_hash to a normative SHA-256 published by a verified frame in this repository's anchor chain. A historical RAPP/1 pin may be retained only as deprecated:true; it does not override the current anchor. Other protocol entries are subordinate to their own canonical authorities and *MUST NOT* claim the rapp/1 name or namespace. - *kind* {type:"kind", kind, family, deprecated} (incl. the three *.re-genesis kinds). A deprecated:true kind entry still registers kind and its family for §7.5 step 1 (steps 1–5 are time- independent); deprecation only tells producers to stop emitting new frames of that kind. - *egg-variant* {type:"egg-variant", variant, deprecated} · *error-code* {type:"error-code", code} (both closed namespaces; unregistered value = not conformant) - *genesis* {type:"genesis", stream_id, frame_hash, deprecated, old_stream_id?, new_stream_id?} — *every* stream registers its creation genesis; re- genesis appends a new one and deprecates all prior (§12.1 step 3); §7.6's "registered genesis" is the sole non-deprecated genesis for a stream_id. - *spki* {type:"spki", rappid, spki_der_b64, deprecated} — the §10 key-discovery source. - *tombstone* {type:"tombstone", Wildfeuer Expires 20 April 2027 [Page 30] Internet-Draft RAPP/1 October 2026 rappid, revoked_utc, sig}, sig owner-signed over canonical(entry \ {sig}). - *re-anchor* {type:"re-anchor", old_rappid, new_rappid, case:("upgrade"|"rotation"|"compromise"|"tag-migrate"), utc, sig, old_key_sig?} — sig owner-signed; old_key_sig a §10 JWS by the *old* key over canonical(entry \ {sig,old_key_sig}), REQUIRED for case:"rotation". This is the normative succession record (§13.2). - *grail-kernel* {type:"grail-kernel", release_scope, grail_id, repository, immutable_ref, object_format, commit, path, mode, blob, sha256, size_bytes, activated_utc, predecessor, declared_by, sig} — exactly these members. release_scope is an absolute HTTPS URI selected by the estate owner; no two entries may share it. grail_id is "grail:" || Hb("rapp/1:grail", kernel_bytes); repository is an absolute HTTPS URI; immutable_ref is a full refs/tags/... name that *MUST* resolve exactly to commit; object_format is "sha1" or "sha256" and fixes the required lowercase hexadecimal length of commit and blob; path at commit *MUST* resolve through the repository tree to exactly one regular blob with mode "100644" or "100755" and object id blob; path is a relative NFC POSIX path with no empty, ".", or ".." component; sha256 is the raw kernel bytes' 64-lowercase-hex SHA-256; size_bytes is their positive uint53 length; activated_utc has the exact §7.4 form; predecessor is null or another grail_id; declared_by is a keyed rappid; and sig is a detached §10 JWS whose protected kid equals declared_by, over canonical(entry \ {sig}). The entry is additionally covered by the registry's §13.1 signature. A consumer verifies the entry signer as the estate owner in effect at activated_utc, verifies the referenced bytes, recomputes both hashes, persists the canonical entry on first activation, applies §11.1, and refuses a missing/mutated prior binding, duplicate grail_id, or locator whose bytes disagree. - *estate_owner* {type:"estate_owner", rappid} (exactly one non-deprecated) · *master-plan* {type:"master- plan", repo, path} (Fed. Const. Art. VII). §7.5 steps 1–5 are time-independent (append-only lookups); *only* step 6 (tombstones) and §13.2 owner tenure are time-scoped, and both are monotone given the §13.1 no-rollback rule. 14. Security considerations * *Integrity:* every object is domain-separated content-addressed (§5); a hostile mirror cannot alter bytes without breaking the hash, so _history is safe given a trusted head_. * *Head freshness is not self-certifying:* the chain authenticates history, not which head is current; a hostile mirror may serve a stale/forked head. Consumers counter with the §7.6 monotonic-head rule; swarm heads *SHOULD* be owner-signed. Wildfeuer Expires 20 April 2027 [Page 31] Internet-Draft RAPP/1 October 2026 * *Cross-stream replay:* without §7.5 step 1a, any genesis/segment of stream A replays as stream B (seq=0, prev=null always pass). Step 1a's stream binding is mandatory. * *Address-space confusion:* §5 domain tags make a particle, wave, egg, or rappid tail with equal hex non-interchangeable; stores key by (space, hash). * *Canonicalization attacks:* the §4 I-JSON input-domain profile (no duplicate keys, no lone surrogates, exact binary64, no normalization ambiguity) removes hash-splitting and NFC-twin vectors. * *Identity forgery / key compromise:* authorship requires a keyed rappid + valid §10 sig; rotation is §6.3 re-anchor (verifiable authorization, §6.3/§13.3 — a self-asserted _migrated_from is refused); compromise is a §13 tombstone enforced at verify time. Because a tombstone gates on the frame's producer-controlled utc, a compromised key can still emit frames stamped just below revoked_utc; after a compromise the owner *SHOULD* advance affected stream heads (or re-genesis) past revoked_utc. * *Root of trust:* the registry is the estate's signed root (§13.1); it is authenticated by an owner signature anchored to the out-of- band estate_owner rappid fingerprint, registry_seq-monotonic against rollback, and freshness-checked (a stale registry silently un-revokes keys and hides re-geneses). * *Producer-controlled utc (DoS/merge bias):* a future-dated head can brick a stream (successors refused as earlier) and bias UTC- first merges. A consumer *SHOULD* refuse a frame whose utc exceeds receipt time by >300 s, and adversarial-scope merges *SHOULD* rank by min(utc, first-seen); a bricked stream converges by re-genesis (§12.1). * *Sealed-artifact limits:* a public mirror sees ciphertext and metadata. AES-GCM protects confidentiality only while the DEK remains secret; a recipient that has lawfully decrypted plaintext can copy it. Revocation stops future wrapped-key release, not prior possession. Shared passwords, embedded master keys, moving- branch URLs, nonce reuse under one DEK, and claims that bytecode cannot be inspected after decryption are non-conformant security postures (§9.2.1). Wildfeuer Expires 20 April 2027 [Page 32] Internet-Draft RAPP/1 October 2026 * *Kernel substitution:* testing mutable ring bytes and restoring the Grail only after approval creates an untested release. §11.1 requires the release-shaped artifact to contain the pinned bytes before its tests, attestations, and approval; a digest mismatch is a blocking kernel-drift finding. * *Kernel registry rollback/rebinding:* persisting only registry_seq does not preserve an activated Grail declaration across a malicious higher-sequence registry. Consumers also persist every activated canonical grail-kernel entry and reject its later removal, mutation, or duplication (§11.1). 15. References 15.1. Normative References [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, . [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008, . [RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, May 2010, . [RFC6979] Pornin, T., "Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)", RFC 6979, DOI 10.17487/RFC6979, August 2013, . [RFC7405] Kyzivat, P., "Case-Sensitive String Support in ABNF", RFC 7405, DOI 10.17487/RFC7405, December 2014, . [RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493, DOI 10.17487/RFC7493, March 2015, . Wildfeuer Expires 20 April 2027 [Page 33] Internet-Draft RAPP/1 October 2026 [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 2015, . [RFC7516] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", RFC 7516, DOI 10.17487/RFC7516, May 2015, . [RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, DOI 10.17487/RFC7518, May 2015, . [RFC7797] Jones, M., "JSON Web Signature (JWS) Unencoded Payload Option", RFC 7797, DOI 10.17487/RFC7797, February 2016, . [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, January 2017, . [RFC8037] Liusvaara, I., "CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JSON Object Signing and Encryption (JOSE)", RFC 8037, DOI 10.17487/RFC8037, January 2017, . [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . [RFC8410] Josefsson, S. and J. Schaad, "Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 for Use in the Internet X.509 Public Key Infrastructure", RFC 8410, DOI 10.17487/RFC8410, August 2018, . [RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, June 2020, . [RFC9562] Davis, K., Peabody, B., and P. Leach, "Universally Unique IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, May 2024, . 15.2. Informative References Wildfeuer Expires 20 April 2027 [Page 34] Internet-Draft RAPP/1 October 2026 [RAPP1-ANCHOR] "RAPP/1 specification anchor (append-only chain of record)", 2026, . Appendix A. Acknowledgements The specification was hardened through adversarial review rounds recorded in its revision log. The chain of record and its verifier are at [RAPP1-ANCHOR]. Author's Address Kody Wildfeuer Wildhaven Homes LLC Email: wildhavenhomesllc@gmail.com Wildfeuer Expires 20 April 2027 [Page 35]