Concepts
Workflow modes
Workflow code has to run somewhere, and Hopskip runs it in one of four modes: a Wasm module, a V8 isolate, a deterministic microVM, or an HTTP integration for code it cannot host at all. Which one your workflow uses depends on the language it is written in.
The mode changes what is under your code, not what your code can rely on. Every mode gives the workflow the same contract:
- deterministic replay: the same recorded history reproduces the same execution (Determinism & the sandbox),
- a checkpoint at every
await, with snapshots and millisecond resume (Memory snapshots), - the same event log, the same
hopcommands, the same activity dispatch.
| Mode | What executes your code | Languages | Determinism enforced by |
|---|---|---|---|
| Wasm module | the universal worker, embedding Wasmtime | Rust, Python, Go, Haskell | the host imports the module is allowed to link |
| V8 isolate | the V8 worker, a real V8 engine in the worker process | TypeScript | the same ABI surface, rebound by the host |
| AD-VM microVM | a forked Firecracker microVM | anything that runs on Linux: JVM, BEAM, Ruby, native binaries | the hypervisor, at the syscall layer |
| HTTP integration | your own system | systems with no code access | not enforced; Core owns retries and idempotency |
Wasm module
Most guest languages compile to a Wasm module, and the worker executes
that module with Wasmtime. The module imports exactly the versioned
workflow ABI (hopskip:workflow@0.1): logical time, a seeded random,
durable sleep, activity invoke and await, searchable-state emission, and
logging. There is no network, no filesystem, and no wall clock to link
against, which is why non-deterministic workflow code fails at
instantiation instead of misbehaving at replay
(Compatibility & versioning).
A snapshot of a suspended Wasm workflow is its linear memory plus exported globals, nothing else, so instantiation and resume are sub-millisecond.
How each language gets to a module differs, and the build handles it:
- Rust compiles to
wasm32-unknown-unknowndirectly. - Go builds a reactor module with Go 1.24’s wasip1 support; its runtime’s WASI imports are rewritten into deterministic local functions at build time.
- Haskell compiles through the GHC Wasm backend into a reactor with no WASI imports at all.
- Python wraps your workflow in a pinned interpreter compiled to Wasm.
See each SDK page for the build commands.
V8 isolate
TypeScript workflows do not compile to Wasm. They run on a real V8 engine inside the V8 worker process, as ordinary JavaScript with the SDK’s async machinery.
The determinism rules are the same and are enforced the same way, at the
boundary: Date.now() returns logical event time, Math.random() is
seeded from the workflow ID, and fetch inside workflow code is not
available. The SDK’s bundler wraps your entry point with shims that
speak the same workflow ABI a Wasm module would.
At each await, V8 parks the suspended frame on its own heap, so a
snapshot is that heap. hop dev runs the V8 executor in-process, which
is why a TypeScript workflow runs with the same one command as a Rust
one. See the TypeScript SDK.
AD-VM microVM
Some code cannot compile to Wasm at all: JVM services, Ruby or Python native extensions, threaded native binaries. The AD-VM tier runs those unchanged inside a deterministic microVM: a forked Firecracker whose device models are patched so the guest’s clock, entropy, and external input come from the engine rather than the hardware. The hypervisor traps the timestamp counter, serves deterministic entropy seeded from the workflow ID, and advances time only when the engine applies an event, which is the same clock rule the other modes enforce with host imports.
A snapshot in this tier is a full VM image, and restoring one takes on the order of 5 to 10 milliseconds rather than sub-millisecond, so the tier exists for the languages that need it, not as the default. It also needs KVM, so it runs on Linux hosts.
The hop sandbox command gives you the
same determinism guarantees on a laptop using a container runtime and a
supervisor instead of a hypervisor: useful for testing code destined for
either tier, since recordings made locally replay on the production
tier.
HTTP integration
For workloads Hopskip cannot host, the engine can hand dispatches to your own system as signed HTTP calls: a webhook gateway translates a task dispatch into a request, with a synchronous 200 for quick work or 202-plus-callback for long-running work, and Core owns the retries, timeouts, and idempotency keys. A related co-process agent embeds in an existing monolith, maintaining the worker stream and executing tasks by spawning language-native processes. In this mode Hopskip schedules and remembers but does not execute your code, so determinism is your side of the contract.
Which mode will my workflow use?
You do not pick a mode directly; the language does:
- TypeScript: V8 isolate
- Rust, Python, Go, Haskell: Wasm module
- JVM, BEAM, Ruby, native binaries: AD-VM microVM
- no code access (an existing HTTP system): webhook gateway
Whatever the mode, the workflow’s history, replay, snapshots, activity
dispatch, and the hop command surface are the same.