Skip to content
For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending .md to the URL or sending Accept: text/markdown.

State Snapshots & Restore

For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /a4-server/state-snapshots.md.

All server state — VM entity tables, projection caches, sorted views — lives in memory. By default a restarted server starts empty and rebuilds state only from new stream events. State snapshots change that: the server periodically writes its in-memory state to a pluggable store and, on startup, rehydrates from the latest snapshot and resumes the Yellowstone stream from a safe watermark. A restarted server comes back with its history in seconds.

Snapshots are opt-in and off by default.

Terminal window
export ARETE_SNAPSHOT_ENABLED=true
export ARETE_SNAPSHOT_URL=file:///var/lib/arete/snapshots

Or configure programmatically:

use arete_server::{Server, SnapshotConfig};
let mut snapshots = SnapshotConfig::default();
snapshots.enabled = true;
snapshots.url = Some("file:///var/lib/arete/snapshots".to_string());
Server::builder()
.spec(my_stack::spec())
.websocket()
.http()
.snapshots(snapshots)
.start()
.await?;
Env varDefaultMeaning
ARETE_SNAPSHOT_ENABLEDfalseMaster opt-in
ARETE_SNAPSHOT_URLfile:///var/lib/arete/snapshots, a plain path, or s3://bucket/prefix (see below)
ARETE_SNAPSHOT_INTERVAL_SECS60Periodic snapshot cadence
ARETE_SNAPSHOT_KEEP4Retained snapshots (older ones are pruned)
ARETE_SNAPSHOT_ON_SHUTDOWNtrueFinal snapshot on SIGTERM/SIGINT
ARETE_SNAPSHOT_MIN_MUTATIONS1Skip a cycle when fewer mutation batches were applied since the last snapshot
ARETE_SNAPSHOT_MAX_RESUME_AGE_SLOTS1500Snapshots older than this (estimated in slots) hydrate state but start the stream live
ARETE_SNAPSHOT_READY_MAX_LAG_SLOTS50/ready stays 503 after a resume until the parser is within this many slots of the tip
ARETE_SNAPSHOT_READY_MAX_HOLD_SECS60Upper bound on how long /ready can be gated after a resume

The default build supports local filesystem URLs; a directory on a persistent volume (for Kubernetes, a PVC) is all a self-hosted deployment needs.

Enable the snapshot-object-store cargo feature to store snapshots in cloud object storage:

arete-server = { version = "0", features = ["snapshot-object-store"] }

Then point ARETE_SNAPSHOT_URL at an object prefix — s3://bucket/prefix, gs://bucket/prefix, or az://container/prefix. Credentials come from each provider’s standard environment (for AWS: AWS_ACCESS_KEY_ID/ AWS_SECRET_ACCESS_KEY, AWS_REGION, or instance/workload identity such as IRSA on EKS). Run one server per prefix — the snapshot manager assumes a single writer.

Each cycle the server:

  1. Waits for in-flight VM updates and their queued projection batches to finish, then briefly blocks new updates with a per-runtime consistency barrier.
  2. Dumps the VM entity tables, projection caches, and resume watermark — the highest slot the projector has applied — from that same processing cut.
  3. Releases processing, serializes and compresses the captured state, writes one atomic blob to the store (temp file + rename), and prunes old snapshots.

On startup, the server loads the newest snapshot, validates it, rehydrates the projection caches before the WebSocket server starts (so the first client’s snapshot-on-subscribe is already warm), rebuilds sorted views, and hands the VM state to the stream runtime, which resumes the Yellowstone subscription with from_slot = watermark.

The overlap between the watermark and whatever the old process saw after its last snapshot is replayed by the stream and deduplicated by the snapshotted version trackers — no gap, no double-applied events. Timestamps, resolver results, and append histories are preserved exactly, which pure replay cannot guarantee.

A snapshot embeds a fingerprint of the compiled stack bytecode. If you deploy a build whose stack logic changed, the old snapshot is discarded and the server cold-starts (today’s behavior) with a clear log line. The same applies to snapshots with a different format version, mismatched program ids, or corrupt/truncated blobs — restore problems never block startup.

  • If the snapshot is older than ARETE_SNAPSHOT_MAX_RESUME_AGE_SLOTS (estimated from wall-clock age), state still hydrates but the stream starts live: providers only support from_slot replay within a limited window. Account-derived state self-heals from full account writes; only instruction events inside the gap are missed.
  • A restored replay retains a parser-processed checkpoint across reconnects; it never falls back to a live subscription while replay is active. This avoids silently skipping unfinished history. Configure the maximum snapshot age conservatively so the initial checkpoint remains inside the provider’s replay window.
  • Snapshot write failures are logged and skipped — snapshotting never takes down a healthy server.
  • Slot-scheduled callbacks (documented as non-durable).
  • In-flight async resolver requests — they re-queue on the next relevant event. Cached resolver results are preserved, honoring their original TTLs across the restart.
  • WebSocket clients’ broadcast backlog — clients receive a fresh snapshot on reconnect anyway.