This page describes Meta-specific storage adapters. It does not replace Meerkat’s own runtime-backed session durability model.
Meta storage backends
JSON file store
JsonFileSessionStore is an optional operational adapter. It stores a JSON
array of SessionPersistenceRow records in one caller-selected file, shared
by all session IDs written to that store. It is not the default canonical
gateway session store.
append_rows acquires one shared write lock,
reads the existing array, appends the supplied rows, and rewrites the data
file. The default lock path is data_path.with_extension("lock")
(/var/meta/sessions.lock above); override it with with_lock_path(...).
The JsonStoreLockRecord contains owner_pid and created_at_ms.
Stale lock recovery. During write-lock acquisition, a valid lock record
is recoverable only after it reaches stale_lock_threshold and its owner PID
is no longer alive. The default threshold is 30 seconds; configure it with
with_stale_lock_threshold(Duration). A fresh lock still produces
JsonFileSessionStoreError::LockHeld even if its owner has died, and an aged
lock with a live owner remains protected. For a malformed lock record, the
store uses the lock file’s modification age instead. A stale lock is removed
before the store retries acquisition.
BigQuery adapter
For production-oriented deployments, the BigQuery adapter writes session rows to a configured dataset and table. Supplyproject_id and an OAuth
access_token from the host’s configuration and credential handling:
BIGQUERY_PROJECT_ID.
For the token, a non-empty explicit value takes precedence, followed by
BIGQUERY_ACCESS_TOKEN, GOOGLE_OAUTH_ACCESS_TOKEN, and GOOGLE_ACCESS_TOKEN
in that order. A non-empty explicit project ID likewise precedes the
environment fallback. Missing project or token configuration fails real
reads and non-empty inserts; an empty insert is a no-op. The adapter does
not perform automatic application-default credential discovery.
The separate validate_bigquery_naming(&BigQueryNaming) policy validator
checks dataset and table names; new_native does not invoke it:
- Dataset and table names must be non-empty
- Only ASCII alphanumeric characters, underscores, and hyphens are allowed
Session persistence rows
Session state is serialized asSessionPersistenceRow records:
All five fields have Serde defaults when omitted. Turn counts or other
application state belong inside
payload, not in additional top-level fields.
Session store contracts
SessionStoreContract is serializable capability metadata returned by
session_store_contracts(&decisions), not an implementation trait or shared
CRUD interface:
The first three capability flags are
true for both adapters. file_locking
and crash_recovery are true only for the JSON adapter.
The concrete APIs differ:
read_rows returns row history rather than deduplicating it. Append a row
with deleted: true to represent deletion; these adapters do not expose a
shared per-session delete method. Integration tests exercise each concrete
adapter’s insertion and materialization behavior.
Materialization
Meta provides two materialization functions for session inspection:materialize_latest_session_rows— returns the row with the greatestupdated_at_msper session; a later input row wins a timestamp tiematerialize_live_session_rows— returns each session’s latest row only when that row hasdeleted == false
On-disk layout and durability
On persistent gateway launches (persistent_state), every session lives in
Meerkat Core’s runtime store, runtime.sqlite in the state directory: its
transcript and its resume, archive and retire state. Meta keeps no session
database of its own beside it. A session projection that Meta 0.9.3 kept,
either a session database (sessions.sqlite3, or its older spellings
sessions.db and sessions.sqlite) or session rows in the continuity
database (continuity.sqlite3, continuity.db or
identity_continuity.sqlite) of a runtime with a roster provider, makes a
0.9.4 launch refuse the directory (-32014,
NativeStorageMigrationRequired) until meta_gateway storage-migrate-native
has moved its sessions into Core’s stores and retired the projection. A
store_path that names a separate session database file is refused too. See
Upgrading to Meta 0.9.4.
Durability is fail-closed: a runtime store that cannot open is a startup
error, never a silent in-memory fallback. A persistent launch keeps its
sessions under the state directory, so it refuses a declared in-memory
runtime store: runtime_options.runtime_store = {"storage": "memory"} on
rpc_gateway (runtime_store.memory() / runtimeStore.memory() in the
SDKs) answers -32014, and ephemeral_runtime_store(true) on the Rust
builder is refused with ConflictingConfiguration. For sessions that live
only as long as the process, launch without persistent_state. The storage
census on meta/status (storage.slots) reports each store’s durability
resolution and backend. See
Configuration: Storage layout and durability.
Runtime correlation
An acceptedmeta/send_message response includes a best-effort session_id
correlation value. Use a non-empty value for follow-up session inspection and
debugging, but do not treat it as an atomic receipt identifying the exact
accepting session across concurrent identity rebinding.
On the identity plane, the gateway reads the session binding after delivery
and falls back to the resolve-time binding. If neither can be observed, a
successful send can return accepted: true with session_id: "". Empty means
unknown: it is not a usable session ID and does not mean delivery failed.
See also
- Configuration — session store settings, storage layout, durability classes
- RPC API —
meta/session_store/bigquery,meta/storage/doctor - BigQuery naming — naming validation rules
