STXT Schema

Status:
Aurora
last modified:
2026-09-07

1. Introduction

This document is STXT-SCHEMA-SPEC; the other specifications cite it by that name.

This document defines the specification of the STXT Schema language, a mechanism for validating STXT documents through formal semantic rules.

A schema:

  • Is an STXT document with namespace @stxt.schema.
  • Defines the nodes, types, and cardinalities of the target namespace.
  • Does not modify the base syntax of STXT; it operates on the already parsed structure.

This document defines what a schema is and how it validates. The way a tool locates schemas on the file system (.stxt directories, user and system levels, STXT_PATH) is defined in STXT-DISCOVERY-SPEC.

1.1 Version of this specification

This specification carries its own date and its own status in the Last modif and Status fields of its Metadata, independent of the other STXT specifications' and with the meaning fixed by STXT-SPEC §1.1. It is in Aurora. It depends on STXT-SPEC: a schema is an STXT document.

2. Terminology

The keywords "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" must be interpreted according to RFC 2119 and RFC 8174: they carry that meaning only when they appear in uppercase, as here.

Terms such as node, indentation, namespace, inline and >> block retain their meaning in STXT-SPEC.

3. Relationship between STXT and Schema

Schema validation occurs after STXT parsing:

  1. Parsing the document into a hierarchical STXT structure.
  2. Resolving the effective namespace of each node.
  3. Applying the corresponding schema.

An implementation MAY apply validation during the parsing process, provided that such validation remains loosely coupled to the base parser. This allows errors to be detected before the full parsing is completed.

4. General Structure of a Schema

A schema document MUST have as its root node:

Schema (@stxt.schema): <target_namespace>

Rules:

  • <target_namespace> MUST be a valid namespace according to STXT-SPEC.
  • The root node Schema MUST belong to the @stxt.schema namespace.
  • The schema document MAY include a Description node.
  • The schema document MUST include one or more Node nodes.
  • A schema document is exactly one Schema node: neither more than one root node, nor none (section 13, condition 12).

Example:

Schema (@stxt.schema): com.example.docs
    Description: Example schema
    Node: Document
        Type: GROUP
        Children:
            Child: Author
            Child: Date
                Max: 1
            Child: Content
                Min: 1
                Max: 1
            Child: Metadata (org.example.meta)
                Max: 1
    Node: Author
    Node: Date
        Type: DATE
    Node: Content
        Type: TEXT

5. One schema per namespace

For each logical namespace:

  • There MUST NOT be more than one effective schema simultaneously.
  • If an implementation has several candidate schemas for the same namespace, the choice is fixed by STXT-DISCOVERY-SPEC: the level closest to the document wins, and two definitions of the same namespace at the same level leave that namespace with no active definition.
  • For a specific validation, there MUST be only one effective schema.

The empty namespace is not validated. A node whose effective namespace is "" (that of everything that neither declares nor inherits a namespace, STXT-SPEC §7) is valid by definition: a validator MUST NOT look up a schema for it nor emit SCHEMA_NOT_FOUND for it. Since children inherit the namespace, a document without a namespace stays entirely outside validation; a node that declares a namespace inside it is validated, against the schema of that namespace. Consequently, no schema or template MAY have "" as its target namespace: nothing could ever resolve against it.

6. Closed content model

STXT Schema uses a closed content model. This means that, for each node in the document, only the direct children explicitly declared in the schema are allowed.

Rules:

  • If a Node declares a Children block, its instances in the document MAY ONLY have as direct children the nodes declared through Child (each identified by its logical pair canonical name + effective namespace).
  • If a Node does not declare Children, its instances in the document MUST NOT have any direct children (total closure).
  • The appearance of an undeclared direct child MUST cause a validation error.

Any Node can be the root of a document. The schema does not distinguish between "root" nodes and "child" nodes: a document whose root node is any Node of the namespace is valid if that node satisfies its rules. So, against the schema of section 15.5, both Document (com.example.docs): … and Title (com.example.docs): … are valid roots. This allows partial documents (a fragment, one file per section) without extra schemas; a schema that wants to restrict the root does so by convention, not by declaration.

