Full guide: Install docs· Platforms: darwin-arm64, darwin-amd64, linux-amd64, linux-arm64
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.
See it live
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
✓ FsReadread 128 bytes from /data/prompt.txt
✗ Netnet_get blocked — Network not granted
✗ Envenv_get blocked — Environment not granted
No container. No microVM. The same binary. Different permissions.
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.
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,};
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.