Database-first state refactor
기준일: 2026-07-26
공식 기준: Database-first state refactor
Database-first state refactor 문서는 OpenClaw 공식 문서(refactor/database-first)를 한국어로 정리한 가이드입니다. Migration plan for making SQLite the primary durable state and cache layer while keeping config file-backed 명령·설정 키·코드 예시는 공식 문서를 그대로 보존하며, 해석과 절차 안내는 한국어로 제공합니다. 최종 동작은 설치된 CLI 버전과 공식 원문을 확인하세요.
핵심 요약
Migration plan for making SQLite the primary durable state and cache layer while keeping config file-backed
한국어 가이드 범위: refactor/database-first 경로의 설정·명령·제약·예시를 학습용으로 재구성합니다.
문서 구성
공식 문서의 주요 섹션은 다음과 같습니다.
- Database-First State Refactor
- Decision
- Hard Contract
- Goal state and progress
- Hard goal
- Goal states
- Current state
- Remaining work
- Do not regress
- Code-Read Assumptions
- Code-Read Findings
- Current Code Shape
- Target Schema Shape
- Doctor Migration Shape
- Migration Inventory
- Migration Plan
- Phase 0: Freeze The Boundary
- Phase 1: Finish The Global Control Plane
- Phase 2: Introduce Per-Agent Databases
- Phase 3: Replace Session Store APIs
- Phase 4: Move Transcripts, ACP Streams, Trajectories, And VFS
- Phase 5: Backup, Restore, Vacuum, And Verify
- Phase 6: Worker Runtime
- Phase 7: Delete The Old World
- Backup And Restore
- Runtime Refactor Plan
- Performance Rules
- Static Bans
- Done Criteria
상세 내용
Database-First State Refactor
이 섹션의 세부 항목은 공식 문서 Database-First State Refactor를 참고하세요.
Decision
transcript, VFS, artifact, and large per-agent runtime state
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Global database:
~/.openclaw/state/openclaw.sqlite - Agent database: one SQLite database per agent for agent-owned workspace,
- Configuration stays file-backed:
openclaw.jsonremains outside the
Hard Contract
This migration has one canonical runtime shape:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Session rows persist session metadata only. They must not persist
- Transcript identity is always SQLite identity:
{agentId, sessionId}plus sqlite-transcript://...is not a runtime or protocol identity. New code must- Legacy
sessions.json, transcript JSONL,.jsonl.lock, pruning, truncation, - Legacy session config aliases belong only to doctor migration. Runtime does
- Session routing identity is typed relational state. Hot runtime and UI paths
- Channel-level direct-message markers such as
dmversusdirectare routing - Legacy hook handler config belongs only to doctor warning/migration surfaces.
- Runtime startup, hot reply paths, compaction, reset, recovery, diagnostics,
- Tests should seed and assert SQLite transcript rows through
runEmbeddedPiAgent(...), prepared worker runs, and the inner embedded- Runner diagnostics must store runtime/cache/payload trace records in SQLite.
- Raw stream logging uses
OPENCLAW_RAW_STREAM=1plus SQLite diagnostics rows. - QMD memory indexing must not export SQLite transcripts to markdown files.
- The QMD SDK subpath is QMD-only for new code. SQLite session transcript
- Built-in memory indexes live in the owning agent database. Runtime config and
Goal state and progress
이 섹션의 세부 항목은 공식 문서 Goal state and progress를 참고하세요.
Hard goal
주요 항목:
- One global SQLite database owns control-plane state:
- One per-agent SQLite database owns data-plane state:
- Config remains file-backed.
openclaw.jsonis not part of this database - Legacy files are doctor migration inputs only.
- Runtime never writes or reads session or transcript JSONL as active state.
Goal states
is forbidden for this refactor unless it is explicitly documented as doctor-only.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
not-started: file-era runtime code still writes active state.migrating: doctor/import code can move file data into SQLite.dual-read: temporary bridge reads both SQLite and legacy files. This statesqlite-runtime: runtime reads and writes SQLite only.clean: legacy runtime APIs and tests are removed, and the guard preventsdone: docs, tests, backup, doctor migration, and changed checks prove the
Current state
runtime APIs use {agentId, sessionId} or {agentId, sessionKey}, and sessions.json is doctor-only legacy input.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Sessions:
cleanfor runtime. Session rows live in the per-agent database, - Transcripts:
cleanfor runtime. Transcript events, identities, snapshots, - PI embedded runner:
clean. Embedded PI runs, prepared workers, compaction, - Cron:
cleanfor runtime. Runtime usescron_jobsand cron-ownedtask_runs; - Task registry:
clean. Task and Task Flow runtime rows live in - Plugin state:
clean. Plugin state/blob rows live in the shared global - Memory:
sqlite-runtimefor built-in memory and session transcript indexing. - Backup:
sqlite-runtime. Backup stages compact SQLite snapshots, omits live - Workspace setup:
sqlite-runtime. Setup completion, workspace attestations, - Doctor migration:
migrating, intentionally. Doctor imports legacy JSON, - Exec approvals:
file-runtime. TypeScript and macOS still read and write the - E2E scripts:
cleanfor runtime coverage. Docker MCP seeding writes SQLite
Remaining work
they are doctor legacy inputs. Files: src/cron/service.test-harness.ts, src/cron/service.runs-one-shot-main-job-disables-it.test.ts, src/cron/service/timer.regression.test.ts, src/cron/service/ops.test.ts, src/cron/service/store.test.ts, src/cron/service.heartbeat-ok-summary-suppressed.test.ts, src/cron/service.main-job-passes-heartbeat-target-last.test.ts, src/cron/store.test.ts. Proof: pnpm check:database-first-legacy-stores; rg -n 'storePath' src/cron --glob '!**/commands/doctor/**'.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Rename cron runtime-test store variables away from
storePathunless - Remove or rename obsolete file-era export test mocks.
- Make the Docker runtime-context legacy JSONL seed obviously doctor-only.
- Keep Kysely generated types aligned after any schema change.
- Re-run focused tests for touched stores, commands, and scripts.
- Before declaring
done, run the changed gate or remote broad proof.
Do not regress
The shipped per-agent version 1 schema has one bounded runtime migration to version 2 for stable memory-source identities.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- No transcript locators.
- No active session files.
- No fake JSONL test fixtures except doctor legacy migration tests.
- No raw SQLite access where Kysely is expected.
- No new file-era DB migrations. The global schema remains at version
1.
Code-Read Assumptions
No follow-up product decisions are blocking this plan. The implementation should proceed with these assumptions:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Use
node:sqlitedirectly and require a WAL-reset-safe Node runtime - Keep exactly one normal configuration file. Do not move config, plugin
- Runtime compatibility files are not required. Legacy JSON and JSONL files are
openclaw doctor --fixowns legacy file-to-database migration. Runtime- Credential compatibility follows the same rule: runtime credentials live in
- Generated model catalog state is database-backed. Runtime code must not write
- Runtime must not migrate, normalize, or bridge transcript locators. Active
- Runtime SQLite transcript reads do not run old JSONL entry-shape migrations or
- Legacy transcript JSONL parse/migration helpers are doctor-only. Runtime
- The old runtime-owned JSONL transcript streaming helper was deleted. Doctor
- Codex app-server bindings use the OpenClaw
sessionIdas the canonical - Context engines receive the current runtime contract directly. The registry
- Backup output should remain one archive file. Database contents should enter
- Transcript search is useful but not required for the first database-first
- Worker execution should stay experimental behind settings while the database
Code-Read Findings
The current branch is already past the proof-of-concept stage. The shared database exists, Node node:sqlite is wired through a small runtime helper, and former stores now write to state/openclaw.sqlite or the owning openclaw-agent.sqlite database.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Session
storePathis no longer a runtime identity, test fixture shape, or - Session writes no longer pass through the old in-process
store-writer.ts - Legacy path discovery still has valid migration uses, but runtime code should
- Agent-owned tables live in per-agent SQLite databases. The global DB keeps
- Doctor already imports several legacy files. The cleanup is to make that a
Current Code Shape
The branch already has a real shared SQLite base:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- The runtime floor now requires a WAL-reset-safe Node build: 22.22.3+,
src/state/openclaw-state-db.tsopensopenclaw.sqlite, sets WAL,- Kysely table types and runtime schema modules are generated from disposable
- Runtime stores derive selected and inserted row types from those generated
- The global SQLite schema remains at
user_version = 1. The per-agent schema - Relational ownership is enforced where the ownership boundary is canonical:
- Current shared tables include
agent_databases, - Arbitrary plugin-owned state does not get host-owned typed tables. Installed
- Plugin migrations are data migrations over plugin-owned namespaces, not host
src/state/openclaw-agent-db.tsopens- Global and per-agent databases record a
schema_metarow with database role, - Per-agent session identity now has a canonical
sessionsroot table keyed by - Per-agent external conversation identity is relational too:
- Transcript events, transcript snapshots, and trajectory runtime events now
- Memory-core indexes now use explicit agent-database tables
- Subagent run recovery state now lives in typed shared
subagent_runsrows - Current conversation bindings now live in typed shared
- Delivery queue recovery now overlays typed queue columns for channel, target,
- TUI last-session restore pointers now live in typed shared
- Discord command deployment hashes now live in the shared plugin-state SQLite
- Default TTS prefs now live in shared plugin-state SQLite rows keyed under the
- Secret target metadata now talks about stores instead of pretending every
- Secret audit no longer scans retired per-agent
auth.jsonfiles. Doctor owns - Legacy auth profile path helpers now live in doctor legacy code. Core auth
- Subagent run recovery and OpenRouter model capability cache runtime modules
Target Schema Shape
Keep schemas explicit. Host-owned runtime state uses typed tables. Plugin-owned opaque state uses plugin_state_entries / plugin_blob_entries; there is no generic host kv table.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
state_leases(scope, lease_key, owner, expires_at, heartbeat_at, payload_json, created_at, updated_at)
exec_approvals_config(config_key, raw_json, socket_path, has_socket_token, default_security, default_ask, default_ask_fallback, auto_allow_skills, agent_count, allowlist_count, updated_at_ms)
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
agent_databases(agent_id, path, schema_version, last_seen_at, size_bytes)
task_runs(...)
task_delivery_state(...)
flow_runs(...)
subagent_runs(run_id, child_session_key, requester_session_key, controller_session_key, created_at, ended_at, cleanup_handled, payload_json)
current_conversation_bindings(binding_key, binding_id, target_agent_id, target_session_id, target_session_key, channel, account_id, conversation_kind, parent_conversation_id, conversation_id, target_kind, status, bound_at, expires_at, metadata_json, updated_at)
plugin_binding_approvals(plugin_root, channel, account_id, plugin_id, plugin_name, approved_at)
tui_last_sessions(scope_key, session_key, updated_at)
plugin_state_entries(plugin_id, namespace, entry_key, value_json, created_at, expires_at)
plugin_blob_entries(plugin_id, namespace, entry_key, metadata_json, blob, created_at, expires_at)
media_blobs(subdir, id, content_type, size_bytes, blob, created_at, updated_at)
skill_uploads(upload_id, kind, slug, force, size_bytes, sha256, actual_sha256, received_bytes, archive_blob, created_at, expires_at, committed, committed_at, idempotency_key_hash)
skill_upload_chunks(upload_id, byte_offset, size_bytes, chunk_blob)
web_push_subscriptions(endpoint_hash, subscription_id, endpoint, p256dh, auth, created_at_ms, updated_at_ms)
web_push_vapid_keys(key_id, public_key, private_key, subject, updated_at_ms)
apns_registrations(node_id, transport, token, relay_handle, send_grant, installation_id, relay_origin, topic, environment, distribution, token_debug_suffix, updated_at_ms)
apns_registration_tombstones(node_id, deleted_at_ms)
node_host_config(config_key, version, node_id, token, display_name, gateway_host, gateway_port, gateway_tls, gateway_tls_fingerprint, gateway_context_path, updated_at_ms)
device_identities(identity_key, device_id, public_key_pem, private_key_pem, created_at_ms, updated_at_ms)
device_auth_tokens(device_id, role, token, scopes_json, updated_at_ms)
macos_port_guardian_records(pid, port, command, mode, timestamp)
workspace_setup_state(workspace_key, workspace_path, version, bootstrap_seeded_at, setup_completed_at, updated_at)
workspace_path_aliases(alias_key, alias_path, workspace_key, workspace_path, updated_at_ms)
workspace_attestations(workspace_key, attested_at_ms, updated_at_ms)
workspace_generated_bootstrap_hashes(workspace_key, filename, sha256)
native_hook_relay_bridges(relay_id, pid, hostname, port, token, expires_at_ms, updated_at_ms)
model_capability_cache(provider_id, model_id, name, input_text, input_image, reasoning, supports_tools, context_window, max_tokens, cost_input, cost_output, cost_cache_read, cost_cache_write, updated_at_ms)
agent_model_catalogs(catalog_key, agent_dir, raw_json, updated_at)
managed_outgoing_image_records(attachment_id, session_key, agent_id, message_id, created_at, updated_at, retention_class, alt, original_media_id, original_media_subdir, original_content_type, original_width, original_height, original_size_bytes, original_filename, record_json, cleanup_pending)
gateway_restart_sentinel(sentinel_key, version, kind, status, ts, session_key, thread_id, delivery_channel, delivery_to, delivery_account_id, message, continuation_json, doctor_hint, stats_json, payload_json, updated_at_ms)
channel_pairing_requests(channel_key, account_id, request_id, code, created_at, last_seen_at, meta_json)
channel_pairing_allow_entries(channel_key, account_id, entry, sort_order, updated_at)
voicewake_triggers(config_key, position, trigger, updated_at_ms)
voicewake_routing_config(config_key, version, default_target_mode, default_target_agent_id, default_target_session_key, updated_at_ms)
voicewake_routing_routes(config_key, position, trigger, target_mode, target_agent_id, target_session_key, updated_at_ms)
update_check_state(state_key, last_checked_at, last_notified_version, last_notified_tag, last_available_version, last_available_tag, auto_install_id, auto_first_seen_version, auto_first_seen_tag, auto_first_seen_at, auto_last_attempt_version, auto_last_attempt_at, auto_last_success_version, auto_last_success_at, updated_at_ms)
config_health_entries(config_path, last_known_good_json, last_promoted_good_json, last_observed_suspicious_signature, updated_at_ms)
sandbox_registry_entries(registry_kind, container_name, session_key, backend_id, runtime_label, image, created_at_ms, last_used_at_ms, config_label_kind, config_hash, cdp_port, no_vnc_port, entry_json, updated_at)
cron_jobs(store_key, job_id, name, description, enabled, delete_after_run, created_at_ms, agent_id, session_key, schedule_kind, schedule_expr, schedule_tz, every_ms, anchor_ms, at, stagger_ms, session_target, wake_mode, payload_kind, payload_message, payload_model, payload_fallbacks_json, payload_thinking, payload_timeout_seconds, payload_allow_unsafe_external_content, payload_external_content_source_json, payload_light_context, payload_tools_allow_json, delivery_mode, delivery_channel, delivery_to, delivery_thread_id, delivery_account_id, delivery_best_effort, failure_delivery_mode, failure_delivery_channel, failure_delivery_to, failure_delivery_account_id, failure_alert_disabled, failure_alert_after, failure_alert_channel, failure_alert_to, failure_alert_cooldown_ms, failure_alert_include_skipped, failure_alert_mode, failure_alert_account_id, next_run_at_ms, running_at_ms, last_run_at_ms, last_run_status, last_error, last_duration_ms, consecutive_errors, consecutive_skipped, schedule_error_count, last_delivery_status, last_delivery_error, last_delivered, last_failure_alert_at_ms, job_json, state_json, runtime_updated_at_ms, schedule_identity, sort_order, updated_at)
delivery_queue_entries(queue_name, id, status, entry_kind, session_key, channel, target, account_id, retry_count, last_attempt_at, last_error, recovery_state, platform_send_started_at, entry_json, enqueued_at, updated_at, failed_at)
commitments(id, agent_id, session_key, channel, account_id, recipient_id, thread_id, sender_id, kind, sensitivity, source, status, reason, suggested_text, dedupe_key, confidence, due_earliest_ms, due_latest_ms, due_timezone, source_message_id, source_run_id, created_at_ms, updated_at_ms, attempts, last_attempt_at_ms, sent_at_ms, dismissed_at_ms, snoozed_until_ms, expired_at_ms, record_json)
migration_runs(id, started_at, finished_at, status, report_json)
migration_sources(source_key, migration_kind, source_path, target_table, source_sha256, source_size_bytes, source_record_count, last_run_id, status, imported_at, removed_source, report_json)
backup_runs(id, created_at, archive_path, status, manifest_json)
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
sessions(session_id, session_key, session_scope, created_at, updated_at, started_at, ended_at, status, chat_type, channel, account_id, primary_conversation_id, model_provider, model, agent_harness_id, parent_session_key, spawned_by, display_name)
conversations(conversation_id, channel, account_id, kind, peer_id, parent_conversation_id, thread_id, native_channel_id, native_direct_user_id, label, metadata_json, created_at, updated_at)
session_conversations(session_id, conversation_id, role, first_seen_at, last_seen_at)
session_routes(session_key, session_id, updated_at)
session_entries(session_id, session_key, entry_json, updated_at)
transcript_events(session_id, seq, event_json, created_at)
transcript_event_identities(session_id, event_id, seq, event_type, has_parent, parent_id, message_idempotency_key, created_at)
transcript_snapshots(session_id, snapshot_id, reason, event_count, created_at, metadata_json)
vfs_entries(namespace, path, kind, content_blob, metadata_json, updated_at)
tool_artifacts(run_id, artifact_id, kind, metadata_json, blob, created_at)
run_artifacts(run_id, path, kind, metadata_json, blob, created_at)
trajectory_runtime_events(session_id, run_id, seq, event_json, created_at)
memory_index_meta(key, value)
memory_index_sources(id, path, source, hash, mtime, size)
memory_index_chunks(id, path, source, start_line, end_line, hash, model, text, embedding, updated_at)
memory_embedding_cache(provider, model, provider_key, hash, embedding, dims, updated_at)
memory_index_state(id, revision)
cache_entries(scope, key, value_json, blob, expires_at, updated_at)
transcript_events_fts(session_id, seq, text)
vfs_entries_fts(namespace, path, text)
Doctor Migration Shape
Doctor should call one explicit migration step that is reportable and safe to rerun:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- One migration pass discovers all legacy file sources and produces a plan
- Doctor creates a verified pre-migration backup archive before importing
- Imports are idempotent and keyed by source path, mtime, size, hash, and target
- Successful source files are removed or archived after the target database has
- Failed imports leave the source untouched and record a warning in
- Runtime code reads SQLite only after the migration exists.
- No downgrade/export-to-runtime-files path is required.
openclaw doctor --fix
Migration Inventory
tasks/runs.sqlite sidecar importer is deleted. Snapshot saves upsert by task id and delete only missing task/delivery rows.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Task registry runtime writes now use the shared database; the unshipped
- Task Flow runtime writes now use the shared database; the unshipped
- Plugin state runtime writes now use the shared database; the unshipped
- Builtin memory search no longer defaults to
memory/<agentId>.sqlite; its - Builtin memory reindex resets only memory-owned tables in the agent database.
- Sandbox container/browser registries from monolithic and sharded JSON. Runtime
- Cron job definitions, schedule state, and run history now use shared SQLite;
- Device identity/auth, push, update check, commitments, OpenRouter model
- Device/node pairing and bootstrap records now use typed SQLite tables
- Device-pair notification subscribers and delivered-request markers now use the
- Voice-call call records now use the shared SQLite plugin-state table under the
- QQBot gateway sessions, known-user records, and ref-index quote cache now use
- Discord model-picker preferences, command-deploy hashes, and thread bindings
- BlueBubbles catchup cursors and inbound dedupe markers now use SQLite plugin
- Telegram update offsets, sticker cache entries, reply-chain message cache
- iMessage catchup cursors, reply short-id mappings, and sent-echo dedupe rows
- Microsoft Teams conversations, polls, SSO tokens, and feedback learnings now
- Matrix sync cache, storage metadata, thread bindings, inbound dedupe markers,
- Nostr bus cursors and profile publish state now use SQLite plugin state under
- Active Memory session toggles now use SQLite plugin state under
- Skill Workshop proposal queues and review counters now use SQLite plugin state
- Outbound delivery and session delivery queues now share the global SQLite
- ACPX process leases now use SQLite plugin state under
acpx/process-leases - Backup and migration run metadata
- Agent session roots and compatibility-shaped session-entry payloads. Done for
Migration Plan
이 섹션의 세부 항목은 공식 문서 Migration Plan를 참고하세요.
Phase 0: Freeze The Boundary
Make the durable-state boundary explicit before moving more rows:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Add a
migration_runstable to the global database. - Add a single doctor-owned state migration service for file-to-database import.
- Make
planread-only and makeapplycreate a backup, import, verify, and - Add static bans so new runtime code cannot write legacy state files while
Phase 1: Finish The Global Control Plane
Keep shared coordination state in state/openclaw.sqlite:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Agents and agent database registry
- Task and Task Flow ledgers
- Plugin state
- Sandbox container/browser registry
- Cron/scheduler run history
- Pairing, device, push, update-check, TUI, OpenRouter/model caches, and other
- Backup and migration metadata
- Gateway media attachment bytes. Done for runtime writes; direct file paths
- Debug proxy capture sessions, events, and payload blobs. Done: captures live
Phase 2: Introduce Per-Agent Databases
Create one database per agent and register it from the global DB:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
sessionsas the canonical session root, withsession_entriesas theconversationsandsession_conversationsas the normalized providertranscript_events- transcript snapshots and compaction checkpoints. Done for runtime writes.
vfs_entriestool_artifactsand run artifacts- agent-local runtime/cache rows. Done for worker scoped caches.
- ACP parent stream events
- trajectory runtime events when they are not explicit export artifacts
~/.openclaw/state/openclaw.sqlite
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite
Phase 3: Replace Session Store APIs
Done for runtime. The file-shaped session store surface is not an active runtime contract:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Runtime no longer calls
loadSessionStore(storePath)or treatsstorePathas - Runtime row operations are
getSessionEntry,upsertSessionEntry, - Whole-store rewrite helpers, file writers, queue tests, alias pruning, and
- Deprecated root-package compatibility exports delegate to the doctor-only
sessions.jsonparsing remains only in doctor migration/import code and- Runtime lifecycle fallback reads SQLite transcript headers, not JSONL first
Phase 4: Move Transcripts, ACP Streams, Trajectories, And VFS
Make every agent data stream database-native:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Transcript append writes go through one SQLite transaction that ensures the
- ACP parent stream logs become rows, not
.acp-stream.jsonlfiles. Done. - ACP spawn setup no longer persists transcript JSONL paths. Done.
- Runtime trajectory capture writes event rows/artifacts directly. The explicit
- Disk workspaces stay on disk when configured as disk mode.
- VFS scratch and experimental VFS-only workspace mode use the agent DB.
Phase 5: Backup, Restore, Vacuum, And Verify
openclaw backup create does this archive verification by default; --no-verify skips only the post-write archive pass, not the snapshot creation integrity check.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Checkpoint every global and agent database.
- Snapshot each DB with SQLite online backup followed by offline
VACUUM. - Archive compact DB snapshots, config, external credentials, and requested
- Omit raw live
*.sqlite-waland*.sqlite-shmfiles. - Verify by opening every DB snapshot and running
PRAGMA integrity_check. - Restore copies snapshots back to their target paths. Restored global DBs use
Phase 6: Worker Runtime
Keep worker mode experimental while the database split lands:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Workers receive agent id, run id, filesystem mode, and DB registry identity.
- Each worker opens its own SQLite connection.
- Parent keeps channel delivery, approvals, config, and cancellation authority.
- Start with one worker per active run; add pooling only after lifecycle and DB
Phase 7: Delete The Old World
Done for runtime session management. The old world is allowed only as explicit doctor input or support/export output:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- No runtime
sessions.json, transcript JSONL, sandbox registry JSON, task - No JSON/session file pruning, file transcript truncation, session file locks,
- No runtime compatibility exports whose purpose is keeping old session files
- Explicit support exports remain user-requested archive/materialization
Backup And Restore
Backups should be one archive file, but database capture should be SQLite-native:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
Runtime Refactor Plan
with one atomic migration from the shipped version 1 memory-source shape.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Resolve global DB and per-agent DB paths.
- Keep the global schema at
user_version = 1. Per-agent DBs use version2 - Add close/checkpoint/integrity helpers used by tests, backup, and doctor.
- Move plugin state tables into the global database. Done for runtime
- Move task registry tables into the global database. Done for runtime
- Move Task Flow tables into the global database. Done for runtime writes;
- Move builtin memory-search tables into each agent database. Done; explicit
- Delete duplicate database openers, WAL setup, permission helpers, and
- Create agent DB on demand through the global database registry. Done.
- Move runtime session entries, transcript events, VFS rows, and tool
- Do not migrate branch-local shared-DB session entries, transcript events,
- Remove
storePathas the runtime identity. Done for runtime and guarded - Use
{ agentId, sessionKey }row operations. - Replace whole-store delete/insert with
upsertSessionEntry, - Delete
store-writer.tsand writer-queue tests. Done. - Delete runtime legacy-key pruning and alias-delete parameters from session
- Make sandbox registry reads and writes SQLite-only. Done.
- Import monolithic and sharded JSON only from the migration step. Done.
- Remove sharded registry locks and JSON writes. Done.
- Keep one typed registry table instead of storing registry rows as generic
- Done for runtime lock creation and runtime lock APIs.
- The standalone legacy
.jsonl.lockdoctor cleanup lane is removed. - State integrity no longer has a separate orphan transcript-file pruning
- Gateway singleton coordination uses typed SQLite
state_leasesrows under - Generic plugin SDK dedupe persistence no longer uses file locks or JSON
Performance Rules
write transactions. Do not layer synchronous lock retries above SQLite's single busy wait.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- One connection per thread/process is fine; do not share handles across
- Use WAL,
foreign_keys=ON, a 5s busy timeout, and shortBEGIN IMMEDIATE - Keep write transaction helpers synchronous unless/until an async transaction
- Keep parent delivery writes small and transactional.
- Avoid whole-store rewrites; use row-level upsert/delete.
- Add indexes for list-by-agent, list-by-session, updated-at, run id, and
- Store large artifacts, media, and vectors as BLOBs or chunked BLOB rows, not
- Keep opaque plugin-state entries small and scoped.
- Add SQL cleanup for TTL/expiration instead of filesystem pruning.
Static Bans
Add a repo check that fails new runtime writes to legacy state paths:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
sessions.json*.trajectory.jsonlexcept materialized support-bundle outputs.acp-stream.jsonlacp/event-ledger.jsoncache/*.jsonruntime cache filesagents/<agentId>/agent/auth.jsonagents/<agentId>/agent/models.jsoncredentials/oauth.jsongithub-copilot.token.jsonopenrouter-models.jsonauth-profiles.jsonauth-state.jsonexec-approvals.jsonopenclaw-workspace-state.jsonworkspace-state.jsonworkspace-attestations/*.attested- sibling
<workspace>.attested - Matrix
credentials*.jsonandrecovery-key.json cron/runs/*.jsonlcron/jobs.jsonjobs-state.jsondevice-pair-notify.jsondevices/pending.json/devices/paired.json/devices/bootstrap.jsonnodes/pending.json/nodes/paired.json(retired 2026.7: folded into paired device records at gateway startup)identity/device.json
Done Criteria
JSON, task sidecar SQLite, or plugin-state sidecar SQLite. The unshipped task and plugin-state sidecar SQLite importers are deleted.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문과
--help를 확인하세요.
주요 항목:
- Runtime data and cache writes go to the global or agent SQLite database.
- Runtime no longer writes session indexes, transcript JSONL, sandbox registry
- Legacy file import is doctor-only.
- Backup produces one archive with compact SQLite snapshots and integrity proof.
- Agent workers can run with disk, VFS scratch, or experimental VFS-only
- Config and explicit credential files remain the only expected persistent
- Repo checks prevent reintroducing legacy runtime file stores.
실습 체크리스트
- 공식 문서와 로컬 버전을 대조합니다:
https://docs.openclaw.ai/refactor/database-first - 관련 CLI는
openclaw --help및 하위 명령--help로 옵션을 확인합니다. - 설정 변경 시
openclaw config/openclaw doctor로 유효성을 검사합니다. - Gateway·채널·플러그인 변경 후에는 필요 시 Gateway를 재시작합니다.
자주 쓰는 명령·설정 예시
state_leases(scope, lease_key, owner, expires_at, heartbeat_at, payload_json, created_at, updated_at)
exec_approvals_config(config_key, raw_json, socket_path, has_socket_token, default_security, default_ask, default_ask_fallback, auto_allow_skills, agent_count, allowlist_count, updated_at_ms)
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
agent_databases(agent_id, path, schema_version, last_seen_at, size_bytes)
task_runs(...)
task_delivery_state(...)
flow_runs(...)
subagent_runs(run_id, child_session_key, requester_session_key, controller_session_key, created_at, ended_at, cleanup_handled, payload_json)
current_conversation_bindings(binding_key, binding_id, target_agent_id, target_session_id, target_session_key, channel, account_id, conversation_kind, parent_conversation_id, conversation_id, target_kind, status, bound_at, expires_at, metadata_json, updated_at)
plugin_binding_approvals(plugin_root, channel, account_id, plugin_id, plugin_name, approved_at)
tui_last_sessions(scope_key, session_key, updated_at)
plugin_state_entries(plugin_id, namespace, entry_key, value_json, created_at, expires_at)
plugin_blob_entries(plugin_id, namespace, entry_key, metadata_json, blob, created_at, expires_at)
media_blobs(subdir, id, content_type, size_bytes, blob, created_at, updated_at)
skill_uploads(upload_id, kind, slug, force, size_bytes, sha256, actual_sha256, received_bytes, archive_blob, created_at, expires_at, committed, committed_at, idempotency_key_hash)
skill_upload_chunks(upload_id, byte_offset, size_bytes, chunk_blob)
web_push_subscriptions(endpoint_hash, subscription_id, endpoint, p256dh, auth, created_at_ms, updated_at_ms)
web_push_vapid_keys(key_id, public_key, private_key, subject, updated_at_ms)
apns_registrations(node_id, transport, token, relay_handle, send_grant, installation_id, relay_origin, topic, environment, distribution, token_debug_suffix, updated_at_ms)
apns_registration_tombstones(node_id, deleted_at_ms)
node_host_config(config_key, version, node_id, token, display_name, gateway_host, gateway_port, gateway_tls, gateway_tls_fingerprint, gateway_context_path, updated_at_ms)
device_identities(identity_key, device_id, public_key_pem, private_key_pem, created_at_ms, updated_at_ms)
device_auth_tokens(device_id, role, token, scopes_json, updated_at_ms)
macos_port_guardian_records(pid, port, command, mode, timestamp)
workspace_setup_state(workspace_key, workspace_path, version, bootstrap_seeded_at, setup_completed_at, updated_at)
workspace_path_aliases(alias_key, alias_path, workspace_key, workspace_path, updated_at_ms)
workspace_attestations(workspace_key, attested_at_ms, updated_at_ms)
workspace_generated_bootstrap_hashes(workspace_key, filename, sha256)
native_hook_relay_bridges(relay_id, pid, hostname, port, token, expires_at_ms, updated_at_ms)
model_capability_cache(provider_id, model_id, name, input_text, input_image, reasoning, supports_tools, context_window, max_tokens, cost_input, cost_output, cost_cache_read, cost_cache_write, updated_at_ms)
agent_model_catalogs(catalog_key, agent_dir, raw_json, updated_at)
managed_outgoing_image_records(attachment_id, session_key, agent_id, message_id, created_at, updated_at, retention_class, alt, original_media_id, original_media_subdir, original_content_type, original_width, original_height, original_size_bytes, original_filename, record_json, cleanup_pending)
gateway_restart_sentinel(sentinel_key, version, kind, status, ts, session_key, thread_id, delivery_channel, delivery_to, delivery_account_id, message, continuation_json, doctor_hint, stats_json, payload_json, updated_at_ms)
channel_pairing_requests(channel_key, account_id, request_id, code, created_at, last_seen_at, meta_json)
channel_pairing_allow_entries(channel_key, account_id, entry, sort_order, updated_at)
voicewake_triggers(config_key, position, trigger, updated_at_ms)
voicewake_routing_config(config_key, version, default_target_mode, default_target_agent_id, default_target_session_key, updated_at_ms)
voicewake_routing_routes(config_key, position, trigger, target_mode, target_agent_id, target_session_key, updated_at_ms)
update_check_state(state_key, last_checked_at, last_notified_version, last_notified_tag, last_available_version, last_available_tag, auto_install_id, auto_first_seen_version, auto_first_seen_tag, auto_first_seen_at, auto_last_attempt_version, auto_last_attempt_at, auto_last_success_version, auto_last_success_at, updated_at_ms)
config_health_entries(config_path, last_known_good_json, last_promoted_good_json, last_observed_suspicious_signature, updated_at_ms)
sandbox_registry_entries(registry_kind, container_name, session_key, backend_id, runtime_label, image, created_at_ms, last_used_at_ms, config_label_kind, config_hash, cdp_port, no_vnc_port, entry_json, updated_at)
cron_jobs(store_key, job_id, name, description, enabled, delete_after_run, created_at_ms, agent_id, session_key, schedule_kind, schedule_expr, schedule_tz, every_ms, anchor_ms, at, stagger_ms, session_target, wake_mode, payload_kind, payload_message, payload_model, payload_fallbacks_json, payload_thinking, payload_timeout_seconds, payload_allow_unsafe_external_content, payload_external_content_source_json, payload_light_context, payload_tools_allow_json, delivery_mode, delivery_channel, delivery_to, delivery_thread_id, delivery_account_id, delivery_best_effort, failure_delivery_mode, failure_delivery_channel, failure_delivery_to, failure_delivery_account_id, failure_alert_disabled, failure_alert_after, failure_alert_channel, failure_alert_to, failure_alert_cooldown_ms, failure_alert_include_skipped, failure_alert_mode, failure_alert_account_id, next_run_at_ms, running_at_ms, last_run_at_ms, last_run_status, last_error, last_duration_ms, consecutive_errors, consecutive_skipped, schedule_error_count, last_delivery_status, last_delivery_error, last_delivered, last_failure_alert_at_ms, job_json, state_json, runtime_updated_at_ms, schedule_identity, sort_order, updated_at)
delivery_queue_entries(queue_name, id, status, entry_kind, session_key, channel, target, account_id, retry_count, last_attempt_at, last_error, recovery_state, platform_send_started_at, entry_json, enqueued_at, updated_at, failed_at)
commitments(id, agent_id, session_key, channel, account_id, recipient_id, thread_id, sender_id, kind, sensitivity, source, status, reason, suggested_text, dedupe_key, confidence, due_earliest_ms, due_latest_ms, due_timezone, source_message_id, source_run_id, created_at_ms, updated_at_ms, attempts, last_attempt_at_ms, sent_at_ms, dismissed_at_ms, snoozed_until_ms, expired_at_ms, record_json)
migration_runs(id, started_at, finished_at, status, report_json)
migration_sources(source_key, migration_kind, source_path, target_table, source_sha256, source_size_bytes, source_record_count, last_run_id, status, imported_at, removed_source, report_json)
backup_runs(id, created_at, archive_path, status, manifest_json)
schema_meta(meta_key, role, schema_version, agent_id, app_version, created_at, updated_at)
sessions(session_id, session_key, session_scope, created_at, updated_at, started_at, ended_at, status, chat_type, channel, account_id, primary_conversation_id, model_provider, model, agent_harness_id, parent_session_key, spawned_by, display_name)
conversations(conversation_id, channel, account_id, kind, peer_id, parent_conversation_id, thread_id, native_channel_id, native_direct_user_id, label, metadata_json, created_at, updated_at)
session_conversations(session_id, conversation_id, role, first_seen_at, last_seen_at)
session_routes(session_key, session_id, updated_at)
session_entries(session_id, session_key, entry_json, updated_at)
transcript_events(session_id, seq, event_json, created_at)
transcript_event_identities(session_id, event_id, seq, event_type, has_parent, parent_id, message_idempotency_key, created_at)
transcript_snapshots(session_id, snapshot_id, reason, event_count, created_at, metadata_json)
vfs_entries(namespace, path, kind, content_blob, metadata_json, updated_at)
tool_artifacts(run_id, artifact_id, kind, metadata_json, blob, created_at)
run_artifacts(run_id, path, kind, metadata_json, blob, created_at)
trajectory_runtime_events(session_id, run_id, seq, event_json, created_at)
memory_index_meta(key, value)
memory_index_sources(id, path, source, hash, mtime, size)
memory_index_chunks(id, path, source, start_line, end_line, hash, model, text, embedding, updated_at)
memory_embedding_cache(provider, model, provider_key, hash, embedding, dims, updated_at)
memory_index_state(id, revision)
cache_entries(scope, key, value_json, blob, expires_at, updated_at)
transcript_events_fts(session_id, seq, text)
vfs_entries_fts(namespace, path, text)
openclaw doctor --fix
~/.openclaw/state/openclaw.sqlite
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite
관련 링크
이 가이드는 공식 문서를 한국어 학습용으로 재구성한 것입니다. 옵션 기본값·플래그 이름은 설치 버전에 따라 달라질 수 있습니다.