macroonz_compiler/identity/types.rs
1//! The identity home's declarations: the subject roster, the role roster, the transcript and its derivation record, the generator facts, and the two citation shapes.
2//!
3//! Declarations only.
4//! Every constructor that must see a private field lives in `type_guard.rs`, declared below as this file's own child so the invariant nucleus and the fields it protects are never separated by a module boundary.
5
6use crate::bounded::Bounded;
7use core::marker::PhantomData;
8
9#[path = "type_guard.rs"]
10mod guard;
11
12/// The stem every subject and every grammar this compiler owns is declared under.
13pub const MACROONZ_STEM: &str = "macroonz/identity";
14
15/// One identity subject, by the name the derive-key grammar spells it with and the stem of whoever owns it.
16///
17/// The pair is what separates one subject's identities from another's, so both are DECLARED beside the marker rather than taken from the Rust spelling: a refactor that silently renamed every identity derived for a type would be a law change nobody wrote down.
18/// The trait is open, and a consumer's name that happens to match this compiler's roster is a different key space rather than a collision.
19pub trait Subject: Copy + 'static {
20 /// The subject's declared segment of the derive-key context.
21 const NAME: &'static str;
22
23 /// The stem of whoever declared it.
24 const STEM: &'static str;
25}
26
27crate::subjects! {
28 stem = MACROONZ_STEM;
29 /// The token material one expansion was handed.
30 CapturedDeclaration = "captured-declaration",
31 /// One helper attribute's material, read beside a declaration.
32 CapturedHelper = "captured-helper",
33 /// What a request MEANT, ahead of anything decided about it.
34 ProjectionIntent = "projection-intent",
35 /// The canonical facts one kind-specific content value carries.
36 ProjectionContent = "projection-content",
37 /// One projection plan.
38 Plan = "plan",
39 /// One generated unit — the thing a plan declares it will materialize.
40 GeneratedUnit = "generated-unit",
41 /// One rendered unit — the thing a renderer actually materialized.
42 RenderedUnit = "rendered-unit",
43 /// The canonical bytes of one rendered unit.
44 OutputBytes = "output-bytes",
45 /// One proved closure between a plan's declared membership and what a renderer produced.
46 Closure = "closure",
47 /// One explanation, answered over a plan and the closure that proved its rendering.
48 Explanation = "explanation",
49 /// One closed expansion: the whole account one compilation produced.
50 ClosedExpansion = "closed-expansion",
51 /// One node of the origin graph.
52 OriginNode = "origin-node",
53 /// One subject a plan explicitly does not claim.
54 Nonclaim = "nonclaim",
55 /// One subject a decision trace entry is about.
56 Traced = "traced",
57 /// One stable name this compiler wrote down, standing for a value it declares.
58 DeclaredName = "declared-name",
59 /// One version of the generator itself.
60 GeneratorVersion = "generator-version",
61 /// One related issue a diagnostic points at.
62 RelatedIssue = "related-issue",
63 /// The whole refusal body one diagnostic's related set commits to, as opposed to any single issue inside it.
64 /// A separate subject from [`RelatedIssue`] because one key space holding two LEVELS over one material collides by construction: a body's preimage is the framing of its issues, so an issue whose own material happened to be that framing would derive the identity of the body it aliased.
65 RelatedBody = "related-body",
66 /// One projection profile — the posture a request ran under.
67 ProjectionProfile = "projection-profile",
68 /// One projection kind, named by identity where a decoded route may name a kind this compiler does not implement.
69 ProjectionKind = "projection-kind",
70 /// One contract a diagnostic expected to hold.
71 Contract = "contract",
72 /// One callable entry point.
73 ServiceEntry = "service-entry",
74}
75
76/// One identity this compiler derived, tagged by the subject it names.
77///
78/// Holding one means these thirty-two bytes came from a complete [`Transcript`] under the profile that transcript names, and would come out the same again from the same transcript on any machine.
79///
80/// # Authority
81///
82/// Collision resistance is claimed AS BLAKE3's, for the transcript as [`Transcript`] specifies it, at the [`Version`] the deriving [`Profile`] declares — and nothing broader.
83///
84/// # Construction
85///
86/// The only road is [`Identity::derived`], which takes a typed transcript; nothing wraps arbitrary bytes.
87/// `S` is a `PhantomData` parameter, so an identity naming one subject is a different type than one naming another regardless of bytes, and their derive-key contexts differ too — the separation is a runtime fact and not only a compile-time one.
88///
89/// # Nonclaims
90///
91/// It does not claim that two things this compiler considers different always have different transcripts; that is the transcript's completeness, which each mint site owns and documents.
92#[derive(Clone, Copy)]
93pub struct Identity<S: Subject>([u8; 32], PhantomData<S>);
94
95/// One projection plan's own identity.
96pub type PlanId = Identity<Plan>;
97
98/// One proved closure's own identity.
99pub type ClosureId = Identity<Closure>;
100
101/// One complete explanation's own identity.
102pub type ExplanationId = Identity<Explanation>;
103
104/// One closed expansion's own identity.
105pub type ClosedExpansionId = Identity<ClosedExpansion>;
106
107/// The seat one identity stands in inside its grammar.
108///
109/// A role is part of the derive-key context AND a member of every transcript, so two identities derived from one anchor under different roles are different twice over: separated before a byte of the transcript is read, and disagreeing inside it.
110///
111/// A row's declared name and slot are what the bytes carry, so a row is APPENDED and never renumbered — renumbering an occupied slot re-encodes transcripts that were already encoded.
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
113pub enum Role {
114 /// The token material one expansion was handed.
115 CapturedDeclaration,
116 /// One projection plan.
117 Plan,
118 /// One node of the origin graph.
119 OriginNode,
120 /// One generated unit a plan declares it will materialize.
121 GeneratedUnit,
122 /// One rendered unit a renderer actually materialized.
123 RenderedUnit,
124 /// The canonical bytes of one rendered unit.
125 OutputBytes,
126 /// One bundle materialized across a single publication boundary.
127 Bundle,
128 /// One proved closure between a plan and its rendering.
129 Closure,
130 /// One closed expansion.
131 ClosedExpansion,
132 /// One projection intent — what a request meant, ahead of what it decided.
133 ProjectionIntent,
134 /// One explanation, answered over a plan and its closure.
135 Explanation,
136 /// The documentation rows one captured declaration carries, read as a second fact over the surface its semantic commitment already names.
137 DeclarationDocumentation,
138 /// One stable name this compiler wrote down.
139 DeclaredName,
140 /// The generator's declared name and the shape it renders.
141 GeneratorVersion,
142 /// One refusal body, or one issue inside it, as a diagnostic points at it.
143 DiagnosticRelation,
144 /// One helper attribute's material, read as an independent fact over the surface its semantic commitment already names.
145 ///
146 /// Several helpers may stand here at once; they are separated by the roster position each one is derived at, never by a grammar of their own.
147 CapturedHelper,
148 /// One kind-specific content commitment.
149 ProjectionContent,
150 /// One projection kind qualified by the producer that owns its generated names.
151 ProjectionKind,
152}
153
154/// One position in one grammar's own order.
155///
156/// There is no `Ord`: positions of two different grammars are not comparable, and nothing here ranks them.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub struct Version(u32);
159
160/// One preimage grammar: which members a mint site writes, in what order, carrying what material.
161///
162/// A grammar exists because a preimage is genuinely its own, never because a type is.
163/// The stem sits ahead of the name, so one owner's `"plan"` and another's are two key spaces rather than one reached twice.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165pub struct Profile {
166 stem: &'static str,
167 name: &'static str,
168 version: Version,
169}
170
171/// What one transcript hangs off.
172///
173/// Each posture is written as a distinct byte ahead of its commitment, so a rooted transcript can never encode as an anchored one whose anchor happened to be empty.
174/// The bytes are declared here rather than left in an encoder body, because an independent reader re-deriving a transcript needs them: [`Anchoring::Rooted`] is `0`, [`Anchoring::UnderOwner`] is `1`, [`Anchoring::UnderProjection`] is `2`, and a value is appended rather than renumbered.
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176pub enum Anchoring {
177 /// No anchor at all — the root of one derivation chain, where the material is the whole of what varies.
178 Rooted,
179 /// Anchored under an identity a CONSUMER minted, carried at full width.
180 UnderOwner([u8; 32]),
181 /// Anchored under another identity this compiler derived, carried at full width.
182 UnderProjection([u8; 32]),
183}
184
185/// The COMPLETE preimage one [`Identity`] is derived from.
186///
187/// A transcript is the exact byte string handed to the digest, and the specification is complete: an independent implementation needs what follows and nothing else.
188///
189/// Two primitives.
190/// `u32be(n)` and `u64be(n)` are the integer in four or eight big-endian bytes; `bytes(x)` is `u64be(x.len())` followed by the bytes of `x`, and every variable-length member is written that way, so no two member sequences can be cut at a different boundary and produce one byte string.
191///
192/// The members, in exactly this order, with no separators and no padding:
193///
194/// | # | member | encoding |
195/// | - | ------ | -------- |
196/// | 1 | profile stem | `bytes(utf8)` of [`Profile::stem`] |
197/// | 2 | profile name | `bytes(utf8)` of [`Profile::name`] |
198/// | 3 | profile version | `u32be`, that grammar's own position |
199/// | 4 | subject | `bytes(utf8)` of [`Subject::NAME`] |
200/// | 5 | role | `bytes(utf8)` of [`Role::name`] |
201/// | 6 | role slot | one byte, [`Role::slot`] |
202/// | 7 | anchoring | one byte, [`Anchoring::slot`] |
203/// | 8 | anchor | `bytes(…)` — empty when rooted, else the full thirty-two |
204/// | 9 | material | `bytes(…)` — the full material, never a fold |
205/// | 10 | position | `u32be` |
206///
207/// The derive-key context is [`Profile::context_for`] over the same subject and role, and the identity is `blake3::derive_key(context, transcript)`.
208/// The subject's stem is a segment of that context and is not a member here, so two subjects spelled alike under different stems derive under different keys.
209/// The generator is not a member either: it is carried for the derivation record ([`Transcript::provenance`]) and written into no preimage.
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211pub struct Transcript<'material> {
212 profile: Profile,
213 generator: GeneratorIdentity,
214 role: Role,
215 anchoring: Anchoring,
216 material: &'material [u8],
217 position: u32,
218}
219
220/// The inspectable record of ONE derivation.
221///
222/// The identity answers "which thing is this?" and is thirty-two bytes; the record answers "where did those thirty-two bytes come from?" and is inspection material.
223/// They are separate values so neither constrains the other: the transcript can be complete because it is not stored, and the record can be honest because it is written once where the derivation happened rather than copied everywhere the identity goes.
224///
225/// The material is stated by its LENGTH and not carried, because material is unbounded and a record that copied it would double every rendering in memory to say something the rendered unit already holds.
226/// That length is not a fold and identifies nothing; the identity is what commits to the material, at full width.
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
228pub struct Provenance {
229 subject_stem: &'static str,
230 subject: &'static str,
231 role: Role,
232 profile: Profile,
233 generator: GeneratorIdentity,
234 anchoring: Anchoring,
235 material_length: u64,
236 position: u32,
237}
238
239/// The version of the SHAPE a generator renders: a different token layout, a different set of roles, a different contract realized.
240///
241/// It is deliberately not the package version, which moves for reasons that cannot reach the output and is worthless as the fact a reader judges staleness by.
242/// **It is not a segment of any preimage** either: a bump renames no identity, because which generator rendered a thing is a fact ABOUT the derivation and rides [`Provenance`], while what the thing IS rides the preimage its grammar declares.
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244pub struct ShapeVersion(u32);
245
246/// Which generator produced an identity, and under which rendered shape.
247///
248/// The name and the shape version are the two load-bearing facts a staleness comparison reads.
249/// The package version is recorded and read back but compared by nothing, because a report of "a different generator" on a version bump nobody's output noticed is noise dressed as provenance.
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
251pub struct GeneratorIdentity {
252 name: &'static str,
253 shape: ShapeVersion,
254 package: &'static str,
255}
256
257/// This generator, as every derivation record here names it.
258pub const GENERATOR: GeneratorIdentity = GeneratorIdentity::declared(
259 "macroonz",
260 ShapeVersion::declared(1),
261 env!("CARGO_PKG_VERSION"),
262);
263
264/// One identity a CONSUMER minted, cited by the subject the consumer names it under.
265///
266/// This compiler mints nothing for a consumer and checks nothing here: the bytes cross unchanged, and holding one says the compiler refers exactly to that identity and says nothing else — nothing about authority, freshness, availability, or equivalence.
267#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
268pub struct OwnerIdentity {
269 /// The subject the minting side names it under.
270 pub subject: &'static str,
271 /// The identity's declared raw-byte storage order.
272 pub bytes: [u8; 32],
273}
274
275/// One owning home and one fact it declares, by the stable names that home wrote down.
276///
277/// Every selection, omission, exclusion, and non-applicability in this compiler cites one.
278/// A bare boolean would say a decision happened without saying whose fact decided it, which is exactly the explanation the compiler owes.
279#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
280pub struct OwnerFact {
281 /// The owning home, by its declared name.
282 pub home: &'static str,
283 /// The fact that home declares, by its declared stable name.
284 pub name: &'static str,
285}
286
287/// Bytes one human projection may carry.
288///
289/// A projection that does not fit refuses rather than truncating, so the magnitude is the length past which a sentence is a different sentence and not a longer one.
290pub const HUMAN_TEXT_LIMIT: usize = 512;
291
292/// One bounded human-readable rendering of a typed value.
293///
294/// It is a projection and only a projection: derived from typed values, carried for a person to read, and never read back.
295/// No decision, no identity, and no refusal anywhere in this compiler consults one.
296#[derive(Debug, Clone, PartialEq, Eq, Hash)]
297pub struct HumanProjection(Bounded<u8, HUMAN_TEXT_LIMIT>);