Reference / core / v1.1.1

Musher Document Core Specification — Specification v1

Status: Stable Family: core

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 RFC 2119 and RFC 8174 when, and only when, they appear in all capitals, as shown here.

What is normative. This document defines the behaviour every Musher document family shares. It publishes no JSON Schema of its own (§10): each family's bundle is the executable form of that family's structural validity, and states this document's envelope rules for that family's documents. The conformance corpus — this document's own, and each family's — is the executable form for observable outcomes. This document, the corpora, and each family schema where it states a rule of this document are normative, and none is permitted to disagree with another. Examples, generated documentation, and validator message text are informative.

A disagreement between two normative artifacts is a defect in this specification and blocks a release. Until it is fixed this document governs — but that is how to read a broken contract, not a licence for a schema or a fixture to be wrong.


1. Scope

The Musher Document Core Specification states the rules every Musher document family shares: the document envelope, version compatibility, the catalog item, the identifier grammars more than one family uses, the validation layers and the YAML profile, the shared diagnostic codes, what a conformance claim covers, and the security considerations every document raises.

It defines no kind. No document declares this specification, and no document is validated against it alone. A document belongs to a family — today component, blueprint or listing — and that family's specification applies this one, binding the parameters §1.1 leaves open.

A family specification cites a clause of this document by its major line and section, as core v1 §6.1, and always as a link to the clause's anchor (§9).

Out of scope for this document

1.1 Bindings

This document leaves some parameters to the family that applies it. A family specification MUST bind each of them in its §2, in a table whose first column is headed "Core parameter":

Core parameterWhat the family statesRead by
kindThe constant its documents declare.CORE-ENV-002
metadataThe section defining its metadata object: its identity fields and any description it requires.CORE-ENV-003
Fields accepting nullEach field that accepts null, with the section that justifies it, or that there are none.CORE-ENV-007
Item documentThe file name its document takes at an item root, or that its documents sit inside an item rather than naming one.§4.1

A family specification MUST also declare its normative dependencies, in a table headed "Normative dependencies" that follows the bindings table and names each specification whose rules the family applies, with the major line it applies. Every family names core. A family that applies a rule another family defines names that family too. A family's diagnostic registry comprises its own table, core's (§7), and those of the families it declares as dependencies. Tooling reads the table, so it stays a table.

A family specification narrows this document where it says so and relaxes it nowhere.

1.2 Admission

This section is informative. The test is decided in ADR 0022 §2, and restated here so a reader proposing a rule can apply it.

A rule belongs in this document only when all three of these hold:

  1. It can be stated without naming a kind. A rule that names COMPONENT cannot bind a family that has no components.
  2. At least two families apply it. A rule only one family applies is that family's rule under another name.
  3. Within the major version, it can only grow by addition. A release of this document reaches every family at once, so narrowing one of its rules is a new major of every family.

A rule that looks shared can still fail the first condition. Component's value shapes are applied by component and by blueprint, and they stay in component §6.3: they are the grammar of a block only a component defines, and blueprint reaches them through its declared dependency on component.

2. Document envelope

Every Musher specification document shares one envelope:

specVersion: v1
kind: <KIND>
metadata: { … }
spec: { … }
IDFieldRequirement
CORE-ENV-001specVersionREQUIRED. Declares the document-format compatibility family, independent of any API URL version.
CORE-ENV-002kindREQUIRED. MUST be the constant the family specification binds (§1.1).
CORE-ENV-003metadataREQUIRED. Identity, and any description the family binds.
CORE-ENV-004specREQUIRED. The definition itself.

CORE-ENV-005 — Unknown properties MUST be rejected with ERR_UNKNOWN_FIELD at every level, not only at the root of the envelope. A misspelled field is an error, never a silently ignored one — including when the misspelled field is optional, where ignoring it would silently substitute the default. A property that is defined, by a schema release the validator does not hold, is the same error for a reason §3 gives.

