Pangram verdict · v3.3
We believe this text is mainly AI, with some human-written content.
AI likelihood · overall
AIArticle text · 1,571 words · 18 segments analyzed
A config format for humans SOML is short for Sindre's Objectively Marvelous Language. SOML is a config format that is pleasant to write and easy to read. The top level needs no braces, keys need no quotes, line breaks replace commas, and comments go almost anywhere. A value's type comes from its syntax, never from its content, so what you see is what you get: '123' is a string and 123 is an int. Dates like 2026-09-19, instants like 2026-09-19T14:00:00Z, and durations like 30s are real values, so nobody has to guess the unit of timeout: 30. Paths and regexes need no escaping, and prose goes in block strings. One canonical form and a shared conformance suite mean every implementation reads your file the same way. JSON has the strictness but not the comfort: quotes, commas, and braces everywhere, and no comments. YAML has the comfort but not the clarity: - list markers, nested indentation, and types guessed from content, so country: no becomes false and version: 3.10 becomes 3.1. SOML gives you the comfort and the strictness, without the guessing.
WarningDraft spec. Feedback welcome.
name: 'api-gateway' port: 8080 # The load balancer expects this port. timeout: 30s launch-date: 2027-09-19 windows-path: 'C:\Users\sindre\dev' description: ''' Terminates TLS. Routes requests to internal services. ''' allowed-origins: [ 'https://example.com' 'https://example.org' ] The same in JSON: { "name": "api-gateway", "port": 8080, "timeout": 30, "launch-date": "2027-09-19", "windows-path": "C:\\Users\\sindre\\dev", "description": "Terminates TLS.\nRoutes requests to internal services.", "allowed-origins": [ "https://example.com", "https://example.org" ] } Objectives SOML is a data format for files that people write and read. It is JSON without the noise, and with comments. It keeps JSON's tree of objects and arrays and its strictness, and adds literal strings, block strings, dates, instants, and durations. Its design rests on one rule: a value's type comes from its syntax, never from its content. '123' is a string and 123 is an int. Nothing is ever retyped by inspecting it. Why SOML Less noise than JSON, and comments. Keys need no quotes, even with dashes, a line break replaces a comma, and the top level needs no braces. Comments are allowed wherever whitespace may go. What you see is the type. A string always has quotes, and a value without quotes must be a number, a date, an instant, a duration, or a keyword, or the file has an error. So country: no is an error rather than false, and '10.20' and 10.20 are a string and a float, exactly as written. Strict where JSON is vague. Duplicate keys, lone surrogates, and numbers out of range are errors, not behavior that each parser decides. An int is exact to 64 bits, and 3 and 3.0 stay different types.
Pleasant to write.
Literal strings, so paths and regexes need no escaping, and dedented block strings for prose. Dates, instants, and durations are values. 2026-09-19 is a calendar date, 2026-09-19T14:00:00Z is an instant, and 1h30m is a duration, so no reader has to parse a string or guess a unit. Indentation is free. Braces and line breaks give the structure, so tabs, spaces, and copy and paste never change what a file means, except inside a block string, where indentation is text.
One canonical form.
Equal values give equal bytes, for hashing, signing, and comparing. A separate formatter keeps your comments, member order, and the spelling of each value. Its rules are part of the spec, so every conforming formatter gives the same output for the same file.
Small and tested.
The grammar is one pass with no lookahead, and every implementation runs the language-neutral conformance suite of nearly 1000 cases, so every implementation reads a file the same way. Example /* Everything SOML can express, in one document.
A document may also be a top-level array. */ # Keys are bare.
Dashes are allowed, so no key needs quotes. name: 'soml' launch-date: 2027-09-19 released-at: 2026-09-19T14:00:00Z 'the name': 'quotes are the escape hatch, when a key needs spaces' 404: 'and a key may begin with a digit' # Strings: '...'
is literal, "..." is escaped. Use ' by default.
regex: '^\d{4}-\d{2}-\d{2}$' windows-path: 'C:\Users\sindre\dev' shell: 'echo "$HOME" | tr -d "\n"' escaped: "line one\nline two\t\"quoted\"\u{1f600}" # Block strings are dedented by the closing delimiter's column. description: ''' A block string keeps its line breaks. The leading tab of each line is stripped, because the closing delimiter sits at one tab. ''' # Numbers: int and float are different types. replicas: 3 ratio: 0.75 budget: 1_000_000 mask: 0b1010_1100 permissions: 0o644 color: 0xFF8800 flags: 0x00FF limit: infinity floor: -infinity # Booleans and null. drained: false owner: null # Durations: exact, in h, m, s, ms, us, and ns. retry-after: 1m30s # Arrays. A comma or a line break separates items. labels: ['prod', 'eu-west', 'canary'] matrix: [[1, 2], [3, 4]] mixed: [1, 'two', true, null] # Objects. The top-level object needs no braces. pool: { min: 2 max: 16 } # Records in arrays. Braces are required, and that is deliberate. maintainers: [ {name: 'Sindre Sorhus', email: 'sindresorhus@gmail.com'} {name: 'Random Person', email: 'person@example.com'} ] # Line comments, and block comments, wherever whitespace may go. # Inline objects and one-line braced documents work too: healthcheck: {path: '/health', interval: 10s, grace: 1.5s} Why not YAML? SOML keeps what I like about YAML: comments, no braces around the file, no commas at the ends of lines, and keys without quotes. It removes what hurts: in YAML, the content of a value decides its type. In SOML, the syntax decides, so 'no' is a string, 3.10 is a float, and 2 is an int, in every implementation. What SOML adds: Block comments with /* */, in addition to #. Durations such as 30s and 1h30m, so timeout: 30 no longer makes the reader guess the unit. Dates such as 2026-09-19 and instants such as 2026-09-19T14:00:00Z. YAML 1.2 dropped timestamps, but many parsers still read them, so a date in YAML may or may not be a string. A canonical form, so equal values give equal bytes for hashing and signing. YAML defines one for scalars only. A formatter whose rules are part of the spec. It keeps your comments, key order, and the spelling of each value. YAML says comments are not content, so tools may drop them. Editing rules, so a tool can change one value and keep the comments and layout of the rest. Error messages that usually say what to write instead. What SOML fixes: Values change type without warning. In PyYAML (YAML 1.1), country: no is false, zip: 01234 is octal 668, and time: 22:22 is base-60 1342. YAML 1.2 fixed those, but most of the ecosystem still follows 1.1, and 1.2 still guesses: version: 3.10 is the float 3.1, and ~, Null, and an empty value are null. SOML strings always have quotes, so country: no is an error. Keys are not always strings. 1: x gives an integer key in YAML, and PyYAML reads on: x as the boolean true. SOML keys are always strings, so 404: is the key '404'. Indentation is the structure. One space too few moves a key into another object, and the file still parses, which is why YAML forbids tabs there. SOML gets its structure from braces and line breaks, so indentation only helps the reader, except in a block string, where it is text. Block strings add a newline. YAML's default | keeps a trailing \n in the value; SOML does not. Large ints lose precision. js-yaml turns id: 9007199254740993 into 9007199254740992. A SOML int is an exact int64; a larger one is an error. Duplicate keys go unnoticed. For a: 1 then a: 2, PyYAML keeps 2 and says nothing. The spec forbids duplicates, but after type resolution 1: and 01: may or may not collide, depending on the schema. SOML compares the decoded strings and reports each duplicate. A missing value is null. timeout: with no value is null in YAML, and a file with only comments is null in PyYAML. SOML rejects both, so a forgotten value cannot pass as a default. The language is large. YAML has two syntaxes for every list and map, two block string styles with three newline rules, indentation indicators, anchors, aliases, tags, multiple documents per file, three processing stages, and three recommended schemas. SOML has one syntax for each, one block string style, and one set of type rules.
Parsers disagree.
In the YAML test matrix (newest snapshot 2022), PyYAML fails 73 of 402 cases. 2024-01-01 is a date in PyYAML and a string in js-yaml 5, and 1_000 is 1000 in PyYAML and a string in js-yaml. SOML implementations share one conformance suite of nearly 1000 cases. Two features are a security risk. Aliases make YAML bombs possible: a small file expands into billions of nodes. Tags let a loader construct objects, and PyYAML has had four remote code execution CVEs rated 9.8. safe_load is opt-in, and even a loader named “safe” can turn aliases on. SOML has neither, one way to load, and limits on nesting and ranges.
Why not TOML?
SOML keeps what TOML gets right: predictable types, literal strings, insignificant indentation, and duplicate keys as errors. It removes what hurts: table rules where a key's meaning depends on what came before it, and values that parsers silently round, truncate, or read differently. A SOML file means one thing, no matter where a line sits or which parser reads it.
What SOML adds: Top-level arrays.