Standard library

Built-ins, no imports required

Every built-in type, function and capability available in any Capa program. For language syntax and semantics, see the language reference.

Primitive types

TypeSize / RangeNotes
Int64-bit signedArithmetic does not check for overflow
Float64-bit IEEE 754
StringUTF-8Immutable
Bool / Chartrue/false · code pointChar is a str of length 1 at runtime
Unit()For functions with no return value

String methods

length() / is_empty()Code-point count · emptiness
to_upper() / to_lower() / trim()Case and whitespace
contains / starts_with / ends_withBool
split(sep)List<String>
replace(old, new) / substring(start, end)String (substring clamps out-of-range)
char_at(i) / index_of(needle)Option<String> · Option<Int>
bytes()List<Int>, the UTF-8 bytes

List<T>

Mutable homogeneous list. Construct with [a, b, c] or by push. Index with xs[i] (unchecked) or get(i) (safe).

length() / is_empty() / contains(x)Int · Bool · Bool
push(x)Append (mutation)
first() / last() / get(i)Option<T>
map(f) / filter(p) / fold(init, f)Transform · keep · reduce

Ranges: a..b (exclusive) and a..=b (inclusive) produce a List<Int> and support the full List API. Float endpoints are excluded; a..b..c is a syntax error.

for i in 0..10            # 0, 1, ..., 9
    stdio.println("${i}")

let evens = (0..10).filter(fun (x: Int) -> Bool => x % 2 == 0)

Map<K, V>

Hash map. Construct via new_map() with a required type annotation.

length() / is_empty() / contains_key(k)Int · Bool · Bool
get(k)Option<V>
set(k, v)Insert / update (mutation)
keys() / values() / pairs()List<K> · List<V> · List<(K, V)>

Set<T>

Set of unique elements. Construct via new_set() with a type annotation.

length() / is_empty() / contains(x)Int · Bool · Bool
add(x) / remove(x)No-op if duplicate / absent
to_list()List<T>

Option<T>

Built-in sum type Some(T) | None.

is_some() / is_none()Bool
unwrap_or(default)T
map(f) / and_then(f) / filter(p) / or_else(f)Option<…>
ok_or(err)Result<T, E>

Result<T, E>

Built-in sum type Ok(T) | Err(E). The ? operator propagates Err in functions that return Result.

is_ok() / is_err() / unwrap_or(d)Bool · Bool · T
map(f) / and_then(f) / map_err(f) / or_else(f)Result<…>
ok() / err()Option<T> · Option<E>

JsonValue

Built-in sum type: JNull | JBool(Bool) | JNum(Float) | JStr(String) | JArr(List<JsonValue>) | JObj(Map<String, JsonValue>).

is_null()Bool
as_bool() / as_num() / as_string()Option<Bool> · Option<Float> · Option<String>
as_array() / as_object()Option<List<…>> · Option<Map<…>>
parse_json(s) / to_json(j)Result<JsonValue, String> · String

Conversions & panic

Capa has no implicit numeric coercion: Float + Int is a type error. Convert explicitly at the call site.

parse_int(s) / parse_float(s)Option<Int> · Option<Float>
to_float(i) / to_int(f)Float · Int (truncates toward zero)
new_map() / new_set()Require a let annotation to pin the types

panic(message) terminates the program immediately: no unwinding, no catch. panic: <message> is written to stderr and the process exits non-zero. The contract is identical on every backend (Wasm and Component Model trap, translated to exit 1).

Python interoperability

Both functions cross the Capa/Python trust boundary and require Unsafe as the first argument. Crossing loses Capa's static guarantees: the Python value can do anything its type allows, with full ambient authority. --manifest marks such functions has_unsafe: true.

fun square_root(unsafe: Unsafe, x: Float) -> Float
    let math = py_import(unsafe, "math")
    return py_invoke(unsafe, math.sqrt, [x])

Capabilities

Stdio

print(s), println(s), eprintln(s) (stderr), read_line() -> Result<String, IoError>.

Fs

read(p) / write(p, c)Result<String, IoError> · Result<(), IoError>
exists(p) / is_dir(p)Bool (false on denied paths)
mkdir(p) / list_dir(p)Result<(), …> · Result<List<String>, …>
restrict_to(prefix) / allows(path)Attenuated Fs (monotonic) · query without I/O

Env · Clock · Random

env.get(name) / env.args()Option<String> · List<String>
env.restrict_to_keys(keys)Attenuated Env
clock.now_secs() / now_monotonic() / sleep(s)Float · Float · ()
clock.restrict_to_after(t)Active only after timestamp (takes the maximum threshold)
random.int_range(low, high) / float_unit()Int in [low,high) · Float in [0,1)
random.with_seed(seed)Deterministic sequence

Net

A Net from main is unrestricted; restrict_to(host) returns a fresh Net whose authority is the intersection with {host} (monotonic). get(url) returns Err immediately if the host is outside the restriction set, before any system call.

fun main(net: Net, stdio: Stdio)
    let api = net.restrict_to("api.example.com")
    match fetch(api)
        Ok(body) -> stdio.println(body)
        Err(e)   -> stdio.eprintln("${e}")

