<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Stof Blog</title>
        <link>https://stof.dev/blog</link>
        <description>Stof Blog</description>
        <lastBuildDate>Fri, 03 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[The Problem With JSON Schema (And What We Did Differently)]]></title>
            <link>https://stof.dev/blog/the-problem-with-json-schema</link>
            <guid>https://stof.dev/blog/the-problem-with-json-schema</guid>
            <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[JSON Schema validates a separate copy of your data's shape, in a separate file, that has to be kept in sync by hand. Stof attaches the validation rule directly to the field it protects, so it can't drift.]]></description>
            <content:encoded><![CDATA[<p>JSON Schema's core limitation isn't its syntax — it's that a schema is always a second, separate document describing the shape of a first one, and nothing forces the two to stay in agreement. Add a field to your data and forget the schema, and nothing tells you. Stof takes a different approach: <code>#[schema(...)]</code> attaches a validation rule directly to the field it protects, in the same document, so there's no second file that can quietly fall out of sync.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-json-schema-actually-struggles">Where JSON Schema Actually Struggles<a href="https://stof.dev/blog/the-problem-with-json-schema#where-json-schema-actually-struggles" class="hash-link" aria-label="Direct link to Where JSON Schema Actually Struggles" title="Direct link to Where JSON Schema Actually Struggles" translate="no">​</a></h2>
<p><strong>It's a separate file that has to be kept in sync by hand.</strong> The schema and the data it validates live in different places, maintained by whoever remembers to update both. There's no mechanism that enforces the connection — just discipline, which is exactly the kind of thing that erodes under deadline pressure.</p>
<p><strong>It has no concept of units.</strong> A field documented as "memory in MB, must be at least 256" is a string with a comment, not an enforced rule — JSON Schema can check that the value is a number, but has no way to know that <code>"2GB"</code> and <code>256000000</code> describe the same thing, or that a value in KB shouldn't be silently accepted where MB was intended.</p>
<p><strong>Cross-field validation is awkward at best.</strong> Checking that one field's value is consistent with another's needs <code>$data</code> references or vendor-specific extensions that most tooling doesn't fully support — the schema starts working against its own format to express something that would be a one-line comparison in real code.</p>
<p><strong>The validation logic usually gets duplicated anyway.</strong> Even with a schema in place, most teams end up writing the same checks again in application code — for error messages the schema can't produce, for logic too complex to express declaratively, or just because it's faster than fighting the schema format for an edge case.</p>
<p>None of this makes JSON Schema poorly designed for what it targets: validating the shape of arbitrary JSON, in a language-agnostic, toolable way. The problem is structural, not an implementation detail — it's what happens when validation has to live somewhere other than the data itself.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-as-part-of-the-data">Validation as Part of the Data<a href="https://stof.dev/blog/the-problem-with-json-schema#validation-as-part-of-the-data" class="hash-link" aria-label="Direct link to Validation as Part of the Data" title="Direct link to Validation as Part of the Data" translate="no">​</a></h2>
<p>In Stof, <code>#[schema(...)]</code> is an attribute on the field it validates:</p>
<div class="language-stof codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-stof codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#[type]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Plan: {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    #[schema((target_value: str): bool =&gt; target_value.len() &gt; 0)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    str label: 'Growth Plan'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    #[schema((target_value: hr): bool =&gt; target_value &gt; 0hr)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    hr reset_inc: 1hr</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<p><code>schemafy(target)</code> checks a target object against every field that has one, and the unit-typed field actually means something — <code>1hr</code> and <code>60min</code> are the same value as far as the rule is concerned, and a plain number with no unit doesn't silently pass as if it had the right one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="beyond-a-single-rule">Beyond a Single Rule<a href="https://stof.dev/blog/the-problem-with-json-schema#beyond-a-single-rule" class="hash-link" aria-label="Direct link to Beyond a Single Rule" title="Direct link to Beyond a Single Rule" translate="no">​</a></h2>
<p>A field's <code>#[schema(...)]</code> doesn't have to be one function. A list runs as a pipeline, each check short-circuiting on the first failure:</p>
<div class="language-stof codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-stof codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#[schema((</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    (target_value: unknown): bool =&gt; (typeof target_value) == 'str',</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    (target_value: str): bool =&gt; target_value.contains('@'),</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">))]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">email: 'someone@example.com'</span><br></div></code></pre></div></div>
<p>A bare <code>#[schema]</code> with no function tells <code>schemafy</code> to recurse into that field if the target's value is an object — validating nested structure without hand-writing traversal logic. And <code>schemafy(target, remove_invalid = true)</code> doesn't just report a failure; it strips whatever didn't pass, turning validation into cleanup in the same step. <code>remove_undefined = true</code> does the same for fields the schema never mentioned at all — filtering and renaming as a batch, not a separate pass.</p>
<p>None of this requires a second file, a different syntax to learn on top of the data format, or a build step to keep generated types in sync. It's the same document, doing one more thing.</p>
<p>If you want to see this built into a complete, runnable example rather than isolated snippets, <a class="" href="https://stof.dev/cookbook/config">A Self-Validating Config</a> walks through the whole thing end to end, and <a class="" href="https://stof.dev/learn/prototypes-and-schemas">Prototypes &amp; Schemas</a> is the full reference.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="faq">FAQ<a href="https://stof.dev/blog/the-problem-with-json-schema#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ" translate="no">​</a></h2>
<p><strong>Does Stof replace JSON Schema entirely?</strong>
For validating a Stof document, yes — <code>#[schema]</code> is the built-in mechanism and doesn't need JSON Schema alongside it. If you're validating plain JSON from a system that isn't Stof-aware, JSON Schema is still a reasonable tool for that narrower job.</p>
<p><strong>Can a Stof schema validate data that didn't originate as Stof?</strong>
Yes. <code>schemafy(target)</code> works on any object in the graph, including one built from <code>parse(json, target, 'json')</code> — the target doesn't need to have been created as an instance of the schema's own prototype.</p>
<p><strong>What happens when validation fails?</strong>
<code>schemafy</code> returns <code>false</code> rather than throwing, so a failed validation is a value you check, not an exception you have to catch. Combined with <code>remove_invalid</code>, it can also actively clean up the target instead of just reporting the problem.</p>
<p><strong>Is this the same idea as Zod, Yup, or io-ts?</strong>
Similar goal — colocating validation with a type definition instead of a separate schema file — but those libraries validate data at the boundary of a TypeScript application. Stof's schemas live inside the document itself, portable to any host that runs Stof, not tied to one language's type system.</p>]]></content:encoded>
            <category>json-schema</category>
            <category>validation</category>
            <category>schemas</category>
            <category>data-runtime</category>
        </item>
        <item>
            <title><![CDATA[What Is a Data Runtime?]]></title>
            <link>https://stof.dev/blog/what-is-a-data-runtime</link>
            <guid>https://stof.dev/blog/what-is-a-data-runtime</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A data runtime executes data directly, the way a JavaScript runtime executes JavaScript — documents that carry their own logic instead of waiting for a separate program to interpret them.]]></description>
            <content:encoded><![CDATA[<p>A <strong>data runtime</strong> is a system that executes data directly, the same way a JavaScript runtime executes JavaScript. Instead of treating data as an inert value that some separate program has to load and interpret, a data runtime runs documents that carry their own logic — fields, types, and functions together, in one place, sandboxed and portable across whatever host embeds it. <a href="https://stof.dev/" target="_blank" rel="noopener noreferrer" class="">Stof</a> is a data runtime: a superset of JSON where the data validates itself, transforms itself, and acts.</p>
<!-- -->
<p>That's the whole definition. Everything below is what it actually means in practice, and why the category needed a name in the first place.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="not-a-format-not-a-language-not-a-database">Not a Format, Not a Language, Not a Database<a href="https://stof.dev/blog/what-is-a-data-runtime#not-a-format-not-a-language-not-a-database" class="hash-link" aria-label="Direct link to Not a Format, Not a Language, Not a Database" title="Direct link to Not a Format, Not a Language, Not a Database" translate="no">​</a></h2>
<p>It's worth being precise about what a data runtime <em>isn't</em>, since the three nearest neighbors all miss something specific.</p>
<p>It's not a data format. JSON, YAML, and TOML describe a snapshot — inert the moment it lands, meaning nothing on its own until some other program decides what to do with it. A data runtime document is still just data structurally (Stof is a strict superset of JSON), but it doesn't stop there.</p>
<p>It's not a programming language, at least not in the general-purpose sense. There's no separate compilation step, no standalone executable, no notion of a "program" independent of the data it operates on. The logic exists specifically to serve the data it's attached to.</p>
<p>It's not a database. There's no query engine, no storage layer, no schema migration tooling. A data runtime document is a single, self-contained unit — closer to an object with methods than a table with rows.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-actually-runs">What Actually Runs<a href="https://stof.dev/blog/what-is-a-data-runtime#what-actually-runs" class="hash-link" aria-label="Direct link to What Actually Runs" title="Direct link to What Actually Runs" translate="no">​</a></h2>
<p>Concretely, valid JSON is already valid Stof:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"host"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.0.0.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"port"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Add types and a function, and it's still the same document:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">str host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.0.0.0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">int port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">fn </span><span class="token function" style="color:#d73a49">url</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> str </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">http://</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">self</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">host</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">:</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">self</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">port</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>url()</code> isn't a separate script that knows how to read this config. It's part of the document, callable the same way a field is readable — <code>doc.call('url')</code> returns a real value, computed from data that lives right next to the function that computes it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-category-needed-a-name">Why This Category Needed a Name<a href="https://stof.dev/blog/what-is-a-data-runtime#why-this-category-needed-a-name" class="hash-link" aria-label="Direct link to Why This Category Needed a Name" title="Direct link to Why This Category Needed a Name" translate="no">​</a></h2>
<p>Every interchange format describes a snapshot. The moment it crosses a wire, it's inert again on the other side, and whatever receives it has to already know, out of band, what to do with it. That's where schema drift comes from — a validation rule living in a separate file, or a separate service, that quietly stops matching the data it's supposed to describe. It's where "the API changed and nobody told the client" comes from. It's why nearly every serious system ends up bolting a DSL onto JSON or YAML eventually: Terraform invented HCL, GitHub Actions grew a custom expression language inside YAML, OpenAPI is JSON Schema with extensions piled on top.</p>
<p>None of those projects were wrong to need more than plain data. They just didn't have a runtime designed to hold both halves at once, so they built one, ad hoc, specific to their own tool. A data runtime is the general version of that same instinct — sandboxed by default, so logic that arrives from somewhere you don't control is safe to execute; portable, so the same document behaves identically whether it's running natively, in WebAssembly, or embedded in a Python host; and extensible, so a document can grow — parsing new fields, new types, even new functions into itself while it runs, instead of being a fixed shape someone has to redeploy to change.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="faq">FAQ<a href="https://stof.dev/blog/what-is-a-data-runtime#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ" translate="no">​</a></h2>
<p><strong>Is a data runtime the same thing as a database?</strong>
No. A database manages persistent storage, queries, and multi-record relationships. A data runtime document is a single self-contained unit of data and logic — no query engine, no storage layer of its own.</p>
<p><strong>How is this different from a templating engine or a DSL?</strong>
A templating engine or DSL is a separate tool that reads a document and produces an output. A data runtime document <em>is</em> the executable thing — the logic lives inside it, not in a separate interpreter built to understand it.</p>
<p><strong>Is it safe to run a data runtime document from an untrusted source?</strong>
That's the specific problem sandboxing solves. A well-built data runtime — Stof included — restricts a document to touching only what it's explicitly handed: no filesystem, no network, unless a host deliberately provides one. Logic arriving over the wire is safe to execute precisely because of that boundary.</p>
<p><strong>Do I have to migrate my existing data to use one?</strong>
Not with Stof specifically — it's a strict superset of JSON, so valid JSON is already a valid Stof document. Adopting it is a decision to add logic where you need it, not a migration.</p>]]></content:encoded>
            <category>data-runtime</category>
            <category>positioning</category>
            <category>definitions</category>
        </item>
        <item>
            <title><![CDATA[The Origin of Stof: From CAD Software to a Data Runtime]]></title>
            <link>https://stof.dev/blog/origin-of-stof</link>
            <guid>https://stof.dev/blog/origin-of-stof</guid>
            <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Stof didn't start as a product. It came out of a decade spent building parametric and geometry formats for CAD and graphics systems, and one architectural insight that turned out to generalize.]]></description>
            <content:encoded><![CDATA[<p>Stof wasn't designed as a product. It came out of roughly a decade spent building parametric and geometry formats for CAD and graphics systems — Siemens, Lockheed Martin, Configura, Anark — work that has almost nothing to do with web APIs or config files on the surface, and everything to do with them underneath.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-cad-software-teaches-you-about-data">What CAD Software Teaches You About Data<a href="https://stof.dev/blog/origin-of-stof#what-cad-software-teaches-you-about-data" class="hash-link" aria-label="Direct link to What CAD Software Teaches You About Data" title="Direct link to What CAD Software Teaches You About Data" translate="no">​</a></h2>
<p>A parametric CAD model isn't really a static shape. It's a set of constraints and relationships — this face stays perpendicular to that one, this length depends on that parameter — that the software has to recompute continuously as a designer drags something around. The geometry is downstream of the logic, not a separate thing from it. A solid model that couldn't recompute its own constraints wouldn't be a CAD model at all; it'd just be a picture of one.</p>
<p>Spend long enough building systems like that, and you stop thinking of "data" and "the logic that operates on data" as naturally separate concerns. They were never separate in the systems I was working on. The industry convention of splitting them — a data file here, an application that interprets it over there — started to look less like a law of nature and more like an artifact of which tools happened to get popular for which jobs.</p>
<p>The specific architectural insight that came out of that work: represent everything as a flat graph of nodes and data components, connected by pointers rather than nested copies. Not a deep tree where moving a subtree means recursively copying everything underneath it — a flat structure where a node is just an entry in a list, and its relationships to other nodes are pointers, cheap to redirect. Once geometry, constraints, and the functions that maintain them are all just data components on that same graph, moving or transforming any part of the document stops being an expensive, special operation. It's just updating a pointer.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="trying-to-send-wasm-over-the-wire">Trying to Send WASM Over the Wire<a href="https://stof.dev/blog/origin-of-stof#trying-to-send-wasm-over-the-wire" class="hash-link" aria-label="Direct link to Trying to Send WASM Over the Wire" title="Direct link to Trying to Send WASM Over the Wire" translate="no">​</a></h2>
<p>Stof itself started as a solo project, well before it needed to be anything more than an experiment — I was trying to send WebAssembly over the wire, and kept running into the same wall: wasm binaries are way too big, and whatever format carried the data had no way to also carry the logic that made the data useful once it arrived. Every option meant picking a lane. Either the payload was inert data that the receiving end had to already know how to interpret, or it was executable code with no natural place to keep the structured data it needed to operate on. There's also all of the tooling and dependencies to deal with.</p>
<p>The flat-graph model from the CAD work combined with wasm was the thing that made a different answer feel obvious: if functions are just another kind of data component — no different in kind from a field, just a different type of thing attached to the same node — then a single document can carry both without one being bolted onto the other. Fields, functions, and later on richer data like images and PDFs all became the same kind of citizen on the graph: data components, some of which happen to be callable. Let function-type components manipulate the graph they're attached to, sandbox what they're allowed to reach, and the rest of what Stof is followed from there — a superset of JSON specifically because JSON was already the shape of "plain data," and nothing about adding logic required breaking that.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="from-solo-project-to-production">From Solo Project to Production<a href="https://stof.dev/blog/origin-of-stof#from-solo-project-to-production" class="hash-link" aria-label="Direct link to From Solo Project to Production" title="Direct link to From Solo Project to Production" translate="no">​</a></h2>
<p>Stof has been running in production for a while now, including as the policy engine underneath <a href="https://limitr.dev/" target="_blank" rel="noopener noreferrer" class="">Limitr</a> — every plan, credit limit, and validation rule there is a live Stof document, not a config file that Limitr's application code separately has to interpret. That's the same idea from the CAD work, just pointed at billing policy instead of geometry: the rule and the data it governs are the same object, so they can't quietly drift apart the way a schema and the thing it validates so often do.</p>
<p>None of this was originally in service of a thesis about "data and logic belong together." It came from a decade of watching what happens when you build systems where that was already true, and noticing that most of the software industry had, for understandable historical reasons, ended up somewhere else.</p>
<p>If you want the more structured version of where this is headed — the actual technical claims, not the origin story — <a class="" href="https://stof.dev/blog/what-is-a-data-runtime">What Is a Data Runtime?</a> is the place to start.</p>]]></content:encoded>
            <category>origin-story</category>
            <category>data-runtime</category>
            <category>engineering</category>
        </item>
        <item>
            <title><![CDATA[We Deserve Better Than JSON as a DSL]]></title>
            <link>https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl</link>
            <guid>https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl</guid>
            <pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Every team ends up bolting a DSL onto JSON, YAML, or TOML eventually. The problem was never the format — it's asking data to carry logic it was never designed to hold.]]></description>
            <content:encoded><![CDATA[<p>It always starts innocently. You're creating a new config or endpoint with some dynamic behavior, thinking "keep it stupidly simple." What's simpler than JSON or TOML? Some key-value pairs, maybe some nesting — we'll actually keep it clean this time.</p>
<p>Any experienced programmer knows what comes next.</p>
<!-- -->
<p>Six months later you're hunting for a <code>$ref</code> in a sea of JSON, writing helper tools, trapped under a stack of legacy decisions, full of regret.</p>
<p>I've done it. You've done it. The entire industry has done it. GitHub Actions is YAML with a custom expression language bolted on. Terraform invented HCL because JSON wasn't expressive enough. OpenAPI is JSON Schema with extensions piled on top. Every AI framework has its own JSON-based tool definition format that's slightly different from the others.</p>
<p>The problem isn't the formats. JSON, YAML, TOML — they're all fine at what they do. The problem is that we keep asking them to carry logic they were never designed to hold, then building increasingly elaborate scaffolding when they inevitably buckle.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-if-your-data-could-just-do-whats-needed">What if your data could just do what's needed?<a href="https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl#what-if-your-data-could-just-do-whats-needed" class="hash-link" aria-label="Direct link to What if your data could just do what's needed?" title="Direct link to What if your data could just do what's needed?" translate="no">​</a></h2>
<p>Here's the part that made me want to build a real data runtime — <a href="https://stof.dev/" target="_blank" rel="noopener noreferrer" class="">Stof</a>.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> stofAsync </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@formata/stof'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> doc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> stofAsync</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    name: 'Stof'</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="display:inline-block;color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    fn loaded() -&gt; str {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        const stof = await Ext.fetch();</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        parse(stof, self);</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        self.say_hello()</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    }</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c"></span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">lib</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Ext'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'fetch'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">fn say_hello() -&gt; str {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        'Hello, ' + (self.name ?? 'World') + '!'</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    }</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'loaded'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Hello, Stof!</span><br></div></code></pre></div></div>
<p>The document starts without a <code>say_hello</code> function. It fetches more Stof from somewhere — an API, another service, an agent — then parses it into itself and calls the function that just arrived.</p>
<p>Stof runs in a WASM sandbox built in Rust and is just a document of data, like JSON (actually a superset of JSON). It can't touch your filesystem, network, or memory unless you explicitly bridge it to the host environment with <code>doc.lib()</code>. You control exactly what the context can reach.</p>
<p>This means a service can share its capabilities as Stof. Not a description of what it can do, but the actual logic. The consumer parses it into context and starts using it immediately — no client library, no SDK, no redeployment. Your system ships with certain capabilities and gains more at runtime.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-whole-picture">The Whole Picture<a href="https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl#the-whole-picture" class="hash-link" aria-label="Direct link to The Whole Picture" title="Direct link to The Whole Picture" translate="no">​</a></h2>
<p>Stof is a superset of JSON, so your existing data is already valid — but with functions, types, unit conversions, and async execution built into the format itself, instead of a layer bolted on top.</p>
<p>Instead of trying to replace existing interchange formats, Stof is the glue layer that works with all of them. Parse JSON, YAML, TOML, Stof, or more into a single document at any time, add the logic that belongs, and send it anywhere. Export portions to whichever format your app expects internally.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> doc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> stofAsync</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">#[type]</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">Server: {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    port: 8080</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    host: 'localhost'</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    secure: false</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    MiB memory: 500GiB</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="display:inline-block;color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    fn url() -&gt; str {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        let url = self.secure ? 'https://' : 'http://';</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        url += self.host + ':' + self.port;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">        url</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    }</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Parse JSON, YAML, TOML, binary, or more Stof into the same document</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Server "prod": {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    "host": "prod.example.com",</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    "port": 443,</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    "secure": true,</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    "memory": "2GB"</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">call</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'prod.url'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// https://prod.example.com:443</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'prod.memory'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// ~1907 MiB (auto-converted from GB)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">doc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'toml'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'prod'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></div><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">host = "prod.example.com"</span><br></div><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">port = 443</span><br></div><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">secure = true</span><br></div><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">memory = 1907.3486328124998   # MiB</span><br></div><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><br></div></code></pre></div></div>
<p>The <code>Server</code> type defines shape, defaults, and behavior. When you parse new data in — JSON, YAML, TOML, whatever — and cast it to that type, it gets the functions and validation for free.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="schemas-that-dont-drift">Schemas That Don't Drift<a href="https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl#schemas-that-dont-drift" class="hash-link" aria-label="Direct link to Schemas That Don't Drift" title="Direct link to Schemas That Don't Drift" translate="no">​</a></h2>
<p>You know what's worse than writing a JSON Schema? Keeping it in sync with the thing it validates.</p>
<p>Here's the JSON Schema for a simple server config:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"$schema"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://json-schema.org/draft-07/schema#"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"properties"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"port"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"integer"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"exclusiveMinimum"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"maximum"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">65536</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"address"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"minLength"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"memory"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"description"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Memory in MB, must be at least 256"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"required"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"address"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>That's a separate file, a separate format, a separate thing to maintain. And notice <code>memory</code> — because JSON Schema has no concept of units, the best you can do is write a comment and hope the person reading it notices. The validation logic for that field lives somewhere else entirely, probably in your application code.</p>
<p>In Stof, validation lives on the fields themselves — and because this part is pure Stof, no TypeScript host required, you can run it right here:</p>
<div class="panel_ynNN"><div class="head_AQNo"><span class="headLabel_KSjD">server-schema.stof</span><span class="headActions_C7LG"><button type="button" class="button button--sm button--outline button--secondary copyBtn_iyfg" aria-label="Copy code">Copy</button><button type="button" class="button button--sm button--primary runBtn_eq0x" disabled="">Loading runtime…</button></span></div><pre class="body_vZGw"><code><span class="tk-attr">#[type]</span>
<span class="tk-type">Server</span>: {
  <span class="tk-attr">#[schema((target_val: int): bool =&gt; target_val &gt; 1024 &amp;&amp; target_val &lt;= 65536)]</span>
  <span class="tk-type">int</span> port: <span class="tk-num">8080</span>

  <span class="tk-attr">#[schema((target_val: str): bool =&gt; target_val != "")]</span>
  <span class="tk-type">str</span> address: <span class="tk-str">"localhost"</span>

  <span class="tk-attr">#[schema((target_val: MiB): bool =&gt; target_val &gt;= 256MB)]</span>
  <span class="tk-type">MiB</span> memory: <span class="tk-num">2GB</span>
}

<span class="tk-attr">#[main]</span>
<span class="tk-kw">fn</span> <span class="tk-fn">main</span>() {
  <span class="tk-kw">const</span> good = <span class="tk-kw">new</span> { port: <span class="tk-num">8080</span>, address: <span class="tk-str">"prod.example.com"</span>, memory: <span class="tk-str">"2GB"</span> };
  <span class="tk-fn">pln</span>(<span class="tk-kw">self</span>.<span class="tk-type">Server</span>.<span class="tk-fn">schemafy</span>(good));

  <span class="tk-kw">const</span> bad = <span class="tk-kw">new</span> { port: <span class="tk-num">80</span>, address: <span class="tk-str">"prod.example.com"</span>, memory: <span class="tk-str">"2GB"</span> };
  <span class="tk-fn">pln</span>(<span class="tk-kw">self</span>.<span class="tk-type">Server</span>.<span class="tk-fn">schemafy</span>(bad));
}</code></pre><div class="output_xTQ1 "><div class="outputInner_Kglv"><span class="outputLabel_R6hZ ">Output</span><pre class="outputText_N3ey"></pre></div></div></div>
<p>A port between 1024 and 65536, a non-empty address, and at least 256MB of memory. The last one is meaningful because Stof understands units as types — pass <code>"2GB"</code> and it converts, pass <code>"100MB"</code> and it fails. The schema can't drift from the data, because it <em>is</em> the data. (More on this pattern in <a class="" href="https://stof.dev/cookbook/config">A Self-Validating Config</a>, if you want to see it built out into a full recipe.)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-real-production-use-case">A Real Production Use Case<a href="https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl#a-real-production-use-case" class="hash-link" aria-label="Direct link to A Real Production Use Case" title="Direct link to A Real Production Use Case" translate="no">​</a></h2>
<p><a href="https://limitr.dev/" target="_blank" rel="noopener noreferrer" class="">Limitr</a> is an open source pricing and enforcement engine built on Stof. The entire policy — plans, credits, limits, validation logic — lives in a single Stof document. It's a good example of what "data that carries its own logic" looks like once it's past the toy stage.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="give-it-a-try">Give It a Try<a href="https://stof.dev/blog/we-deserve-better-than-json-as-a-dsl#give-it-a-try" class="hash-link" aria-label="Direct link to Give It a Try" title="Direct link to Give It a Try" translate="no">​</a></h2>
<p>The fastest way in is the <a href="https://play.stof.dev/" target="_blank" rel="noopener noreferrer" class="">playground</a> — runs in your browser via WASM, no install needed. Every concept page in the <a href="https://stof.dev/" target="_blank" rel="noopener noreferrer" class="">docs</a> has one built in too.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @formata/stof      </span><span class="token comment" style="color:#999988;font-style:italic"># TypeScript / JavaScript</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> stof         </span><span class="token comment" style="color:#999988;font-style:italic"># Python</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">cargo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> stof-cli   </span><span class="token comment" style="color:#999988;font-style:italic"># CLI</span><br></div></code></pre></div></div>
<ul>
<li class=""><a href="https://stof.dev/" target="_blank" rel="noopener noreferrer" class="">Docs</a> — install, the full standard library, and worked examples</li>
<li class=""><a href="https://github.com/dev-formata-io/stof" target="_blank" rel="noopener noreferrer" class="">GitHub</a> — source and issues</li>
<li class=""><a href="https://discord.gg/Up5kxdeXZt" target="_blank" rel="noopener noreferrer" class="">Discord</a> — come tell us about your use case</li>
</ul>
<p>There's also a <a href="https://marketplace.visualstudio.com/items?itemName=Formata.stof" target="_blank" rel="noopener noreferrer" class="">VS Code extension</a> for Stof syntax highlighting.</p>
<p>Apache 2.0.</p>]]></content:encoded>
            <category>positioning</category>
            <category>schemas</category>
            <category>data-runtime</category>
        </item>
    </channel>
</rss>