CORE-ENV-006 — A validator encountering a specVersion it does not support MUST reject the document with ERR_UNSUPPORTED_SPEC_VERSION and MUST NOT attempt a best-effort interpretation.

CORE-ENV-007 — an optional field is omitted, not written null. A field accepts null only where null means something omission does not, and a family specification names each field that accepts null (§1.1). A field written null outside those places is rejected in the structural phase with ERR_INVALID_TYPE. This rule is here rather than beside each field because it is a property of the whole contract: a three-state field — absent, null, present — modelling a two-state idea is three states in every SDK generated from a family's schema, forever, and nothing reads the third.

Note. This envelope is deliberately not a Kubernetes-style apiVersion: <group>/<version>. specVersion is Musher's document-format discriminator and one convention beats two. See ADR 0001.

3. Version compatibility

For the same document and pinned external context, a compatible release MUST preserve acceptance and defined meaning: defaults, binding recipients, reference selection, encodings and sensitivity. Moving a check between structural and semantic phases does not alter this guarantee. Changing operational admission policy is separate and MUST NOT silently rewrite document meaning.

specVersion: v1 declares a compatibility family, not an exact schema. A validator MUST evaluate the document against the newest v1.x.y schema release it holds.

Within a major version, validation MUST NOT become stricter. A document that validated against v1.0.0 MUST validate against every later v1.x.y.

A validator MUST reject unsupported constructs. A document using only its supported subset may be accepted even when authored against a newer release. A field introduced in v1.3.0 is, to a validator holding v1.1.0, a property the schema does not define, and it is rejected in the structural phase with ERR_UNKNOWN_FIELD like any other. A validator MUST NOT ignore, strip, or pass through a property it cannot evaluate, and MUST NOT relax the rule on a claim that the property is new — no such claim is verifiable.

The code is ERR_UNKNOWN_FIELD because the validator cannot tell the two cases apart. specVersion names the family, not the release, so nothing in the document says which release it was written against. A field from a later release and a misspelling are the same bytes to a validator that holds neither definition. A code that said so — ERR_SCHEMA_TOO_OLD — would require the validator to know what it is missing, which is precisely what it does not have.

Guessing is the failure §2 exists to prevent. Ignoring a misspelled optional field silently substitutes the default; a field from a newer release is that hazard with a worse ending. The author wrote it, the schema defining it exists, and the only party who cannot see it is the validator. A document rejected for a field the operator can look up is recoverable. A document accepted with that field dropped is not.

Because the diagnostic cannot name the cause, the implementation is where an operator has to find it. An implementation SHOULD make the schema release it evaluated against discoverable — in the diagnostic, in a --version-style output, or both — and SHOULD name updating the validator among the remedies. Message text is not normative (§7), so this is a recommendation about what an implementation surfaces, not about the words it chooses.

Why the release is not pinned in the document. Letting specVersion carry v1.3.0 would turn this failure into ERR_UNSUPPORTED_SPEC_VERSION, which names the cause exactly. That alternative is still rejected. specVersion is the document-format discriminator and declaring a family is the whole of its job (§2); pinning a release there asks every author to name a floor they have no way to know, and makes a document that would validate everywhere fail against the releases it never needed.

The guarantee runs one way. An old document validates against a new validator; a new document does not validate against an old one. A field added in v1.N.0 is therefore usable only where the consumer holds v1.N.0 or later, and a document that must validate everywhere is written against the oldest release its consumers hold.

4. Items

Some rules are about a document's surroundings rather than its contents: that its slug matches the directory holding it, that a path stays inside that directory, that a sibling a family expects is beside it. This section defines the surroundings those rules are measured against, once, and states the one of them that every family shares.

4.1 The item directory

A document does not always travel alone. Documents that belong together — a blueprint and its sibling listing, for example — belong to a catalog item: one directory holding one deployable thing.

