Start
Seven things nothing else gives you.
Seven capabilities you cannot get from the four languages a working programmer would otherwise reach for — each one shipped, and each one with the gate that proves it holds. Every idea here has a lineage, and the About page names it; what follows is what only Grasm puts in your hands.
1. A dependency cannot reach further than its manifest
This is the reason the language exists, and no production toolchain a working programmer would reach for enforces it. A package writes down the effects it may perform. The compiler infers what the code actually does, through every call and into every dependency, and refuses to build when the two disagree — in the package, and in every consumer of it.
[package]
name = "mathlib"
capabilities = [] # reaches nothing at allA trusted pure library ships a patch release that starts reading
/etc/passwd. Same name, same signature, same return value.
scripts/security_bench.sh runs that tamper against four toolchains
on every green build:
Three of them ship it. Everywhere else the answer is a runtime sandbox or a human reading the diff; here it is a type error, before anything runs.
The test of whether that is a real design or only a mechanism is how much of
the library set can live under it. Of 660 libraries,
563 declare no capability at all — they
compute and nothing else — and only 97 ask for any.
A library that joins paths, formats a date, parses JSON, hashes a password or
validates a schema acquires no reach by doing so. Of the ones that do ask,
88 ask for io and 17 for net
— some for both — and 2 for
unsafe: one
library in 660 may call into C at all, and which one is a line in
a manifest rather than a thing you have to go and find out.
How it works →
2. The effect row is inferred, so a signature cannot lie
You do not write the row after /. The compiler works it out
from the body and checks it against whatever was declared, so a function that
starts touching the network stops compiling — and so does every caller that
was not given the same permission. A declaration you have to maintain by hand
is a declaration that drifts; this one cannot.
fn safe_div(a: i64, b: i64) -> i64 ! MathError {
if b == 0 {
fail(DivByZero)
}
a / b
}
fn load(path: str) -> str ! IoError / {io} {
try read_file(path)
}3. Four engines, and one of them is the referee
A language with one implementation and a test suite can only ask "did the output change". Grasm has four engines — interpreter, bundle, ahead-of-time native, and wasm — and the interpreter is normative: the specification is written against it and the other three have to match it byte for byte on stdout, exit code and diagnostics.
That turns a test suite into an oracle. All 520 standalone
programs in the corpus run through the interpreter, the bundle and the native
build and are compared on every build; the last recorded run of that gate
covered 511 of them. 415 also run in the browser engine
— 96 are skipped, because they want a filesystem, a socket, a
process or threads, and both figures are from a run on 15 October 2026 — and
all 511 are compiled for x86_64, linked
with cc -arch x86_64 and run under Rosetta, which
reaches code the arm64 build never does: readdir$INODE64 exists
only there, and x86_64 passes variadic arguments in registers where arm64 puts
them on the stack. A defect does not have to be predicted to be caught — it
only has to make two engines disagree.
4. A run recorded in production replays in a browser
GRASM_RECORD writes a journal of everything a run read from
outside itself. GRASM_REPLAY hands it back, and the replaying run
reaches outside itself for nothing — not the reads, and not
the writes. All 28 journalled builtins, on all four engines:
the whole io, net and proc surface bar
print, which is repeated on purpose.
The part that is unusual: a journal recorded by the native build
replays byte-identically inside a wasm module that has no sockets, no
processes and no filesystem — TCP session and child process included.
scripts/replaytest.sh additionally runs each of those builtins
under an empty journal and requires it to refuse, so a builtin that quietly
stopped being recorded fails the build.
5. Ownership in tiers, and no lifetime annotations anywhere
Four ways to own a value, chosen per value rather than per language. Value
semantics by default; region when a scope should own its
allocations; ref — a generational reference — when a graph needs
one; unsafe where the guarantees stop, named in the source so a
grep finds every place they do.
No 'a, no borrow checker to argue with, and no garbage
collector either. The four tiers →
6. The specification is normative, and every clause has a program
254 normative clauses in 3,561 lines, under a hard ceiling —
every addition since has been paid for by compressing prose already written —
and 258 programs in spec/conformance, so a clause is
not a sentence about intent but a file that fails. 167 clauses are
stable and will not change incompatibly before 2.0;
26 are provisional and 65 are
experimental, and a clause moves between those levels one at a
time, on its own evidence.
7. A hello-world is 85.7 KB, on a stable toolchain
It was 351.1 KB, of which about 135 KB was Rust's own backtrace
symbolizer, reached from the default panic hook. All four crates a compiled
program links are now no_std; nm shows zero
symbolizer symbols; no nightly compiler is involved.
The nine defects that came out of that conversion →
What this does not buy you
The same list is on the About and Status pages, because it belongs next to every claim above and not only at the end of the site.
| If you need | Then |
|---|---|
| Libraries | There are 660, and every one was written inside this repository. Python has hundreds of thousands. |
| To be faster than Rust | You will not be. The geomean is 0.965× of Rust on the benchmark suite, and no benchmark here claims a win over it. On the systems suite Grasm loses etl by 3.6×, is at throughput parity on proxy and last on its tail, and wins memory everywhere — 8× on chat, which is 8.4 MB against Rust's 69.4. Every row → |
| To hire | Nobody outside this repository has written Grasm. |
| A native debugger | There is a DAP server and it works against the interpreter. The Cranelift backend emits no DWARF, so GDB and LLDB cannot meaningfully inspect a compiled binary. |
| async/await | There are threads, channels, structured concurrency and non-blocking I/O on one thread. There is no async/await, and tasks still refuse every effect. |
| Linux, today | CI compiles it. This machine type-checked it for the first time on 15 October 2026, which is the first time the 2,100 lines of cfg(not(macos)) went through a compiler here: cargo check --release --target x86_64-unknown-linux-gnu --workspace passes for the whole workspace, every crate. In a debug profile one crate does not build — the runtime's no_std staticlib, which rustc refuses to compile under unwinding panics on any platform, and release is the only profile it is ever built in. Nothing has been linked or run on Linux, and a type-check is not a working binary. |
The one-line version. Everywhere else, the boundary between your code and a dependency you did not read is enforced at run time, by a sandbox, or not at all. Here it is enforced by the type checker, and there are four engines that agree on what that means.