This model is consistent with the STXT philosophy of failing loudly: a misspelled node (for example Titel instead of Title) is detected as an undeclared child instead of being silently accepted.

Note on evolution. A schema admits compatible extensions —adding a Child with a minimum cardinality of zero, lowering a Min, raising a Max: changes that allow more cases, not fewer— without any existing document ceasing to validate. The nuance is that compatibility is relative to the extended definition: a document that uses the addition does not validate against an earlier copy of the schema, so if the consumers of the namespace do not update their copy at the same time, the same document may validate in one and fail in another. Whether an extension needs a new version therefore depends on the use case: on how the definition is distributed, not only on the change. For those situations, and always for incompatible changes —a new mandatory Child, removing one, narrowing a cardinality—, the recommended practice is to version the namespace in the namespace itself (for example com.example.docs.v1com.example.docs.v2), so that each version has its schema and documents declare the version they use.

Unresolved cross-namespace nodes. Declaring a Child that points to another namespace does not require having the schema of that namespace: loading a schema never looks inside other schemas, so the result is independent of load order. The check happens when validating a document: if a node of that namespace appears, its schema MUST be available and the node MUST exist in it as a Node:; otherwise, the document is invalid. A cross-namespace child that is declared but never used poses no problem (see section 8.1).

7. Node Definition (Node:)

7.1 Basic form

Node: Node Name
    Description: Node description
    Type: INLINE
    Children:
        Child: Child Name (another.namespace)
            Min: 1
            Max: 1

Rules:

  • The inline value of Node MUST be a valid node name according to STXT-SPEC.
  • Matching between a Node and the nodes of the document is done by canonical name (STXT-SPEC §6): Node: Node Name validates node-name, NODE_NAME and Node Name, and the same holds for Child.
  • Each Node MUST be unique within the schema at the canonical name level.
  • Each Node defines the semantics of the node in the schema's target namespace.
  • If Type is omitted, the default type is INLINE.
  • A Node MUST NOT contain more than one Description node, more than one Type node, more than one Children node, nor more than one Values node.
  • Only types that allow children (see section 9) MAY declare Children. Declaring Children in a type that does not allow children MUST cause a schema error.
  • The name of a Child MAY include a namespace when the child belongs to another namespace (section 8).
  • Min and Max are optional within a Child (section 10).

7.2 Values in ENUM types

Node: Node Name
    Description: Node description
    Type: ENUM
    Values:
        Value: value 1
        Value: value 2
        Value: value 3

The ENUM type, and only ENUM, MAY specify a Values node with the allowed values through Value nodes. If Values exists, it MUST contain at least one Value node. If a Node declares Type: ENUM, it MUST include Values. Each Value MUST have a non-empty inline value after trimming: an empty Value: MUST cause a schema error (VALUE_EMPTY), because an enumeration whose only valid value is the empty string makes no sense. For the same reason a Child: without a value is an error (INVALID_NODE_NAME, section 13.1).

Since ENUM does not allow children (section 9), a Node of type ENUM MUST NOT declare Children.

8. Children (Children:) and cross-namespaces

A node MAY have a Children entry. If Children exists, it MUST contain one or more Child nodes with the information of the allowed children.

A Child MAY belong to another namespace, in which case it is indicated in the name of the Child itself. Example:

Node: node name
    Children:
        Child: child name (child.namespace)
            Min: 0
            Max: 1
  • If the namespace is omitted, the Child belongs to the target namespace of the current schema.
  • If an explicit namespace is indicated, the Child belongs to that specific namespace.
  • Within the same Children node, an implementation MUST NOT accept two Child nodes that point to the same logical pair canonical name + effective namespace.

8.1 Explicitly defined nodes

Every node that appears in Children must have its own definition as Node: in its corresponding schema. When this is checked depends on the child's namespace: for children of the schema's own namespace, at schema load; for children of another namespace, at document validation (when the node appears).

This rule excludes children without a definition and guarantees that all nodes have defined semantics.

This implies:

  • A Child without an explicit namespace MUST have its Node: definition in the schema itself; its absence MUST cause a schema error at load (section 13).
  • Declaring Child: Metadata (org.example.meta) does not require having the schema of org.example.meta: loading a schema never looks inside other schemas (load-order independence).
  • If a Metadata (org.example.meta) node appears in a document, the schema of org.example.meta MUST be available and MUST define Node: Metadata; otherwise, it is a document validation error (section 13). A cross-namespace child that is declared but never used poses no problem.