An item document is a document whose file name its family specification binds (§1.1). The directory containing an item document is the item root. It is what §4.2's rule is measured against, and what a family's rules about paths inside an item are measured against.

Item documents are siblings. An item holding more than one item document holds them in the same directory, so the directory containing one is the directory containing the others, and an item has one item root however many item documents it holds. A document whose family names no item document sits inside an item rather than at its root, and has no item root of its own.

A document with no directory has no item root. Every rule measured against an item root needs one, so an implementation handed a document rather than a directory MUST NOT report any of them. It has not been given the means to check, and a diagnostic it cannot substantiate is worse than a silence.

4.2 Item identity

One rule binds an item document to the item holding it. It is semantic, it is measured against the item root §4.1 defines, and it reads a field the item document's family defines in its metadata.

IDRuleDiagnostic
CORE-ITEM-001An item document's metadata.slug MUST equal the item directory name.ERR_SLUG_MISMATCH

A document handed over with no directory has no item root, and §4.1 says what that means for this rule.

The item, not its siblings, is what an item document is measured against. An item's documents agree on their identity because each agrees with the one directory holding them, never by being compared with one another. Nothing here requires an item to hold any particular item document, or two of them to agree on anything beyond the name they share with their directory. What a family's other metadata fields count is that family's to say: blueprint §3 says what an item's revision counts, and component §4 says why a component's is a different number.

5. Identifier grammars

A grammar named here is used by more than one family, and is stated once. A family that uses one cites it. Schema sources reference generated shared grammar definitions; the deterministic bundler embeds those definitions into each self-contained publication. Families do not independently maintain the patterns.

5.1 The label grammar

A label matches ^[a-z][a-z0-9-]{0,61}[a-z0-9]$: a lowercase letter, then up to sixty-one lowercase letters, digits or hyphens, then a lowercase letter or a digit. It is a DNS label, two to sixty-three characters long, and it is the grammar ADR 0007 §4 gives an identifier that is composed into a host name or materialised under its own name. metadata.slug, a blueprint node name and a component volume name all take it.

The grammar carries no requirement ID. The rule a document violates is its family's rule that a field takes the grammar, and a value outside it is rejected in the structural phase with ERR_INVALID_VALUE.

5.2 The reference grammar

A reference names a fact this document cannot contain. It is written ${{ <namespace>.<path> }}, and it stands where a value would stand:

template: "https://${{ self.endpoints.web.publicHostname }}/oauth/cb"

A document is sealed before the thing it describes exists. The address that deployment will answer on is assigned afterwards, so no author can write it down, and no field can carry it. A reference is how a document names it anyway.

References use single-pass substitution, without functions, operators, conditionals, arithmetic, recursive expansion or fallback expressions. Families explicitly identify the permitted positions and namespaces.

The grammar. A reference is ${{, optional whitespace, a namespace, one or more .-separated segments, optional whitespace, and }}. A namespace is one of the names this section reserves. A segment matches ^[a-z][a-zA-Z0-9]*$, which admits every name the identifier grammars ADR 0007 §4 gives a referable thing, and excludes ., so a dotted path is read one way. Whitespace inside the braces is insignificant; this specification and the examples write one space inside each brace pair.

