Skip to content

Burroughs

Go → Wasm → Burroughs

A WebAssembly runtime written in pure Go, designed around one guest language: Go. Correctness-neutral, performance-partisan. The B5000 favored ALGOL. Burroughs favors Go.

Read the docs Read the blog GitHub

In v0.12.0, a fork-built Go program runs through burroughs run with GOMAXPROCS > 1, several agents executing guest code at once on OS threads, and garbage collection across them. How that is witnessed, and what the witness does not show.

What it is

  • Pure Go. No cgo, and no dependency outside the Go standard library. The module is github.com/scttfrdmn/burroughs, and Burroughs is a library first: its CLI's burroughs run goes through the same public API and nothing else.
  • Go-partisan, correctness-neutral. Burroughs' host contract (threads, preemption, the memory model at host boundaries, stacks, the WASI 0.3 event loop) is written around what the Go runtime needs from a host. Any guest that conforms to the spec should run correctly, and the upstream WebAssembly spec test suite is how that is checked. Where a design tradeoff exists, the choice that favours Go wins, in API surface and optimization priorities, never in correctness.
  • An interpreter. The pipeline is decoder, internal form, validator, interpreter. There is no compiler, and none is on the roadmap.
  • A research engine first. Production hardening against hostile modules is a stated non-goal of the current contract (v0.1).

The name comes from the Burroughs Large Systems (the B5000, 1961), stack machines designed so that one high-level language would run best on them. Wasm is a stack machine too.

What works today, in plain terms

As of v0.12.0 (the release quoted below, when this page was written):

  • Core WebAssembly (the MVP and Wasm 2.0 features, plus SIMD, relaxed SIMD, and four Wasm 3.0 proposals: extended constant expressions, tail calls, multi-memory and memory64) runs, from Go (burroughs.Instantiate) or from the command line (burroughs run). The spec suite's core board reports no failures.
  • Go programs built with GOOS=wasip1 GOARCH=wasm run through WASIP1Config.Run or burroughs run, with arguments, an environment, the standard streams, and read-only or writable directory grants.
  • A WASI preview-1 guest can start its own threads. A module that imports "burroughs" "spawn" and exports mstart starts agents on OS threads over its shared memory, through those same two paths. That is the door the Phase 4 Go fork uses, so a fork-built Go program runs with real threads. Core Instantiate supplies no imports, so it still starts no second thread, and host-started threads are not built.
  • Components load, and their typed exports can be called from Go with u32, string, list and record values. The async component ABI, cancellation through context, and wasi:cli/run worlds work.
  • Not yet: stack switching, a full WASI 0.3 host, and two proposals that stay gated off (GC and exception handling). Linux and macOS (64-bit) are built and tested; Windows runs the tests in a separate CI workflow as a measurement, not a gate, and is unsupported.

The known limits page has the details, with issue numbers.

The current release: v0.12.0 (2026-10-28)

Implements contract v0.1.

What changes by default: four Wasm 3.0 proposals, one stamped flip each. Extended constant expressions, tail calls, multi-memory and memory64 are on by default (ADRs 0107–0110, Added below). Each flip moved the core board by exactly its own lane's pre-registered reading, with 0 new failures, identically on darwin/arm64 and janus x86-64. Each keeps an env-var rollback: BURROUGHS_EXTENDED_CONST=0, BURROUGHS_TAIL_CALL=0, BURROUGHS_MULTI_MEMORY=0 and BURROUGHS_MEMORY64=0. Exception handling and GC stay off, with their open work (#95; #172, #262), and return_call_ref is GC's.

Guest-started threads, through the public paths. A WASI preview-1 module that imports "burroughs" "spawn" and exports mstart starts agents on OS threads over its shared memory, through WASIP1Config.Run and burroughs run (#1122).

  • The contract names the door: an append to §2 T-1.
  • The end-to-end witness is a committed fork-built Go program. Its output equals stock Go's, with a digest recomputed natively, and 4 agents are sampled executing at once against a floor of 3.
  • v0.11.0's limit "no public path starts a second thread" is lifted for the WASIP1 path. Core Instantiate still supplies no imports (host-started threads are not built), and a component's thread.spawn* is still refused.

Public-path defects fixed:

  • burroughs run <component> is routed whatever flags are given (#1128).
  • A mistyped Component.Call argument is the caller's error, not ErrUnsupported (#1129).
  • A shared memory used as a Canonical ABI memory option is refused by name (#1141).

A hardware weak-memory reading (scripts/hwlitmus.sh, #1126). MP, SB, LB and IRIW run at 2²⁰ trials per cell on darwin/arm64, janus x86-64 and Graviton3, over the engine and a neuter build without ADR 0054's atomics, with a native control and wasmtime as comparisons.

  • The engine shows 0 SC-forbidden outcomes everywhere.
  • Only Apple arm64 discriminates: there the neuter shows SB's forbidden outcome in every run and MP's in most. On Graviton3 and x86-64 the interpreter's store-to-load latency hides the reorderings, and those zeros are not evidence.
  • Contract B-MM-5 gains a non-normative note saying so (Scott's answer, option B). It changes no MUST.

Contract v0.1 appends, on Scott's answer of 2026-10-28:

  • §2 T-1's guest door;
  • §6's interpretation of R-2, flagged as one: a stackless lift's callback is a pull;
  • B-MM-5's note.

Board (make conformance): the core board reads 63769 pass, 0 fail, 0 unsupported, 1368 gated, 0 unimplemented over 256 files (v0.11.0: 60957 pass, 4187 gated). The 2819 vectors answered are the four flips' own lanes, 2812 passing and 7 bound. The all-gates-on lane reads 65109 pass, 0 fail, 0 gated. The threads lane reads 576 pass, 41 fail, 2 bound over 4 files, unchanged from v0.11.0, and all 41 failures are superseded-corpus rows (threads_superseded_test.go's register), with 0 engine defects.

Known limits, open on default-on paths, each with its issue:

  • A guest that calls runtime.LockOSThread and then needs another M crashes on the Phase 4 fork (#1157), with "fatal error: on a locked thread with no template thread". The throw is the fork runtime's own. v0.12.0's end-to-end witness does not pin its workers for this reason.
  • Close with a zero bound returns nil on Windows (#945), carried. Windows is outside the claim.
  • A Go runtime fatal was seen once on CI's arm64 runner in internal/spec (#1089), carried. It has not repeated.
  • array.new*'s backing allocation is not polled, so Stop can take over 1ms on a GC array of about 64 Ki elements or more (ADR 0104's named limit). This applies only with GC on, which is not the default.
  • Carried from v0.11.0, both still open:
  • a shared-everything-threads heap type is refused as "malformed", not by name (#1120);
  • a Go guest's stop-the-world still depends on the fork's compiler-inserted loop checks (#1064, parked).

Quoted from the v0.12.0 block of the engine's CHANGELOG.md, regenerated from the latest release tag on every build. References to sections "below" are to that CHANGELOG block.

Where to go next

  • Docs: install, the CLI, the Go API, four tutorials, concepts, limits and the roadmap. Every example program on those pages is run by this site's CI against the latest Burroughs release.
  • Blog: Building Burroughs, an eleven-part story of how the engine and its unusual process were built, from an empty repository to v0.12.0.
  • GitHub: the code, the CHANGELOG.md, the decision records, and the issue tracker, where the project's status lives.

Burroughs is licensed under Apache 2.0, © 2026 Scott Friedman.