CX · Reference guideComparison to alternatives
PlaygroundDownloadsAboutv0.18.0-pre.1

Comparison to alternatives

Reach for CX for nearly every data-format need and for most general-purpose programs. One syntax carries the document, the query, the transform and the program, so the layers other stacks glue together are one surface here. This section states CX's case: first beside Python — the standard CX is built to meet and exceed (cx-vs-python) — then against each format, covering feature parity, performance, ecosystem, learning curve, schema, identity / hash and round-trip lossiness, with a closing "when to reach for X" for the cases where one layer is the whole problem.

CX and Python — the standard to meet and exceed

Python is not what CX sets out to replace. It is the standard CX measures itself against: readable on day one, batteries included, as good for a ten-line script as for a system. CX is built to meet that standard and to go past it where data work hurts. Each claim below stands beside a conformance case this guide's cx replayed when the page was built.

Exact numbers by default. In Python a fraction is a binary float until you import decimal. In CX a fraction literal is an exact base-10 decimal, and arithmetic keeps it one:

prog.cx

        [?let [= $subtotal 19.99] [= $shipping 5.01] [total [+ $subtotal $shipping]]]
      
        $ cx prog.cx
[total 25.00]
      

Fixture ap-decimal-float-mix-right in conformance/llm/antipatterns.cxd — replayed against this cx when the page was built.

Mixing a decimal with a binary float is refused out loud, naming the one bridge between them, rather than rounded in silence:

prog.cx

        [+ 1.5 1.0e0]
      
        $ cx prog.cx
[err code=cx-err:CXER0100 message='+: decimal/bigint arithmetic admits only int/bigint/decimal operands — [cast] is the only decimal↔float bridge (L44)' at='1:1']
      

Fixture ap-decimal-float-mix-wrong in conformance/llm/antipatterns.cxd — replayed against this cx when the page was built.

Effects are granted, never inherited. A Python script can read every file its process can. A CX program reads nothing until you grant it, and the refusal is an ordinary value that names the flag which would allow it:

prog.cx

        [?lib 'cx-stdlib/io']
[$io:read-file "/var/log/app.log"]
      
        $ cx prog.cx
[err code=cx-err:CXER0271 message='E_CAP_DENIED: read capability required for io-read-file; none granted (grant via --allow-read)' at='2:1']
      

Fixture io-001-read-file-cap-denied in deps/cx-core-code/conformance/stdlib/io.cxd — replayed against this cx when the page was built.

Regular expressions that do not backtrack. Python's re is a backtracking engine. CX's re is RE2: a construct that would need backtracking, such as a backreference, is refused when the pattern compiles:

prog.cx

        [?lib 'cx-stdlib/re']
[$re:compile "(.)\\1"]
      
        $ cx prog.cx
[err code=cx-err:CXER3200 message='E_RE_FEATURE_UNSUPPORTED: CXER3200:E_RE_FEATURE_UNSUPPORTED: (.)\1' at='2:1']
      

Fixture re-003-compile-backreference-unsupported in deps/cx-core-code/conformance/stdlib/re.cxd — replayed against this cx when the page was built.

Errors are values. An error flows out of anything built from it, so a failure is a value you can inspect where it surfaces, not an exception unwinding past you:

prog.cx

        [?let [= $e [err code='c' message='m']] [report $e]]
      
        $ cx prog.cx
[err code=c message=m]
      

Fixture program-construction-err-004-captured-err-propagates in deps/cx-core-code/conformance/code.cxd — replayed against this cx when the page was built.

Updates never mutate. [?modify] answers a new document; the one it started from is unchanged:

input.cx

        [users [user name=Alice]]
      

