Skip to content
Documentation GitHub
Architecture

Database Schema Reference

Last Updated: 2026-03-07 Schema Version: V001 (consolidated baseline) Source: crates/infrastructure/sqlite/src/migrations/mod.rs


Each workspace has its own SQLite database at {workspace_path}/inklings.db. The database uses WAL (Write-Ahead Logging) mode for concurrent read access and stores all workspace content: pages, blocks, tags, attachments, sync state, embeddings, event history, and more.

The schema is managed by rusqlite_migration using PRAGMA user_version for version tracking. The current schema is a single consolidated baseline (WORKSPACE_V001). Future migrations are added as WORKSPACE_V002, etc.



TablePurposeKey ColumnsNotable Indexes
workspaceSingle-row workspace metadataid, name, cloud_id, sync_enabled, metadata_cursor, deletion_cursor
pagesPage entities with parent-child hierarchyid, slug (unique), parent_slug (FK self), title, raw_markdown, page_type, is_deleted, layoutidx_pages_slug, idx_pages_parent_slug, idx_pages_page_type, idx_pages_is_deleted, idx_pages_deleted_at (partial: is_deleted=1)
blocksBlock content within pagesid, page_id (FK pages), slot_id, content (text), content_loro (BLOB), content_loro_version, content_type, content_type_metadata, areaidx_blocks_page_id; unique on (page_id, slot_id)
frontmatterKey-value metadata per pagepage_id (FK pages), key, valuePK on (page_id, key)
TablePurposeKey ColumnsNotable Indexes
typesType definitions (Page, Folder + user-defined)id, slug (unique), name, is_system, default_layout_id, sort_orderidx_types_slug, idx_types_is_system
propertiesProperty definitions for typed pagesid, slug (unique), name, value_type, config (JSON)idx_properties_slug
type_property_refsJunction: which properties belong to which typestype_id (FK types), property_id (FK properties), sort_orderPK on (type_id, property_id)
page_type_assignmentsJunction: which types are assigned to which pagesid, page_id (FK pages), type_id (FK types), scope, source_ididx_page_type_assignments_page, idx_page_type_assignments_type; unique on (page_id, type_id)
container_rulesAuto-assign types to pages created in foldersid, folder_page_id (FK pages), type_id (FK types), depthidx_container_rules_folder, idx_container_rules_type; unique on (folder_page_id, type_id)
collection_viewsSaved view preferences per typeid, type_slug (unique), view_mode, column_config (JSON)idx_collection_views_type_slug
layoutsReusable CSS Grid layout definitionsid, name (unique), layout (JSON), is_builtinidx_layouts_name
TablePurposeKey ColumnsNotable Indexes
tag_groupsGrouping for tagsid, name, slug (unique), color
tagsTag entitiesid, name, slug (unique), color, group_id (FK tag_groups)idx_tags_group_id
page_tagsJunction: tag-to-page assignmentspage_id (FK pages), tag_id (FK tags), assigned_atidx_page_tags_tag_id; PK on (page_id, tag_id)
TablePurposeKey ColumnsNotable Indexes
referencesWiki-link tracking ([[Display|slug]])id (autoincrement), source_page_id (FK pages), target_page_id (FK pages, nullable), target_slug, display_text, headingidx_references_target, idx_references_source, idx_references_slug
TablePurposeKey ColumnsNotable Indexes
attachmentsUploaded file metadataid, original_filename, file_extension, content_type, size_bytes, content_hash (unique), sync_statusidx_attachments_hash (unique), idx_attachments_sync_status, idx_attachments_pending_sync (partial: sync_status IN ('local_only', 'failed'))
attachment_referencesJunction: which pages reference which attachmentsattachment_id (FK attachments), page_id (FK pages), block_ididx_attachment_refs_page; PK on (attachment_id, page_id)
attachment_sync_queuePending attachment uploads to cloudid (autoincrement), attachment_id (FK attachments), retry_count, synced_atidx_attachment_sync_queue_pending (partial: synced_at IS NULL)
TablePurposeKey ColumnsNotable Indexes
sync_statePer-block CRDT version vectors and sync cursorblock_id (PK), local_version_vector (BLOB), remote_version_vector (BLOB), last_synced_at
sync_queueOffline push queue for block CRDT updatesid (autoincrement), block_id, update_bytes (BLOB), retry_count, version_vector (BLOB)idx_sync_queue_created
sync_dead_lettersFailed sync entries exceeding retry capid (autoincrement), block_id, update_bytes (BLOB), retry_count, last_erroridx_sync_dead_letters_failed_at
page_metadata_syncPer-field LWW state for metadata syncpage_id, field, value, changed_at, device_idPK on (page_id, field)
metadata_sync_queuePending metadata field changes for pushid (autoincrement), page_id, field, value, changed_at, device_id, synced_atidx_metadata_sync_queue_pending (partial), idx_metadata_sync_queue_page_field (partial)
page_tombstonesTombstones for remotely deleted pagespage_id (PK), deleted_by_device, page_title, deleted_at, had_local_edits
deletion_sync_queuePending page deletion tombstones for pushid (autoincrement), page_id, page_title, device_id, synced_atidx_deletion_sync_queue_pending (partial), idx_deletion_sync_queue_page (partial)
TablePurposeKey ColumnsNotable Indexes
page_embeddingsDense vector embeddings for semantic searchpage_id (PK, FK pages), model_id, model_version, embedding (BLOB, 768 f32 = 3072 bytes)idx_page_embeddings_model
TablePurposeKey ColumnsNotable Indexes
event_logAppend-only structural change logid, entity_type, entity_id, event_type, before_value, after_value, device_id, timestampidx_event_log_entity, idx_event_log_timestamp, idx_event_log_entity_id, idx_event_log_timestamp_desc
bookmarksNamed anchors in the event timelineid, name, description, timestamp, created_by
event_log_summariesCollapsed history between bookmark intervalsid (autoincrement), entity_type, entity_id, from_bookmark_id, to_bookmark_id, summary, event_countidx_event_log_summaries_entity
page_historyLegacy audit shadow table (read-only, no write triggers)id (autoincrement), page_id, field, old_value, new_value, changed_atidx_page_history_page_id, idx_page_history_restore

