Capabilities

The capability system lets you express resource requirements in function types. A function annotated with needs Cap(IO) can only be called by code that also holds that capability — enforced at compile time with no runtime cost.


Declaring requirements

needs lists the capabilities a function requires:

needs Cap(IO)
fn write_log(msg : String) : () do
  File.append("app.log", msg ++ "\n")
end

A caller that doesn’t declare needs Cap(IO) will get a compile error if it tries to call write_log. The requirement propagates upward through the call graph automatically.


Proof capabilities

proof cap declares a capability that can only be created inside one module — useful for authority tokens:

mod Admin do
  proof cap AdminCap

  fn make_cap() : Cap(AdminCap) do
    cap AdminCap
  end

  needs Cap(AdminCap)
  fn delete_all_users() : Result((), String) do
    Db.execute("DELETE FROM users")
  end
end

Outside Admin, no code can manufacture a Cap(AdminCap). It can only pass one through that it received from Admin.make_cap(). The capability becomes an unforgeable proof of authorization.


Realtime exclusion

Tagged(X, Realtime) marks a computation as realtime-safe. Calling Cap(Alloc), Cap(IO), or Cap(Panic) inside a realtime-tagged function is a compile error:

fn process_sample(buf : Tagged(Buffer, Realtime)) : Tagged(Buffer, Realtime) do
  -- allocating here would be a compile error
  transform(buf)
end

This statically prevents audio/video processing code from accidentally allocating or blocking.


Complete example: sandboxed plugin runner

mod Plugin do
  proof cap PluginCap

  fn grant() : Cap(PluginCap) do cap PluginCap end

  needs Cap(PluginCap)
  fn run(code : String) : Result(String, String) do
    sandbox_eval(code)
  end
end

mod Main do
  fn main() do
    let _cap = Plugin.grant()
    -- _cap satisfies the `needs Cap(PluginCap)` requirement for run() below;
    -- caps are ambient — not passed as arguments
    match Plugin.run("1 + 1") do
      Ok(v)  -> println("result: " ++ v)
      Err(e) -> println("error: " ++ e)
    end
  end
end

The PluginCap ensures that only code explicitly granted the capability can invoke the sandbox runner. Untrusted code paths can never call Plugin.run — not because of a runtime check, but because the type won’t compile without the cap.

march — interactive
Click run on any snippet to try it here.
march>