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.

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

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

fn main(caps: Caps) -> int {
let code = match caps.fs_read {
Some(cap) => match read_file(cap, "/tmp/vow_poc.txt") {
Ok(s) => len(s),
Err(e) => 0 - e
},
None => 0 - 100
};
return code;
}

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.

Narrow a capability at the syscall boundary:

// narrower FsRead; enforced at the syscall boundary
let narrow = cap.limit_to("/tmp/safe");

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