Skip to content
Night Owls.dev
Jump to a page

@nightowlsdev/storage-local

Adapter/StorageMastra

A local LibSQL-backed Mastra snapshot store for the CLI bootstrap runtime, run a swarm on just a model key, before Supabase.

What it does

Exposes `createMastraLibsqlStore({ url, id? })`, which constructs a `@mastra/libsql` `LibSQLStore` for the CLI 'builder' bootstrap runtime, letting you run a Night Owls swarm with only a model key, before the project has Supabase/runners wired. You pass the returned store to `defineSwarm({ mastraStore })` in `@nightowlsdev/core`. This is the engine-wall (Option B) seam: the `@mastra/libsql` import lives here as a peripheral peer so `@nightowlsdev/cli` imports only this factory and stays Mastra-free; the return type is `unknown` so no `@mastra/*` type crosses the public API. `url` is `:memory:` for a single session or an absolute `file:` path for resume/replay across processes (it throws on a relative file: url).

Install

pnpm add @nightowlsdev/storage-local

Key exports

  • createMastraLibsqlStore

Usage

storage-local.ts
import { defineSwarm } from "@nightowlsdev/core";
import { createMastraLibsqlStore } from "@nightowlsdev/storage-local";

// :memory: for a single session, or an absolute file: url to resume across processes.
const mastraStore = createMastraLibsqlStore({ url: "file:/abs/path/owl.db" });

const swarm = defineSwarm({ agents, mastraStore });

What it provides

storage-local is the zero-infrastructure snapshot store for the CLI bootstrap runtime: a single factory, createMastraLibsqlStore({ url }), that returns a LibSQL-backed Mastra store you hand to defineSwarm({ mastraStore }). It lets a swarm suspend and resume on nothing but a model key, before the project has Supabase or durable runners wired. It is also an engine-wall seam — the heavy @mastra/libsql peer lives here and the return type is unknown, so @nightowlsdev/cli imports this factory and never @mastra/* directly.

When to use it

  • The CLI / owl builder bootstrap runtime — run a swarm with only a model key, before Supabase and runners exist.
  • Local development or a demo where you want durable suspend/resume on one machine without standing up Postgres.
  • A single-process script that should resume across restarts — pass an absolute file: url.
  • Tests that need a real (not mocked) Mastra store without a database — use :memory:.

When not to

  • Any multi-instance or serverless deployment — LibSQL is a single-file, single-process store with no cross-instance event bus, cache invalidation, or container floor.
  • Production persistence, an agent version catalog, a live redacted event stream, or tenant scoping — that is @nightowlsdev/storage-supabase.
  • You need the full core StorageAdapter (agents / runs / events / messages / scratchpad) — this exports ONLY the Mastra snapshot store, not a StorageAdapter.

Alternatives

  • @nightowlsdev/storage-supabaseAnything past one machine: durable, multi-tenant, multi-instance persistence with the full StorageAdapter, versioned agents, a Realtime event bus, and a Postgres floor.
  • InMemoryStorage (from @nightowlsdev/core)A single session or a test where you do not even need on-disk resume — a zero-dependency in-memory floor with nothing to install.

Strengths

  • Zero infrastructure — a model key plus a file path (or :memory:) is the entire setup.
  • Keeps the CLI Mastra-free (the engine wall): the @mastra/libsql peer is confined to this adapter and no @mastra/* type crosses its public API.
  • A file-backed url gives real durable suspend/resume across process restarts on one machine.

Limits & trade-offs

  • Single-process only — no cross-instance broadcast, cache invalidation, or container floor.
  • Only the Mastra snapshot store; it does not implement the core StorageAdapter seams (agents / runs / events / messages / scratchpad).
  • file: urls must be ABSOLUTE — a relative path throws at construction, on purpose, so a resume never breaks silently when the working directory changes.
  • Requires the @mastra/libsql and @mastra/core peers installed alongside.

How it works

createMastraLibsqlStore({ url, id? }) constructs a @mastra/libsql LibSQLStore and returns it typed as unknown. You pass it to defineSwarm({ mastraStore }) (or SwarmEngine({ mastraStore })), where the engine uses it to persist and reload durable suspend/resume snapshots. A url of :memory: lives for the process; an absolute file:/… path persists to disk and survives a restart. The constructor throws on a relative file: url so a resume never silently breaks after a cwd change. The unknown return type is deliberate — it keeps the @mastra type out of the public API so the CLI stays Mastra-free.

Examples

In-memory vs file-backed

:memory: lives for this process; an absolute file: path survives a restart.

storage-local-example-1.ts
import { createMastraLibsqlStore } from "@nightowlsdev/storage-local";
import { defineSwarm } from "@nightowlsdev/core";

// :memory: — state lives for this process only (a single CLI session, a test).
const ephemeral = createMastraLibsqlStore({ url: ":memory:" });

// file: — an ABSOLUTE path; suspend/resume snapshots persist across restarts.
const persistent = createMastraLibsqlStore({
  url: "file:/home/me/.nightowls/project.db",
  id: "project",
});

const swarm = defineSwarm({ agents, mastraStore: persistent });

The absolute-path guard

A relative file: url throws at construction, so a resume never breaks after a directory change.

storage-local-example-2.ts
// Throws: "libsql file: url must be ABSOLUTE (file:/…) so resume survives a cwd change".
createMastraLibsqlStore({ url: "file:owl.db" });

Doing the parts it doesn't support

  • Durable persistence across instancesThis store is single-process. For a multi-instance or serverless deployment, use @nightowlsdev/storage-supabase — it adds a Postgres container floor, a Realtime event bus, and LISTEN/NOTIFY cache invalidation on top of durable resume.
  • The core StorageAdapter seams (agents / runs / events / messages)This package is only the Mastra snapshot store. For the full StorageAdapter use @nightowlsdev/storage-supabase; for a throwaway single session use InMemoryStorage from @nightowlsdev/core.

Related

  • storage-supabaseGraduate from the local bootstrap store to the durable, multi-instance StorageAdapter.
  • coredefineSwarm({ mastraStore }) is where this plugs in; InMemoryStorage is the no-disk alternative.
  • cliThe owl CLI's builder bootstrap uses this factory to stay Mastra-free (the engine wall).
  • getting-startedInstall the framework, run your first agent on a model key, then wire storage and runner adapters.