Skip to main content
A data runtime for Rust, JavaScript, and Python

JSON that runs.

Stof is JSON with functions. Put the logic next to the data, and your app runs the document in a sandbox that can only touch what you hand it.

A model can write it. A person can read it. Your app can run it.

cargo add stofpip install stof
doc.stofedit me
{
    "values": { "a": 42, "b": 24 },

    fn sum() -> int {
        self.values.a + self.values.b
    }
}
app.ts
import { StofDoc } from '@formata/stof'; const doc = await StofDoc.new(src); await doc.call('sum');
output
…

Units are values, not strings.

"timeout": "5m" is a string someone has to parse later. In Stof the unit is part of the number, and the math converts.

units.stofPlayground ↗
cm height: 6ft + 2in
MiB memory: 2GB
s ttl: 5min + 30s

#[main]
fn main() {
  pln(self.height);
  pln(self.memory);
  pln(self.ttl);
}
Output

The config checks itself.

Declare the rule once on a type, point it at any object. No schema file, no validation library.

server.stofPlayground ↗
#[type]
Server: {
  #[schema((v: int): bool => v > 0 && v < 65536)]
  int port: 8080
}

config: { port: 70000 }

#[main]
fn main() {
  pln(<Server>.schemafy(self.config));   // false
  self.config.port = 443;
  pln(<Server>.schemafy(self.config));   // true
}
Output

It can't touch what you didn't hand it.

No file system, network, or environment unless your app turns them on. The sandbox isn't a setting, it's how a document runs. Your app gives a document functions with doc.lib(...), and that's all it can call.

sandbox.stofPlayground ↗
#[main]
fn main() {
  fs::read('/etc/passwd');
}
Output

Stof runs every customer's pricing at Limitr, in production, on every call.