Case study 16 / 26
Halyard
A deterministic, gas-metered, capability-gated register VM for running code you don't trust — and replaying exactly what it did.
- Status
- Research
- Period
- 2026
- Domain
- security · experiments
- Language
- Rust
- Last push
- 05 SEP 2026
- License
- MIT
- Source of claims
- README.md, docs/ISA.md, ARCHITECTURE.md
A small register machine (16 × i64, fixed 8-byte instructions) for executing agent tools, workflow actions and plugins where the host must bound how long code runs, control what it can touch, and prove what happened. Pure Rust, zero dependencies, with an assembler, disassembler and interactive debugger.
01/The problem
WebAssembly is the right answer for portable, fast sandboxing — and a large specification with no built-in gas metering and no serialisable machine state. Lua-style embeds aren't deterministic and can't be paused and resumed byte for byte. Orchestrators running untrusted steps need something bounded, deterministic, inspectable and resumable.
02/The system
A small register machine (16 × i64, fixed 8-byte instructions) for executing agent tools, workflow actions and plugins where the host must bound how long code runs, control what it can touch, and prove what happened. Pure Rust, zero dependencies, with an assembler, disassembler and interactive debugger.
03/Implementation
- 01Deterministic: no wall clock, no OS randomness, no threads; a seeded xorshift and a state hash that fingerprints the whole machine.
- 02Metered: every instruction has a fixed gas cost; running out of gas stops cleanly and can be resumed — preemption without threads.
- 03Capability-gated: the only exits are numbered syscalls, each behind an explicit capability, down to individual host-function ids.
- 04Resumable: snapshot/restore serialise registers, memory, stacks, heap, handler, gas and RNG — pause on one machine, finish on another.
- 05Recoverable traps for division by zero, out-of-bounds memory, stack overflow, bad jumps, illegal instructions and denied capabilities.
- 06Harvard layout: code lives outside data memory, so no self-modifying code and no jumping into data.
04/Engineering
Gas is part of the ISA
Costs are documented per instruction and exposed to the program itself, rather than bolted on as an external timer.
Snapshots are the unit of scheduling
A VM that ran out of gas is a complete, serialisable value — and tests prove a resumed run ends in the same state hash as an uninterrupted one.
Fixed-width instructions
Trivially decodable 8-byte instructions make the disassembler exact and the execution trace cheap.
05/Interface
No product screenshots are published for this project. The visual above is a code-driven representation of how it behaves, built from the repository source — not a screenshot.
06/Tech stack
- Rust
- Zero dependencies
- Custom ISA
- Assembler
- Debugger
07/Result
Verified outcomes
- 28 tests covering every trap, gas accounting to the unit, capabilities from both sides, snapshot determinism and corrupt-input rejection.
- Reported in the README (one run): ~660M instructions/s in a tight loop; snapshot + restore of 64 KiB in 67 µs.
Known limitations
- A switch-dispatch interpreter with bounds checks on every access — gas, not CPU, is the intended limit.
08/Links
Next case study
Bellows →