VOW

A systems language for code you didn't write

VOW

An AI wrote this script. Do you run it?

In Vow, you can — because a program can only touch what you explicitly allow. No container. No microVM. The permission is part of the language.

curl -fsSL https://vowlang.dev/install | sh

Or clone and build from source — see install.

Grant Board

Seat a capability token to grant it at runtime. Empty slot means that effect is denied — no sandbox, no microVM.

agent.vow

function load_prompt(path: String) -> Result<String, Error> needs FileRead {
    return read_file(path)?
}

function post_result(url: String, body: String) -> Result<Int, Error> needs Network {
    return http_post(url, body)?
}

function main(caps: Caps) -> Int {
    with caps.file_read {
        let prompt = load_prompt("/data/prompt.txt")?
        print("read ${prompt.len} bytes")
    }
    with caps.network {
        let code = post_result("https://hooks.example/agent", "ok")?
        print("posted status ${code}")
    }
    with caps.env {
        let model = env_get("MODEL")?
        print("model=${model}")
    }
    return 0
}

Command

vow run agent.vow \
  --grant file-read:/data

Output

  • ok [FileRead] read 128 bytes
  • DENIED [Network] Network not granted — post_result needs Network
  • DENIED [Env] Env not granted — env_get needs Env

exit 1

What changes when you use Vow

Three situations you already know. The name of the idea comes last.

A script needs to read one config file. In Python, nothing stops it from also reading your SSH keys and posting them somewhere.

load_users.vow
function load_users(path: String) -> Result<List<User>, Error>
    needs FileRead
{
    let text = read_file(path)?
    return parse_json(text)
}

function main(caps: Caps) -> Int {
    with caps.file_read {
        return load_users("users.json")?
    }
}
$ vow run load_users.vow --grant file-read:users.json
# SSH keys, the network, and the rest of the disk were never granted.

This is capability-based security.

The signature is the permission list. Those permissions are values the program cannot create for itself — you pass them in at run time with --grant.

The AI's code looked right. It was wrong about one edge case, and you found out in production.

withdraw.vow
function withdraw(balance: Int, amount: Int) -> Int
    requires amount > 0
    requires amount <= balance
    ensures result >= 0
{
    return balance - amount
}

These are contracts.

You write requires / ensures. The body — often AI-written — is checked against that intent at run time. A human states the promise; the compiler holds the code to it.

Your function ran for 3 ms. The garbage collector paused it for 40.

local_only.vow
function local_only() -> Int {
    let s = "abc" + "def"
    let xs = [1, 2, 3]
    return s.len + xs.len
}

function main(caps: Caps) -> Int {
    return local_only()
}

No garbage collector, and no manual free().

Memory lives in arenas. The compiler works out when memory is no longer needed for values that never leave a function — no runtime overhead for that path. Measured cold start on this machine: 1.785 ms median cold start (methodology).

How it works in 4 steps

From source to a binary that can only touch what you seat on the grant list.

  1. Step 1

    You write a function that needs a permission

    function load_users(path: String) -> Result<List<User>, Error>
        needs FileRead {
        return read_file(path)?
    }
  2. Step 2

    The permission appears in the signature — the compiler enforces it

    // compile error if you call read_file without FileRead in scope
    needs FileRead
  3. Step 3

    You compile to a single native binary

    $ vow build agent.vow -o agent
    # → ./agent
  4. Step 4

    You grant permissions at run time; anything ungranted is impossible, not just blocked

    $ ./agent --grant file-read:/data/users.json
    # network and env were never granted — those APIs cannot run

Measured comparison

Numbers from the latest bench run — not estimates. Where Vow loses, it stays in the table. Every figure links to BENCHMARKS.mdfor methodology and machine spec.

Apple M4, 10 cores, 16 GiB · generated 2026-07-24T16:54:09Z

Who it's for

Systems and backend engineers, agent/infra builders, and anyone paying for code-execution sandboxes today.

FAQ

Questions this page tends to raise. Honest answers, including the uncomfortable ones.

How is this different from a container or a sandbox?
A container or microVM isolates a whole process from the host. Vow isolates effectful APIs inside the language: the program cannot open a file or socket unless you pass that permission at start. No extra hypervisor, image, or policy engine — the permission list is the function signature plus --grant. Containers still matter for multi-tenant OS isolation; Vow is for when the code itself should not be able to ask for more than you allowed.
What stops a program from just calling libc directly?
Nothing — yet. FFI is not supported, and that is deliberate for v1: an unrestricted foreign call would punch a hole in the permission model. Until FFI exists under a capability gate, the boundary is the Vow stdlib entry points. See Limitations.
Is this production ready?
No. Vow is pre-1.0. It is ready to evaluate the idea: try the compiler, run the Grant Board mental model, read STATUS, and measure what the benchmarks show. It is not ready to ship production services on.
Why not just use Rust / Deno permissions / WASI?
Rust gives memory safety without a garbage collector, but ambient OS access is still ambient unless you add a sandbox. Deno’s permission flags and WASI’s capability model are close cousins — credit where it is due. Vow puts permissions in the type signature of every effectful function and aims at small native binaries for short-lived jobs. Pick the tool that matches the boundary you need; this one is for “I did not write this script, and I will only grant what it needs.”

Status

Vow is early and pre-1.0. Current milestone M9(0.0.1-dev): 15 of 27 language features working. Enough to evaluate the idea — not enough to ship production services on.

See the full feature table on/status. Track work onGitHub. Readwhat does not work yet before you plan around it.