Skip to content
Pilotoo
v0.1

Storage

Storage exists on both sides. The server keeps conversations, checkpoints, usage and more behind ports, with PostgreSQL as the default backend. The client keeps small things, such as unsent drafts and the theme, in a KeyValueStorage that you supply.

You supply three async methods. The kit’s stores keep drafts, the theme preference, the last open conversation and finished presentation decks in it. The demo web app backs it with localStorage:

src/storage.ts
import { draftStore, resilientStorage, type KeyValueStorage } from '@pilotoo/chat-core';
const local: KeyValueStorage = {
getItem: async (key) => localStorage.getItem(key),
setItem: async (key, value) => localStorage.setItem(key, value),
removeItem: async (key) => localStorage.removeItem(key),
};
export const drafts = draftStore(resilientStorage(local), { prefix: 'myapp.pilotoo.' });

createMemoryStorage() serves a host with nothing to persist to. On React Native, back the same interface with your own storage library.

resilientStorage never throws or rejects. A failing storage, such as a private window that blocks localStorage, falls back to memory for the session, so only persistence is lost.

With @pilotoo/ui/chat, ChatProvider takes storage and storagePrefix for the last open conversation and defaults to localStorage.

Each server port has a default backend, and you replace any of them through the option of the same name in PilotooModule.forRoot. This server keeps conversations in its own database:

PilotooModule.forRoot({
env: process.env,
conversationStore: myConversationStore,
});

If you supply every persistence port yourself, no database connection is made. See Running without PostgreSQL.

On a user’s own machine the local backend keeps the same data in one SQLite file instead.

Store Holds
draftStore Unsent composer text per conversation (NEW_CONVERSATION_DRAFT for a new one)
themePreferenceStore system, light or dark
lastConversationStore The conversation to reopen
presentationDeckStore Finished presentation decks

All take { prefix }; every key carries it.

The PostgreSQL tables are created on first use, in the pilotoo_app schema. Nothing needs a manual migration step.

Data Option Default backend Tables
Conversation catalog conversationStore PostgreSQL conversations
Agent definitions (the tenant’s own agents) agentDefinitionStore, agentDefinitionSource PostgreSQL agent_definitions, tenant_provisioning
Run checkpoints checkpointer PostgreSQL LangGraph’s checkpoint tables, in the pilotoo_langgraph schema
Run events runEventStorage PostgreSQL run_events
Background subagent tasks backgroundTaskStorage PostgreSQL background_tasks
Token usage usageStore PostgreSQL usage_calls
Schedules and their runs scheduleStorage PostgreSQL schedules, schedule_runs
Uploaded files attachmentStore S3-compatible bucket when ATTACHMENTS_S3_BUCKET is set, otherwise off none: objects in the bucket

Provider, profile, system-agent and skill definitions are files under the configuration folder, not database rows. See Models, Agents and Skills. A few small bookkeeping tables, such as seed_upgrades, also live in pilotoo_app; they are internal.

These stores belong to features in preview. See Workflows and Connections.

Data Option Default backend Tables
Workflow definitions, runs and run events workflowStorage PostgreSQL workflows, workflow_runs, workflow_run_events
Connections, pending OAuth states, tenant provider configs, audit log and locks ConnectionStorage port PostgreSQL connections, connection_oauth_states, connection_provider_configs, connection_audit, connection_locks