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, Author and Publisher
  • 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: (?) TEXT

Shall 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 1

In 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: C:\Users\ana
	Path: C:\Windows\STXT\@stxt.template\
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:

Authors:
	Author: María Pérez
	Author: Juan García
	Author: Ana López

And a sequence of different elements is written in order:

Book: Modern Software Architecture
	Chapter: Introduction
	Chapter: Communication between services
	Appendix: Glossary
	Chapter: Deployment

The 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).
Name: Joan
Name:      Joan

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.
Name with value: The value
Name without value:
Name with namespace (the.namespace):
Text node >>

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-8

Comments 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 Architecture

Style 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.BOOK is 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.template and @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: 120

Order 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: (?) TEXT

It 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, a YYYY-MM-DD date.
  • Summary: (?) TEXT: optional, and its value is text (inline or block).
  • Chapter: (*) with Content: (?) TEXT inside: 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: TEXT

The 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: (?) TEXT

The 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