Documentation Index
Fetch the complete documentation index at: https://docs.oktolabs.ai/llms.txt
Use this file to discover all available pages before exploring further.
Storage paths
Pulse is local-first. Every byte the server writes — board metadata, cards, specs, attachments, knowledge-graph nodes — lives under a single root directory on your machine. By default that root is~/.okto-pulse/. You can move it, mount it as a Docker volume, or back it up with tar. There is no remote dependency.
This page documents the on-disk layout, the override knobs, the Docker volume gotcha, and how to move the data directory between machines safely.
For runtime configuration (ports, bind hosts, logging) see Server configuration. For the full env-var reference see Environment variables.
Default layout
Source:okto-pulse/src/okto_pulse/community/config.py:CommunitySettings._derive_paths (inventory:160–174).
Path.home() / ".okto-pulse" when data_dir is empty, evaluated at startup by CommunitySettings._derive_paths (inventory:162).
| Path | Contents | Source |
|---|---|---|
~/.okto-pulse/data/pulse.db | SQLite database, WAL mode, FK ON. 38 SQLAlchemy tables (boards, cards, specs, queue, audit, etc.) | inventory:165, inventory:1022–1077 |
~/.okto-pulse/uploads/ | Per-card file attachments referenced by Attachment.file_path | inventory:166 |
~/.okto-pulse/boards/{board_id}/graph.lbug | One LadybugDB graph per board: nodes, edges, HNSW vector indexes | inventory:167, inventory:927 |
~/.okto-pulse/global/discovery.lbug | Cross-board discovery index (entities, search history) | inventory:168, inventory:928 |
~/.okto-pulse/mcp_traces/ | One JSONL file per MCP session when tracing is on | inventory:771–772 |
The graph store is LadybugDB, not Kuzu. Kuzu was retired on 2026-05-03; the file extension
.lbug is the LadybugDB on-disk format. Some env-var names (KG_KUZU_BUFFER_POOL_MB, KG_CONNECTION_POOL_SIZE) keep the legacy KUZU prefix for backward compatibility — they configure the LadybugDB engine.Overriding the data directory
DATA_DIR is the single knob. It maps to the data_dir field on CommunitySettings (community/config.py:11) — pydantic-settings reads it with case_sensitive=False and no env-prefix, so the field name uppercased is the env-var name. Set it before okto-pulse serve starts and every subdirectory rebases under the new root.
KG_BASE_DIR (inventory:189, inventory:773) — when set independently it relocates only the per-board and global graph files, leaving SQLite and uploads at the original path:
| Env var | Default | Affects | Source |
|---|---|---|---|
DATA_DIR | ~/.okto-pulse | All paths above | CommunitySettings.data_dir (community/config.py:11–22, inventory:162) |
KG_BASE_DIR | ~/.okto-pulse | boards/, global/ only | CoreSettings.kg_base_dir (inventory:189) |
DATABASE_URL | sqlite+aiosqlite:///{data_dir}/data/pulse.db | SQLite location | CoreSettings.database_url (inventory:185) |
UPLOAD_DIR | {data_dir}/uploads | Attachments only | CoreSettings.upload_dir (inventory:186) |
MAX_UPLOAD_SIZE | 10485760 (10 MB) | Per-attachment cap | CoreSettings.max_upload_size (inventory:187) |
Docker: the volume gotcha
Inside the official container, the data root is/data and is set via DATA_DIR=/data and KG_BASE_DIR=/data. The compose file ships in okto-pulse/docker-compose.yml and declares an unprefixed named volume:
okto-pulse_okto-pulse-data), you are on the wrong volume.
See Docker install for the full container recipe.
Backup strategy
Pulse data is plain files. A consistent backup needs two things: SQLite at a transaction boundary, and the LadybugDB files quiesced.Stop the server (or pause writes)
The simplest correct backup is offline. Stop For a containerized deploy:
okto-pulse serve and the consolidation worker stops with it.Snapshot the data directory
A single For the Docker named volume:
tar of the root captures everything Pulse needs to restore.Online (hot) backup
If you cannot stop the server, use SQLite’s online backup API forpulse.db and copy the LadybugDB files separately. SQLite first:
boards/, global/, and uploads/. There is a small window where the SQLite snapshot may reference KG queue entries that have not yet landed in LadybugDB; on restore, the consolidation worker will replay any in-flight entries from the queue table.
Do not copy the
.lbug files while a write is in flight. The advisory lock in kg/workers/advisory_lock.py prevents concurrent writers, but it does not prevent a copy tool from reading a half-written page. Pause writes (stop the server) or rely on a filesystem snapshot (LVM, ZFS, APFS) for atomic point-in-time copies.Migrating between machines
Pulse stores no machine-specific state. You can move the data directory verbatim from one host to another as long as the receiving Pulse version matches.spec.in_progress because the validated→in_progress evaluation gate is enforced at the MCP layer only. Stop both servers, copy data/pulse.db and the relevant boards/{board_id}/graph.lbug, then trim the SQLite rows that should not move (queue entries, audit log) before restarting.
Resetting state
okto-pulse reset deletes the data directory after confirmation. Source: cli.py cmd_reset (inventory:139–151).
Inspecting current paths
okto-pulse status prints the resolved data directory and database path:
cli.py:567–614 (cmd_status) (inventory:67–78).
The 198 MCP tools exposed by the running process operate against the paths shown here — every board read, every consolidation, every attachment upload resolves under this root.
Related
Server configuration
Ports, bind hosts, CORS, logging, and worker tuning.
Environment variables
Full reference of every
CoreSettings field and its env-var name.Knowledge Graph
What lives in
graph.lbug and how consolidation populates it.Docker install
The named-volume recipe and the unprefixed-volume gotcha.