Skip to content
→ All work

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

  1. 01Deterministic: no wall clock, no OS randomness, no threads; a seeded xorshift and a state hash that fingerprints the whole machine.
  2. 02Metered: every instruction has a fixed gas cost; running out of gas stops cleanly and can be resumed — preemption without threads.
  3. 03Capability-gated: the only exits are numbered syscalls, each behind an explicit capability, down to individual host-function ids.
  4. 04Resumable: snapshot/restore serialise registers, memory, stacks, heap, handler, gas and RNG — pause on one machine, finish on another.
  5. 05Recoverable traps for division by zero, out-of-bounds memory, stack overflow, bad jumps, illegal instructions and denied capabilities.
  6. 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 →