Stores

Store

@openclaw/fs-safe/store

The store subpath bundles two managed wrappers around the same safe-write primitives root() uses:

import {
  ensureJsonDurableQueueDirs,
  fileStore,
  fileStoreSync,
  jsonStore,
  loadPendingJsonDurableQueueEntries,
  resolveJsonDurableQueueEntryPaths,
  writeJsonDurableQueueEntry,
  type FileStore,
  type FileStoreOptions,
  type FileStoreSync,
  type JsonStore,
  type JsonStoreOptions,
} from "@openclaw/fs-safe/store";
HelperUse it for
fileStore()Multi-file directories with safe relative paths, size limits, atomic replacement, stream writes, copy-in, and TTL-based pruning.
fileStoreSync()Synchronous variant of fileStore() for places that genuinely cannot await.
jsonStore()A single keyed JSON state file with explicit fallback, atomic writes, and optional sidecar locking around read-modify-write updates.
Durable JSON queue helpersAppend/load/ack JSON entry files using atomic writes and delivered markers.
Private file-store modefileStore({ private: true }) for credentials, tokens, and per-agent state at 0600 files under 0700 directories.

fileStore().json("rel.json") and jsonStore({ filePath }) are intentionally separate primitives. Use fileStore().json(...) when JSON state lives alongside other files in the same managed directory; use jsonStore({ filePath }) when you have a single absolute path and want the keyed JSON shape directly.

#Picking a shape

  • Multi-file directory under one root — reach for fileStore(). It exposes write, writeJson, writeText, writeStream, read*, open, copyIn, remove, and pruneExpired against safe relative paths.
  • One JSON state file — reach for jsonStore({ filePath }). Its update() and updateOr() methods cover the merge-into-defaults and read-modify-write cases.
  • Credentials or tokens — pass private: true to fileStore(). Same store shape; writes route through the secret-file atomic path with 0600/0700 permissions.
  • Durable work queues — use the durable JSON queue helpers when each work item is a standalone JSON file and acknowledgement is represented by moving it through a short-lived .delivered marker.

#Durable JSON queues

The durable queue helpers are intentionally low-level. They do not decide retry, dedupe, or recovery policy; they just provide the filesystem mechanics that several queue implementations otherwise rewrite by hand.

await ensureJsonDurableQueueDirs({ queueDir, failedDir });

const paths = resolveJsonDurableQueueEntryPaths(queueDir, id);
await writeJsonDurableQueueEntry({
  filePath: paths.jsonPath,
  entry,
  tempPrefix: "queue",
});

const pending = await loadPendingJsonDurableQueueEntries({ queueDir, tempPrefix: "queue" });

id must be a single safe path segment: non-empty, not dot-prefixed, and made from letters, numbers, _, -, and .. Slashes, backslashes, NUL bytes, ., and .. are rejected.

Use ackJsonDurableQueueEntry() after durable processing succeeds and moveJsonDurableQueueEntryToFailed() when the caller wants to quarantine an entry for inspection.