STXT vs YAML

YAML and STXT use indentation to define the hierarchy. The mental model is different: YAML has maps and lists, like JSON, and STXT has trees of text nodes, like XML.

The same content, twice

A service descriptor in YAML:

service: billing
replicas: 2
ports:
  - 8080
  - 8443
maintainer: ana@example.com
notes: |
  Restarted after the 2026-08-31 incident.
  The TLS certificate expires on 2027-03-01.

And in STXT:

Service: billing
	Replicas: 2
	Ports:
		Port: 8080
		Port: 8443
	Maintainer: ana@example.com
	Notes >>
		Restarted after the 2026-08-31 incident.
		The TLS certificate expires on 2027-03-01.

A simple document looks alike in the two. The differences are these:

YAML STXT
Values The spelling decides the type: 2 is an integer and "2" a string Every value is text. The type, when needed, is declared by the template
Lists Two syntaxes: block (- 8080) and flow ([8080, 8443]) There is no list syntax: a repeated child is the list (FAQ)
Indentation Any number of spaces per level, and no tabs One tab or four spaces per level
Multi-line text Block scalars, with several indicators A block node >>
Namespaces None Part of the language (STXT-SPEC §7)
Schemas External tools Part of the language, and optional

When the spelling changes the meaning

In YAML, an unquoted value is converted to a type according to its form. The rules changed between versions 1.1 and 1.2 of the language:

country: NO         # false in YAML 1.1; the string "NO" in 1.2
version: 3.10       # the number 3.1 in both
window: 12:30       # the integer 750 in YAML 1.1 (sexagesimal); a string in 1.2
enabled: yes        # true in 1.1; the string "yes" in 1.2

Keeping NO, 3.10 or 12:30 as text requires quoting them. In STXT a value is always the characters written:

Settings:
	Country: NO
	Version: 3.10
	Window: 12:30
	Enabled: yes
Node name Node value
Country NO
Version 3.10
Window 12:30
Enabled yes

If Enabled must be a boolean, the template says so (Enabled: (1) BOOLEAN). In that case the validator rejects yes, and does not convert it.

Multi-line text

A YAML block scalar combines several indicators: the style (| literal or > folded), the final line break (-, + or none) and, when needed, the indentation:

literal: |
  first line
  second line
folded: >
  first line
  second line
kept: |+
  first line

next: value

Each combination produces a different string:

Key Value
literal "first line\nsecond line\n"
folded "first line second line\n"
kept "first line\n\n" (it keeps the empty line that follows)

STXT has a single form, the block node. The text is literal, with no variants and no escape characters:

Notes >>
	first line
	second line

Additional indentation and intermediate empty lines are kept. Trailing whitespace on each line and trailing empty lines are discarded (STXT-SPEC §10).

Security

YAML includes two features that STXT does not have:

  • Anchors and aliases (&a, *a): a small document can expand into a huge structure in memory.
  • Type tags (!!python/object, !ruby/object): implementation-specific tags have allowed arbitrary objects to be instantiated while loading a document. Hence the safe_load functions of the libraries.

STXT has no references, anchors, tags, inclusion or entities. Parsing is linear, line by line and in a single pass (STXT-SPEC §15).

Validation

YAML is validated with external tools, usually JSON Schema over the loaded structure. In STXT schemas are part of the language. The descriptor from the beginning, with a namespace:

Service (com.example.deploy): billing
	Replicas: 2
	Port: 8080
	Port: 8443
	Maintainer: ana@example.com
	Notes >>
		The TLS certificate expires on 2027-03-01.

And the template for its namespace:

Template (@stxt.template): com.example.deploy
	Structure >>
		Service (com.example.deploy):
			Replicas: (1) NATURAL
			Port: (+) NATURAL
			Maintainer: (1) EMAIL
			Notes: (?) TEXT

The Ports container is not needed here: the (+) cardinality already says that Port repeats. A document with errors does not validate:

# ERROR: this document does not validate
Service (com.example.deploy): billing
	# `two` is not a NATURAL
	Replicas: two
	# At least one `Port` is missing
	# `ana` is not an EMAIL
	Maintainer: ana
	# `Region` is not in the template
	Region: eu-west-1

What about TOML?

TOML shares its goal with STXT: files that a person writes and reads. The hierarchy is not in the indentation but in headers that repeat the full path of their table:

[[job]]
name = "database"

[[job.step]]
run = "pg_dump acme > acme.sql"

[[job.step]]
run = "gzip acme.sql"

[[job]]
name = "documents"

[[job.step]]
run = "tar cf documents.tar /srv/docs"

Each [[job.step]] belongs to the last [[job]] opened. In STXT each level is written once, and a child belongs to the node it is indented under:

Backup:
	Job: database
		Step: pg_dump acme > acme.sql
		Step: gzip acme.sql
	Job: documents
		Step: tar cf documents.tar /srv/docs

As in YAML, the type of a value is decided by its spelling: 14 is an integer and "14" a string. Strings always go between quotes, and multi-line ones cannot be indented with their table. It has no namespaces or schemas.

What about NestedText?

NestedText makes several of the decisions of STXT: every value is text, with no implicit types or escape characters, and the structure is in the indentation. The descriptor from the beginning:

service: billing
replicas: 2
ports:
    - 8080
    - 8443
maintainer: ana@example.com
notes:
    > Restarted after the 2026-08-31 incident.
    > The TLS certificate expires on 2027-03-01.

And in STXT:

Service: billing
	Replicas: 2
	Ports:
		Port: 8080
		Port: 8443
	Maintainer: ana@example.com
	Notes >>
		Restarted after the 2026-08-31 incident.
		The TLS certificate expires on 2027-03-01.

2 and 8080 are strings in both, and multi-line text is literal: in NestedText each line starts with >, and in STXT it goes under a block node.

NestedText keeps the YAML data model (dictionaries, lists and strings), and leaves types and validation to the application. It has no namespaces or schemas.