//! Domain-separation tags. //! //! Epiphany hashes domain-separated preimages so that two semantically //! different chunks with identical raw bytes never share a content address //! (Chapter 8 §"Domain-Separated Preimages"). Every tag is a fixed 8-byte //! ASCII string beginning with `MUSC`. Centralizing them here keeps the set //! drift-free: there is exactly one definition of each tag in the workspace. /// A fixed 8-byte domain-separation tag. Always the first bytes of a hash /// preimage (see [`crate::Preimage`]). /// /// The spec's domain-tag vocabulary is closed: the reserved built-ins /// ([`DomainTag::BUILTINS`]) plus extension-introduced *system-derived* tags, /// which "MUST begin with `MUSCS` and have length exactly 8 bytes" (Chapter 5). /// Every tag is an 8-byte ASCII string. The field is private and the /// constructors enforce that vocabulary, so a nonconforming tag — wrong prefix /// (`b"BAD_TAG!"`), non-ASCII bytes, or an unregistered `MUSC....` domain — /// cannot be minted and therefore cannot reach [`crate::derive_system_counter`] /// or a hash preimage. To mint an extension's own tag use [`SystemDomainTag`]. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct DomainTag([u8; 8]); impl DomainTag { /// Length of every domain tag, in bytes. pub const LEN: usize = 8; /// The prefix marking a *system-derived* tag (Chapter 5: /// "Additional domain tags introduced by registered extensions MUST begin /// with `MUSCS` and have length exactly 8 bytes"). The three built-in /// system tags ([`Self::SYSTEM_VOICE`], [`Self::SYSTEM_PITCH`], /// [`Self::SYSTEM_ANOMALY`]) also carry it. const SYSTEM_PREFIX: &'static [u8] = b"MUSCS"; /// The raw 8 ASCII bytes. #[inline] pub const fn as_bytes(&self) -> &[u8; 8] { &self.0 } // --- Built-in tags (Chapter 8 §"Domain-Separated Preimages", Ch. 5/6). --- /// `.musc` chunk payloads. pub const CHUNK: DomainTag = DomainTag(*b"MUSCCHNK"); /// Manifest chunk payloads. pub const MANIFEST: DomainTag = DomainTag(*b"MUSCMANI"); /// Blob payloads; a `BlobId` is the [`crate::ContentHash`] under this tag. pub const BLOB: DomainTag = DomainTag(*b"MUSCBLOB"); /// `ConflictId` derivation (Chapter 6 §"Conflict Identity"). pub const CONFLICT: DomainTag = DomainTag(*b"MUSCCONF"); /// Canonical operation-envelope hash, `EnvelopeHash` (Chapter 6 §6.5). pub const ENVELOPE: DomainTag = DomainTag(*b"MUSCENVH"); /// Glyph-catalog metrics hash (Chapter 7 §"Glyph Catalog Identity"). pub const FONT_METRICS: DomainTag = DomainTag(*b"MUSCFNTM"); /// `ManifestId` derivation (Chapter 8 / deferred-types table). pub const MANIFEST_ID: DomainTag = DomainTag(*b"MUSCMNIF"); /// System-promoted voice counter derivation (Chapter 5 §"System-Derived"). pub const SYSTEM_VOICE: DomainTag = DomainTag(*b"MUSCSVCE"); /// System-derived pitch counter derivation (Chapter 5 §"System-Derived"). pub const SYSTEM_PITCH: DomainTag = DomainTag(*b"MUSCSPCH"); /// `IntegrityAnomalyId` derivation (Chapter 5 §"System-Derived Counter /// Collisions"). Reserved built-in: anomalies are core, not an extension /// concern (Pass 11, item 1.4). pub const SYSTEM_ANOMALY: DomainTag = DomainTag(*b"MUSCSANM"); /// `LayoutObjectId` derivation (Chapter 7 §"Provenance", /// Requirement `req:layoutir:object-id-derivation`). Like /// [`Self::FONT_METRICS`], this is a reserved built-in but **non-canonical**: /// layout-object ids are not document state and enter no content hash, so the /// tag lives in the layout namespace, not among the canonical system tags /// (Chapter 8 §"Domain-tag registry"). pub const LAYOUT_OBJECT_ID: DomainTag = DomainTag(*b"MUSCLOID"); /// Every built-in tag, in declaration order. The closed core vocabulary /// (the nine canonical tags plus the non-canonical layout tags /// [`Self::FONT_METRICS`] and [`Self::LAYOUT_OBJECT_ID`]). pub const BUILTINS: [DomainTag; 11] = [ Self::CHUNK, Self::MANIFEST, Self::BLOB, Self::CONFLICT, Self::ENVELOPE, Self::FONT_METRICS, Self::MANIFEST_ID, Self::SYSTEM_VOICE, Self::SYSTEM_PITCH, Self::SYSTEM_ANOMALY, Self::LAYOUT_OBJECT_ID, ]; /// Constructs a domain tag from raw bytes, accepting only the spec's closed /// vocabulary: a reserved built-in, or a well-formed extension system tag /// (begins `MUSCS`). Every byte must be printable ASCII. Returns `None` /// otherwise — wrong prefix, non-ASCII bytes, or an unregistered /// `MUSC....` domain that is neither built-in nor a `MUSCS` system tag. /// This is the checked entry point for decoding a tag from storage/interop. #[inline] pub fn from_bytes(raw: [u8; 8]) -> Option { if Self::is_valid_bytes(&raw) { Some(DomainTag(raw)) } else { None } } /// Validity predicate for the closed vocabulary: printable-ASCII, not a /// file-format magic byte string, and either a registered built-in or a /// `MUSCS`-prefixed system tag. #[inline] fn is_valid_bytes(raw: &[u8; 8]) -> bool { if !raw.iter().all(u8::is_ascii_graphic) { return false; } if Self::is_file_magic(raw) { return false; } DomainTag::BUILTINS.iter().any(|b| b.as_bytes() == raw) || raw.starts_with(Self::SYSTEM_PREFIX) } /// File-format magic byte strings are in the same 8-byte `MUSC*` /// namespace, but they are not hash-domain tags and must not be reused by /// extension system identifiers. #[inline] fn is_file_magic(raw: &[u8; 8]) -> bool { raw == &BUNDLE_MAGIC || raw == &SUPERBLOCK_MAGIC } /// Whether this is one of the reserved built-in tags ([`Self::BUILTINS`]). #[inline] pub fn is_builtin(&self) -> bool { Self::BUILTINS.contains(self) } /// Whether this is a *system-derived* tag (begins `MUSCS`): a built-in /// [`Self::SYSTEM_VOICE`] / [`Self::SYSTEM_PITCH`] / [`Self::SYSTEM_ANOMALY`] /// or an extension tag minted via [`SystemDomainTag::new_extension`]. #[inline] pub fn is_system_derived(&self) -> bool { self.0.starts_with(Self::SYSTEM_PREFIX) } /// Whether this is an *extension-introduced* system tag: system-derived and /// not a reserved built-in. The three built-in system tags return `false` /// here — they are reserved, not extension-introduced. #[inline] pub fn is_extension_system_tag(&self) -> bool { self.is_system_derived() && !self.is_builtin() } } /// A [`DomainTag`] proven to be *system-derived* (begins `MUSCS`): a built-in /// [`DomainTag::SYSTEM_VOICE`] / [`DomainTag::SYSTEM_PITCH`] / /// [`DomainTag::SYSTEM_ANOMALY`], or an /// extension-introduced tag. Only these are admissible seeds for /// [`crate::derive_system_counter`] (Chapter 5 §"System-Derived Identifiers"). /// /// Carrying the precondition in the type — rather than checking it at the call /// site — makes `derive_system_counter` total: it is impossible to seed a /// system identifier from, say, [`DomainTag::CHUNK`], because that value cannot /// be turned into a `SystemDomainTag`. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct SystemDomainTag(DomainTag); impl SystemDomainTag { /// Built-in: system-promoted voice counters (`MUSCSVCE`). pub const VOICE: SystemDomainTag = SystemDomainTag(DomainTag::SYSTEM_VOICE); /// Built-in: system-derived pitch counters (`MUSCSPCH`). pub const PITCH: SystemDomainTag = SystemDomainTag(DomainTag::SYSTEM_PITCH); /// Built-in: integrity-anomaly identifiers (`MUSCSANM`). pub const ANOMALY: SystemDomainTag = SystemDomainTag(DomainTag::SYSTEM_ANOMALY); /// Wraps a domain tag if it is system-derived; returns `None` otherwise. #[inline] pub fn new(tag: DomainTag) -> Option { if tag.is_system_derived() { Some(SystemDomainTag(tag)) } else { None } } /// Mints an *extension-introduced* system-derived tag from raw bytes, /// enforcing the Chapter 5 rule: printable ASCII, begins `MUSCS`, and does /// not collide with a reserved built-in. The only sanctioned way for an /// extension to introduce its own system-derived domain tag. #[inline] pub fn new_extension(raw: [u8; 8]) -> Option { let tag = DomainTag::from_bytes(raw)?; if tag.is_extension_system_tag() { Some(SystemDomainTag(tag)) } else { None } } /// The underlying domain tag. #[inline] pub const fn tag(self) -> DomainTag { self.0 } /// The raw 8 ASCII bytes. #[inline] pub const fn as_bytes(&self) -> &[u8; 8] { self.0.as_bytes() } } // --- File-format magic byte strings (Chapter 8 §"The Bundle Layout"). --- // // These are not hashing domain tags; they are the literal magic bytes that // open the fixed header and the superblock slots. They are centralized here // alongside the domain tags so the full set of 8-byte `MUSC*` constants has a // single home. `epiphany-bundle` (Agent D) consumes them. /// Bundle fixed-header magic: ASCII `"MUSCBND\0"` (8 bytes, trailing NUL). pub const BUNDLE_MAGIC: [u8; 8] = *b"MUSCBND\0"; /// Superblock-slot magic: ASCII `"MUSCSUPR"` (8 bytes). pub const SUPERBLOCK_MAGIC: [u8; 8] = *b"MUSCSUPR"; #[cfg(test)] mod tests { use super::*; #[test] fn every_tag_is_eight_ascii_bytes_starting_with_musc() { for t in DomainTag::BUILTINS { assert_eq!(t.as_bytes().len(), DomainTag::LEN); assert!(t.as_bytes().starts_with(b"MUSC"), "{t:?}"); assert!(t.as_bytes().iter().all(|b| b.is_ascii()), "{t:?}"); } } #[test] fn tags_are_pairwise_distinct() { let tags = DomainTag::BUILTINS; for (i, a) in tags.iter().enumerate() { for b in &tags[i + 1..] { assert_ne!(a, b, "duplicate domain tag {a:?}"); } } } #[test] fn exact_tag_spellings_match_spec() { // Locked literally against Chapter 8 / Chapter 5 / Chapter 6. assert_eq!(DomainTag::CHUNK.as_bytes(), b"MUSCCHNK"); assert_eq!(DomainTag::MANIFEST.as_bytes(), b"MUSCMANI"); assert_eq!(DomainTag::BLOB.as_bytes(), b"MUSCBLOB"); assert_eq!(DomainTag::CONFLICT.as_bytes(), b"MUSCCONF"); assert_eq!(DomainTag::ENVELOPE.as_bytes(), b"MUSCENVH"); assert_eq!(DomainTag::FONT_METRICS.as_bytes(), b"MUSCFNTM"); assert_eq!(DomainTag::MANIFEST_ID.as_bytes(), b"MUSCMNIF"); assert_eq!(DomainTag::SYSTEM_VOICE.as_bytes(), b"MUSCSVCE"); assert_eq!(DomainTag::SYSTEM_PITCH.as_bytes(), b"MUSCSPCH"); assert_eq!(DomainTag::SYSTEM_ANOMALY.as_bytes(), b"MUSCSANM"); assert_eq!(DomainTag::LAYOUT_OBJECT_ID.as_bytes(), b"MUSCLOID"); assert_eq!(&BUNDLE_MAGIC, b"MUSCBND\0"); assert_eq!(&SUPERBLOCK_MAGIC, b"MUSCSUPR"); } #[test] fn builtin_system_tags_are_not_extension_tags() { // They are system-derived (begin MUSCS)... assert!(DomainTag::SYSTEM_VOICE.is_system_derived()); assert!(DomainTag::SYSTEM_PITCH.is_system_derived()); assert!(DomainTag::SYSTEM_ANOMALY.is_system_derived()); // ...but reserved built-ins, NOT extension-introduced. assert!(DomainTag::SYSTEM_VOICE.is_builtin()); assert!(DomainTag::SYSTEM_ANOMALY.is_builtin()); assert!(!DomainTag::SYSTEM_VOICE.is_extension_system_tag()); assert!(!DomainTag::SYSTEM_PITCH.is_extension_system_tag()); assert!(!DomainTag::SYSTEM_ANOMALY.is_extension_system_tag()); // A non-system tag is neither. assert!(!DomainTag::CHUNK.is_system_derived()); assert!(!DomainTag::CHUNK.is_extension_system_tag()); } #[test] fn from_bytes_accepts_only_the_closed_vocabulary() { // Wrong prefix. assert!(DomainTag::from_bytes(*b"BAD_TAG!").is_none()); assert!(DomainTag::from_bytes(*b"SHA2CHNK").is_none()); // Right format prefix but unregistered, non-system domain. assert!(DomainTag::from_bytes(*b"MUSCWXYZ").is_none()); // Non-ASCII payload byte (0xFF) is rejected even with a MUSC prefix. assert!(DomainTag::from_bytes([b'M', b'U', b'S', b'C', 0xFF, b'A', b'B', b'C']).is_none()); // Control byte (NUL) is not printable ASCII. assert!(DomainTag::from_bytes(*b"MUSCS\0\0\0").is_none()); // Built-in: accepted. assert_eq!( DomainTag::from_bytes(*b"MUSCCHNK").unwrap(), DomainTag::CHUNK ); // The layout-object-id tag is a registered (non-system) built-in. assert_eq!( DomainTag::from_bytes(*b"MUSCLOID").unwrap(), DomainTag::LAYOUT_OBJECT_ID ); assert!(DomainTag::LAYOUT_OBJECT_ID.is_builtin()); assert!(!DomainTag::LAYOUT_OBJECT_ID.is_system_derived()); // Extension system tag: accepted. assert!(DomainTag::from_bytes(*b"MUSCSEXT") .unwrap() .is_extension_system_tag()); } #[test] fn system_domain_tag_enforces_the_chapter5_rule() { // Must begin MUSCS. assert!(SystemDomainTag::new_extension(*b"MUSCXXXX").is_none()); // Must not collide with a reserved built-in. assert!(SystemDomainTag::new_extension(*b"MUSCSVCE").is_none()); // MUSCSANM is now a reserved built-in too (Pass 11): not extension-mintable. assert!(SystemDomainTag::new_extension(*b"MUSCSANM").is_none()); // Non-ASCII rejected. assert!( SystemDomainTag::new_extension([b'M', b'U', b'S', b'C', b'S', 0xFF, b'A', b'B']) .is_none() ); // File-format magic strings are reserved in the shared MUSC* namespace. assert!(SystemDomainTag::new_extension(SUPERBLOCK_MAGIC).is_none()); // A genuine extension tag is accepted and classified correctly. let ext = SystemDomainTag::new_extension(*b"MUSCSEXT").unwrap(); assert!(ext.tag().is_extension_system_tag()); // Built-in system tags wrap; non-system tags do not. assert_eq!(SystemDomainTag::VOICE.tag(), DomainTag::SYSTEM_VOICE); assert_eq!(SystemDomainTag::ANOMALY.tag(), DomainTag::SYSTEM_ANOMALY); assert!(SystemDomainTag::new(DomainTag::SYSTEM_PITCH).is_some()); assert!(SystemDomainTag::new(DomainTag::SYSTEM_ANOMALY).is_some()); assert!(SystemDomainTag::new(DomainTag::CHUNK).is_none()); } }