Grasm v1.0.0 Get started

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 all
mathlib/grasm.toml — the ceiling, in one line a reviewer can see.

A 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:

GrasmCOMPILE ERRORinferred effects (io) exceed the declared ceiling ([])
Rustbuilds cleanstd::fs is ambient; no capability is recorded
Gobuilds cleanos.ReadFile is importable by any package
Pythoncompiles cleanopen() needs no import and no declaration

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)
}
! MathError is a failure the caller must handle. / {io} is a permission the caller must have.

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.

difftest · crosstest · wasmtest — 511 programs, 0 mismatched

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 needThen
LibrariesThere are 660, and every one was written inside this repository. Python has hundreds of thousands.
To be faster than RustYou 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 hireNobody outside this repository has written Grasm.
A native debuggerThere 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/awaitThere 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, todayCI 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.