9. Types

Types define:

  1. The form of the node value (inline, >> block, both, or none).
  2. Whether the node allows children.
  3. Content validation.

They are defined inside Node, through a Type element. Example:

Node: node name
	Type: NODE_TYPE
	Children:
		Child: Child Name

9.1 Model of two independent properties

Each type is described through two independent properties:

  • Value form: INLINE, BLOCK, INLINE/BLOCK or NONE.
  • Allows children: YES or NO.

These two properties are independent from each other. In particular, "allows children" is not derived from the value form. The compatibility rule is as follows:

Only the two generic structural types — INLINE and GROUP — allow children. All types with specific content validation are leaves (they do not allow children).

Rationale: as soon as a type declares that it validates a specific datum (NUMBER, DATE, BOOLEAN, ENUM, etc.), that node is a datum, and a datum is a leaf. If both value and structure are needed, the generic INLINE type is used, which exists precisely for that; and if only structure is needed, GROUP is used.

Other considerations:

  • The type does not control requiredness; only the form and validity of the value. Requiredness of appearance is controlled through cardinality.
  • The value of Type MUST match exactly one of the types defined in this section.
  • Declaring Children in a Node whose type does not allow children MUST cause a schema error.

9.2 Basic structural types

A conforming implementation MUST support these types and MUST validate their structure.

Type Value form Allows children Description / Validation
INLINE INLINE YES Inline : text. Default type. Optional value without specific validation. Allows children.
GROUP NONE YES Does not allow textual value. Structured children only.
BLOCK BLOCK NO >> text block only. Does not allow children.
TEXT INLINE/BLOCK NO Generic text. It may be inline : or >> block. Does not allow children.

9.3 Basic INLINE content types

A conforming implementation MUST support these types and MUST validate their structure.

Type Value form Allows children Description / Validation
BOOLEAN INLINE NO true or false.
NUMBER INLINE NO Decimal number, optional sign and exponent (see 9.4).
ENUM INLINE NO Only specified values (see 9.6).

9.4 Extended INLINE content types

A conforming implementation MUST support these types and SHOULD validate their structure.

Type Value form Allows children Description / Validation
INTEGER INLINE NO Integer with an optional sign.
NATURAL INLINE NO Unsigned integer (≥ 0).
DATE INLINE NO YYYY-MM-DD date, valid in the calendar.
TIME INLINE NO hh:mm:ss time, in range.
TIMESTAMP INLINE NO ISO 8601 date and time, YYYY-MM-DDThh:mm[:ss[.f…]][Z|±hh:mm].
UUID INLINE NO 8-4-4-4-12 hexadecimal UUID, case-insensitive.
URL INLINE NO Absolute URL: scheme and host required (see below).
EMAIL INLINE NO Email address, bare or with a display name (see below).

Grammar of the types of 9.3 and 9.4. Each type accepts exactly the strings of its rule, applied to the inline value as the parser delivers it (already trimmed). This is the normative grammar: two conforming implementations accept and reject the same values.

BOOLEAN   = "true" | "false"                              ; exact, case-sensitive
NUMBER    = [ sign ] ( digits [ "." [ digits ] ] | "." digits ) [ ( "e" | "E" ) [ sign ] digits ]
INTEGER   = [ sign ] digits
NATURAL   = digits                                        ; no sign
DATE      = YYYY "-" MM "-" DD                            ; an existing date of the (proleptic)
                                                          ; Gregorian calendar, year 0000–9999
TIME      = hh ":" mm ":" ss                              ; 00–23, 00–59, 00–59; no fraction, no zone
TIMESTAMP = DATE "T" hh ":" mm [ ":" ss [ "." digits ] ] [ "Z" | sign hh ":" mm ]
                                                          ; same ranges; seconds, fraction and zone optional
UUID      = 8hex "-" 4hex "-" 4hex "-" 4hex "-" 12hex     ; hex = [0-9A-Fa-f]; version and variant not checked
sign      = "+" | "-"
digits    = digit { digit }                               ; ASCII 0–9 only; leading zeros allowed

