AI writes the code. You control what it touches.

Vow programs can only reach the files, network, and environment you hand them. The boundary is in the language — not wrapped around it.

curl -fsSL https://install.vowlang.dev | sh
export PATH="$HOME/.local/bin:$PATH"
  1. Run both lines (install + PATH)
  2. Verify: vow version && vpm version

AI agents generate code that runs on your servers — with no built-in guardrails for files, APIs, or secrets. Containers and microVMs are the workaround.Vow puts the boundary in the language.

Toggle a permission. Watch what changes.

File access

read from /data/prompt.txt

Network

blocked · fetch_host cannot run when off

Environment

blocked · read_model cannot run when off

$ vow run agent.vow --grant fs-read:/data
  • FsRead read 128 bytes from /data/prompt.txt
  • Net net_get blocked — Network not granted
  • Env env_get blocked — Environment not granted

No container. No microVM. The same binary. Different permissions.

New to match, enum patterns like Circle(r), or Some/None? Read how match works ↓

Understanding match

Vow uses match everywhere you need to branch on a value — enums, options, and results. One arm runs; the compiler requires every case.

The syntax

match takes a value, compares it against patternson the left of each =>, and evaluates the expression on the right of the matching arm. The whole match expression returns that value — like an expression-orientedswitch in other languages.

match <value> {
    <pattern> => <expression>,
    <pattern> => <expression>
}

You can also write switch s { case Circle(r): … } — it desugars tomatch with the same exhaustiveness rules.

Matching enums — Circle(r)

Enums are tagged unions. You construct with the enum name (Shape::Circle(2)) but match with bare variant names. Parentheses bind the payload into variables:

shapes.vow
enum Shape {
    Circle(int),
    Rect(int, int)
}

fn area(s: Shape) -> int {
    return match s {
        Circle(r) => 3 * r * r,
        Rect(w, h) => w * h
    }
}
  • Circle(r) — if s is a circle, bind its radius to r and run3 * r * r
  • Rect(w, h) — if s is a rectangle, bind width and height
  • Exhaustive — omit Rect andvow check fails. Every variant must have an arm.

Tutorial:Structs, enums, and match

Matching options — Some / None

Option<T> is a built-in enum with variants Some(value) andNone. Same pattern: bind the inner value in the pattern, handle absence in the other arm.

main(caps: Caps) fields like caps.net and caps.fs_read areOption<Cap>Some when you passed --grant, Nonewhen you did not:

http_api.vow — main
return match caps.net {
    Some(cap) => serve.listen(a, cap, 8787),
    None => 0 - 1,
};

Without --grant net:, caps.net is None and the program returns0 - 1 instead of calling serve.listen.

Matching results — Ok / Err (not try/catch)

Result<T, E> is another enum: Ok(value) on success, Err(code)on failure. I/O builtins return Result; you match or use try/catchsugar — not stack-unwinding exceptions.

Some(cap) => … answers “was the grant passed?” Ok(s) => … answers “did the read succeed?” They look similar because both use match, but they mean different things.

Option — grant present?

return match caps.fs_read {
    Some(c) => with c { read_config("/data/users.json") },
    None => 0 - 1,
};

Result — did I/O succeed?

return match read_file(path) {
    Ok(s) => len(s),
    Err(_) => 0 - 1,
};

Full tutorial:Option & Result·Control flow·Capabilities

Three pillars

The AI's function reads one config file. Nothing in Python stops it from also reading your SSH keys and posting them to a server.

Capability-based permissions

Every effectful function declares what it needs in its signature. If you didn't grant it, the program cannot call it. Period.

read_config.vow
fn read_config(path: String) -> int needs FsRead {
    return match read_file(path) {
        Ok(s) => len(s),
        Err(_e) => 0 - 1
    }
}

fn main(caps: Caps) -> int {
    return match caps.fs_read {
        Some(c) => with c { read_config("/data/config.json") },
        None => 0 - 1
    }
}
$ vow run read_config.vow --grant fs-read:/data
# SSH keys, network, and the rest of the disk were never granted.

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

Contracts — human intent, compiler enforcement

You write what must be true. The compiler holds AI-written bodies to that promise at runtime — and eventually at compile time.

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

fn main(caps: Caps) -> int {
    return withdraw(100, 30)
}

Your function runs in 3ms. The garbage collector pauses it for 40ms. At scale, that pause is your p99.

No GC. No manual free().

