Memory and Persistence
The network and instance state model that Lyquid developers use through
state! is not a key-value object model. A running Lyquid is WASM bytecode
backed by LyteMemory, the virtual memory layer behind
Direct Memory Architecture (DMA).
The useful mental model is a suspendable cloud instance with a preserved memory
image: Rust objects live in a fixed guest address space, while the host loads
memory pages as they are touched and writes changed pages back later. This is the
mechanism underneath both network and instance state variables.
Because LyteMemory is transparently exposed as normal linear memory to the WASM
runtime, JIT-compiled Lyquid code can access state with ordinary load/store
instructions. The result is close to native in-memory data-structure performance
on the host, without a serialization or storage-lookup step on every access.
That is the main developer benefit: shape state as in-memory Rust data types
rooted in state!, and let LyteMemory preserve them across calls.
Segments
LyteMemory has three logical segments:
| Segment | Use | Lifetime | Scope |
|---|---|---|---|
| Volatile | Scratch space and host I/O buffers | One call | Current call |
| Network | Shared state for sequenced execution | Across calls and versions | All nodes at one LyquidNumber |
| Instance | Node-local state | Across calls | One node's hosted Lyquid |
Network memory is versioned by sequencing. Instance memory is node-local and not versioned. Both use the same DMA model: stable guest addresses, lazy page loading, and dirty-page persistence.
The Lyquor VM and generated LDK code handle segment selection for you. State
declared in state! becomes rooted, long-lived data in the matching segment,
effectively 'static for that deployed Lyquid layout. Local stack values and
heap allocations made during a call remain ordinary Rust temporaries and are
dropped normally unless their contents are moved into declared state. For
Lyquid-level globals, use state!; do not try to create persistence by leaking
pointers or relying on mutable static values.
Lyquid developers should not call state_set or state_get directly. The LDK
uses low-level host APIs for its own state bookkeeping, and the Lyquor VM uses
the same underlying stores for LyteMemory paging. Page keys identify LyteMemory
pages; values are chunked page contents.
Atomicity and Durability
Network and instance state are both in persistent LyteMemory, but they have different durability boundaries.
Network state is versioned. Atomicity happens at the version commit
boundary, and that boundary is deterministically defined by the sequence
backend. Once a call is sequenced, its durability is already determined by the
sequence backend, even before Lyquor VM execution: the VM must eventually
execute that sequenced call. The developer mental model is similar to a
replicated log in cloud systems, and also to blocks in a blockchain: each
committed LyquidNumber is a clear, well-defined state boundary.
Instance state has call-level atomicity, but durability is best effort. A completed instance call may update node-local memory, but a crash can still revert it to an earlier flushed state. There is no block-like cutoff where you can say the instance state is guaranteed durable.
Treat instance state as a persistent cache: it is long-lived and useful across calls, but not the right surface for data that needs explicit commit/abort durability.
The LDK is expected to expose a dedicated key-value style host API for instance
functions that need explicit data commits. That API would be separate from
state! memory.
Current Layout Constants
These constants are defined by the current LDK in the lyquid crate:
| Constant | Current Value | Description |
|---|---|---|
LYTESTACK_BASE | 0x30000000 | Base address for Lyquid call stacks |
LYTEMEM_BASE | 0x80000000 | Base address for persistent LyteMemory |
LYTEMEM_SIZE_IN_MB | 4096 | Total WASM linear-memory cap |
NETWORK_MEMSIZE_IN_MB | 1024 | Network segment cap |
INSTANCE_MEMSIZE_IN_MB | 1024 | Instance segment cap |
VOLATILE_MEMSIZE_IN_MB | 1024 | Volatile segment cap |
Normal Lyquid application code should not need these constants. Treat them as
implementation constants, not values to copy into application logic. If you are
writing low-level code that genuinely depends on the layout, import them from
lyquid instead of duplicating the numbers.
Future LDK versions may improve or change the layout constants for newly built Lyquids. A deployed Lyquid's bytecode is built against one layout, and its persisted pages and pointer catalogs use that layout. That fixed deployed layout is what keeps older working Lyquid bytecode compatible when newer LDKs evolve.
Takeaway
- Treat variables defined in
state!as the Lyquid's global variables. They are the persistent roots your functions read and mutate across calls. - Avoid dependencies that hide mutable process-global state, including
static mut, mutablestaticvalues behind locks, thread-local mutable caches, or singleton registries, when they are directly or indirectly used by the type of a Lyquid state variable. - Treat instance state as a node-local persistent cache with best-effort durability.