# p/moul/x/vm/vmkit
The host ABI shared by every guest virtual machine that runs inside a gno
realm: a stepping contract with an explicit fuel budget, a capability-scoped
`Host`, and a snapshot format that lets a guest program outlive the transaction
that started it.
A guest VM implements `Machine`. Everything else here is the machinery a VM
should not have to write twice: the `Meter`, the canonical `Writer`/`Reader`
snapshots are built from, an in-memory `TestHost`, and the avl-backed `Store` a
realm keeps its instances in.
First consumer: [`p/moul/x/vm/bf`](../bf). Live demo:
[`r/moul/x/vm/bfdemo`](/r/moul/x/vm/bfdemo/v0).
```go
type Machine interface {
Step(h Host, fuel int64) (used int64, status Status)
Snapshot() []byte
Restore(b []byte) error
}
```
`Status` is one of `Running`, `Halted`, `Trapped`, `OutOfFuel`.
## What the ABI is for
**Fuel is the interface, not the backstop.** Running out of fuel yields
`OutOfFuel` plus a machine that can be snapshotted, never a panic. Gas still
bounds the transaction; it is just not the thing a guest program is written
against.
**Continuations.** `Snapshot` and `Restore` mean a program runs across blocks.
The realm stores the bytes, the next caller pays for the next slice. Gno realm
code cannot pause itself; a guest can. The property that makes this real is
tested rather than asserted: five slices of one fuel unit must produce exactly
what one slice of five produces, for every program in `bf`'s corpus.
**Capabilities, not ambient authority.** A guest gets exactly the `Host` it was
handed. `Send` returns `ErrNotGranted` unless the deploying realm funded a
budget, storage is scoped to the instance, and nothing is looked up. Gno itself
has ambient authority through the realm frame, so the guest is where the
capability-secure version can actually be tried.
## Meter is not a hot-path type
`Meter` exists for the API boundary: compute a slice budget, charge it once,
report what was used. Calling `Meter.Charge` once per guest instruction was
measured at **+86% on top of an entire interpreter dispatch loop**, and more
than tripled its allocation count. A machine should count fuel in a local and
settle up with the caller. The numbers are in [`bf`'s README](../bf/README.md).
`Charge` spends nothing when the budget cannot cover the request, so a machine
that stops for lack of fuel is exactly at the instruction it could not pay for,
and resuming re-executes that instruction and no other.
## The snapshot codec
`Writer` and `Reader` are fixed-width big-endian with length-prefixed bytes, so
the encoding is canonical: the same machine state always produces the same
bytes, on every node. A snapshot is consensus state, so two nodes encoding it
differently is a fork, and a short read is a hard error rather than a zero
value.
Machines are not required to use it, but a machine that invents its own layout
owes the zoo an explanation: the cross-VM snapshot cost comparison only means
something when the encodings match.
## Instance and Store
`Instance` is one guest program as a realm stores it: code, snapshot between
slices, status, and the fuel accounting that survives the transaction. It holds
bytes rather than a `Machine` on purpose, which is what makes the storage cost
of a paused program measurable.
`Instance.Run` folds one slice back into the instance and leaves it untouched
when `Restore` fails, so a snapshot that cannot be decoded costs the caller gas
but never corrupts the stored program.
<!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) -->
---
Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
**Dependency graph:**

> 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).
<!-- END GNOCONTRACTS FOOTER -->
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.