Capabilities
I/O is not ambient. Effectful APIs take a permission the program cannot create for itself. Values of types like FsRead and Net cannot be constructed in user code — they arrive only via main(caps: Caps) after the runtime parses --grant flags (or via needs / with sugar once a grant is in scope).
Try it
Section titled “Try it”Seat and eject tokens. The command line and output update immediately (static simulation — nothing is executed on the server).
Grant Board
Seat a capability token to grant it at runtime. Empty slot means that effect is denied — no sandbox, no microVM.
agent.vow
fn load_prompt(path: String) -> int needs FsRead {
return match read_file(path) {
Ok(s) => len(s),
Err(_e) => 0 - 1
}
}
fn fetch_host(host: String) -> int needs Net {
return match net_get(host) {
Ok(c) => c,
Err(_e) => 0 - 1
}
}
fn read_model() -> int needs Env {
return match env_get("MODEL") {
Ok(s) => len(s),
Err(_e) => 0 - 1
}
}
fn main(caps: Caps) -> int {
let n = match caps.fs_read {
Some(c) => with c { load_prompt("/data/prompt.txt") },
None => 0 - 1
}
let h = match caps.net {
Some(c) => with c { fetch_host("api.openai.com") },
None => 0 - 1
}
let m = match caps.env {
Some(c) => with c { read_model() },
None => 0 - 1
}
print "fs=${n} net=${h} env=${m}"
return 0
}Command
$ vow run agent.vow --grant fs-read:/dataOutput
- ok [FsRead] read 128 bytes from /data/prompt.txt
- DENIED [Net] net_get blocked — Network not granted
- DENIED [Env] env_get blocked — Environment not granted
exit 1
Working pattern today
Section titled “Working pattern today”fn read_poc(path: String) -> int needs FsRead { return match read_file(path) { Ok(s) => len(s), Err(_) => 0 - 1 };}
fn main(caps: Caps) -> int { return match caps.fs_read { Some(c) => with c { read_poc("/tmp/vow_poc.txt") }, None => 0 - 100 };}Run with a grant:
vow run app.vow -- --grant fs-read:/tmpWithout the grant, caps.fs_read is None. A function that never receives a capability cannot call read_file. That is the compile-time guarantee: no cap in the signature (or reachable locals) means no I/O.
match caps.net — not try/catch
Section titled “match caps.net — not try/catch”HTTP servers follow the same Option pattern:
return match caps.net { Some(cap) => serve.listen(a, cap, 8787), None => 0 - 1,}match— pick exactly one arm; the compiler requires every case (SomeandNonehere).Some(cap)— network was granted; passcapinto APIs thatneeds Net.None— no--grant net:…; return an error exit code instead of listening.
This handles absence of a grant, not runtime errors inside serve.listen. File/network failures use Result with Ok / Err. Full walkthrough: Reading the code on the landing page and Option & Result.
FileRead is accepted as an alias for FsRead in some surfaces; prefer FsRead / fs-read: as in the examples.
Also granted today: --grant threads, --grant proc: (or proc:/path), --grant clock:, --grant rand:, --grant db:…, --grant log:sink, and optional net::PORT / net:*:PORT.
Attenuation and manifests
Section titled “Attenuation and manifests”Narrow further in code with .scoped_to(…) on any cap value (FS limit_to remains an alias). Load grants from a file:
vow run app.vow --manifest vow.grants.toml -- --audit-logOptional --audit-log records enforcement at I/O choke points. Bound CPU with --budget cpu-ms:N and measure with vow profile <file> [--samples N]. vow inspect <binary> [--json] reads build metadata when present.
Narrow grants at the process boundary (path prefix, host allowlist, env key, proc path). Over-broad grants (fs-read:/, empty net:, bare proc:) remain powerful — that is operator responsibility, not a compiler bug.
Why this matters for AI-generated code
Section titled “Why this matters for AI-generated code”Pass only the grants a tool needs. Revoke by not passing the capability — no container restart required to shrink the blast radius.
- Contracts —
requires/ensures - Limitations — security caveats