STXT Tutorial
STXT is an indentation-based, hierarchical format designed for people. It has no escape characters, and its documents can be read as plain text.
An STXT document in ten lines
# First example of a document, with all the elements
Book (com.acme.book):
Title: Modern Software Architecture
Authors:
Author: María Pérez
Author: Juan García
Publisher: ACME Editorial
Summary >>
This book offers a practical view of patterns and best practices
for designing distributed, scalable systems.Indentation defines the structure
An STXT document is an ordered tree of nested nodes. Every node must have a name, and depending on the type of node it may have a value or an associated text.
In this example we have a main node Book (we will talk about com.acme.book later on).
This node has the following children: Title, Authors, Publisher and Summary.
In turn, Authors has 2 Author children.
With this example we can already see that indentation defines the structure:
Book
├── Title
├── Authors
├── Author
├── Author
├── Publisher
├── Summary
Inline nodes : and block nodes >>
There are two types of node, and we have already seen them in the example:
- Inline nodes:
Book,Title,Authors,AuthorandPublisher - Block nodes:
Summary
Inline nodes may have an associated value: for example, the first Author
has the value María Pérez, and Publisher has the value ACME Editorial.
In addition, inline nodes may have other nodes as children.
Block nodes have no associated value: they can only have a block of text lines, starting on the very next line. The text lines must always have a greater indentation than that of the block definition. The most important thing to remember is that the text of the block is not interpreted, it is literal text.
So the text of Summary is:
This book offers a practical view of patterns and best practices
for designing distributed, scalable systems.
Comments
The first line of the example is a comment. Comments
are lines that start with # (indentation whitespace does not count).
Namespaces
Nodes may belong to a namespace. A namespace
is just a grouping. In our example, Book belongs to the
namespace com.acme.book. This makes it different from the Book of the
namespace com.demo.
To define a namespace it is enough to put it in parentheses
right after the definition of the name: Book (com.acme.book).
An important feature is that if a node does not define a namespace,
it inherits that of its parent. That is, unless redefined, children
inherit the namespace of the parent. So, in the example, Title also
belongs to the namespace com.acme.book, and so do Authors, Author,
Publisher and Summary.
Schemas and validation
STXT allows documents to be validated directly, with schemas written in the language itself. This validation covers which structure is valid, the number of child nodes and what the values of the nodes must look like.
This is enough for a namespace to become a vocabulary for that specific domain.
Schemas can also be defined as templates. The one for the example would be the following:
Template (@stxt.template): com.acme.book
Description >>
Book: Template for publisher book records
Structure >>
Book:
Title: (1)
Authors: (1)
Author: (+)
ISBN: (?)
Publisher: (?)
Published: (?) DATE
Summary: (?) TEXT
Chapter: (*)
Content: (?) TEXTShall we try the Playground?
At this point we have already seen the basics of using STXT. The next step can be to go deeper into the concepts we have already learned, or to try it directly in the Playground.
The examples of this tutorial and of the portal can be tried in the playground by clicking the "Open in the Playground" icon.
The playground also starts with a mini-tutorial, which can be skimmed through, or other documents can be tried directly.
Nodes in depth
Nodes with a namespace and without a namespace
Nodes may define a namespace. If a node does not define one and does not inherit it from its parent, it is said to be a node without a namespace. Since namespaces are inherited, if a node has a namespace, its child nodes will have one too, either inherited or redefined. The first example is a node with a namespace, so it is inherited by all its child nodes:
Book (com.acme.book)
├── Title (com.acme.book)
├── Authors (com.acme.book)
├── Author (com.acme.book)
├── Author (com.acme.book)
├── Publisher (com.acme.book)
├── Summary (com.acme.book)
This is the same example without a namespace:
Book:
Title: Modern Software Architecture
Authors:
Author: María Pérez
Author: Juan García
Publisher: ACME Editorial
Summary >>
This book offers a practical view of patterns and best practices
for designing distributed, scalable systems.Book
├── Title
├── Authors
├── Author
├── Author
├── Publisher
├── Summary
As it has no namespace we cannot associate it with any schema, so only the structure can be validated, not the content.
Hierarchy and levels
Indentation defines the hierarchy
Let us show two examples in which the hierarchy changes with the level:
# Example 1
Node 1:
Node 2:
Node 3:
Node 4:
Node 5:
# Example 2
Node 1:
Node 2:
Node 3:
Node 4:
Node 5:The hierarchy is different, and it follows directly from the indentation:
Example 1
=========
Node 1
├── Node 2
├── Node 3
├── Node 4
├── Node 5
Example 2
=========
Node 1
├── Node 2
├── Node 3
├── Node 4
├── Node 5
A level is written with 1 tab or 4 spaces.
Main rules:
- One tab is one level.
- Four spaces are one level. They must be multiples of four. No other number of spaces is allowed.
- Tabs and spaces are not mixed on the same line. Either only tabs, or only spaces.
- Levels must be consecutive. You cannot jump from level 1 to level 3.
Inline nodes
Inline nodes are all the nodes of the form Node name:.
That is, they use : as the separator. These nodes can have children
and are the ones that define the main structure of documents. Example:
Node 1: value 1, main node
Node 2: value 2, child of node 1
Node 3: value 3, child of node 1
Node 4: value 4 # Child of 3: This is part of the value!
Node 5: child of node 1In this example the structure can be seen directly:
Node 1
├── Node 2
├── Node 3
├── Node 4
├── Node 5
And so can the values.
| Node name | Node value |
|---|---|
| Node 1 | value 1, main node |
| Node 2 | value 2, child of node 1 |
| Node 3 | value 3, child of node 1 |
| Node 4 | value 4 # Child of 3: This is part of the value! |
| Node 5 | child of node 1 |
The value of Node 4 deserves a special mention. The value is everything
after the first :. From there on there is no interpretation at all:
everything is text, and no escape characters are needed. For this reason
there may be :, # and other characters that are reserved for the node name.
Another example:
Paths
├── Path
├── Path
| Node name | Node value |
|---|---|
| Path | C:\Users\ana |
| Path | C:\Windows\STXT\@stxt.template\ |
Block nodes
Block nodes are all the nodes of the form Node name >>.
That is, they use >> as the separator, and carry nothing else on that line.
These nodes have no value and no children: they only have the text lines
below them, with more indentation. Example:
Summary >>
This line is at the level of the block.
This one is indented further, and that indentation is part of the text.
# This is not a comment: it is text.
Title: this is not a node either.The text of Summary is:
This line is at the level of the block.
This one is indented further, and that indentation is part of the text.
# This is not a comment: it is text.
Title: this is not a node either.
The text starts one level below the >> node. That indentation is the only thing
removed: any additional indentation is part of the text. Inside the block
there is no interpretation at all: :, # and >> are text, and no escape
characters are needed. For this reason a block fits any content:
a paragraph, a code fragment, a piece of Markdown or a text in another format.
A block ends at the first non-empty line with an indentation less than or equal to that of the >> node.
That line may be a node or a comment. Empty lines do not end the block:
- Those that come before more text are part of the block, as empty lines.
- Those left at the end are discarded: they separate the document visually, but they are not content.
Node names
A name admits letters, digits and combining marks from any script (Latin, Greek,
Cyrillic, Arabic, Devanagari, CJK...), plus the separators -, _ and space. It
admits no other symbol: no :, no parentheses, no #. It must contain at least
one letter or digit.
To decide whether two nodes are the same, their canonical name is compared: the
name in lower case, with each run of separators reduced to a single - and with no
hyphens at the ends. Accents and script are kept. In this example, the value of
each node is its canonical name:
Un nombré con äcento: un-nombré-con-äcento
UN NOMBRÉ CON ÄCENTO: un-nombré-con-äcento
TAMaÑo número 2__ y 3: tamaño-número-2-y-3
Пример 1: пример-1
Nombre 日本語: nombre-日本語Title, title and TITLE- are the same node.Peña and Pena are different nodes, as they are different words.
So a document can be written in any language without the language altering the words or merging two names that are different to the reader. The only exception is namespaces, which are limited to ASCII.
Lists
STXT has no special syntax for lists. The children of a node are
an ordered sequence, and the same name can be repeated. We have already seen it
with Authors:
And a sequence of different elements is written in order:
Book: Modern Software Architecture
Chapter: Introduction
Chapter: Communication between services
Appendix: Glossary
Chapter: DeploymentThe order of appearance is kept. How many times each child may appear is stated by validation, which we will see later on.
Trimming of values and text
- In an inline node, the whitespace at the start and at the end of the value is removed. That is, trim() on the left and on the right.
- In a block node, the whitespace at the end of each line and the trailing empty lines are removed. The whitespace at the start of the block is kept. That is, trim() on the right of the lines and removal of the trailing empty lines (not the leading ones).
In both cases the value of Name is Joan.
Style recommendations
These are recommendations and not rules:
- A single space between the name and the namespace.
:goes right after the name, or after the namespace if there is one.- A single space between
:and the value. - A space before
>>. - No more than one space in a row in names.
- A single indentation style in the whole document: either tabs, or spaces.
Multiple nodes in a document
Documents may have several root nodes, independent of each other. This allows STXT to be used directly in large files, without having to define another kind of file. One application could be log files, read in streaming:
Log: Start of user creation
Level: INFO
Request ID: 3e45bad6-3a82-4959-844e-9eefd4c418a3
Message >>
Creating the user:
- Name:
- Age: 19
Log: Error creating user
Level: ERROR
Request ID: 3e45bad6-3a82-4959-844e-9eefd4c418a3
Message: Cannot invoke "String.length()" because "name" is null
Stacktrace >>
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at com.example.service.UserService.validateName(UserService.java:42)
at com.example.service.UserService.createUser(UserService.java:27)
at com.example.controller.UserController.register(UserController.java:58)
at com.example.App.main(App.java:15)Comments in depth
We have already seen that a comment is a line that starts with #, not counting
indentation whitespace. Comments are discarded: they are not part of the
tree or of the data.
# A book record
Book:
# The title goes first
Title: Modern Software Architecture
ISBN: 978-84-123456-7-8Comments are whole-line.
There are no end-of-line comments: in Title: My book # note, the # and what
follows it are part of the value, as we have seen with Node 4.
Indentation of comments
The indentation of a comment is validated like that of a node: the same rules for tabs and spaces and, at most, one level more than the last node. But a comment does not change the hierarchy.
Comments and the end of a block node
Inside a block node there are no comments: a # there is text. A # line with an
indentation less than or equal to that of the >> node is a comment, and ends the block:
Book:
Summary >>
Text of the summary.
# This is text of the block.
# This is a comment, and it ends the block.
Title: Modern Software ArchitectureStyle of comments
It is recommended to put each comment at the same level as the next node, which is the node it describes.
Namespaces in depth
We have already seen that a node may belong to a namespace, written in parentheses after the node name. The namespace says which vocabulary the nodes belong to, and it is what allows them to be validated. This is the document from the beginning, now complete:
Book (com.acme.book):
Title: Modern Software Architecture
Authors:
Author: María Pérez
Author: Juan García
ISBN: 978-84-123456-7-8
Publisher: ACME Editorial
Published: 2025-10-01
Summary >>
This book offers a practical view of patterns and best practices
for designing distributed, scalable systems.
Chapter: Introduction to architecture
Content >>
This chapter presents basic concepts:
monoliths, microservices and design criteria.
Chapter: Communication between services
Content >>
Protocols, messaging and integration patterns are described.The rules for writing a namespace are few:
- Only ASCII
[a-z0-9]and dots, with at least two parts (a.b). - Upper case is lowercased:
COM.ACME.BOOKis the same namespace. - The convention is to use a reversed domain of one's own, as in Java, so that two organizations do not collide.
- Namespaces starting with
@are special:@stxt.*is reserved for the language itself. Later on we will see two:@stxt.templateand@stxt.schema. A namespace of one's own never starts with@.
A document with a namespace that has no definition is a correct document, but its content cannot be validated.
Namespace inheritance
In the example only Book declares the namespace com.acme.book. That is
enough, because the namespace is inherited: Title, Authors, Author,
Chapter, Content... every descendant of Book belongs to
com.acme.book without writing it.
One consequence of this is that, once a node defines a namespace, its children will have a namespace too, since there is no established way of removing it.
Multiple namespaces
A child may declare another namespace, and then its descendants inherit the new one:
Order (com.example.orders): 1234
Customer (com.example.customers): Ana López
Email: ana@example.com
Total: 120Order and Total belong to com.example.orders. Customer and Email
belong to com.example.customers. At the end of the tutorial we will see how a
document with several namespaces is validated.
Validating documents
@stxt.template
A template describes what shape the documents of a namespace must have: which
nodes exist, how many times each one appears and of which type its values are. It
is one more STXT document, with namespace @stxt.template, and its
Structure >> block has the same shape as the documents it describes:
Template (@stxt.template): com.acme.book
Description >>
Book: Template for publisher book records
Structure >>
Book:
Title: (1)
Authors: (1)
Author: (+)
ISBN: (?)
Publisher: (?)
Published: (?) DATE
Summary: (?) TEXT
Chapter: (*)
Content: (?) TEXTIt reads almost like the book document. What is in parentheses is the cardinality (how many times that node may appear inside its parent) and what comes after it, if anything, the type of the value:
Title: (1): exactly one.Authors, the same.Author: (+): one or more.ISBN: (?): zero or one.Publisher, the same.Published: (?) DATE: optional and, if present, aYYYY-MM-DDdate.Summary: (?) TEXT: optional, and its value is text (inline or block).Chapter: (*)withContent: (?) TEXTinside: any number of chapters, each with its optional text.
The two book documents we have seen validate with this template: the one from the beginning and the complete one.
Book admits no child that is not in the template.
A Pages node inside Book is an error, and not an extra piece of data that is ignored.
Cardinalities
| Form | Meaning |
|---|---|
(1) |
Exactly one. |
(?) |
Zero or one. |
(*) |
Any number. |
(+) |
One or more. |
(n) |
Exactly n. |
(n+) |
n or more. |
(n-) |
Up to n. |
(min,max) |
Between min and max. |
Types
The type goes after the cardinality; if not given, it is INLINE. The most common:
| Type | Form of the value | What it validates |
|---|---|---|
INLINE |
inline | Simple text. Admits children. It is the default type. |
GROUP |
no value | Structure only: the node groups, it carries no value. |
TEXT |
inline or >> |
Text, with no interpretation. |
MARKDOWN |
inline or >> |
Text that whoever consumes it must treat as Markdown. |
NUMBER |
inline | A number. |
DATE |
inline | A YYYY-MM-DD date, checked against the calendar. |
ENUM |
inline | One of the values of a list: ENUM [a, b, c]. |
Only INLINE and GROUP admit children.
@stxt.schema
A schema (@stxt.schema) describes the same as a template, but node by
node: each Node with its type and the list of its Child entries, with
Min/Max cardinalities. Every template compiles to an equivalent schema, and
validating with one or the other gives the same result.
Schema (@stxt.schema): com.acme.book
Node: Book
Children:
Child: Title
Min: 1
Max: 1
Child: Authors
Min: 1
Max: 1
Child: ISBN
Max: 1
Child: Publisher
Max: 1
Child: Published
Max: 1
Child: Summary
Max: 1
Child: Chapter
Node: Authors
Children:
Child: Author
Min: 1
Node: Chapter
Children:
Child: Content
Max: 1
Node: Title
Node: Author
Node: ISBN
Node: Publisher
Node: Published
Type: DATE
Node: Summary
Type: TEXT
Node: Content
Type: TEXTThe template is shorter and looks like the document. The schema is longer and more explicit. For a namespace there can be only one active definition, template or schema.
The final document
A complete document that validates both with the template and with the schema:
Book (com.acme.book):
Title: Modern Software Architecture
Authors:
Author: María Pérez
Author: Juan García
ISBN: 978-84-123456-7-8
Published: 2025-10-01
Summary: A practical introduction to the architecture of modern systems.
Chapter: Introduction
Content >>
Basic concepts and goals of the book.Summary is an inline node here, and in the previous examples it was a block node:
TEXT admits both forms. Publisher does not appear, and it does not need to: it was (?).
How this is done in a real project (the template in a .stxt/ directory, the
document next to it and stxt validate from the terminal, or the errors in the
editor) is shown in The working environment.
Several namespaces in one document
We have already seen that any node may declare its own namespace, and that its descendants inherit it. Each namespace is validated against its own definition, so a vocabulary is defined once and incorporated from others. For example, reviews, which could accompany any other product just as well.
In the template that incorporates the foreign vocabulary, the external node is declared with its namespace and its cardinality. Its shape is not described there, but in the template of its own namespace. The complete set (the document and the two templates) fits in a single file:
Book (com.acme.book):
Title: Modern Software Architecture
Authors:
Author: María Pérez
Author: Juan García
ISBN: 978-84-123456-7-8
Published: 2025-10-01
Review (com.acme.reviews):
Reviewer: Ana López
Score: 9
Comment >>
A clear, well-structured guide, with examples
that are easy to follow.
Review (com.acme.reviews):
Reviewer: Luis Martín
Score: 8
Template (@stxt.template): com.acme.book
Structure >>
Book:
Title: (1)
Authors: (1)
Author: (+)
ISBN: (?)
Published: (?) DATE
Review (com.acme.reviews): (*)
Template (@stxt.template): com.acme.reviews
Structure >>
Review:
Reviewer: (1)
Score: (1) NUMBER
Comment: (?) TEXTThe book template, reduced here, adds a single new line,
Review (com.acme.reviews): (*): a book admits any number of reviews, and what
a review is, the template of com.acme.reviews says. The reviews are
validated against their template, and the rest of the book against its own.
An STXT document may have several root nodes, and this one has three: the book and the two templates. Fitting in one file is a possibility and not an obligation: usually each template is in its own file, and documents and definitions find each other by namespace.
Where to go next
- FAQ: short answers to the usual questions.
- The working environment: organising a project, installing templates, validating from the terminal and in continuous integration.
- Design principles: why the language is the way it is.
- The use cases, each with its documents and its template: corporate documents, AI and LLMs, CMS and publishing, configuration files, streaming logs, RFCs and contracts.
- The specifications: STXT-SPEC for the syntax, STXT-SCHEMA-SPEC and STXT-TEMPLATE-SPEC for validation, STXT-DISCOVERY-SPEC for where definitions are looked up and STXT-TREE-SPEC for the JSON tree the tools produce.