[ANN] Kotoba: a Clojure-shaped language that compiles to WebAssembly, with capability-based safety (feedback wanted)

What Kotoba is

Kotoba is a small Clojure-shaped language profile for compiling capability-checked programs to WebAssembly. It’s aimed at code that should be inspectable, portable, and constrained: AI-generated cells, sandboxed automation, repository policy, and other untrusted programs where the host decides which capabilities are available.

Three things up front, since ‘Clojure-shaped’ invites the obvious pushback:

  1. It’s a Clojure-family profile/subset, not a full Clojure or ClojureScript implementation. Canonical source extension is .kotoba; .clj, .cljc, and .cljs are accepted as compatibility formats. It is not “any JVM Clojure or ClojureScript program runs.”

  2. Compilation target is WebAssembly, compile-first. The public surface is kotoba -e / kotoba wasm ... – not an interpreter with a Wasm backend bolted on.

  3. Capability-confined safety is the point, not an add-on. Capabilities are explicit, scoped, typed values (never ambient strings a module can summon by name); a host-side guard intersects what a module was actually handed against policy before any call runs.

30-second tour

kotoba -e '(+ 1 2)'
kotoba wasm build examples/hello.kotoba -o hello.wasm
kotoba wasm safe-policy examples/policy-demo.kotoba
kotoba wasm safe-build examples/policy-demo.kotoba --policy policy.edn -o policy-demo.wasm

A few design notes

The compiler, CLI, and conformance tooling that process this Clojure-shaped language are themselves written in Clojure/ClojureScript – an earlier Rust implementation was fully retired in favor of this CLJC authority.

Safety is benchmarked against Rust, not copied from it: capability confinement (deny-by-default, typed capabilities, signed audit receipts) is designed to exceed Rust-style memory safety alone, not just match it. Rather than a full ownership/borrow/lifetime system, we shipped a narrow affine check scoped only to capability values – a handle can be consumed at most once per execution path – with a documented limitation instead of a broader, harder-to-verify mechanism.

The rest of the stack

Alongside the language, kotoba-lang/kotobase plays the role Datomic plays for Clojure – same datom/EAVT/Datalog model, but reimagined for the distributed web: content-addressed blake3/CIDv1 blocks instead of a single transactor over SQL/DynamoDB, a Prolly Tree instead of a B-tree, an immutable commit DAG instead of a single log, and a CACAO-authenticated edge runtime instead of a JVM peer with a direct storage connection.

Where to look

Question for this community

We shipped a narrow affine-typing check scoped only to capability values instead of a general ownership/borrow system, on the reasoning that it’s what the actual threat model needs and it’s small enough to verify exhaustively. It has a documented gap (tracked per binding name, not per value) that doesn’t weaken runtime enforcement but is a real limitation. Where do you draw the line between “ship the narrow useful slice, gaps documented” and “go build the complete thing first”?"

1 Like

This text reads like it was written by Claude.