Little documentation
Guides and APIReference

Storage Adapters

The shipped local and customDir adapters, plus a proposed object-store shape.

Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status

Persistent Dirs are developer-backed. First-party adapters make common stores easy without forcing Little Harness to own every app's data model.

What ships in alpha

localHost, localDir, projectDir, and customDir are exported today. The s3Dir object-store shape below is proposed — it is not exported yet; use customDir(...) with your own callbacks to back an object store in the meantime.

Shipped: Local Adapter

localDir() is the shipped filesystem adapter. It maps a harnessDir to a local folder and is exposed through Little Harness rather than leaking arbitrary host paths to the model.

import { localDir, projectDir } from "@little-stack/harness";

localDir({
  harnessDir: "/persistent/dev",
  sourceDir: "persistent/dev-memory",
});

Project folders require explicit intent.

localDir({
  harnessDir: "/persistent/project-docs",
  sourceDir: projectDir("./docs"),
  commit: "read-only",
});

See the localHost() reference for the full localDir / projectDir signatures.

Shipped: customDir (app databases and object stores)

customDir() is the shipped, typed helper for backing a harnessDir with your own storage. It is a thin wrapper over a PersistentDir entry — the same object you could place directly in persistentDirs — so you get types and intent without Little Harness owning your data model.

The simplest shape provides load / store (load the whole directory, commit changes at the commit boundary):

import { customDir } from "@little-stack/harness";

customDir({
  harnessDir: "/persistent/user",
  load: async ({ extraBody }) => db.files.loadFolder(extraBody.userId),
  store: async ({ extraBody, changes }) =>
    db.files.applyChanges(extraBody.userId, changes),
});

You can also express the store as per-path list / read / write / delete callbacks instead of load / store:

customDir({
  harnessDir: "/persistent/docs",
  list: ({ extraBody, path }) => docs.list(extraBody.userId, path),
  read: ({ extraBody, path }) => docs.read(extraBody.userId, path),
  write: ({ extraBody, path, content }) =>
    docs.write(extraBody.userId, path, content),
  delete: ({ extraBody, path }) => docs.delete(extraBody.userId, path),
});

Note this shape is not lazy yet: at turn start the runtime calls list once at the root (path: "") and then reads every file entry that single call returned, before execution. It does not recurse into directory entries — your root list must return a flattened list of every file (with nested paths like guides/setup.md) or nested files are silently skipped. Truly lazy loading (fetching files only when touched) is future work, so keep large stores out of hot dirs for now. A per-path store still preserves the commit model: it does not turn every shell write into an immediate backing-store mutation unless the developer explicitly chooses that behavior.

Proposed: Object Store Adapter

A convenience adapter mapping a harnessDir to a bucket and prefix. This shape is illustrative — s3Dir is not exported yet; back an object store with customDir today.

// Proposed shape — not exported yet.
s3Dir({
  harnessDir: "/persistent/user",
  bucket: "app-agent-memory",
  prefix: ({ extraBody }) => `users/${extraBody.userId}/`,
});

Equivalent adapters could exist for Cloudflare R2 and Google Cloud Storage.

On this page