stof 0.9.21

Data Runtime. JSON that runs.
Documentation

Install

npm i @formata/stof     # TypeScript / JavaScript

cargo add stof          # Rust

pip install stof        # Python

Full setup for every language, the complete standard library, and worked end-to-end examples: stof.dev

Quickstart

Every JSON document you have is already a Stof document. Start with the data you have, add logic where you need it, and export back to JSON - or YAML, or TOML - any time.

import { stofAsync } from '@formata/stof';

const doc = await stofAsync`
{
    "name": "Stof"
    "hello": ():str => \`Hello, \${self.name}!\`
}`;

console.log(await doc.call('hello')); // Hello, Stof!
import { StofDoc } from '@formata/stof';

const doc = await StofDoc.parse({
    first: 'John',
    last: 'Doe',
    domain: 'example.com'
});

doc.parse(`
fn fill(domain: str = 'example.com') {
    self.domain = domain;
    self.email = \`\${self.first.lower()}.\${self.last.lower()}@\${self.domain}\`;
}`);

await doc.call('fill', 'stof.dev');
console.log(doc.record());

/*
{
    first: 'John',
    last: 'Doe',
    domain: 'stof.dev',
    email: 'john.doe@stof.dev',
}
*/

What Stof Is

Stof is a data runtime — not a data format, not a programming language, not a database.

The same way a JavaScript runtime executes JavaScript, a data runtime executes data. A Stof document doesn't just describe something; it validates itself, transforms itself, and acts, wherever the runtime lands.

Concretely: valid JSON is already valid Stof. You're not migrating to a new format — you're handing your existing data a place to keep the logic that used to live somewhere else.

Why it's different

  • Data and logic belong together. Splitting them across a data file and a separate codebase creates schema drift, versioning hell, and is a limitation for modern distributed systems. Stof documents carry both, as one unit.
  • Portable means portable. The same document runs native, in WebAssembly, or embedded in another host, with the same behavior every time. If it doesn't run everywhere, it isn't portable — it's just convenient sometimes.
  • Sandboxed by default. A document can only see and manipulate itself unless you explicitly hand it more. Logic that arrives over the wire has to be safe to run without a review cycle first.
  • Documents should be able to grow. A running document parsing new fields, new types, even new functions into itself isn't an edge case — it's the model working as intended. Data that can only be replaced, never extended, is still just a snapshot.
  • Interop with all data formats. A superset of JSON that is compatible with every other data format, even ones that don't exist yet — binary, images, PDFs, DOCX, YAML, TOML, JSON, etc.

Where This Came From

Stof comes out of a decade spent building parametric and geometry formats for CAD and graphics systems. That's where the core architectural insights come from: represent everything as a flat graph of nodes and data components (ECS + DAG), connected by pointers rather than nested copies, and moving or transforming part of the document stops being expensive. Everything — fields, functions, PDFs, images, binaries — become data components on a graph of nodes. Allow function components to manipulate the graph, and you have Stof.

Stof started as a solo project while trying to send wasm over the wire, and it's been running in production since — including as the policy engine underneath Limitr, where every plan, credit limit, and validation rule is a live Stof document.

Learn More

  • stof.dev — install docs, the full standard library, and examples
  • Discord — talk to the team and community
  • GitHub Issues — bugs and feature requests

License

Apache 2.0. See LICENSE for details.