STF 1.0 · draft specification

A structured text format for configuration and data.

Every value carries its own type: dates, timestamps, decimals, big integers, and binary are part of the grammar.

config.stf
# 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`],
}

Why STF

A format a reader can trust without a schema beside it.

Types in the syntax

DATE, TIMESTAMP, DECIMAL, BIGINT, and BINARY are grammar, so a reader never infers a type from a key name.

Exact decimals

DECIMAL(1.5) and DECIMAL(1.50) are different values. Scale survives the round trip, so money is representable.

Strict by design

Every rejection maps to exactly one documented code, and a conversion that would lose a type fails instead.

Canonical form

One byte encoding per value, so a document can be hashed, signed, and diffed byte-for-byte.

Record streams

A .stfs file is one document per line, and a malformed record never invalidates the rest of the stream.

Editor and CLI

stf check, fmt, lint, canon, and convert, plus a language server that reports the same codes as CI.

How it compares

Where a format permits a capability but implementations disagree.

CapabilitySTFJSONJSON5YAMLTOMLIon
Dates and timestamps as a distinct type🟡
Exact decimals, scale preserved
Integers beyond 2⁵³ without loss🟡🟡
Binary data as a distinct type🟡
Comments
Duplicate keys rejected🟡
One documented code per rejection
Canonical form in the same specification🟡
✅ yes🟡 varies by implementation❌ no

One specification, no dialects

Every rule below is normative, so two conformant parsers cannot disagree about a document.

  • §3eleven value kinds, defined independently of any host language.
  • §13parse(serialize(v)) ≡ v, and no constructor inferred from string content.
  • §14canonical form: one byte encoding per value.
  • §15a mandatory nesting depth limit, defaulting to 64.
  • codesone documented code per rejection; message text is not normative.
events.stfs
@version(1.0)
{at: TIMESTAMP(2026-01-15T10:30:00Z), level: `warn`}
{at: TIMESTAMP(2026-01-15T10:30:04Z), level: `info`}