Schema V002 adds a ref_code column to four tables for external addressing (deep links, MCP API, share URLs):

TableColumnDefinitionIndex
pagesref_codeTEXT NOT NULL DEFAULT ''idx_pages_ref_code (partial, WHERE ref_code != '')
blocksref_codeTEXT NOT NULL DEFAULT ''idx_blocks_ref_code (partial)
bookmarksref_codeTEXT NOT NULL DEFAULT ''idx_bookmarks_ref_code (partial)
attachmentsref_codeTEXT NOT NULL DEFAULT ''idx_attachments_ref_code (partial)

The partial unique index (WHERE ref_code != '') allows the backfill UPDATE (hex(randomblob(8))) to run on existing rows without triggering false uniqueness violations. All rows created after V002 receive a proper 11-character base62 ref_code generated via RefCode::generate().

See Identifier Strategy for the full three-tier model.


A contentless FTS5 virtual table with 3 columns and BM25 weighting:

ColumnWeightSource
title10.0pages.title
content1.0pages.raw_markdown
tags5.0Aggregated tags.name via page_tags join

Configuration: content='' (contentless), contentless_delete=1.

The FTS index is maintained by 5 triggers:

TriggerFires OnPurpose
pages_fts_insertAFTER INSERT ON pagesIndex new non-deleted pages
pages_fts_updateAFTER UPDATE ON pagesRe-index on page content/title change
pages_fts_deleteBEFORE DELETE ON pagesRemove from index on permanent delete
page_tags_fts_insertAFTER INSERT ON page_tagsRe-index page when tag is added
page_tags_fts_deleteAFTER DELETE ON page_tagsRe-index page when tag is removed
tags_fts_name_updateAFTER UPDATE OF name ON tagsRe-index all pages with renamed tag

Soft-deleted pages (is_deleted = 1) are excluded from the FTS index. The {{property:value}} syntax is tokenized naturally by FTS5’s unicode61 tokenizer — braces and colons act as separators.


