A snapshot that only one host can read is a local implementation detail.
That is not a criticism. Local snapshots are extremely useful. They make rollback, warm start, debugging, and fault recovery possible.
But “this process can resume” and “this program can resume somewhere else” are different properties.
The second one needs more than memory pages and a CPU register file. It needs an addressable description of execution state, a complete dependency closure, a portable host contract, and a rule for re-establishing authority on the destination.
This is the direction behind Hologram’s space contract: a browser, native machine, mobile device, or bare-metal board becomes a place Hologram can run by implementing the same storage, resolution, runtime, and host-capability surface.
A suspended session is not “the VM file on node 17.”
It is an object.
Machine state is not yet portable state
A hypervisor snapshot may capture:
- guest memory
- virtual CPU state
- emulated device state
- disk overlays
- backend-specific metadata
Restoring it usually assumes:
- the same virtual hardware model
- the same hypervisor format
- compatible kernel and firmware behavior
- the same attached devices
- the same network identity
- the same external storage paths
That is enough for same-host or same-cluster restoration. It is not a general application portability contract.
flowchart TD
V[Hypervisor snapshot] --> M[Memory pages]
V --> C[CPU state]
V --> D[Virtual devices]
V --> P[Backend metadata]
M --> H{Compatible host?}
C --> H
D --> H
P --> H
H -- yes --> R[Restore locally]
H -- no --> X[Format or environment mismatch]
A portable runtime needs to decide which state belongs to the application and which state belongs to the machine that happened to host it.
Address the suspension, not its location
Let be a canonical snapshot object containing the runtime state the application needs to continue.
Its identity is:
The snapshot can then refer to other objects by address:
- module or application archive
- memory image
- filesystem roots
- model weights
- configuration
- capability request
- event-log position
- runtime version
flowchart LR
S[Snapshot κ] --> A[Application archive κ]
S --> M[Memory state κ]
S --> F[Filesystem root κ]
S --> C[Configuration κ]
S --> E[Event position κ]
S --> R[Runtime contract version]
The snapshot becomes the root of a content graph rather than a pointer to a host directory.
That changes migration. Moving the session means resolving the closure reachable from , verifying each object, and providing a conforming runtime on the destination.
The dependency closure is the portable unit
Copying the root object is not enough.
If the snapshot refers to five tensors, a filesystem manifest, and an application module, all of those objects must be available at the destination.
Define as the transitive set of addressed dependencies reachable from the snapshot root.
A destination can resume only if:
flowchart LR
S[Resolve snapshot κ] --> G[Walk dependency graph]
G --> L{Object local?}
L -- yes --> V[Verify address]
L -- no --> F[Fetch through sync]
F --> V
V --> N{More dependencies?}
N -- yes --> G
N -- no --> B[Begin resume]
Content addressing makes each local store a valid cache of the same immutable object space. There is no need to synchronize mutable block devices before every move if the state is already expressed as immutable objects and roots.
The mutable concept becomes “which root is current,” not “which disk sectors changed.”
A host contract is what makes “somewhere else” meaningful
Portability does not mean every host exposes the same operating-system APIs.
It means the application targets a contract that each host implements.
Hologram calls such a host a space. The core shape includes:
- a
KappaStorefor local content - a resolver or sync path for missing objects
- a container runtime
- entropy
- clocks
- a spawner or event-loop seam
- an interaction surface
flowchart TB
App[Portable application / snapshot]
App --> Contract[Space contract]
Contract --> Browser[Browser space]
Contract --> Native[Native space]
Contract --> Mobile[iOS space]
Contract --> Bare[Bare-metal space]
Browser --> OPFS[OPFS + WASM engine]
Native --> FS[Native store + fast engine]
Mobile --> INT[Interpreter + platform storage]
Bare --> HAL[Block device + run loop]
Platform differences remain behind the contract. The application does not get a browser-only extension trait that changes its identity or semantics.
A host either implements the surface and passes the compatibility tests, or it is not a conforming space.
That refusal is important. “Portable” should not mean “silently loses capabilities when moved.”
Authority must not travel as ambient state
A snapshot can carry computation. It should not carry reusable ambient authority by accident.
Live credentials, host paths, network sockets, temporary leases, and process-local capability handles do not become safe merely because they were serialized.
The destination should re-admit the session against its own policy.
sequenceDiagram
participant A as Source space
participant K as Address store
participant B as Destination space
participant P as Destination policy
A->>A: suspend runtime state
A->>K: store snapshot and closure
K-->>A: snapshot κ
B->>K: resolve and verify snapshot closure
B->>P: request capabilities for resume
P-->>B: fresh attenuated grant or refusal
B->>B: restore state with new handles
The clean rule is:
The destination may grant less authority than the source. It should never inherit more because a stale handle was present in memory.
This is the same distinction that appears in secure microVM forks: memory can be cloned; authority needs a new admission.
Replace resources with stable references
A portable snapshot cannot rely on:
/var/lib/app/model.bin
or:
node-17:/mnt/volume-3
Those are locations inside one environment.
A portable reference names the object and lets the destination resolve an available realization:
κ:model-weights
κ:filesystem-root
κ:configuration
The resolver may find the object:
- in local OPFS
- in a native content store
- on a peer
- through a registry gateway
- inside a bundled archive
The snapshot remains stable while placement changes.
flowchart LR
R[Object reference κ] --> L[Local store]
R --> P[Peer sync]
R --> G[Registry gateway]
R --> B[Bundled archive]
L --> V[Verify on receipt]
P --> V
G --> V
B --> V
This is what separates identity from storage topology.
Not every kind of state should move
A useful portability model needs explicit exclusions.
Some resources cannot be replayed or reconstructed safely:
- an open TCP connection to a third-party server
- a hardware sensor mid-read
- a GPU kernel with backend-specific resident state
- a file descriptor into an unaddressed host path
- a wall-clock assumption that changed while suspended
- a nondeterministic external service interaction
The runtime has several choices:
- reject suspension while the resource is live
- close it and record a reconnect intent
- translate it into an addressed logical resource
- checkpoint through a backend-specific extension and narrow portability
- mark the snapshot as host-bound
What it should not do is call the result portable while hiding the dependency.
A snapshot can carry a portability profile describing its requirements:
A destination must satisfy that profile before resume.
Application snapshots and machine snapshots can coexist
There is no need to choose one snapshot model for every purpose.
A full machine snapshot is excellent when:
- restoring on the same backend
- preserving an operating-system workload
- minimizing resume latency
- debugging exact machine state
An application-level addressed snapshot is better when:
- moving between host classes
- deduplicating state
- resolving dependencies independently
- re-admitting authority
- replaying through a portable runtime contract
flowchart TD
W[Running workload] --> M[Machine snapshot]
W --> A[Application snapshot]
M --> F[Fast same-backend restore]
M --> D[Detailed machine debugging]
A --> P[Cross-space portability]
A --> C[Content graph + deduplication]
A --> R[Fresh capability admission]
A system may use both: machine snapshots as a performance cache and application snapshots as the durable portable representation.
The important part is not to confuse the cache with the contract.
Resume should be a verified transition
A good resume path does more than deserialize bytes.
It verifies:
- the snapshot address
- every dependency address
- the runtime and format versions
- compatibility with the destination space
- the requested capability set
- any provenance or signature requirements
- that stale source-side sessions cannot reconnect
Only then does it instantiate the runtime and restore the application state.
stateDiagram-v2
[*] --> Resolving
Resolving --> Verifying
Verifying --> Admitting
Admitting --> Restoring
Restoring --> Running
Resolving --> Refused: missing closure
Verifying --> Refused: digest mismatch
Admitting --> Refused: capability policy
Restoring --> Refused: incompatible runtime
That sequence makes resume an auditable state transition rather than a file operation.
Portability is a property of the whole stack
A portable language is not enough.
A portable bytecode is not enough.
A portable archive is not enough.
The session moves only when identity, storage, runtime, capability, and host contracts line up.
This is why I like the phrase “space” rather than “backend.” A backend sounds like a code-generation target. A space is the complete place where an object can be resolved, admitted, run, observed, suspended, and resumed.
A program should be able to resume somewhere else.
But “somewhere else” has to be defined as a contract, not a hope.