Skip to content

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).

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:/data

Output

  • 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

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:

Terminal window
vow run app.vow -- --grant fs-read:/tmp

Without 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.

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 (Some and None here).
  • Some(cap) — network was granted; pass cap into APIs that needs Net.
  • None — no network grant; 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 and 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.

Narrow further in code with .scoped_to(…) on any cap value (FS limit_to remains an alias). Load grants from a file:

Terminal window
vow run app.vow --manifest vow.grants.toml -- --audit-log

Optional --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.

Pass only the grants a tool needs. Revoke by not passing the capability — no container restart required to shrink the blast radius.