The escape. $${{ is the only escape. It is a single four-character token rendering a literal ${{, and not per-$ doubling. A $ that begins neither ${{ nor $${{ is literal and needs no escape.

What a reference yields. A reference occupying a whole value yields the value it names. A reference with text around it yields that value's string form, joined with the surrounding text. Whole references preserve logical types. Families define where string composition is permitted and the encoding used there.

A resolved value is never scanned again. The text a reference yields is substituted, and a ${{ within it is literal. Substitution is non-recursive. This does not eliminate value dependencies between explicit input and output bindings; their ordering belongs to blueprint.

Where a reference may be written. Nowhere, unless a family says so. A family opens a named position and names the namespaces admitted there, and a reference anywhere else is text. No family may open a position whose value decides identity or drives resolution itself — a kind, a specVersion, a mapping key, or a reference to another document — because a reference there would have to be resolved before the document could be read.

CORE-REF-001 — In a position a family opens to references, an unescaped ${{ MUST begin a well-formed reference. A malformed one is rejected in the semantic phase with ERR_MALFORMED_REFERENCE.

An unclosed ${{, a namespace with no path, and a path with no namespace are each one keystroke from a reference an author meant to write. Treating them as text carries the mistake through validation, through publication, and into the deployed thing as the literal characters, where it is discovered by whatever reads the value and fails.

CORE-REF-002 — A reference's namespace MUST be one this section reserves. semantic, ERR_UNKNOWN_REFERENCE_NAMESPACE.

CORE-REF-003 — A reference's namespace MUST be one the family admits at that position. semantic, ERR_REFERENCE_NOT_IN_SCOPE.

The reserved namespaces. The set is closed. An author cannot extend it, and a family admits names from it rather than adding to it.

NamespaceNamesv1
selfThe addressing of the thing the value is bound to.Defined by the family that admits it.
parametersA value supplied when a composition is installed.Reserved.
variablesAn organization variable, as visible to the environment an installation deploys into.Defined by blueprint parameters' from.
connectionsAn atomic connection: endpoint, credential and model selected together.Defined by blueprint parameters' from.
deploymentFacts about a deployment.Defined by blueprint parameters' from.
environmentFacts about a target environment.Reserved.
organizationFacts about an owning organization.Reserved.
outputA value another node publishes.Reserved.

A reserved namespace has no meaning in any document of any family at this line, and no family admits one, so writing it is CORE-REF-003. Reserving it is what keeps it from ever becoming a name an author can address. The set may grow by addition within this major version; a name in it may be given a meaning, and no name leaves it.

Namespaces name a concept and never a vendor. A closed set has nothing to collide with, so the convention that prefixes a flat keyspace does not apply here.

A diagnostic quotes the reference, never the value. An implementation reporting any of the three rules above MUST reproduce the reference as written and MUST NOT include a value it resolved or attempted to resolve, on the terms §11 sets for resolved configuration values. Behavioural conformance fixtures exercise implementation obligations.

6. Validation layers

Structural validation is one layer of four. An implementation MUST apply them in order and MUST NOT report a later-layer diagnostic before the earlier layers pass.

PhaseEnforcesWhere it runs
parserThe Musher YAML profile — §6.1.Client and server
structuralThe family's JSON Schema 2020-12 document.Client and server
semanticRules JSON Schema cannot express — reference resolution, path containment, uniqueness across collections.Client and server
capabilityAccount, region, and quota checks.Server only

An evaluator MUST NOT access the network in the parser, structural, or semantic phases. An authorized resolver acquires dependencies separately and supplies pinned context; catalog-backed facts can then be checked offline.

Why the parser phase rejects legal YAML. A duplicate key, an anchor and an alias are all well-formed YAML 1.2, and all three are rejected here.

A duplicate key has no defined winner: parsers disagree on whether the first or the last survives, so a document carrying one means two things. An alias means one thing, but only after expansion, and a document whose meaning depends on being expanded is not readable as the thing it declares — the same reason §2 rejects a misspelled optional field rather than ignoring it. An anchor with no alias is inert, and is rejected anyway: an author who writes one is reaching for a feature this contract does not have, and finding that out at authoring time is better than finding it out when the alias is added.

The bound also matters. Alias expansion is where a small document becomes a large one — the billion-laughs shape — and a validator that must expand before it can measure has no way to refuse cheaply.

ERR_ANCHOR_OR_ALIAS is separate from ERR_INVALID_YAML because the two say different things to an author. One means the document is malformed; the other means it is well-formed and uses something this contract withholds.

6.0 Validation coverage

Results carry status (VALID, INVALID or INCOMPLETE), validationProfile, reached phase, diagnostics and deferred obligations. validationProfile names the core validation obligations, separately from an implementation conformance profile or a behavioural fixture profile. Each deferred obligation identifies its rule, document pointer and missing context. Known failure takes precedence over incompleteness. VALID means all obligations required by the claimed profile completed. No caller may interpret INCOMPLETE as approval.

Profiles are structural (parser and schema), document (including semantic obligations), publication (document plus catalog/publication admission), and deployment (publication plus installation resolution and current policy). Document validation without required item or dependency context is INCOMPLETE.

CORE-ADMISSION-001 — Publication and deployment MUST complete all respective deferred obligations before admission. Synthetic catalog/account fixtures may exercise these checks offline; a production implementation still acquires and verifies its own authorized context.

6.1 The Musher YAML profile

Musher documents are written in a restricted profile of YAML 1.2.2, not in unrestricted YAML. This section is that profile, and it governs every family built on this document — a family specification inherits it rather than restating it.

Every restriction below withholds something YAML 1.2.2 permits. The reason is uniform, and it is the one §2 gives for rejecting a misspelled optional field: a document that means different things to different readers, or that cannot be judged without unbounded work, is not a contract. A feature is therefore withheld when it is legal but ambiguous, not when it is merely unusual.

Encoding and framing

IDRuleDiagnostic
CORE-YAML-001A document MUST be encoded in UTF-8. Malformed UTF-8 is rejected before replacement decoding.ERR_INVALID_UTF8
CORE-YAML-002A document MAY begin with a UTF-8 byte order mark. It carries no meaning and MUST be ignored.—
CORE-YAML-003Line endings MAY be LF or CRLF, and carry no meaning.—
CORE-YAML-004A file MUST contain exactly one YAML document. The --- and ... markers MAY be present; a stream carrying more than one document is rejected.ERR_MULTIPLE_DOCUMENTS

A file holding two documents has no answer to "which one is the document", and picking the first silently discards a thing the author wrote.

Structure

IDRuleDiagnostic
CORE-YAML-005Every mapping key MUST be a string. A numeric, boolean, null, or complex key is rejected.ERR_NON_STRING_KEY
CORE-YAML-006A mapping key MUST NOT appear twice.ERR_DUPLICATE_KEY
CORE-YAML-007A document MUST NOT declare an anchor or an alias.ERR_ANCHOR_OR_ALIAS
CORE-YAML-008A document MUST NOT use a merge key (<<).ERR_MERGE_KEY
CORE-YAML-009A node MUST NOT carry an explicit tag — neither a custom tag (!secret) nor a core-schema tag (!!str).ERR_EXPLICIT_TAG

Mapping keys are property names in every schema this repository publishes, and 1: resolving to the integer one on one parser and the string "1" on another is the duplicate-key problem wearing a different hat.

A merge key is an alias by another name and is withheld for the same reason. It is named separately because << reads as a key rather than as a reference, so an author who writes one is not told about aliases; they are told about <<.

An explicit tag overrides scalar resolution, and scalar resolution is exactly what this profile fixes below. !!str 5 and 5 differ only in a tag, and a contract in which the type of a value depends on an annotation beside it has no stable reading.

Scalar resolution

Scalars resolve by the YAML 1.2 core schema, and by nothing else. true and false are booleans; null and ~ are null; on, off, yes, and no are strings, as YAML 1.2 requires and YAML 1.1 did not. A value whose intended type is not the resolved one MUST be quoted.

This is the one place where naming the version does real work: a YAML 1.1 parser reads no as boolean false, and a country: no in a document read by both is two different documents.

Bounds

An implementation MUST reject a document exceeding any of these, and MUST accept one that does not:

IDBoundLimitDiagnostic
CORE-YAML-010Document size1 MiB (1 048 576 bytes)ERR_DOCUMENT_TOO_LARGE
CORE-YAML-011Nesting depth64 levelsERR_DEPTH_EXCEEDED
CORE-YAML-012Scalar length64 KiB (65 536 bytes)ERR_SCALAR_TOO_LONG

The bounds are stated rather than left to implementations because "be sensible" is not a bound: a document one validator accepts and another refuses on size is not one contract, and an author has no way to discover the limit except by exceeding it somewhere.

Each is far above any document a person writes and far below what makes a parser a denial-of-service surface. Document size MUST be measured before parsing — a limit a parser can apply only after building the tree is not a limit on the work it does. The alias ban already removes the billion-laughs shape; these bound the cases it does not cover.

What carries no meaning

Key order, comments, indentation width, quoting style, and flow-versus-block form are all presentation. Two documents differing only in these are the same document, and an implementation MUST NOT derive meaning from any of them.

Because YAML 1.2 is a superset of JSON, a document written as JSON is a valid Musher document and is read identically.

The byte boundary MUST use fatal UTF-8 decoding. Parser/profile errors and representation depth are checked before conversion to JSON; aliases are never expanded. Implementations return diagnostics rather than conversion exceptions. Non-finite numbers and integers outside ±9007199254740991 are rejected with ERR_INVALID_NUMBER. Other numbers use finite binary64 representation. Logical values may contain null when explicitly admitted by their value schema; this is separate from optional document-field omission.

6.2 The format keyword

This clause is about the JSON Schema keyword format, not about any field named format a family defines. The two are unrelated: such a field is the family's, with its own rules, and no schema this repository publishes uses the keyword at all.

Should one ever appear, format is an annotation and asserts nothing. That is the JSON Schema 2020-12 default rather than a shortcut: format constrains a value only where the Format Assertion vocabulary is explicitly declared, and no Musher schema declares it.

A validator MUST NOT reject a document because a value fails a format keyword, and MUST NOT accept one it would otherwise reject because a value satisfies one.

The reason is portability. format implementations differ — one library's uri accepts what another's rejects, and email is worse — so a schema that depended on format for rejection would validate differently depending on which library a consumer happened to link. A rule that must hold everywhere is written with an assertion keyword instead: pattern, enum, minLength, const. Where a rule cannot be expressed that way, it belongs to the semantic phase and carries a diagnostic of its own.

Adopting the Format Assertion vocabulary later would make previously valid documents invalid, so it is a breaking change and needs an ADR.

7. Diagnostics

Diagnostic codes and the phase at which validation fails are normative. Human-readable messages are not — implementations in different languages emit different text and that is expected. A code identifies a condition and its registry lists all permitted phases. A diagnostic reports the actual phase where the condition was found. Resolution diagnostics additionally identify the stage: parameters, allocation, connections, values, environment or record. Successful value resolution establishes no current authorization, quota or placement grant.

A diagnostic's primary path is a JSON Pointer into the submitted document. Cross-document diagnostics anchor at an authored binding or componentRef and carry related locations with an artifact reference and its actual path. Missing component inputs anchor at componentRef; implementations MUST NOT invent blueprint output fields or absent binding fields as diagnostic locations. Human-readable messages and related artifact labels MUST NOT expose secret values.

The codes below apply to every document of every family. A family specification's diagnostics table adds codes to them, and MUST NOT declare one of them again: a code declared in two tables can be given two meanings, and one code names one condition.

CodePhaseMeaning
ERR_INVALID_YAMLparserThe document is not well-formed YAML 1.2.
ERR_INVALID_UTF8parserOriginal bytes are malformed UTF-8.
ERR_INVALID_NUMBERparserA numeric scalar is non-finite or an unsafe integer.
ERR_DUPLICATE_KEYparserThe same mapping key appears twice.
ERR_ANCHOR_OR_ALIASparserThe document declares a YAML anchor or an alias.
ERR_MULTIPLE_DOCUMENTSparserThe file carries more than one YAML document.
ERR_NON_STRING_KEYparserA mapping key is not a string.
ERR_MERGE_KEYparserThe document uses a merge key (<<).
ERR_EXPLICIT_TAGparserA node carries an explicit YAML tag.
ERR_DOCUMENT_TOO_LARGEparserThe document exceeds the size bound in §6.1.
ERR_DEPTH_EXCEEDEDparserThe document nests deeper than §6.1 permits.
ERR_SCALAR_TOO_LONGparserA scalar exceeds the length bound in §6.1.
ERR_UNSUPPORTED_SPEC_VERSIONstructuralspecVersion is not a supported value.
ERR_WRONG_KINDstructuralkind does not match the family being validated.
ERR_UNKNOWN_FIELDstructuralA property not defined by the schema is present.
ERR_MISSING_FIELDstructuralA required property is absent.
ERR_INVALID_TYPEstructuralA value has the wrong type.
ERR_INVALID_VALUEstructuralA value violates a pattern, enum, or bound.
ERR_SLUG_MISMATCHsemanticmetadata.slug disagrees with the item directory name.
ERR_MALFORMED_REFERENCEsemanticAn unescaped ${{ does not begin a well-formed reference (§5.2).
ERR_UNKNOWN_REFERENCE_NAMESPACEsemanticA reference names a namespace §5.2 does not reserve.
ERR_REFERENCE_NOT_IN_SCOPEsemanticA reference names a reserved namespace the family does not admit at that position.

8. Conformance

A claim to conform to a family release covers two sets of cases: the release's own corpus, and the core corpus (§8.1) at the core edition the release records (§9). An implementation conforms when it produces the declared outcome for every case in both, and MUST pass both under its declared profile. It MAY also run a later core v1 corpus, and MUST NOT substitute one for the other.

Implementations MUST run the fixture corpus in their own CI. Passing a fixture that is declared to fail is a conformance failure.

An implementation MUST declare the profile it claims. Conformance is not a single claim: capability needs an account, a region, and a quota, so an implementation a user runs locally cannot reach it, and an editor integration that checks structure is a useful thing to be without being a control plane. The profiles, and the report shape a claim should take, are defined in docs/conformance.md.

A skipped case is never a passed one. An implementation MUST NOT claim a profile while skipping any case in a phase that profile requires.

8.1 The core corpus

The core corpus is specifications/core/v1/conformance/, and it holds parser cases only. A structural case needs a schema, and this document publishes none; a case about an item needs a document of some family inside a directory.

An adapter runs a core case through its parser alone. expected: pass means the parser accepts the document, and no later phase runs. A core case's document need not be a valid envelope, because no later phase reads it. Where it carries a kind, that value is COMPONENT and has no effect, and an adapter MUST NOT dispatch on it. The parser runs before a family is chosen, so an implementation covering several families runs the core corpus once — once per parser, where it has more than one.

Each family's corpus keeps one parser case, parser-001-reject-duplicate-keys. It shows that the family's pipeline applies the profile at all. It does not show that the pipeline applies all of it, which is what the core corpus is for.

9. Editions

This document has its own release line, tagged core/vX.Y.Z as each family's is. One release of it is an edition.

A family cites the line, not the edition. A family specification cites this document as "core v1 §N" and never names an exact edition in prose: an edition in prose would turn every core patch release into a prose edit in every family. Every citation MUST be a link to the clause's anchor. A section number standing alone is read as a section of the document it appears in, so an unlinked core v1 §6.1 in a family specification points a reader at the family's own §6.1.

A family release records the edition. Each release of a family records the core edition it was built and tested against, and carries that edition's spec.md and corpus with it. A conformance claim against the release names that edition (§8), so the two corpora the claim names both exist and were tested together. How the edition is recorded, the checks that hold a family release while this document has unreleased releasable changes, and why its other unreleased changes only warn, are set out in ADR 0023, as ADR 0037 §3 refines it.

What a release of this document may change. This paragraph is informative: it records this repository's release policy. Within v1, a minor release of this document adds a clause, a case or a relaxation, and adds a code only for a condition an earlier v1 edition already rejects. No release rejects a document an earlier v1 edition accepted — §3 applied to every family at once, which is why §1.2 admits only rules that grow by addition. A rule that needs narrowing is core v2, a new line that each family adopts in a major release of its own.

10. Boundaries and unsupported capabilities

No core schema. This document publishes no JSON Schema. Each family's bundle states the envelope for its own documents, and ADR 0022 §5 records why neither available shape of a shared schema was worth publishing. Adding one later rejects no document and is additive.

Family dispatch. After parser validation, a dispatcher rejects an unknown kind with structural ERR_WRONG_KIND. An explicitly selected family uses that same code when the document's kind differs from its bound constant. Dispatch never precedes parser/profile checks.

Shared grammar ownership. Family sources identify core's label grammar with x-musher-grammar: label. Bundles inline its constraints from §5.1; published bundles are self-contained. Source copies do not independently author the pattern.

Endpoint semantics. Component defines self endpoint paths, as self.endpoints.<name>.<property>. Blueprint consumes that definition. Core defines syntax only.

Reserved namespaces. parameters, environment, organization and output are reserved and unsupported in v1 reference strings. deployment is defined only where blueprint parameters' from admits it.

11. Security considerations

This section is addressed to implementations: what a validator, a CLI, or a control plane must do so that reading an untrusted Musher document is safe. SECURITY.md is the separate question of what counts as a vulnerability in this repository and how to report one.

A Musher document is untrusted input. It arrives from a pull request, a catalog submission, or a POST body, and every consideration below assumes its author is hostile. Each family specification adds what is specific to its own documents.

Parsing. §6.1 is a security boundary as much as an interoperability one. Aliases are refused because expansion is where a small document becomes a large one, and the size bound is measured before parsing because a limit applied after the tree exists is not a limit on the work done to build it. An implementation MUST enforce all three bounds, and MUST NOT raise them to accommodate a document that exceeds them.

An implementation MUST NOT resolve YAML tags to host language types. The tag ban makes this unreachable through a conforming parser, but a parser configured to construct arbitrary objects from tags is the classic deserialisation vulnerability, and the ban is not a substitute for choosing a safe loader.

Regular expressions. Every pattern a Musher schema publishes is free of lookaround and backreferences, which is what lets it compile under RE2 as well as under a backtracking engine. An implementation evaluating these patterns on a backtracking engine SHOULD apply a match timeout regardless: the guarantee is about the patterns published here, not about any the platform composes with them.

Diagnostics. An implementation MUST NOT include resolved configuration values in a diagnostic message. Diagnostics are logged, attached to pull requests, and printed in CI output, and a validator that echoes the value it rejected turns a validation error into a disclosure.

Paths inside an item. A path a document names inside its item is a path an implementation will open, chosen by the document's author. An implementation MUST decide containment on the resolved location rather than on the string: a path is an escape if what it resolves to lies outside the item root, whether it got there by ../, by an absolute path, or by a symbolic link inside the tree.

Symlinks are the case worth stating plainly. A link committed inside an item can point anywhere the process can read, so an implementation MUST resolve links before testing containment, and MUST treat a dangling link resolving outside the root as an escape — containment is a property of the resolved location, not of whether the target happens to exist. The conformance corpus fixtures this directly; see docs/conformance.md.

An implementation MUST NOT follow a path outside the item root even when the resulting file would be readable and would parse. The rule is about what a reviewer of the item can see: a document that reaches outside the directory being reviewed brings in something the review did not cover.

Schema retrieval. Every published bundle is self-contained: all $refs resolve inside $defs, and no validator needs a network request to evaluate a document (Using the schemas). An implementation SHOULD vendor the schema at an exact version rather than fetching it, and MUST verify what it fetches if it does — the .sha256 beside each pinned URL and published.json are there for that. A validator that resolves schemas over the network at validation time is an SSRF primitive and a runtime dependency on an origin it does not control.