The V001 migration seeds two system types and four system properties:

System types: Page (slug: page) and Folder (slug: folder), both with is_system = 1.

System properties (linked to Page type): summary, cover-image, tags, aliases.


The WorkspaceDatabase struct (crates/infrastructure/sqlite/src/connection.rs) implements a writer + reader pool architecture:

ComponentCountPurpose
Writer1All writes and transactions (SQLite allows one writer at a time)
Readers4Concurrent reads via WAL mode, round-robin selection

Every connection (writer and readers) is configured with:

PragmaValuePurpose
busy_timeout5000msWait instead of failing immediately on lock contention
journal_modeWALEnable concurrent reads during writes
foreign_keysONEnforce foreign key constraints
synchronousNORMALBalance durability and performance
cache_size-2000 (2 MB)Per-connection page cache
  • with_reader(f) — execute read-only closure on a reader connection (round-robin)
  • with_writer(f) — execute write closure on the writer connection
  • with_transaction(f) — execute closure within a transaction (always uses writer)
  • with_connection(f) — deprecated alias for with_reader

The agent database is separate from inklings.db. Each workspace has a workspace-level agents.db at {workspace_path}/agents.db, and an account-level agents.db at {tauri_data_dir}/agents.db.

Source: crates/infrastructure/sqlite/src/agents/migrations.rs

channels table (workspace-level agents.db)

Section titled “channels table (workspace-level agents.db)”
ColumnTypeNotes
idTEXT PKUUID
nameTEXTDisplay name (UNIQUE constraint)
descriptionTEXTOptional description
workspace_idTEXTUUID of the owning workspace (added in V007)
is_defaultINTEGER1 for the default channel; exactly one per workspace (added in V007)
created_atTEXTISO-8601 timestamp
created_byTEXTCreator identifier (e.g., "system")

conversations table (workspace-level agents.db)

Section titled “conversations table (workspace-level agents.db)”
ColumnTypeNotes
idTEXT PKUUID
ref_codeTEXT11-char base62 stable identifier (added in V007, backfilled in Rust)
channel_idTEXT FKReferences channels(id) ON DELETE CASCADE
started_atTEXTISO-8601 timestamp
ended_atTEXTISO-8601 timestamp; null while Active
statusTEXT"active", "idle", or "archived"

Indexes: idx_conversations_channel (channel_id), idx_conversations_status (status), idx_conversations_ref_code (ref_code, partial — excludes empty-string sentinel).

scratchpads table (workspace-level agents.db)

Section titled “scratchpads table (workspace-level agents.db)”
ColumnTypeNotes
conversation_idTEXTUUID; part of composite primary key
keyTEXTEntry key; part of composite primary key
valueTEXTJSON-encoded value
updated_atTEXTISO-8601 timestamp
created_atTEXTISO-8601 timestamp

Primary key: (conversation_id, key). Index: idx_scratchpads_conversation_created (conversation_id, created_at) for TTL-based cleanup queries.


  • Page System — page and block entity design, domain rules
  • Sync System — sync engine state machine, cursor safety, LWW patterns
  • Embedding System — embedding pipeline, model details, search integration
  • Event Log System — event recording, timeline queries, history collapse
  • Tag System — tag CRUD, groups, FTS integration
  • Attachment System — upload, storage, sync queue
  • Wiki-Link System — reference tracking, backlinks, rename propagation
  • Layout System — CSS Grid templates, builtin layouts
  • CRDT BLOB Passthrough Pipeline — binary pass-through design for content_loro
  • SQLite WAL Reader/Writer Pool — connection pool rationale
  • Agent Memory System — memory hierarchy, scratchpad, channels, conversations
  • Source: crates/infrastructure/sqlite/src/migrations/mod.rs — full workspace migration SQL
  • Source: crates/infrastructure/sqlite/src/agents/migrations.rs — full agents.db migration SQL
  • Source: crates/infrastructure/sqlite/src/connection.rsWorkspaceDatabase implementation

Was this page helpful?