Version 3.6 — Frozen specification (April 2026).
Reference implementation:synx-core
- Introduction
- Quick Start
- Installation
- Core Syntax
- Directives
- Markers
- Constraints
- Marker Chaining
- Language Bindings
- Binary Format & Diff
- JSON Schema
- Tools & Editors
- Packages
- Security
- Performance
- FAQ
SYNX is a structured data format based on indentation. It encodes the same data model as JSON — objects, arrays, scalars — but uses whitespace and a minimal set of rules instead of braces, brackets, colons, and quotes. The result is a format that is fast to write, easy to read, and friendly to both humans and language models.
SYNX is not a configuration-only format. It supports full data exchange, active validation with constraints, AI-oriented directives, and binary encoding.
JSON requires a lot of punctuation. YAML is flexible but has a famous list of surprising edge cases (booleans, multi-document streams, the Norway problem). TOML is opinionated about tables. SYNX takes a different approach:
- No curly braces, square brackets, colons, or commas in the default static mode
- Nesting expressed purely by indentation (2-space canonical)
- Types inferred automatically; explicit casting available when needed
- One mode for data storage, one mode for validated configuration (
!active)
SYNX values map directly to a JSON-compatible data model:
| SYNX | JSON equivalent | Notes |
|---|---|---|
key value |
"key": "value" |
String scalar |
key 42 |
"key": 42 |
Integer auto-detected |
key 3.14 |
"key": 3.14 |
Float auto-detected |
key true |
"key": true |
Boolean: true/false |
key ~ |
"key": null |
Null literal |
key "" |
"key": "" |
Empty string |
| Indented block | Nested object | Children create object |
- value |
Array element | Dash prefix = array item |
Static Mode (default) — Pure data: key-value pairs, nesting, arrays. No markers, no constraints, no computation. Ideal for data exchange and storage.
Active Mode (!active) — Enabled by the !active directive. Unlocks markers (:env, :calc, etc.) and constraints ([type:int, required]). Designed for configuration files with validation.
| Feature | SYNX | JSON | YAML | TOML |
|---|---|---|---|---|
| Human-readable | Yes | Partial | Yes | Yes |
| No punctuation overhead | Yes | No | Partial | Partial |
| Type inference | Yes | Yes | Yes | Yes |
| Schema / validation | Built-in | External | External | External |
| Env var resolution | Built-in | No | No | No |
| Binary encoding | Yes (.synxb) | No | No | No |
| LLM directive | Yes (!llm) | No | No | No |
| Surprise edge cases | None | None | Many | Few |
Create a file named hello.synx:
name Alice
age 30
active true
score 9.5
bio She writes code
address
city Berlin
country Germany
zip 10115
tags
- rust
- systems
- open-source
That is a complete, valid SYNX document. No quotes, no braces, no commas. Indentation (2 spaces) defines structure.
// JavaScript / TypeScript
import { Synx } from '@aperturesyndicate/synx-format';
const result = Synx.parse(`
name Alice
age 30
active true
`);
console.log(result.name); // "Alice"
console.log(result.age); // 30
console.log(result.active); // true# Python
import synx_native as synx
result = synx.parse(open("hello.synx").read())
print(result["name"]) # Alice
print(result["age"]) # 30
print(result["active"]) # True// Rust
use synx_core::Synx;
let input = std::fs::read_to_string("hello.synx")?;
let map = Synx::parse(&input);
println!("{:?}", map["name"]); // String("Alice")
println!("{:?}", map["age"]); // Int(30)// C#
using Synx;
var result = SynxFormat.Parse(File.ReadAllText("hello.synx"));
Console.WriteLine(result["name"]); // Alice
Console.WriteLine(result["age"]); // 30# CLI
synx parse hello.synx
# → canonical JSON
synx parse hello.synx | jq '.name'
# → "Alice"!active
port[type:int, min:1024, max:65535] 8080
host[required, type:string] localhost
debug[type:bool] false
workers[type:int, min:1, max:32] 4
database_url[required, type:string]:env DATABASE_URL
Now port must be an integer between 1024 and 65535. database_url is read from the environment variable DATABASE_URL. Parsing fails with a clear error if any constraint is violated.
cargo install synx-clinpm install @aperturesyndicate/synx-formatpip install synx-formatdotnet add package APERTURESyndicate.Synx[dependencies]
synx-core = "3.6"Header-only. Copy synx/synx.hpp into your project or use CMake FetchContent.
go get github.com/APERTURESyndicate/synx-format/go.package(url: "https://github.com/APERTURESyndicate/synx-format", from: "3.6.0")implementation("com.aperturesyndicate:synx-engine:3.6.0")code --install-extension APERTURESyndicate.synx-vscodeSYNX infers types automatically from the value text:
| Value | Detected type | Notes |
|---|---|---|
42 |
integer | Digits only |
-17 |
integer | Negative prefix |
3.14 |
float | Decimal point |
true / false |
boolean | Case-sensitive |
~ |
null | Tilde literal |
"" |
empty string | Double quotes |
| Everything else | string | No quotes needed |
Force a type with parentheses after the key:
port(int) 8080
zip_code(string) 90210
flag(bool) true
ratio(float) 2
Available casts: int, float, bool, string.
Nesting uses 2-space indentation:
server
host 0.0.0.0
port 8080
ssl
enabled true
cert /etc/ssl/cert.pem
Result:
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"ssl": {
"enabled": true,
"cert": "/etc/ssl/cert.pem"
}
}
}An empty key with no value and no children creates an empty object: metadata → {}.
Maximum nesting depth: 128 levels.
Arrays use dash-prefixed items:
colors
- red
- green
- blue
mixed
- 42
- hello
- true
- ~
Arrays can contain objects:
users
-
name Alice
role admin
-
name Bob
role user
Maximum array elements: 1,000,000.
Use the pipe operator (|) for block strings:
description |
This is a long text
that spans multiple lines.
Each line is joined with a newline character.
Result: "This is a long text\nthat spans multiple lines.\nEach line is joined with a newline character."
Maximum block size: 1 MiB.
Three comment styles:
# Hash comment (single line)
// Slash comment (single line)
### Block comment
This is a multi-line comment.
Everything between the ### markers is ignored.
###
Directives are top-level lines that begin with ! or #!. They control the document's mode and metadata. A directive must appear before any key-value lines.
| Directive | Description |
|---|---|
!active |
Enable markers, constraints, and validation |
!lock |
Mark document as read-only (metadata flag) |
!include |
Include another SYNX file at the document root level |
!tool |
Reshape output as a tool-call envelope |
!schema |
Export a JSON Schema Draft 2020-12 |
!llm |
Annotate document for LLM consumption (metadata-only) |
Switches the document from static to active mode. Must appear on the very first non-comment line.
!active
db_host:env DATABASE_HOST
max_conn[type:int, min:1, max:1000] 100
app_name[required, type:string] MyApp
What it enables:
- Markers —
:env,:calc,:alias, and all others - Constraints —
[type:int, required, min:0]syntax - Validation — parsing fails with a structured error when a constraint is violated
!active applies to the entire document. There is no per-block activation. If you need mixed static/active sections, use separate files with :include.
Alternative hashbang form: #!mode:active
Warning:
!activemust be the first content line (after optional comments). Placing it elsewhere makes it a string value, not a directive.
Sets the locked flag on the parse result. The data tree is unchanged — it is purely advisory metadata for tooling.
!lock
name Alice
role admin
Tooling that respects !lock should prevent edits, overrides, or patch operations.
Includes another SYNX file and merges its contents at the root level. An optional alias assigns the included tree to a named key.
!include ./db.synx
!include ./auth.synx auth_config
name MyApp
- Path is relative to the current file's directory
- Maximum include depth: 16
- Circular includes produce an error
- Path traversal (
..) is blocked by path-jail security
Tip:
!includeis a directive (top-level). The:includemarker does the same at value level:db:include ./db.synx.
Reshapes the output into a tool-call envelope. The first key becomes the tool name, its children become parameters.
!tool
web_search
query rust async
lang en
results 10
Output:
{
"tool": "web_search",
"params": {
"query": "rust async",
"lang": "en",
"results": 10
}
}Causes the parser to emit a JSON Schema Draft 2020-12 object derived from the document's keys and constraints.
!schema
name[required, type:string]
port[type:int, min:1, max:65535]
Metadata-only directive for LLM tooling. Does not change the parsed data tree.
!llm
persona Assistant
role You are a helpful coding assistant
tone professional and concise
context You have access to the user's codebase
Markers are value-level directives that transform or resolve a value at parse time. They require !active mode. A marker is attached to the key with a colon (no space before it).
General syntax: key:marker_name argument
Markers can be chained: key:marker1:marker2 value
Type casts and constraints precede markers: key(type)[constraints]:marker value
| Marker | Description |
|---|---|
:env |
Read a value from an environment variable |
:calc |
Evaluate a mathematical expression |
:alias |
Reference another key in the same document |
:secret |
Wrap a value as a secret (serializes to [SECRET]) |
:default |
Provide a fallback value |
:random |
Pick a random item from a child list |
:include |
Include and merge another SYNX file |
:import |
Alias for :include |
:template |
Interpolate a string template with {key} placeholders |
:split |
Split a string into an array |
:join |
Join an array into a string |
:unique |
Deduplicate an array |
:geo |
Select value by geographic region |
:i18n |
Look up a translation from a locale bundle |
:ref |
Reference another key, chain with other markers |
:clamp |
Clamp a numeric value to a min:max range |
:round |
Round a numeric value to N decimal places |
:format |
Format a value using printf-style pattern |
:map |
Map child values through a lookup table |
:once |
Generate a value once and cache it |
:version |
Compare a value against a version constraint |
:watch |
Read external file at parse time |
:fallback |
Provide a fallback file path |
:spam |
Rate-limit access to a value |
:prompt |
Attach an LLM prompt label |
:vision |
Reference an image path for vision models |
:audio |
Reference an audio file for audio models |
:inherit |
Inherit fields from parent objects (mixin) |
Read a value from an environment variable at parse time.
Syntax: key:env VAR_NAME
!active
db_host:env DATABASE_HOST
db_port:env DATABASE_PORT
secret_key:env APP_SECRET
If the variable is not set, the value resolves to null. Use :default chaining for a fallback:
!active
db_host:env:default:localhost DATABASE_HOST
port:env:default:8080 PORT
Evaluate a mathematical expression using other key values.
Syntax: key:calc expression
Supports: +, -, *, /, **, %, (). Max expression length: 4096 characters.
!active
base_workers 4
worker_threads:calc base_workers * 2
memory_mb:calc base_workers * 512
half_mem:calc (base_workers * 512) / 2
Expressions are evaluated after all other keys have been resolved. Forward references work. Circular references produce a parse error.
Create a reference to another key. The alias resolves to the target's value.
Syntax: key:alias other_key
Supports dot-paths for nested keys.
!active
primary_host db.internal
replica_host:alias primary_host
config
host db.internal
reporting_host:alias config.host
Wrap a value as a Secret type. Serializes to [SECRET] to prevent exposure in logs.
Syntax: key:secret value
!active
db_password:secret s3cureP@ss!
api_key:secret sk-abc123-xyz789
The
:secretmarker wraps the literal value. Use.reveal()in your code to access the real value.
Provide a literal fallback value. Most commonly chained after :env.
Syntax: key:marker:default:FALLBACK_VALUE argument
!active
# If PORT is not set, use 8080
port:env:default:8080 PORT
# If HOST is not set, use localhost
host:env:default:localhost HOST
# Standalone default
log_level:default info
The fallback value is written after the last colon: :env:default:8080 PORT.
Pick a random item from a child list at parse time. Optional weight numbers control probability.
Syntax: key:random [weight1 weight2 ...]
!active
# Equal probability
greeting:random
- Hello
- Hi there
- Hey
# Weighted: 70% common, 20% rare, 10% legendary
loot:random 70 20 10
- Common Sword
- Rare Shield
- Legendary Helm
Weight rules:
| Situation | Behavior |
|---|---|
| No weights | All items equally probable |
| Sum = 100 | Used as-is |
| Sum ≠ 100 | Auto-normalized (proportions preserved) |
| Fewer weights than items | Remainder split equally |
| More weights than items | Extra weights ignored |
Include another SYNX file and merge its contents at the current level.
Syntax: key:include path/to/file.synx
!active
db_data:include ./db.synx
db_user:alias db_data.user
db_name:alias db_data.name
:import is a complete alias for :include — both behave identically:
!active
db:import ./config/db.synx
cache:import ./config/cache.synx
Max depth: 16 levels. Max file size: 10 MiB. Circular includes are detected.
Interpolate a string template with {key} placeholders.
Syntax: key:template "string with {placeholders}"
!active
server
host localhost
port 8080
api_url:template http://{server.host}:{server.port}/api
Supports dot-paths: {server.host}, {database.port}.
Split a string value into an array.
Syntax: key:split:DELIMITER value
Keywords: space, comma, pipe, dot, slash, dash, newline, semi, tab. Default: comma.
!active
colors:split red, green, blue
# → ["red", "green", "blue"]
hosts:split:comma db1.internal,db2.internal,db3.internal
# → ["db1.internal", "db2.internal", "db3.internal"]
words:split:space hello world foo bar
# → ["hello", "world", "foo", "bar"]
segments:split:slash /usr/local/bin
# → ["", "usr", "local", "bin"]
Join an array of child values into a single string.
Syntax: key:join:DELIMITER
Keywords: slash, pipe, comma, space, dot, dash, newline, semi, tab. Default: comma.
!active
path:join:slash
- home
- user
- documents
# → "home/user/documents"
env_list:join:pipe
- development
- staging
- production
# → "development|staging|production"
Remove duplicate elements from a child array, preserving original order.
Syntax: key:unique
!active
tags:unique
- rust
- systems
- rust
- open-source
# → ["rust", "systems", "open-source"]
Deduplication is case-sensitive string comparison.
Select a value based on the runtime region setting.
Syntax: key:geo
!active
cdn:geo
- US us-east.cdn.example.com
- EU eu-west.cdn.example.com
- AP ap-south.cdn.example.com
Uses options.region to select. If no match, the first item is the fallback.
Select a translated value based on the runtime locale.
Syntax: key:i18n
!active
greeting:i18n
en Hello
ru Привет
de Hallo
Pluralization — append the count field name: :i18n:count_field
!active
items_label:i18n:item_count
en
one {count} item
other {count} items
ru
one {count} предмет
few {count} предмета
many {count} предметов
other {count} предметов
Locale is set via options.lang (default: en). Plural categories follow CLDR rules (en: one/other, ru: one/few/many/other). {count} is substituted with the referenced field's value.
Reference another key and pass the value to subsequent markers in the chain.
Syntax: key:ref target_key
!active
base_rate 100
adjusted:ref:calc:*2 base_rate
The shorthand :ref:calc:*2 base_rate resolves base_rate → 100, then computes 100 * 2 = 200.
Clamp a numeric value to a minimum–maximum range.
Syntax: key:clamp:MIN:MAX value
!active
volume:clamp:0:100 150
# → 100
brightness:clamp:0:255 -10
# → 0
opacity:clamp:0.0:1.0 0.75
# → 0.75
Error if MIN > MAX.
Round a numeric value to N decimal places.
Syntax: key:round:PLACES value
!active
price:round:2 19.999
# → 19.99
pi:round:4 3.14159265
# → 3.1416
whole:round:0 42.7
# → 43
Default: 0 decimal places (rounds to integer).
Format a value using a printf-style pattern.
Syntax: key:format:PATTERN value
!active
price:format:%.2f 19.9
# → "19.90"
code:format:%04d 42
# → "0042"
Supported patterns: %.Nf (float), %d (int), %0Nd (zero-padded). Max width/precision: 4096/1024.
Map child values through a lookup table.
Syntax: key:map:source_key
!active
status_labels
200 OK
404 Not Found
500 Internal Server Error
codes
- 200
- 404
- 500
labels:map:status_labels
- 200
- 404
- 500
# → ["OK", "Not Found", "Internal Server Error"]
No match returns null.
Generate a value once at first parse and cache it for subsequent reads.
Syntax: key:once:TYPE
Types: uuid, timestamp, random.
!active
instance_id:once:uuid
# → "550e8400-e29b-41d4-a716-446655440000" (same on every read)
created_at:once:timestamp
# → "2026-04-01T12:00:00Z" (frozen at first parse)
The cached value is stored in a .synx.lock sidecar file. Re-parsing without cache produces a new value.
Compare a value against a semantic version constraint. Returns a boolean.
Syntax: key:version:OP:REQUIRED value
Operators: >=, <=, >, <, ==, !=.
!active
app_ok:version:>=:1.0.0 1.2.3
# → true
engine_ok:version:>=:2.0.0 1.9.5
# → false
Parses semantic versions (x.y.z…).
Read an external JSON or SYNX file at parse time and inject its content.
Syntax: key:watch:KEY_PATH ./file.json
!active
# Read entire file
all_flags:watch ./flags.synx
# Extract a specific key from a JSON file
db_host:watch:database.host ./infra.json
Optional key path after :watch: extracts a specific nested value. Max depth: 16, max size: 10 MiB. Path-jail enforced.
Despite the name,
:watchdoes not set up a file system watcher. It reads the file once at parse time.
Provide a fallback file path. If the primary source is missing, the fallback is used.
Syntax: key:fallback:FALLBACK_PATH value
!active
config:fallback:./defaults.synx ./overrides.synx
theme:fallback:./themes/default.synx ./themes/custom.synx
Also applies if the value is null or empty.
Rate-limit access to a value.
Syntax: key:spam:MAX:WINDOW_SEC target
!active
api_endpoint:spam:5:60 https://api.example.com/data
# Allow max 5 accesses per 60 seconds.
# On the 6th call: "SPAM_ERR: exceeded 5 calls per 60s"
Window defaults to 1 second if omitted. The counter lives in process memory and resets on restart.
Attach an LLM prompt label to a key.
Syntax: key:prompt:LABEL value
!llm
system:prompt:system You are a helpful assistant. Answer concisely.
context:prompt:user The user is building a Rust web service.
Reference an image file path for vision model input. No engine transformation — applications detect via metadata.
Syntax: key:vision path/to/image.png
!llm
screenshot:vision ./screenshots/app_ui.png
diagram:vision ./docs/architecture.png
Supported formats: PNG, JPEG, GIF, WEBP.
Reference an audio file for audio model input. No engine transformation.
Syntax: key:audio path/to/file.mp3
!llm
recording:audio ./meetings/standup_2026-04-01.mp3
Supported formats: MP3, WAV, OGG, FLAC.
Inherit all fields from one or more parent objects (mixin-style merge). Child fields take priority.
Syntax: child:inherit parent1 parent2 ...
!active
base
host localhost
port 8080
debug false
production:inherit base
host prod.example.com
debug false
staging:inherit base production
host staging.example.com
# staging gets: host staging.example.com, port 8080, debug false
:inherit runs as a pre-pass before all other markers. Parents are merged left to right, then the child's own fields override the result.
Constraints appear in square brackets after the key name, before any marker or value. Multiple constraints are comma-separated. Constraints only work in !active mode.
Syntax: key[constraint1, constraint2:arg] value
With markers: key[constraint1, constraint2:arg]:marker value
| Constraint | Argument | Description |
|---|---|---|
type |
int|float|bool|string|array|object |
Assert value type |
required |
none | Value must be present and non-null |
min |
number | Minimum value or minimum length |
max |
number | Maximum value or maximum length |
pattern |
regex string | String must match the regex |
enum |
value1|value2|… |
Value must be one of the listed options |
readonly |
none | Value cannot be overridden |
!active
port[type:int, min:1024, max:65535] 8080
host[required, type:string] localhost
mode[enum:development|staging|production] development
slug[type:string, pattern:^[a-z0-9-]+$] my-app
workers[type:int, min:1, max:32] 4
# Constraints with markers
db_port[type:int, min:1024]:env:default:5432 DB_PORT
api_host[required]:env API_HOST
Assert the value's data type.
!active
port[type:int] 8080
name[type:string] Alice
ratio[type:float] 3.14
enabled[type:bool] true
Value must be present and non-null. Parsing fails if the field is missing or empty.
!active
api_key[required]:env API_KEY
name[required, min:1] Alice
For numbers — restricts the value range. For strings and arrays — restricts the length.
!active
volume[min:0, max:100] 75
username[min:3, max:30] alice
password[min:8] s3cur3P@ss
String must match a regular expression (RE2 syntax).
!active
email[pattern:^[^@]+@[^@]+\.[^@]+$] user@example.com
slug[pattern:^[a-z0-9-]+$] my-cool-app
Value must be one of the listed options, separated by |.
!active
mode[enum:development|staging|production] development
color[enum:red|green|blue] green
Value cannot be overridden by includes or patches.
!active
version[readonly] 3.6.0
Markers chain with the : separator. Each marker in the chain processes the result of the previous one:
!active
# Read PORT from env, fall back to 8080 if not set
port:env:default:8080 PORT
# Resolve base_rate, then multiply by 2
total:ref:calc:*2 base_rate
# Read env with constraint
db_port[type:int, min:1024]:env:default:5432 DB_PORT
The general order is: key(type)[constraints]:marker1:marker2:... value
SYNX has 12 official language bindings that all pass the same conformance test suite:
| Language | Package | Key API |
|---|---|---|
| Rust | synx-core (crates.io) |
Synx::parse(), parse_active(), stringify(), format(), diff(), compile(), decompile() |
| CLI | synx-cli (cargo install) |
synx parse, synx diff, synx format, synx compile, synx query |
| JavaScript/TS | @aperturesyndicate/synx-format (npm) |
Synx.parse(), loadSync(), toJSON(), toYAML(), diff() |
| Python | synx-format (PyPI) |
synx_native.parse(), stringify(), format(), diff(), compile() |
| C# | APERTURESyndicate.Synx (NuGet) |
SynxFormat.Parse(), ParseActive(), Deserialize<T>(), Serialize<T>() |
| C | synx.h + libsynx_c |
synx_parse(), synx_stringify(), synx_diff(), synx_compile() |
| C++ | synx/synx.hpp (header-only) |
synx::parse(), synx::stringify(), synx::diff() |
| Go | bindings/go (cgo) |
synx.Parse(), synx.Stringify(), synx.Diff() |
| Swift | bindings/swift (SPM) |
SynxEngine.parse(), stringify(), diff(), compile() |
| Kotlin/JVM | synx-engine (JNA) |
SynxEngine.parse(), stringify(), diff() |
| WebAssembly | bindings/wasm |
parse(), stringify(), format(), diff(), compile() |
| Mojo | bindings/mojo (CPython interop) |
parse_json(), stringify_json(), diff_json() |
SYNX supports a compact binary encoding for fast parsing and smaller file sizes:
# Compile to binary
synx compile config.synx
# → config.synxb
# Decompile back to text
synx decompile config.synxb
# → config.synxBinary SYNX is round-trip safe — compile then decompile produces identical output.
Compare two SYNX documents and get typed change operations:
synx diff old.synx new.synxReturns JSON with Added, Removed, and Modified operations with dot-separated key paths.
Use !schema to export a JSON Schema Draft 2020-12 from your document:
!schema
name[required, type:string]
port[type:int, min:1, max:65535]
mode[enum:development|staging|production]
Validate JSON against a schema:
synx json-validate data.json schema.jsonFull language support via the SYNX extension: syntax highlighting, real-time diagnostics, IntelliSense (28 markers, 7 constraints), Go to Definition, Find References, formatting, color preview, inlay hints, live preview panel.
code --install-extension APERTURESyndicate.synx-vscodeStandalone Language Server (LSP over stdio) for any editor:
cargo install --path crates/synx-lspCapabilities: real-time diagnostics (15 checks), completion (markers, constraints, directives), document symbols.
Supported editors: Neovim, Helix, Zed, Emacs, JetBrains, Sublime Text, Visual Studio.
Connect Claude Desktop (or any MCP client) to synx-mcp:
6 tools: validate, parse, format, read_path, write_path, apply_patch.
SYNX has a package system for distributing configuration and custom WASM markers.
- SYNX Config — Reusable configuration files and templates
- WASM Markers — Custom markers compiled to WebAssembly
Packages are published to synx.aperturesyndicate.com.
# Create a new package
synx create
# Install a package
synx install @scope/package-name
# Publish a package
synx login
synx publish
# Search for packages
synx search keyword
# Package info
synx info @scope/package-namename @myscope/my-package
version 1.0.0
description My reusable SYNX config
type config
license MIT
Custom markers compiled to WebAssembly using the SYNX ABI v1:
synx_alloc()— memory allocationsynx_markers()— list supported markerssynx_apply()— apply a marker to a value
8 capability types: string, fs, net, env, encoding, hashing, time, random.
SYNX is designed to be safe by default — no code execution, no eval, no network calls from the parser.
| Risk | SYNX | YAML |
|---|---|---|
| Code execution | No | Yes (!!python/object/apply) |
| Network calls | No | Depends on loader |
| Shell injection | No — :calc uses safe whitelist operators |
Depends on loader |
| Protection | Limit |
|---|---|
| Path jail | :include, :watch, :fallback can't escape project root |
| Include depth | Max 16 levels (configurable) |
| File size | Included files > 10 MiB rejected |
| Calc expression | Max 4096 characters |
| Env isolation | Optional sandboxed env map |
Input bounds: 16 MiB max file, 128 nesting levels, 1M array elements.
| Benchmark | Time |
|---|---|
Synx::parse (110 keys) |
~39 µs |
parse_to_json (110 keys) |
~42 µs |
Synx::parse (4 keys) |
~1.2 µs |
| Parser | µs/parse |
|---|---|
json.loads (3.3 KB) |
13.04 µs |
synx_native.parse (2.5 KB) |
55.44 µs |
yaml.safe_load (2.5 KB) |
3,698 µs |
SYNX parses 67× faster than YAML in Python.
Fuzzing: 7,177 regression test cases across 3 fuzz targets (parser, binary codec, formatter).
Why not YAML?
YAML has many surprising edge cases: NO is boolean, 1:23 is a sexagesimal number, the Norway problem (NO as country code), multi-document streams, implicit type coercion. SYNX avoids all of these by design.
Can I use tabs?
No. SYNX uses spaces only (2-space canonical indent). Tab characters are a parse error.
Do I need quotes?
Never. The value is everything after the first space. If you need an empty string, use "".
Is SYNX a superset of JSON?
No. SYNX is a different format that represents the same data model. Use synx parse / synx convert to convert between them.
What is the frozen spec?
SYNX v3.6.0 is frozen as of April 2026. No breaking changes to the format grammar. Tooling and bindings continue to receive updates.
SYNX v3.6 — Built for AI and humans by APERTURESyndicate