Details:

  • NUMBER is not the JSON number: it accepts +1, 1., .5 and 007. It is a textual value: it is not checked to fit any numeric type of the platform, nor rounded.
  • DATE and TIMESTAMP require an existing date: 2026-02-30 and 2026-13-01 are invalid, 2024-02-29 is valid. TIME and TIMESTAMP require a time in range: 24:00:00 and 10:60:00 are invalid. Leap seconds (ss = 60) are not accepted.
  • TIMESTAMP: the fraction has one or more digits; without a zone, the interpretation (local time or other) belongs to the application; the zone is validated only syntactically (±hh:mm in range), not checked to exist.
  • The expressions of this section and those of 9.5 are what a port must implement; nothing is delegated to the platform's date or URL parsers, which disagree with each other.

The EMAIL type accepts two forms, and the address is validated the same way in both:

  • The bare address: [email protected].
  • The address preceded by a display name, with the address between < and >: Joan Costa <[email protected]>. The name is any non-empty text that contains neither < nor > (quotes, if present, are part of the name; they are not interpreted), and the blank between the name and < is optional.

A conforming implementation MUST accept both forms and MUST NOT accept < and > without a name before them, unbalanced, or followed by anything. This is the usual form of email headers (the RFC 5322 mailbox), without the quoting and comments of that grammar. The normative grammar is this (blank is the one of STXT-SPEC §4: U+0020 or U+0009):

EMAIL     = Address
          | Name { Blank } "<" Address ">"
Name      = non-empty text without "<" or ">", whose last character is not a blank
Address   = Local "@" Domain                      ; at most 254 characters in total
Local     = 1*64( atext | "." )                   ; 1 to 64 characters
atext     = ASCII letter or digit, or one of:  ! # $ % & ' * + / = ? ^ _ ` { | } ~ -
Domain    = 1*( ASCII letter or digit | "." | "-" ) "." TLD
TLD       = 2*63( ASCII letter )                  ; 2 to 63 letters

Notes on EMAIL:

  • ASCII only: internationalized addresses (EAI) and non-ASCII domains not converted to punycode are not accepted. This is deliberate: a single exact grammar, without Unicode tables that every platform versions its own way.
  • Permissive within those limits: the full RFC 5322 dot-atom is not validated ([email protected] and [email protected] pass), nor are the domain labels checked one by one (a..b.example.com passes). The value of the type is catching the missing @, blanks, a domain without a dot or a forgotten TLD, not replicating RFC 5322.
  • The length limits are the practical ones of RFC 5321: 64 characters for the local part and 254 for the whole address; the TLD, 2 to 63 letters.

The URL type accepts only absolute URLs with a scheme and a host. The grammar belongs to this specification, not to the URL parser of each platform, so that every implementation accepts exactly the same values:

URL       = Scheme "://" [ Userinfo "@" ] Host [ ":" Port ] [ "/" Path ] [ "?" Query ] [ "#" Fragment ]

Scheme    = letter { letter | digit | "+" | "-" | "." }       ; case-insensitive
Userinfo  = one or more characters other than blank, "/", "?", "#" and "@"
Host      = "[" one or more of ( hex | ":" | "." ) "]"          ; IPv6 in brackets
          | one or more characters other than blank, "/", "?", "#", "@", ":", "[" and "]"
Port      = digit { digit }
Path      = characters other than blank, "?" and "#"           ; may be empty
Query     = characters other than blank and "#"                ; may be empty
Fragment  = characters other than blank                        ; may be empty