prog.cx

        [?let [= $orig $doc] [= $new [?modify $doc //user/@name [set "Bob"]]] [pair orig=$orig/user@name new=$new/user@name]]
      
        $ cx --data=input.cx prog.cx
[pair orig=Alice new=Bob]
      

Fixture program-modify-003-original-unchanged in deps/cx-core-code/conformance/code.cxd — replayed against this cx when the page was built.

One value model for every format. JSON parses into the same values the language computes with, and a map is a map whatever order its entries were written in:

prog.cx

        [?lib 'cx-stdlib/json']
[$json:parse "{\"a\": 1}"]
      
        $ cx prog.cx
{a: 1}
      

Fixture json-007-parse-object-map in deps/cx-core-code/conformance/stdlib/json.cxd — replayed against this cx when the page was built.

prog.cx

        [?let [= $z 0] ([= {a: 1, b: 2} {b: 2, a: 1}], [= {a: 1, b: 2} {a: 1, b: 3}], [= {a: 1} {a: 1, b: 2}])]
      
        $ cx prog.cx
(true, false, false)
      

Fixture program-map-115-equality-order-independent in deps/cx-core-code/conformance/code.cxd — replayed against this cx when the page was built.

Identity is built in. A value's SHA-256 is taken over its canonical bytes, so the same value written two ways hashes alike:

prog.cx

        [$eq [$cx:hash [$cx:parse "[a   1]"]] [$cx:hash [a 1]]]
      
        $ cx prog.cx
true
      

Fixture cx-025-hash-value-and-source-agree in deps/cx-core-code/conformance/stdlib/cx.cxd — replayed against this cx when the page was built.

When to reach for CX

CX is the default for data, config and interchange. The specialists further down this list are the cases where one layer is the whole problem. Top to bottom, first match wins:

  • You need one syntax for data and code (config that is also a program, templates that are also data, a schema that is also a value). CX is the only format in this comparison that is homoiconic; pick CX.
  • You need content-addressable identity across languages and platforms (cache keys, dedup, signed documents, distributed ledgers). The CX SHA-256 of canonical bytes is the most boring possible implementation; pick CX.
  • You round-trip between JSON / YAML / TOML / XML / Markdown and lose information each time. CX is the superset that preserves all of them losslessly; pick CX.
  • Your workload is bulk analytical scan on tabular data at terabyte scale, exchanged with the broader ecosystem (DuckDB / Spark / pandas). Use Parquet for storage, Arrow for in-memory, optionally with CX as the metadata / config layer.
  • Your workload is wire-format throughput between microservices with a fixed schema, and you control both ends. Use Protocol Buffers or MessagePack — they will be smaller and faster on the wire than any text format.
  • You need a single-file human-edited config with no programmability and no cross-system identity requirements. TOML (Cargo, pyproject) or YAML (Kubernetes, CI) are everywhere; the ecosystem advantage is real.
  • You need a typed, programmable config language with a strong type system. Dhall or Pkl are good fits if the type-system rigor matters more than the homoiconic data-and-code unification.
  • Otherwise (general data / config / interchange with mixed workloads, mid-size documents, multiple consumers in different languages): CX is the all-purpose default.

CX vs JSON

JSON is the universal interchange baseline. CX is a superset projection — anything that round-trips through JSON also round-trips through CX. The interesting comparisons are where CX has features JSON does not, and where JSON has ecosystem JSON does not.

dimension json cx
Schema JSON Schema (separate spec) .cxs — same syntax as data (see schema)
Identity / hash No canonical form; hash drifts on key reorder SHA-256 of canonical bytes; stable across platforms (see hash)
Comments None (in the standard); JSON5 / JSONC are extensions [; … ] blocks + # line comments, preserved by cx fmt, stripped by cx canonical
Trailing commas Forbidden Not applicable — bracket form, no commas in attribute list
Number precision Underspecified; JS doubles by default Sized type annotations (::i64, ::u32, ::f32) (see sized-types)
Multi-line strings Escaped only Triple-quoted, whitespace-preserving (see strings)
Date / datetime String only First-class :date / :datetime scalar (see dates)
Bytes Base64 in a string First-class :bytes scalar (see bytes)
Tooling ecosystem Vast — every language, every editor, every CDN Tier-1 bindings V/Python/Go/Rust; LSP + tree-sitter + VS Code; growing
Wire size Baseline Comparable text form; data-bin / CXCol smaller
Parse speed Highly optimized parsers everywhere Fast reference parser; the release gate holds streaming to a >=200 MB/s floor (not yet passing)
Learning curve Trivial Bracket-and-sigil form; ~one hour for the data subset

When to pick JSON instead of CX. Your consumers are browser JavaScript, a CDN-served config endpoint, or any interface where you cannot install a CX parser. JSON remains the right wire-format choice for ubiquity alone.

When to pick CX instead of JSON. You want identity, schema, comments, mixed content, dates, or bytes without layering JSON Schema + JSON5 + base64 + custom date string conventions on top of JSON. CX gives you the whole stack in one syntax.

CX vs YAML

YAML optimises for human readability of multi-line strings and config-as-code. The cost is the famously large surface area (indentation sensitivity, tag system, anchor / alias / merge keys, the Norway problem).

dimension yaml cx
Indentation Semantic (whitespace-sensitive) Insignificant — bracket-structured
Anchors and aliases &anchor / *alias / << merge keys &anchor / *merge sigils — same idea, no separate merge keyword (see anchor / merge)
Multiline strings | (literal) and > (folded) Triple-quoted (always literal)
Norway problem no parses as false in YAML 1.1 Only true/false are bools; no /yes is a string
Schema JSON Schema (separate) .cxs (same syntax)
Identity / hash No canonical form SHA-256 of canonical bytes (see hash)
Comments # line comments only [; … ] blocks (span lines) + # line comments, preserved by cx fmt
Tooling Kubernetes, GitLab CI, Ansible Growing — LSP + tree-sitter + VS Code; analytical pipelines
Round-trip Lossy in many YAML 1.1 → 1.2 cases Lossless by spec (see roundtrip)

When to pick YAML. Your config consumers are Kubernetes manifests, Ansible playbooks, GitHub Actions workflows, or any tool whose schema is in YAML. The ecosystem advantage is decisive.

When to pick CX. Anywhere you would have wanted YAML minus the indentation footguns, the Norway problem, and the lossy round-trip. The triple-quoted multiline string is as readable as YAML literal blocks without the indentation rules.

CX vs TOML

TOML's pitch is "obviously correct config for configuration files." It excels at flat key-value configs (Cargo.toml, pyproject.toml) and struggles with deep nesting.

dimension toml cx
Nesting Dotted keys / [[arrays of tables]] — awkward past 2-3 levels Native — every element nests
Schema No standard schema language .cxs (see schema)
Identity / hash No canonical form SHA-256 of canonical bytes
Tooling Cargo, pip, Poetry — narrow but deep LSP + tree-sitter; broader workloads than configs alone
Round-trip Lossy — comment placement and key order Lossless
Mixed content Not supported Native (prose form, see prose)
Date / datetime Native datetime Native ::date / ::datetime (plus duration and instant kinds)

When to pick TOML. Your config is flat — language toolchain manifests, Cargo dependencies, app configuration with two levels of nesting. TOML reads beautifully at that scale.

When to pick CX. Your config is genuinely hierarchical, you need cross-config identity, or the same documents will also be processed as data (e.g. shipped as configuration and hashed for a release manifest). CX scales up where TOML hits its ceiling.

CX vs XML

XML is CX's closest structural cousin — both are tree-shaped, both support attributes and mixed content, both have namespaces. The differences are syntactic density and the surrounding ecosystem (XSLT, XPath, XSD).

dimension xml cx
Syntax verbosity Open + close tag pair Single bracket pair
Attribute syntax name="value" inside open tag name=value inside bracket (see attributes)
Mixed content Native Native (see prose)
Namespaces xmlns:prefix=URI xmlns:prefix=URI (XML-aligned, see namespaces)
Selector XPath 3.1 CXPath — XPath 3.1 aligned (see cxpath)
Schema XSD (large, complex) or RELAX NG .cxs (CX syntax, small)
Identity / hash C14N (XML Canonicalization) cx canonical + SHA-256
Templating XSLT (separate language) [?for] / [?modify] — same language as data
Tooling Vast in enterprise / publishing; less in modern web Growing; same ecosystem reach is the long-term goal
Learning curve Familiar to a generation of developers Lower if XML is new; comparable if XML is known

When to pick XML. You are working in publishing / document workflows (DITA, DocBook, SVG), or your consumers are XML-native (SOAP, XHTML, RDF/XML). The tooling depth is unmatched in those niches.

When to pick CX. You want the XML data model (attributes, mixed content, namespaces, XPath) without the syntactic verbosity, without XSD, and with code-as- data unification. CX is what XML would look like if designed today.

CX vs Protocol Buffers

Protobuf is the schema-first binary wire format for service-to-service traffic. It is the right tool when you control both ends, the schema rarely changes, and every byte on the wire matters.

dimension protobuf cx
Wire format Binary — varint + length-prefix Text (CX), or data-bin / CXCol (binary)
Schema requirement Required — wire is unparseable without .proto Optional — value is self-describing
Wire size Typically smallest of any format here Comparable text size; data-bin closer to protobuf
Throughput Very high — compiled (de)serializers High but text-shaped; data-bin closes the gap
Schema evolution Tag-numbered fields — strong forward/backward compat Identity is schema-independent (content-addressed values never re-key); migration is always additive — new representations join, history stays replayable
Tooling protoc + every major language Tier-1 V/Python/Go/Rust; growing
Human readability None — binary opaque Text-first; binary is an optimization
Identity / hash Wire bytes — stable iff schema is fixed Canonical bytes — stable regardless of schema

When to pick Protobuf. You have a controlled RPC system, the schema is well-understood, and the bandwidth / CPU savings are real (mobile, embedded, hot-path services). Honest concession: Protobuf wins on wire throughput in the steady state.

When to pick CX. Your data is also config, also logs, also human-edited at the boundary, also stored on disk for later inspection. Protobuf loses to text formats outside the wire-only path.

CX vs MessagePack

MessagePack is "binary JSON" — schemaless, more compact than text JSON, slightly slower than protobuf because it carries kind tags.

dimension msgpack cx
Wire format Binary, schemaless Text (CX) or binary (data-bin)
Schema None .cxs (optional)
Wire size Smaller than JSON; comparable to data-bin data-bin similar; CXCol smaller for tabular
Type richness Scalars + map + array; ext-type extension Eleven atomic kinds, elements with attributes, mixed content, namespaces
Identity / hash Wire bytes — stable iff serializer is deterministic Canonical bytes — stable by construction
Tooling Wide language coverage Tier-1 V/Python/Go/Rust

When to pick MessagePack. You want JSON semantics on the wire without JSON wire size, and your data is flat / tabular / homogeneous. MessagePack is excellent at being "JSON minus the text.".

When to pick CX. Your data has shape — attributes, mixed content, namespaces, hierarchical structure that MessagePack would force into nested maps. data-bin covers the wire-size argument without giving up the shape.

CX vs Apache Pkl

Pkl is Apple's configuration language — declarative, strongly typed, programmable. The closest "config that is also code" peer to CX, with a different design centre.

dimension pkl cx
Surface Custom syntax — class-oriented Bracket form — homoiconic
Typing Strong, static, gradual Schema-validated (.cxs); type tags on scalars
Computation Lambdas, comprehensions, methods Directives — [?for], [?match], [?modify], [?let]
Output formats JSON / YAML / Plist / Properties + custom renderers CX / JSON / YAML / TOML / XML / Markdown / CSV / JSONL
Identity / hash Not built-in SHA-256 of canonical bytes
Module system Imports, packages, semantic versioning [?lib] / [?def] / [?const] with cx.lock SRI integrity
Tooling LSP, IntelliJ plugin, CLI — solid LSP + tree-sitter + VS Code — comparable
Ecosystem Apple-backed; growing community Open-source; growing community

When to pick Pkl. You want a strongly typed config language with class-oriented composition and your consumers are JSON / YAML output. Pkl's type system is richer than the CX schema language.

When to pick CX. The same documents are also data to be hashed, queried, transformed, or shipped as payloads. Pkl is config-first; CX is data-and-code by design.

CX vs Dhall

Dhall is the typed-config purist's pick — total, non-Turing-complete, with a sound type system and normalised import semantics.

dimension dhall cx
Type system Total, sound, dependent-ish (System Fω) Schema validation; type tags on scalars
Computation λ-calculus — total, no recursion without bound Directives — [?for] / [?match] / [?modify]; bounded by resilience directives
Surface Haskell-flavoured ML syntax Bracket form
Output formats JSON / YAML / Text CX / JSON / YAML / TOML / XML / Markdown / CSV / JSONL
Imports Content-addressed by hash — same idea as CX [?lib] modules pinned by cx.lock SRI hashes
Identity / hash Standard form hash for imports Canonical bytes + SHA-256 for any value
Learning curve Steep — ML / Haskell familiarity helps Moderate — bracket form is unfamiliar but small
Tooling LSP, type-aware; narrow LSP + tree-sitter + VS Code

When to pick Dhall. You want totality, soundness, and a small surface area as design goals; you are willing to pay for the steeper learning curve. Dhall is the right tool for safety-critical config.

When to pick CX. You want the content-addressed identity, the schema-validated config, and the programmable updates of Dhall — plus the ability to carry rich data (mixed content, columnar tables, dates, bytes) in the same syntax. Dhall is config; CX is config + data + code.

CX vs Parquet

Parquet is the columnar storage standard for analytical workloads — terabyte-scale scans, predicate pushdown, compression. CX is not Parquet's competitor; CXCol is, and Parquet is CX's export target.

dimension parquet cx-cxcol
Layout Column-oriented, on-disk Column-oriented (CXCol), in-memory; on-disk possible
Compression Snappy / Gzip / Zstd / LZ4 page-level Dictionary encoding on; compression optional
Schema Embedded — required for read .cxs schema or embedded type tags
Ecosystem Spark, DuckDB, Polars, pandas, Athena, BigQuery Native CX pipelines; Arrow bridge for everything else
Bridge — CX ↔ Parquet bridge via Arrow (see parquet-bridge)
Identity / hash File hash — sensitive to write order Canonical bytes of the logical value
Streaming Row-group oriented Native streaming evaluator (see streaming)
Write speed Pays for compression + conversion on write No compression overhead by default (no maintained benchmark numbers to quote)

When to pick Parquet. Your data is large, tabular, queried by external tools (Spark / Athena / BigQuery / DuckDB), and stored on object storage. Parquet is the lingua franca.

When to pick CXCol. Your data is tabular but the producers and consumers are CX-native (pipelines, in- memory analytical workloads, services that ship columnar payloads). When you need to interchange with the broader ecosystem, export to Parquet.

CX vs Arrow

Arrow is the in-memory columnar standard. Like MessagePack, it is not a CX competitor — it is a layer CX integrates with.

dimension arrow cx-cxcol
Layout Column-oriented, in-memory Column-oriented, in-memory (compatible)
Wire format Flight (gRPC streaming) / IPC data-bin chunked tables
Interop pandas, Polars, DuckDB, Datafusion — zero-copy CX ↔ Arrow bridge (zero-copy, see arrow-bridge)
Schema Embedded .cxs or embedded
Identity / hash None standard Canonical bytes + SHA-256
Use case Cross-process / cross-language analytics Same plus document semantics

When to pick Arrow. You are doing pure analytics — no document semantics, no identity requirements, no schema as a value. Arrow is the right cross-engine in-memory layout.

When to pick CXCol over Arrow. You want Arrow's column-orientation plus CX's document semantics (mixed content rows, hierarchical headers, namespaced columns, hash-stable identity). Use the bridge — they share memory zero-copy.

Summary table

At a glance, the headline trade-offs for each alternative. Pick CX is the column that matters most for an evaluator question.

format strength weakness pick-cx-when
JSON Universal ecosystem No schema, no comments, no identity You need any of: schema / comments / identity / mixed content / dates / bytes
YAML Human-readable config Indentation footguns, lossy round-trip, Norway problem You want YAML readability without the footguns
TOML Flat config readability Awkward at depth, no schema Your config is hierarchical or grows beyond config
XML Tree-shaped, mixed content, XPath ecosystem Verbose, XSD complexity, no homoiconic code-as-data You want the XML data model in a denser syntax with code-as-data unified
Protobuf Wire size and throughput Schema-required, opaque without .proto, no document semantics Your data is also config / docs / hashed payloads, not just RPC wire
MessagePack Compact binary JSON Flat / no document semantics Your data has shape (attributes / mixed content / namespaces)
Pkl Strongly typed config language Config-only; no data identity, narrower output formats The same documents are data and code, not just config
Dhall Total, sound, content-addressed imports Steep learning curve, config-only You want Dhall semantics plus rich data shape and broad outputs
Parquet Analytical scan, ecosystem File-format only, no document semantics Producer and consumer are CX-native; export to Parquet for interchange
Arrow Zero-copy in-memory columnar No document semantics, no identity You want Arrow plus CX semantics (use the bridge — they coexist)

CX's case, in one paragraph. CX wins on unification: the document, the transform, the value model and the identity are one syntax — cx-vs-python shows each, replayed: the update that leaves its original unchanged, JSON arriving as a CX map, the hash over canonical bytes. Where one layer IS the whole problem — a browser-only JSON consumer, a Kubernetes manifest, a terabyte Parquet scan — reach for the specialist, and read its format into CX where the rest of the system lives. Everywhere else, CX is the default.

If you have a workload that does not match any row of this table or §13.1's decision tree, file an issue at https://github.com/cx-home/cx — the comparison is maintained as a living document; new alternatives and new use-case scenarios are added as the ecosystem evolves.

At-a-glance feature matrix

One-screen feature matrix across CX, JSON, YAML, TOML, and XML. The per-format children (§13.2-§13.11) carry the depth; this matrix is the spreadsheet view.

Feature CX JSON YAML TOML XML
Syntax weight brackets, no closing tags curly braces + brackets indent-significant tables + key=val open + close tags
Strong types ✅ int / float / bool / null / sized / decimal / bigint / date / datetime / bytes / atom ❌ number only partial (auto-detect) ✅ int / float / bool / datetime partial (xs:type)
Comments ✅ block and line ❌ ✅ # … ✅ # … ✅
Mixed content ✅ first-class ❌ ❌ ❌ ✅ first-class
Multiple top-level docs ✅ no wrapper required ❌ requires array ✅ via --- separator ❌ single document partial
Attribute / element distinction ✅ explicit ❌ flat keys ❌ flat keys ❌ flat keys ✅ explicit
Type fidelity through round-trip ✅ via --lossless ❌ int↔float coerced partial ✅ preserved partial
Tabular data efficiency ✅ :table block, columnar binary ❌ verbose array-of-objects ❌ verbose partial (array of tables) ❌ verbose
Streaming parser ✅ pull-based handle API partial ❌ usually whole-file ❌ ✅ SAX
Built-in query language ✅ CXPath value kind, 12 axes (XPath 3.1's 13 minus the deprecated namespace axis) ❌ third-party (jq) ❌ third-party (yq) ❌ ✅ XPath 1.0 / XQuery
Pure-functional transform ✅ [?modify] focus + 11 actions, structural sharing ❌ ❌ ❌ partial (XSLT)
Pattern-match dispatch ✅ [?match] heterogeneous arms ❌ ❌ ❌ ❌
Module system ✅ [?def] / [?lib] / cx.lock SRI ❌ ❌ ❌ partial (XInclude)

Conversion-loss matrix

Lossless conversion means the data round-trips without semantic loss; presentation details (comments, whitespace, attribute order) may be normalized. The table below is the honest accounting of what survives, what gets normalized, and what is genuinely lossy.

CX → format → CX Lossless? What is preserved What is normalised or lost
CX → JSON → CX data ✅ / presentation 📋 Element shape, attributes, types via --lossless, values, nesting Comments dropped; element vs attribute distinction collapses to keys
CX → YAML → CX data ✅ / presentation 📋 Element shape, types via !!tags, multiline strings Comments dropped by most parsers; flow-vs-block style normalised
CX → TOML → CX data ✅ / presentation 📋 Element shape via table headers, strong types Comments dropped; section ordering normalised; deep nesting flattened
CX → XML → CX data ✅ / presentation 📋 Element shape, attributes, mixed content, namespaces, PIs Whitespace normalised per xml:space; entity references resolved
CX → CXCol → CX data ✅ Values with full type fidelity — CXCol is the columnar binary export Presentation (comments, whitespace); the strict-canonical text form is cx canonical, not CXCol
CX → CSV → CX data partial :table block columns and rows Non-table data not encodable; cell subtyping collapses

Presentation-layer differences — comments (only XML and CX preserve them; JSON has none; YAML and TOML lose them through most parsers), whitespace (normalised to the target conventions), attribute order (emission may pick a canonical order), anchor names (canonical form renumbers them). These all show up as identical hashes via cx hash because the hash operates on strict-canonical bytes, where presentation is stripped.

Where CX is fast — measured

CX's performance culture is gates, not brags: every number below names its runner, was measured on the reference dev machine (Apple Silicon, -prod build, 2026-08-20), and is re-measured each release. Claims without a measurement do not ship in these docs.

HTTP serving — the headline

A CX HTTP service runs deep in compiled-server territory: ~122,000 requests/second measured against the static guide server (wrk -t8 -c128, containerized, the B18 allocation-work findings), with ~135k req/s on the uncontainerized reference run. That is two orders of magnitude past a typical scripting-language app server and in the league of Go/Rust HTTP frameworks — from [?http-service] declarations, not hand-tuned handler code. (Multi-core scaling is the honestly-stated frontier: see the README status note.)

Server-rendered surfaces

The reference storefront (1,004 products, live derived state, zero view code) renders a full department page in ~7 ms server-side and full category pages over the whole catalog in ~0.13 s. Declarative surfaces are not the slow path: the tree-walk evaluator is compiled native, and per-node dispatch cost is independent of program size.

Streaming evaluation

The lazy-record streaming path evaluates [?for] over streamed documents at ~200 MB/s (the release gate's five-trial protocol) with bounded memory regardless of input size — it never materializes the whole tree, so a multi-gigabyte input streams at full speed in constant residency. [?map] measures 129 MB/s against the same 200 MB/s floor the gate holds every shape to. Streaming is the high-throughput path for text-form bulk data.

XSP — the binary wire, small-and-frequent to bulk

XSP is CX's binary frame and session layer, and it is built for both ends of the shape spectrum. Small and frequent: a frame costs 17 bytes of header plus the principal, a u64 stream-id multiplexes every exchange over one connection (requests, replies, events, and cancels interleave without head-of-line blocking between streams), and credit-based flow control keeps a slow consumer from stalling anyone else. Measured on the reference bench: durable publish commits at p50 2.5 ms flat with respect to journal size, embedded ingest folds ~1,100 events/s, and a checkpointed boot replays 500 entries in 28 ms. Large and bulk: payloads ride CX data-bin — the binary form, no text re-parse on either end — and columnar CXCol pages with zstd compression under the store profile, where result streams and live ∂-updates flow as credit-governed event frames. One frame format serves both shapes across TCP, TLS, Unix sockets, and WebSocket unchanged.

Parsing — honest numbers

The buffered parser runs 35–42 MB/s (median across the fixture sizes; the lexer alone runs 84–104 MB/s) — and every one of those bytes buys far more than a JSON byte does: type ascriptions, sized numerics, anchors and merges, mixed content, comments, and whole typed tables come out of the same single pass, with identity-bearing canonical bytes one step away. That is very fast for a grammar this rich — one parse replaces the parse-then-validate-then-decode tower other stacks bolt together. Parse cost is linear in input bytes and gated to stay that way (the amplification step runs in every post-merge pipeline), so throughput is a constant, not a cliff. For raw bulk, the binary formats below and the streaming path above are the fast path by design.

One engine, thin bindings

Identity operations (hash, canonical, eval) route through the one compiled libcx from every binding, so they are bit-identical AND uniformly fast: the V-native path parses the 35 KB bench fixture in ~1.0 ms, Rust lands within 1.6× of that through its hand-written FFI layer. There is no interpreter tax between your host language and the engine.

When CX wins

Every alternative on this page does one layer. CX wins when the layers are the problem. A real system is data plus a schema plus a query language plus a transform layer plus config plus a wire format plus, increasingly, an agent interface — and the incumbent answer is seven tools with six impedance boundaries between them: JSON with JSON Schema bolted on, jq for queries, a template engine for transforms, YAML for config, protobuf for the wire, and a hand-written tool layer for the agents.

CX is one surface for the whole stack. The same bracket form is the document, the schema that validates it, the query that selects from it, the transform that rewrites it, the config that deploys it, and the program that serves it — [?match] and [?modify] operate on the same values cx hash addresses. Identity is machine-checked, not promised: strict canonical bytes give every value one SHA-256 address on every platform and every binding, which is what makes caching, dedup, signing, and content-addressed storage boring instead of bespoke. Adoption is ring-scoped: take the data format alone (it cannot execute anything), and nothing above it can reach down and make it heavier. The runtime is compiled native — the numbers above, from one binary with no interpreter tax. And the same definitions that serve humans serve agents at parity: the tools face and the ux projection derive from the identical [?def]s, so an agent interface is something you already have, not something you build.

Reach for a specialist when one layer IS the whole problem — §13.1 names those cases. Reach for CX when you would otherwise be gluing the specialists together.