Db

SQLite-backed database with path-prefix attenuation (mirrors Fs). query returns the rows as a JSON-encoded array of arrays of strings, so the wire shape is a single form; callers parse with parse_json and project columns explicitly.

exec(path, sql)Result<(), IoError>, runs DDL / DML (multiple ;-separated statements supported)
query(path, sql)Result<String, IoError>, JSON-encoded rows
restrict_to(prefix) / allows(path)Attenuated Db (monotonic path prefix) · query without I/O

Proc

Sandboxed subprocess execution with basename-prefix attenuation. exec takes the command and a JSON-encoded argv tail (for example ["status", "--short"]); stdout is returned as a String. allows checks the basename on a suffix boundary (restrict_to("git") admits git and git-lfs but not gitlab).

exec(cmd, args_json)Result<String, IoError>, the process stdout
restrict_to(cmd_prefix) / allows(cmd)Attenuated Proc (basename prefix) · query without spawning

Serve

Authority to listen on a TCP address and do I/O on inbound connections. Every other capability reaches out; Serve is the one that is reached. It is deliberately connection-level, not HTTP-level: the trusted runtime binds, accepts and moves bytes, so protocol parsing is ordinary Capa code in a library (see capa_server) rather than trusted-runtime code.

Python backend only. Serve is not available on the Wasm backends, and this is a permanent stance rather than a backlog item: binding a listening socket needs wasi:sockets, which is neither vendored in the compiler nor reachable from the wasmtime bindings the Wasm hosts are built on. Both capa --wasm and capa --wit refuse a program whose signatures reach Serve, before any lowering, and each lists every offending site. Use capa --run / capa --check for those functions. This is the same treatment Unsafe gets.
restrict_to(spec) / allows(addr, port)Attenuated, un-bound Serve over (bind address, port) · query without I/O
listen(addr, port) / local_port()Result<(), IoError> (denied before any socket exists) · Result<Int, IoError>, the port actually bound
accept()Result<Int, IoError>, a connection id (ids start at 1)
recv(conn, max_bytes) / send(conn, bytes)Result<List<Int>, IoError>, an empty list is EOF · Result<(), IoError>, each byte masked with & 0xFF
close(conn) / stop()Close one connection · close the listener and any open connection

A spec is "addr:port", "addr:lo-hi" or "addr:*", with "*" also accepted as the address. Rules accumulate: a bind must satisfy every rule taken so far, so restrict_to only ever narrows and restrict_to("*:*") on an already-narrowed capability restores nothing. A spec that does not parse denies everything, because silently ignoring a typo would widen authority. Enforcement runs before the syscall, so a denied address or port is never bound, not even transiently.

The model is sequential: one open connection at a time, no threads and no async. A second accept while a connection is open returns Err telling the caller to close first. accept, recv and send are bounded at 30 seconds and return Err on expiry rather than hanging. Honest limits: IPv4 only, addresses matched by exact string equality ("127.0.0.1" and "localhost" are different rules), and the check runs on the port you request, so a rule admitting port 0 admits an arbitrary ephemeral port. On an Err from send the number of bytes already transmitted is unspecified: close the connection rather than retry.

recv is the language's one inbound information-flow source and its bytes are @public; send is a public sink on its payload argument only. The lattice models confidentiality, not integrity or taint: @public on an attacker-controlled request asserts only that it is not a secret whose disclosure the analysis must prevent, and asserts nothing about it being trustworthy or safe to act on. See the information-flow section.

fun echo_once(serve: Serve, conn: Int) -> Result<Unit, IoError>
    let request = serve.recv(conn, 1024)?
    return serve.send(conn, request)

fun main(stdio: Stdio, serve: Serve)
    let local = serve.restrict_to("127.0.0.1:8080")
    match local.listen("127.0.0.1", 8080)
        Ok(_) -> match local.accept()
            Ok(conn) ->
                match echo_once(local, conn)
                    Ok(_) -> stdio.println("echoed")
                    Err(e) -> stdio.eprintln("${e}")
                let _ = local.close(conn)
                let _ = local.stop()
            Err(e) -> stdio.eprintln("${e}")
        Err(e) -> stdio.eprintln("${e}")

User-defined capabilities

Libraries declare their own capabilities with the capability keyword; any type that impls it becomes a valid implementor. A cap-bearing struct may hold built-in caps as fields. Only call / method-call right-hand sides produce fresh capability instances that can be bound; a plain alias let dup = mailer is rejected.

capability SendEmail
    fun send(self, to: String, subject: String, body: String) -> Result<Unit, IoError>

impl SendEmail for SmtpMailer
    fun send(self, ...) -> Result<Unit, IoError>
        return Ok(())

The IoError type

Opaque type for I/O errors, available as the error parameter in Result<T, IoError> and in pattern matching. Its string representation is human-readable; the internal contents are private.

match fs.read("x.txt")
    Ok(content) -> stdio.println(content)
    Err(e) -> stdio.eprintln("error: ${e}")