Introduction
STF (Structured Text Format) is a human-readable format for configuration and data interchange, defined by a normative specification, an exact condition-to-error-code mapping, and a conformance corpus that every implementation runs.
# comments are part of the format { service: `checkout-api`, port: 8080, enabled: T, deploy_after: TIMESTAMP(2026-01-15T10:30:00Z), price_cap: DECIMAL(199.00), # scale is data account_id: BIGINT(9007199254740993), signing_key: BINARY(SGVsbG8=), regions: [`eu-west-1`, `us-east-1`], }
The problem
JSON has six types, so everything else travels as a string or a binary64 number, and the reader is left to guess from context:
| JSON | The question it leaves open |
|---|---|
"created": "2026-01-15" | A date, or a string that looks like one? |
"id": "9007199254740993" | A string, or an integer stringified to survive JSON.parse? |
"price": 19.99 | Not exactly 19.99 — it is the nearest binary64. |
"blob": "SGVsbG8=" | Base64 by convention, decoded by whoever remembers. |
Every one of those is answered by a convention held outside the document — a schema, a field naming rule, or a comment in a wiki. STF puts the answer in the document.
{ created: DATE(2026-01-15), id: BIGINT(9007199254740993), price: DECIMAL(19.99), blob: BINARY(SGVsbG8=), }
Replacement, not superset
STF deliberately does not inherit JSON's looseness, so some valid JSON has no STF representation: non-identifier keys, a non-object root, NaN and Infinity, and duplicate keys. A conversion that meets one of those fails loudly rather than guessing.
That is the trade. What it buys is that two implementations cannot quietly disagree: keys have one spelling, duplicates are an error rather than a last-one-wins race, every rejection has one documented code, and there is a canonical byte encoding for hashing and signing.
Where to go next
Syntax — documents, keys, strings, numbers, comments, directives.
Data model — the eleven value kinds and how equality is defined.
Typed constructors —
DECIMAL,BIGINT,DATE,TIMESTAMP,BINARY.Canonical form — one byte encoding per value, plus resource limits.
Error codes — the full index.
Record streams —
.stfs, for append-only logs.Command-line tool — check, format, lint, convert, and the language server.
Migrating from JSON — what converts, and what needs a decision first.
Status
The 1.0 draft is complete and every reference implementation passes the corpus. STF is pre-release: the specification may still change incompatibly until 1.0 is tagged.
The normative documents live in the repository and remain the source of truth — specification, error codes, stream profile, and schema.