Memory lives in arenas. The compiler decides when it ends. Measured cold start: 1.682ms median. Binary size: 16,840 bytes. C-level.

process.vow
fn work() -> int {
    let keep = "outer"
    region {
        let scratch = str_join(["a", "b", "c"], "-")
        print "scratch=${scratch}"
    }
    print keep
    return 7
}

fn main(caps: Caps) -> int {
    return work()
}

These aren't separate features. They compose. A function can be permission-scoped, contract-checked, and GC-free at the same time.

Faster cold start than Go. Smaller binary than Go. C-level execution.

1.682ms

cold start

vs Go: 2.287ms

16,840

binary size

vs Go: 1.60MB

3.390ms

fib(30)

vs Go: 4.518ms

Where Vow loses, it stays in the table. Compile time is 3.7× C — we are working on it.

Measured benchmarks
MetricVowCGoPythonNode
Cold start (median)1.6821.7462.28713.1417.65
Binary size (stripped)16,840 B16,832 B1,673,824 Bnot measured yetnot measured yet
Compile time0.1300.0350.078not measured yetnot measured yet
fib(30) wall (median)3.3903.5314.518123.121.57
loop_sum wall (median)1.6131.6424.750208.524.09
hello wall (median)2.0641.9822.73013.6419.40
Peak RSS (startup)1,328 KB1,344 KB3,264 KB8,256 KB39,344 KB

Apple M4 · generated Aug 2, 2026 · Full methodology →

Is Vow right for your project?

  • AI agent tools that need constrained I/O
  • Backend services where permissions should be visible in code
  • CLI tools and scripts — 16KB binary, 1.68ms start
  • Platforms running user-submitted or generated code
  • Serverless and edge — cold start and size are on the invoice
  • → High-concurrency services

    Async ships but I/O is still blocking in v1

  • → Windows

    Use WSL2: irm install.vowlang.dev/install.ps1 | iex. Native Windows binary is on the roadmap.

  • → FFI to C libraries

    Deliberately absent — an unrestricted FFI call would break the permission model. Gated FFI is planned.

  • → Large existing codebases

    No gradual adoption path yet. Vow is greenfield for now.

Honest about limits. Read the full limitations page before you commit.

From zero to running in 60 seconds.

  1. Step 1 — Install

    curl -fsSL https://install.vowlang.dev | sh
    export PATH="$HOME/.local/bin:$PATH"
    1. Run both lines (install + PATH)
    2. Verify: vow version && vpm version
  2. Step 2 — Create a project

    vow new hello
    cd hello
    # or: vow create my-api --web
  3. Step 3 — Run it

    vow run src/main.vow
    # Hello, Vow.

Read the full getting-started guide →

JS-style async for DB and I/O — without fake promises.

Write async fn helpers with await db_query and ?. Sync HTTP handlers call them with block_on — same ergonomics as Express wrapping async route logic.

Async / await guide →· vow-postgres + async

hello-app/src/main.vow
async fn todo_count() -> int {
    let rows = await db_query(g_handle, "SELECT id FROM todos")?;
    let doc = json.parse(rows)?;
    return json_len(doc)?;
}

fn api_health(_req: request.Request) -> response.Response {
    return vws.json(json.stringify({
        ok: true,
        todos: block_on(todo_count),
    }));
}

FAQ

What exactly is Vow?
Vow is a compiled systems language where every function that touches files, network, or the environment must declare it in its signature — and you control which permissions run at launch. Think of it as writing code where the blast radius is visible before you deploy.
How is this different from a container or a sandbox?
Containers isolate a whole process; Vow confines which I/O builtins your program can call at runtime. Full answer: capabilities.
What stops a program from just calling libc directly?
FFI is not supported in v1 — an unrestricted foreign call would break the permission model. Full answer: limitations.
Is this production ready?
vow 0.1.0 is the first native release — suitable for evaluation and early adopters who pin versions. Full answer: limitations.
How do I add packages?
Use vpm: `vpm init`, `vpm add vow-web-server`, `vpm install`. Default registry is vpm.vowlang.dev. Full answer: vpm docs.
Can I build an HTTP API?
Yes — vow-web-server v0.3 is Express-like (`server()`, `use_middleware`, `router()`) with loopback bind by default. Full answer: vow-web-server docs.
Why not just use Rust / Deno permissions / WASI?
Those tools sandbox at the process or runtime level; Vow puts permissions in every effectful function signature. Full answer: core concepts.