Skip to main content

Documentation Index

Fetch the complete documentation index at: https://zapo.to/llms.txt

Use this file to discover all available pages before exploring further.

A store is where zapo persists everything a session needs to survive a restart: pairing credentials, Signal protocol state, app-state collections, and optionally your message/thread/contact archive. You build one with createStore and pass it to the client.
import { createStore } from 'zapo-js'
import { createSqliteStore } from '@zapo-js/store-sqlite'

const store = createStore({
  backends: {
    sqlite: createSqliteStore({ path: '.auth/state.sqlite', driver: 'auto' })
  },
  providers: {
    auth: 'sqlite',
    signal: 'sqlite',
    senderKey: 'sqlite',
    appState: 'sqlite',
    messages: 'sqlite',
    threads: 'sqlite',
    contacts: 'sqlite',
    privacyToken: 'sqlite'
  }
})

The model

createStore separates backends (where data lives) from providers (which backend each domain uses). This lets you mix backends — e.g. keep hot signal state in Redis while archiving messages in Postgres.
createStore({
  backends: {
    redis: createRedisStore({ redis }),
    postgres: createPostgresStore({ pool })
  },
  providers: {
    auth: 'redis',
    signal: 'redis',
    senderKey: 'redis',
    appState: 'redis',
    privacyToken: 'redis',
    messages: 'postgres',
    threads: 'postgres',
    contacts: 'postgres'
  }
})
Any domain you omit defaults to the built-in memory backend — no backends entry required.

Persisted domains

These hold the state required to keep a session alive. Back them with a durable backend in production.
DomainHolds
authPairing credentials and device identity. Persist this.
signalSignal sessions (umbrella over the sub-stores below).
preKeySignal pre-keys.
sessionSignal sessions.
identitySignal identity keys.
senderKeyGroup sender keys.
appStateApp-state collections (mute, pin, read, archive, …).
privacyTokenTrusted-contact / privacy tokens.

Optional archive domains

These accept 'none' to disable persistence entirely:
DomainHolds
messagesMessage archive (B | 'memory' | 'none').
threadsThread metadata.
contactsContact directory.

Cache domains

Configured under cacheProviders and default to bounded memory with TTLs:
DomainHolds
retryOutbound message retry queue.
groupMetadataGroup metadata cache.
deviceListDevice list cache.
messageSecretMessage-secret cache for addons.
createStore({
  backends: { sqlite },
  providers: { /* ... */ },
  cacheProviders: { groupMetadata: 'sqlite', deviceList: 'sqlite' },
  memory: {
    cacheTtlMs: { groupMetadataMs: 600_000, deviceListMs: 600_000 }
  }
})

Backends

SQLite

@zapo-js/store-sqlite — local, single-process.

PostgreSQL

@zapo-js/store-postgres — distributed, relational.

MySQL

@zapo-js/store-mysql — distributed, relational.

Redis

@zapo-js/store-redis — cache + persistence.

MongoDB

@zapo-js/store-mongo — document store.

Memory

Built in. Great for tests; does not survive a restart.
See the stores reference for each backend’s config options.

Memory-only (tests)

For quick experiments or tests, omit backends entirely — every domain falls back to memory:
const store = createStore({})
const client = new WaClient({ store, sessionId: 'test' }, logger)
A memory-only store loses all credentials on restart, so you re-pair every boot. Use a durable backend for anything long-lived.
Last modified on May 27, 2026