Rules:

  • A conforming implementation MUST accept any scheme of that form (https, ftp, git+ssh, …): there is no list of allowed schemes.
  • The host MUST be non-empty; no top-level domain is required (http://localhost is valid) and nothing is resolved. Non-ASCII characters are accepted as they are, in the host and elsewhere (an IDN is not converted to punycode).
  • An implementation MUST NOT accept a value without a scheme (stxt.dev, www.stxt.dev/x), with a scheme but no // and host (mailto:[email protected], urn:isbn:…, tel:…, file:///etc/hosts), with inner blanks, or with a non-numeric port.
  • The check is syntactic: nothing is normalised, %xx escapes are not decoded, and the resource is not checked to exist.

Design note (non-normative): there is deliberately no general URI type. A URI under RFC 3986 is almost any string with a colon (a:b is one), so such a type would validate little more than TEXT. The value of URL lies in catching the forgotten scheme or the misspelled host. For identifiers that are not web addresses (urn:isbn:…, tel:…) use TEXT or an ENUM; for mail, EMAIL.

9.5 Extended INLINE/BLOCK binary content types

A conforming implementation MUST support these types and MAY validate their structure.

Type Value form Allows children Description / Validation
HEXADECIMAL INLINE/BLOCK NO [0-9A-Fa-f]+. Hexadecimal string.
BINARY INLINE/BLOCK NO [01]+. Binary string.
BASE64 INLINE/BLOCK NO Standard Base64: alphabet A–Z a–z 0–9 + /, optional trailing = padding, no leftover bits.

In all three types, and in both forms, the grammar is applied to the value with no blanks at all: before validating, every space (U+0020) and tab (U+0009) is removed, wherever it is, and in the BLOCK form line breaks and empty lines too, so that validation applies to the concatenation of the lines. This allows grouping digits (DE AD BE EF, 1010 1010) and splitting long content across several lines (e.g. Base64 at 76 columns). No other character is ignored: a separator other than a blank (DE:AD, DE-AD) is an invalid value.

BASE64 is the standard alphabet of RFC 4648 §4 (not the URL-safe one with - and _). The trailing = padding MAY be omitted; the content MUST decode with no leftover bits, that is, re-encode to the same string (padding aside). An empty string is not valid in any of the three types.

9.6 ENUM type

The ENUM type allows explicitly enumerating the values allowed for a node. Rules:

  • Comparison MUST be done on the inline value as delivered by the parser (already trimmed left and right, STXT-SPEC §10.1).
  • Comparison MUST be exact and CASE-SENSITIVE.
  • Comparison MUST NOT apply additional canonicalization, removal of diacritics, nor normalizations equivalent to the canonical name of nodes.
  • The node MUST define Values with Value nodes, which represent the allowed values.
  • Each Value MUST be unique and non-empty (section 7.2).
  • ENUM does not allow children: a Node of type ENUM MUST NOT declare Children.

Example:

Node: Node Name
    Type: ENUM
    Values:
        Value: value 1
        Value: value 2
        Value: value 3

A conforming implementation MUST check ENUM types against their allowed values and MUST reject any value that does not match exactly one of them.

Interoperability note (non-normative). The exact comparison is by code points. Values are literal and are not normalized to NFC (unlike node names, STXT-SPEC §4.3): café in precomposed form (NFC) and café in decomposed form (NFD) look identical but do not match. Some input flows — historically, macOS — produce NFD. Whoever defines ENUM values with diacritics should write them in NFC, which is what the usual editors produce.

9.7 MARKDOWN type

A conforming implementation MUST support this type.

Type Value form Allows children Description / Validation
MARKDOWN INLINE/BLOCK NO Text interpreted as Markdown. No structural validation.

Rules:

  • For validation purposes, MARKDOWN is equivalent to TEXT: all text is valid Markdown, so an implementation MUST NOT reject any value because of its content.
  • Its function is to be an interpretation contract for consumers (renderers, exporters, editors): the text SHOULD be interpreted according to CommonMark. Extensions (e.g. tables) are the application's decision.
  • In the BLOCK form, line breaks and empty lines are preserved (unlike the binary types of section 9.5): they form part of the Markdown text.
  • Like every type with specific content, MARKDOWN does not allow children (section 9.1).

Design note (non-normative): MARKDOWN is the only embedded format that STXT Schema defines, because it covers the only domain that STXT delegates by design — the formatting of prose (bold, links, lists within text) — and because its raw form remains readable, consistent with the Human-First principle. Types for embedded data formats (JSON, XML, YAML, TOML, …) are an explicit non-goal (section 11): STXT is already the structure format, and such a type would bury structure in opaque blobs. Structure is expressed in STXT.

10. Cardinalities

Cardinalities are expressed through the Min and Max nodes within each Child. They are optional non-negative integers that indicate the minimum or maximum number of allowed occurrences of that child.

Rules:

  • If Min is omitted, the effective minimum is 0.
  • If Max is omitted, the effective maximum is unlimited.
  • Min and Max MUST NOT exceed 4294967295 (2³² − 1); a greater value MUST cause a schema error (CARDINALITY_NOT_VALID). The bound is platform-independent and far above any child count reachable under the recommended parser limits (STXT-SPEC §11.2); "no maximum" is said by omitting Max, not with a giant number.
  • Min and Max MUST NOT appear more than once within the same Child.
  • If both exist, Min MUST NOT be greater than Max.
  • Max: 0 is legal and declares a forbidden child: the Child exists in the model but no occurrence is valid. It serves, for instance, to withdraw in one version of a namespace a child that another allowed. An implementation MUST NOT treat it as an error.
  • Cardinality applies per instance of the parent node.
  • Cardinality counts only direct children with the same canonical name and the same effective namespace.
  • Cardinality validation is independent of child order: it counts occurrences, not positions (see section 11).
  • A conforming implementation MUST check cardinalities.

11. Order of children

STXT Schema does not validate the order of children. Validation of cardinality and presence is independent of the position of each child within its parent: it only counts how many times each child appears, not in what order.

Two documents with the same children in a different order validate exactly the same. This is a design decision consistent with the Human-First principle: the author of a document should not have to remember the order of the fields.

Order preserved, not validated. Although order is not validated, the order of appearance of children is preserved in the parsed tree (core guarantee, STXT-SPEC). Therefore, an application that needs positional meaning (for example, consecutive sections of a document) obtains it from the tree itself, not from validation. The semantics of order belong to the application.

Sequential order validation is an explicit non-goal of STXT Schema: introducing ordered content models (in the style of xs:sequence) would complicate validators (automata, particle ambiguity) without a clear benefit for the target use cases.

Other explicit non-goals of STXT Schema are: patterns or regular expressions over values, default values, conditional rules between fields ("if A appears, then B"), and types for embedded data formats (JSON, XML, YAML, TOML, …; see the design note in section 9.7). Keeping the model small and predictable is a design goal: that semantics belongs to the application.

12. Normative Examples

12.1 Schema with cross-namespace references

Schema (@stxt.schema): com.example.docs
    Node: Document
        Type: GROUP
        Children:
            Child: Metadata (org.example.meta)
                Max: 1
            Child: Content
                Min: 1
                Max: 1
    Node: Content
        Type: BLOCK

And in org.example.meta:

Schema (@stxt.schema): org.example.meta
    Node: Metadata
        Type: INLINE

12.2 Valid document

Document (com.example.docs):
    Metadata (org.example.meta): info
    Content >>
        Line 1
        Line 2

12.3 Recursive structure

The closed model allows recursion: a Node may be declared as a child of itself or of an ancestor.

Schema (@stxt.schema): com.example.docs
    Node: Section
        Type: GROUP
        Children:
            Child: Title
                Min: 1
                Max: 1
            Child: Section
    Node: Title
        Type: INLINE

Here Section may contain more nested Section nodes at any depth (limited by the recommended depth limit in STXT-SPEC, security section).

13. Schema Errors

A schema is invalid if:

  1. The root node does not have the form Schema (@stxt.schema): <target_namespace>, or the target namespace is empty or not valid according to STXT-SPEC.
  2. It does not define any Node.
  3. It defines two Node with the same canonical name.
  4. It uses an unknown Type.
  5. It defines Children in a Node whose type does not allow children (section 9).
  6. It defines Values in a Node whose type is not ENUM (section 7.2).
  7. Cardinality is invalid (Min > Max, a value that is not a non-negative integer, a value greater than 4294967295, duplicated Min/Max).
  8. A Node of type ENUM does not define Values, or Values contains no Value.
  9. A duplicated Value appears after inline normalization by trim.
  10. Two equivalent Child nodes appear (same canonical name + effective namespace) within the same Children.
  11. A child appears in Children of the schema's own namespace whose Node is not defined in the schema itself.
  12. The schema document has more than one root node, or none: a schema document is exactly one Schema node.
  13. A node of the schema (Node, Child, Type, Min…) is written in >> form; all of them are inline except Description, which is TEXT.
  14. A Value of an ENUM is empty (section 7.2).

A document is invalid against a schema if:

  • A node presents a direct undeclared child in the Children of its definition (closed model, section 6).
  • A node without declared Children presents any direct child.
  • A declared cardinality is violated.
  • The value of a node does not satisfy validation of its type.
  • The form of the node (inline or >> block, value present or absent, with or without children) is not the one of its type (section 9).
  • An ENUM value does not match exactly any of its Value.
  • A node with a non-empty namespace appears that has no schema available (SCHEMA_NOT_FOUND; nodes with namespace "" are not validated, section 5), or whose name does not exist as a Node: in the schema of its namespace (section 8.1).

13.1 Error codes

As in STXT-SPEC §11.1, every error carries a stable code, identical in every implementation, and is never renamed. A schema document is first validated as a document against the meta-schema of section 15, so its form errors (a Node without a value, two Type, a Min that is not a NATURAL…) come with the codes of the second table; the rules of the first are applied afterwards.

Errors of the schema (first list of this section):

Code Condition
SCHEMA_ROOT_NOT_VALID 1: the root is not Schema (@stxt.schema): …, or the target namespace is not valid
SCHEMA_NAMESPACE_EMPTY 1: the target namespace is empty
SCHEMA_MULTIPLE_ROOTS 12: the schema document has more than one root node, or none
SCHEMA_NODE_NOT_INLINE 13: a node of the schema is written in >> form
NODE_DUPLICATED 3: two Node with the same canonical name
INVALID_NODE_NAME the value of a Node or a Child is not a valid name (section 7.1)
TYPE_NOT_VALID 4: unknown type (the meta-schema catches it first as INVALID_VALUE of the Type ENUM; TYPE_NOT_VALID is left for an implementation whose type registry lacks the type)
CHILDREN_NOT_ALLOWED_FOR_TYPE 5: Children on a type that allows no children
VALUES_NOT_ALLOWED_FOR_TYPE 6: Values on a type other than ENUM
CARDINALITY_NOT_VALID 7: Min or Max is not a non-negative integer, or exceeds 4294967295
MIN_GREATER_THAN_MAX 7: Min greater than Max
VALUES_REQUIRED 8: ENUM without Values, or Values without any Value
VALUE_DUPLICATED 9: two equal Value after trimming
VALUE_EMPTY 14: a Value of an ENUM without a value
VALUES_DUPLICATED two Values nodes in the same Node
CHILD_DUPLICATED 10: two Child with the same canonical name + namespace pair
CHILD_NOT_DEFINED 11: a Child of the same namespace without its Node

Errors of the document against the schema (second list):

Code Condition
SCHEMA_NOT_FOUND node with a non-empty namespace and no schema available
NODE_NOT_DEFINED_IN_SCHEMA the node's name does not exist as a Node in the schema of its namespace
CHILD_NOT_DECLARED direct child not declared in Children (closed model), or any child under a Node without Children
TOO_FEW_CHILDREN fewer occurrences of a child than its Min
TOO_MANY_CHILDREN more occurrences of a child than its Max
INVALID_VALUE the value does not follow the grammar of its type, or is not one of the Value of an ENUM
BLOCK_FORM_REQUIRED inline form where the type requires a >> block (BLOCK)
BLOCK_FORM_NOT_ALLOWED >> block form where the type requires inline
VALUE_NOT_ALLOWED a value on a node of type GROUP
CHILDREN_NOT_ALLOWED children under a node of type TEXT or MARKDOWN
TYPE_NOT_VALID the type declared in the schema does not exist in the implementation
UNEXPECTED_ERROR unforeseen exception of the implementation, with the node's line

14. Conformance

An implementation is conforming if:

  • It fully implements this document.
  • It validates types, value forms, cardinalities, and allowed values (ENUM).
  • It applies the closed content model (section 6).
  • It applies the child compatibility rule by type (only INLINE and GROUP allow children).
  • It applies the strict mandatory definition rule for all nodes referenced in Children (at schema load for children of the same namespace; at document validation for cross-namespace children, section 8.1).
  • It validates cardinalities independently of order.
  • It selects, for each validation, a single effective schema per namespace, and does not validate nodes with an empty namespace (section 5).
  • It rejects invalid documents and schemas.

15. Schema of the Schema (@stxt.schema)

This section defines the official schema of the schema system itself: the meta-schema that validates all documents in the @stxt.schema namespace.

Level of guarantee. The meta-schema validates the form of a schema document (which nodes exist, their types, their cardinalities). It cannot express the conditional or crossed rules of this document (for example "Values only if Type: ENUM", or "Min ≤ Max"), which belong to the language but not to the meta-schema. Therefore, "validating against the meta-schema" is a necessary but not sufficient condition to be a valid schema: in addition, the rules of section 13 must be met.

15.1 Considerations

  • Every schema document is: Schema (@stxt.schema): <target-namespace>
  • A schema contains:
    • Optionally a Description.
    • One or more Node nodes.
  • Each Node:
    • Has an inline value (the node name of the target namespace).
    • May optionally have:
      • Description
      • Type
      • Children
      • Values
  • Each Child (Children element) defines the name (and optionally a different namespace) and may have:
    • Min: Minimum number of nodes that must appear. If the node does not exist there is no established minimum.
    • Max: Maximum number of nodes that may appear. If the node does not exist there is no established maximum.
  • Each Values:
    • May only appear in Node nodes of type ENUM.
    • Contains one or more Value nodes.
  • The names (Schema, Node, Type, Children, Child, Description, Min, Max, Values, Value) belong to the @stxt.schema namespace.

15.2 Complete Meta-Schema

Schema (@stxt.schema): @stxt.schema
    Node: Schema
        Children:
            Child: Description
                Max: 1
            Child: Node
                Min: 1
    Node: Node
        Children:
            Child: Type
                Max: 1
            Child: Children
                Max: 1
            Child: Description
                Max: 1
            Child: Values
                Max: 1
    Node: Children
        Type: GROUP
        Children:
            Child: Child
                Min: 1
    Node: Description
        Type: TEXT
    Node: Child
        Children:
            Child: Min
                Max: 1
            Child: Max
                Max: 1
    Node: Min
        Type: NATURAL
    Node: Max
        Type: NATURAL
    Node: Type
        Type: ENUM
        Values:
            Value: INLINE
            Value: BLOCK
            Value: TEXT
            Value: GROUP
            Value: BOOLEAN
            Value: NUMBER
            Value: ENUM
            Value: INTEGER
            Value: NATURAL
            Value: DATE
            Value: TIME
            Value: TIMESTAMP
            Value: UUID
            Value: URL
            Value: EMAIL
            Value: HEXADECIMAL
            Value: BINARY
            Value: BASE64
            Value: MARKDOWN
    Node: Values
        Type: GROUP
        Children:
            Child: Value
                Min: 1
    Node: Value

Note: in the meta-schema, Node and Child are of default type INLINE (their inline value is the name of the target node or child) and therefore they allow children. This illustrates that INLINE is the type that combines value and structure, the core of the system.

15.3 Quick reading

  • Schema Inline value = target namespace (e.g. com.example.docs). Children: Description (?), Node (+).

  • Node Inline value = target node name (e.g. Document, Author). Optional children:

    • Type: concrete type (if missing ⇒ INLINE).
    • Children: Node with list of allowed Child.
    • Description: explanatory text.
    • Values: Allowed values (ENUM type only).
  • Type Inline (ENUM), with the type name (GROUP, INLINE, NUMBER, etc.).

  • Children GROUP: contains one or more Child nodes.

  • Description TEXT: may be inline or multiline.

  • Values GROUP: contains one or more Value nodes.

  • Value Inline value with one of the allowed values for the ENUM.

15.4 Minimal valid example

Schema (@stxt.schema): com.example.docs
    Node: Document

15.5 Complete example

Schema (@stxt.schema): com.example.docs
    Description: Example schema
    Node: Document
        Type: GROUP
        Children:
            Child: Title
                Min: 1
                Max: 1
            Child: Author
            Child: Metadata (org.example.meta)
                Max: 1
    Node: Title
        Type: INLINE
    Node: Author
        Type: INLINE