schema

arizukoreference › schema

Every SQLite table in arizuko's shared messages.db. Schema lives in store/migrations/ (embedded via store/store.go:13); gated applies them at boot, every other daemon connects read/write but never migrates. State described here is the cumulative result of migrations 0001 through 0057 — the 0049–0057 run added cost-log, proxyd-routes, unified ACL, topic lineage, pane sessions, and group visibility.

30 live tables, grouped by concern. Two carry-overs from the 0001 baseline (grants, channels) have no live Go writers and are noted as vestigial.

1. Identity & ACL

auth_users

OAuth-issued user identities. One row per subject across all linked providers; linked_to_sub points at the canonical row when this row is a secondary identity.

columntypenulldefaultmeaning
idINTEGERPK autoincrement
subTEXTNOT NULL UNIQUEOAuth subject (e.g. github:1234, google:abc)
usernameTEXTNOT NULL UNIQUEdisplay handle
hashTEXTNOT NULLpassword hash (legacy local-auth)
nameTEXTNOT NULLfull name
created_atTEXTNOT NULLRFC3339
linked_to_subTEXTNULLcanonical sub when this row is a linked identity (0040)

Indexes: PK on id; unique on sub, username; partial idx_auth_users_linked_to_sub on linked_to_sub WHERE linked_to_sub IS NOT NULL.

Writers: store/auth.go:28 (INSERT), :90 (UPDATE linked_to_sub), :95 (INSERT linked), onbod/main.go:468 (UPDATE username on claim).

Readers: proxyd (login), webd, dashd, onbod.

Migrations: created 0001; 0040 added linked_to_sub.

auth_sessions

Server-side session table (refresh-token store). JWTs are short-lived; this row carries the long-lived refresh state.

columntypenullmeaning
token_hashTEXT PKNOT NULLSHA-256 of the cookie token
user_subTEXTNOT NULLowning auth_users.sub
expires_atTEXTNOT NULLRFC3339
created_atTEXTNOT NULLRFC3339

Writers: store/auth.go:121 (INSERT), :144 (DELETE).

Readers: proxyd, webd.

Migrations: created 0001.

acl

Unified permission rows. Answers "may principal P perform action A on scope S?" Replaced the legacy user_groups + grant_rules tables in v0.38.0. Principals are OAuth subs (google:…), folder agents (folder:…), room/user JIDs, or roles (role:operator). Match logic in auth.Authorize; effect='deny' wins over allow.

columntypenulldefaultmeaning
principalTEXTNOT NULLsub, folder, JID, or role:<name>
actionTEXTNOT NULLinteract, admin, mcp:<tool>, or *
scopeTEXTNOT NULLfolder glob
effectTEXTNOT NULL'allow''allow' or 'deny'
paramsTEXTNOT NULL''JSON predicate constraints (e.g. {"jid":"telegram:*"})
predicateTEXTNOT NULL''reserved for richer expressions
granted_byTEXTNULLactor source (CLI / dashd / migration)
granted_atTEXTNOT NULLRFC3339

PK: (principal, action, scope, params, predicate, effect). Indexes: acl_by_principal_action, acl_by_scope.

Writers: store/acl.go (AddACLRow, RemoveACLRow); cmd/arizuko grant; dashd ACL editor; migration 0053 (back-fill).

Readers: many — proxyd, webd, dashd, onbod, gated (every folder-scoped HTTP route via auth.Authorize); MCP list_acl.

Migrations: created 0052; 0053 back-filled from user_groups + grant_rules then dropped those tables.

acl_membership

Identity and role indirection. One edge (child, parent) means "anything child proves it is, it also is parent." Used for role membership ((sub, role:operator)) and for OAuth-mediated JID claims ((discord:user/811…, google:114alice)). Walked transitively at authorization time.

columntypenullmeaning
childTEXTNOT NULLthe holder of the identity
parentTEXTNOT NULLthe role or canonical identity it expands to
added_byTEXTNULLactor source
added_atTEXTNOT NULLRFC3339

PK: (child, parent). Indexes: acl_membership_by_child.

Writers: store/acl.go (AddMembership, RemoveMembership); arizuko grant <sub> **; OAuth callback for JID claims; migration 0053 (back-fill of pre-0053 user_groups('**') rows and user_jids bindings).

Readers: gated, proxyd, webd, dashd, onbod (via auth.Authorize walking Ancestors(sub)).

Migrations: created 0052.

identities + identity_claims + identity_codes

Cross-channel identity merge (spec 5/9). One identities row is a canonical person; identity_claims binds one or more platform sender subs to it; identity_codes are short-lived link codes minted by /auth/link-code. Advisory — agents read via inspect_identity, never enforce.

table.columntypemeaning
identities.idTEXT PKcanonical identity ID
identities.nameTEXT NOT NULLdisplay name
identities.created_atTEXT NOT NULLRFC3339
identity_claims.subTEXT PKplatform sender sub
identity_claims.identity_idTEXT NOT NULLidentities.id
identity_claims.claimed_atTEXT NOT NULLRFC3339
identity_codes.codeTEXT PKshort-lived link code
identity_codes.identity_idTEXT NOT NULLidentities.id
identity_codes.expires_atTEXT NOT NULLRFC3339

Indexes: idx_identity_claims_id on identity_claims(identity_id).

Writers: store/identities.go:34 (INSERT identity), :111 (INSERT OR REPLACE claim), :119 (DELETE claim), :134 (INSERT code), :169/:175/:186 (claim on code redeem + GC).

Readers: gated (inspect_identity MCP tool), api/api.go (link-code inbound detection).

Migrations: all three created 0035.

2. Messages, sessions, turns

messages

The main bus. Every inbound and outbound message lands here. Rebuilt by migration 0023 to drop dead columns and re-source identity around source.

columntypenulldefaultmeaning
idTEXT PKNOT NULLplatform-specific message ID
chat_jidTEXTNOT NULLtyped JID of the chat
senderTEXTNOT NULLtyped sender JID
sender_nameTEXTNULLdisplay name
contentTEXTNOT NULLmessage body
timestampTEXTNOT NULLRFC3339Nano
is_from_meINTEGERNOT NULL0set on outbound
is_bot_messageINTEGERNOT NULL0set on agent output
forwarded_fromTEXTNULLupstream forward source
reply_to_idTEXTNULL0003
reply_to_textTEXTNULLparent excerpt
reply_to_senderTEXTNULLparent sender (typed JID after 0042)
topicTEXTNOT NULL''topic-sessions key (0008)
routed_toTEXTNOT NULL''folder this message was routed to (0015)
verbTEXTNOT NULL'message'event type (0017)
attachmentsTEXTNOT NULL''JSON []chanlib.InboundAttachment (0019)
sourceTEXTNOT NULL''handling adapter; receiver inbound, deliverer outbound
erroredINTEGERNOT NULL0marked when a prior run failed; re-fed tagged (0030)
turn_idTEXTNULLinbound msg ID that triggered this outbound (0038)
statusTEXTNOT NULL'sent''sent' | 'pending' | 'failed' (0039)
chat_nameTEXTNOT NULL''human channel/group name (0046)

Indexes: idx_messages_chat_ts on (chat_jid, timestamp); idx_messages_turn_id on turn_id WHERE turn_id IS NOT NULL; idx_messages_status on status WHERE status != 'sent'.

Writers: store/messages.go:19 (PutMessage), timed/main.go:132 (scheduled-task inject); UPDATEs in store/messages.go (status, content, errored, delivered).

Readers: many — gated, dashd, webd, davd, vited, router, ipc.

Migrations: 0001, 0003 (reply_to_id), 0005 (source, group_folder), 0008 (topic), 0015 (routed_to), 0017 (verb), 0019 (attachments), 0023 (rebuild; drop group_folder), 0030 (errored), 0038 (turn_id), 0039 (status), 0042/0043 (typed JIDs), 0046 (chat_name).

messages_fts FTS5 shadow

An FTS5 virtual table over messages.content, kept in sync by triggers. Backs the find_messages MCP tool. messages.id is TEXT, so the shadow keys on the implicit INTEGER rowid SQLite gives the table. Tokenizer is unicode61 remove_diacritics 2 — Czech, Spanish, Japanese match without surprises (Czech "úroveň" matches "uroven").

columntypenulldefaultmeaning
contentFTS5indexed copy of messages.content
rowidINTEGERcontent_rowid='rowid' — joins back to messages.rowid

Sync: triggers messages_fts_ai (insert), messages_fts_au (update), messages_fts_ad (delete) mirror every change. Queried via messages_fts MATCH ? with bm25() rank and snippet() highlight.

Readers: store/messages.go:700 (FindMessages).

Migrations: 0070.

sessions

The active Claude Code session ID per (folder, topic). Used to resume an existing conversation in the in-container agent.

columntypenulldefaultmeaning
group_folderTEXTNOT NULLfolder path
topicTEXTNOT NULL''topic key
session_idTEXTNOT NULLClaude Code session UUID
parent_topicTEXTNULLtopic this one forked from ('' = main); NULL when no lineage (main itself)
forked_atTEXTNULLRFC3339Nano UTC fork point — audit metadata (the parent’s session file is already copied into the child’s session)
observed_cursorTEXTNULLper-topic high-water mark over is_observed=1 messages; gateway advances it after each turn

PK: (group_folder, topic).

Writers: store/sessions.goSetSession (UPSERT session_id), EnsureTopicLineage (seed lineage on first turn), ForkTopic (explicit branch), UpdateObservedCursor, DeleteSession.

Readers: gated.

Migrations: created 0001; rebuilt 0008 with topic column + composite PK; 0055 add parent_topic, forked_at, observed_cursor (spec 6/F).

session_log

Audit log of every agent run.

columntypemeaning
idINTEGER PK AUTOINCREMENT
group_folderTEXT NOT NULLfolder
session_idTEXT NOT NULLClaude Code session UUID
started_atTEXT NOT NULLRFC3339
ended_atTEXT NULLRFC3339
resultTEXT NULLoutcome summary
errorTEXT NULLerror message
message_countINTEGER NULLmessages processed

Writers: store/sessions.go:91 (gateway, at spawn end).

Readers: dashd.

Migrations: created 0001.

turn_results

Idempotent record per (folder, turn_id) so a webd round-handle protocol can fetch all frames of a single run by FK lookup instead of timestamp correlation.

columntypemeaning
folderTEXT NOT NULL
turn_idTEXT NOT NULLinbound message ID
session_idTEXT NULLresumed Claude session
statusTEXT NOT NULL'ok' / 'error'
recorded_atTEXT NOT NULLRFC3339Nano

PK: (folder, turn_id).

Writers: store/turns.go:15 (gateway).

Readers: webd (round-handle for /slink/<token>/{turn_id}/...).

Migrations: created 0036.

system_messages

System-origin events (e.g. migration broadcasts) merged into the agent's message stream.

columntypemeaning
idINTEGER PK AUTOINCREMENT
group_idTEXT NOT NULLtarget folder
originTEXT NOT NULL'self', 'platform', etc.
eventTEXT NOT NULLevent name
attrsTEXT NULLJSON attrs
bodyTEXT NOT NULL DEFAULT ''event payload
created_atTEXT NOT NULLRFC3339

Writers: store/sessions.go:54.

Readers: gated.

Migrations: created 0001.

chat_reply_state

Last bot reply ID per (chat_jid, topic) — used to chain assistant replies into a single visible thread on platforms that support thread/reply pointers.

columntypemeaning
jidTEXT NOT NULLchat JID (typed after 0043)
topicTEXT NOT NULL DEFAULT ''topic key
last_reply_idTEXT NOT NULLlast bot message ID

PK: (jid, topic).

Writers: store/messages.go:401.

Readers: gateway/adapter outbound layer.

Migrations: created 0014; 0043 rewrote rows to typed JID.

pane_sessions

Slack assistant-pane state per (team, user, thread_ts). Persists across slakd restarts; the in-memory map is a read-through cache. See slack pane for the concept.

columntypenullmeaning
team_idTEXTNOT NULLSlack workspace ID
user_idTEXTNOT NULLSlack user who opened the pane
thread_tsTEXTNOT NULLpane thread timestamp
channel_idTEXTNOT NULLDM channel that hosts the pane
context_jidTEXTNULLworkspace channel the user is currently viewing
opened_atTEXTNOT NULLRFC3339Nano UTC
last_status_atTEXTNULLlast setStatus call — used to debounce

PK: (team_id, user_id, thread_ts). Index: idx_pane_sessions_channel on channel_id.

Writers: store/pane_sessions.goUpsertPane, SetPaneContext, SetPaneStatusAt.

Readers: slakd (pane lookup keyed by channel), gateway (pane-context hint in buildAgentPrompt).

Migrations: created 0056.

3. Groups (folders), chats, routes

groups

The folder hierarchy. Path is identity (tier from depth via auth.Resolve). Created in 0020 by renaming the older registered_groups table and rekeying by folder.

columntypenulldefaultmeaning
folderTEXT PKNOT NULLfolder path (e.g. atlas/support)
nameTEXTNOT NULLdisplay name
added_atTEXTNOT NULLRFC3339
container_configTEXTNULLJSON GroupConfig (mounts, timeout, max_children)
parentTEXTNULLparent folder path
updated_atTEXTNULLRFC3339
productTEXTNOT NULL'assistant'product slug (0044)
openINTEGERNOT NULL11 = visible to sibling folders as ambient source (cross-folder <observed>); 0 = sealed off. Spec 6/F. Toggled via set_group_open.
observe_window_messagesINTEGERNULLper-group cap on ambient messages surfaced per turn (NULL = fall back to OBSERVE_WINDOW_MESSAGES). Per-route override wins.
observe_window_charsINTEGERNULLper-group cap on ambient chars per turn (NULL = fall back to OBSERVE_WINDOW_CHARS).

Writers: store/groups.go:30 (INSERT), :50 (DELETE), :250 (SetGroupOpen), SetGroupObserveWindow.

Readers: many — gated, dashd, webd, davd, onbod, timed, every MCP tool that scopes to a folder.

Migrations: created 0020; 0041 dropped state, spawn_ttl_days, archive_closed_days; 0044 added product; 0057 added open, observe_window_messages, observe_window_chars (spec 6/F).

chats

Per-chat state: agent cursor, sticky routing pins, single-user/group classification. Rebuilt 0023; is_group re-added 0033.

columntypenulldefaultmeaning
jidTEXT PKNOT NULLtyped chat JID
agent_cursorTEXTNULLlast-processed message ID (0020 moved from registered_groups)
sticky_groupTEXTNULLfolder pin via @folder command (0016)
sticky_topicTEXTNULLtopic pin via #topic command (0016)
is_groupINTEGERNOT NULL01 = group chat; gates user-secret overlay (0033)

Writers: store/groups.go:84 (upsert cursor), :209 (is_group), :237 (sticky_group), :252 (sticky_topic).

Readers: gated (routing, cursor, single-user check).

Migrations: 0001, 0002 (channel-prefix normalize), 0016 (sticky_group/sticky_topic), 0020 (added agent_cursor), 0023 (rebuild: dropped name/channel/is_group/last_message_time/errored), 0030 (DROP errored), 0033 (re-add is_group), 0042 (typed JIDs in PK).

routes

Inbound match-target table. match is space-separated key=glob pairs evaluated by router.RouteMatches; target is the destination folder with an optional #fragment. #observe is the only reserved mode (store without firing a turn); any other fragment (folder#deploy, folder#triage) pins the routed message to that topic. Routes evaluate in (seq, id) order; first match wins.

columntypenulldefaultmeaning
idINTEGER PK AUTOINCREMENTNOT NULL
seqINTEGERNOT NULL0evaluation order (lower first)
matchTEXTNOT NULL''space-separated key=glob predicates
targetTEXTNOT NULLdestination folder, optionally suffixed with #mode
observe_window_messagesINTEGERNULLper-route override for the trailing #observe message count folded into a trigger turn (0/NULL = fall back to OBSERVE_WINDOW_MESSAGES)
observe_window_charsINTEGERNULLper-route char cap on the #observe context window (0/NULL = fall back to OBSERVE_WINDOW_CHARS)

Indexes: idx_routes_seq on seq (post-0022 rebuild).

Writers: store/routes.go:38 (AddRoute), :55 (DELETE on folder tear-down), :62 (replace set), :72 (DELETE by id), onbod/main.go:974/:1039.

Readers: gated (ResolveRoute), dashd, ipc (route MCP tools).

Migrations: created 0001; 0008 ensure-exists + unique index; 0012 fix prefix seq; 0022 rebuild (drop jid/type, add match); 0042/0043 rewrite predicates to typed JIDs; 0054 drop impulse_config, encode mode in target fragment, add observe_window_*.

route_tokens

URL-bound credentials. One row per issued URL; the JID prefix drives sender attribution and default output style. Each URL is bound to its JID prefix kind: /chat/<token>/ accepts only web: tokens, /hook/<token> only hook: tokens. Mechanics shared at the handler; binding enforced at token lookup.

columntypenullmeaning
token_hashBLOB PKNOT NULLsha256 of the raw token; raw token returned once at issuance
jidTEXTNOT NULLweb:<folder>[/<suffix>] or hook:<folder>/<source>[/<suffix>]<folder> is the destination, distinct from owner_folder
owner_folderTEXTNOT NULLissuing folder; bounds revocation
created_atTEXTNOT NULLRFC3339

Indexes: route_tokens_jid on jid.

Writers: gated (insertRouteToken, called from both MCP issue_chat_link/issue_webhook and REST POST /v1/route_tokens/*).

Readers: webd (hash + lookup on every /chat/* and /hook/* request); gated (list, revoke).

web_routes

webd's reverse-proxy route table: path_prefixfolder with an access mode.

columntypenullmeaning
path_prefixTEXT PKNOT NULLe.g. /dash/
accessTEXTNOT NULL CHECK IN ('public','auth','deny','redirect')
redirect_toTEXTNULLdestination when access='redirect'
folderTEXTNOT NULLowning folder (ACL key)
created_atTEXTNOT NULLRFC3339

Writers: store/web_routes.go:25 (UpsertWebRoute), :40 (DELETE).

Readers: webd.

Migrations: created 0045.

4. Onboarding & invites

onboarding

Admission state machine. A new sender lands here; gates and tokens flow through this row.

columntypenullmeaning
jidTEXT PKNOT NULLtyped JID of the unknown sender
statusTEXTNOT NULL'awaiting_message' | 'queued' | 'approved' | etc.
prompted_atTEXTNULLRFC3339
createdTEXTNOT NULLRFC3339
tokenTEXTNULLopaque admission token (0024)
token_expiresTEXTNULLRFC3339
user_subTEXTNULLlinked auth_users.sub on claim
gateTEXTNULLgate name that admitted (0027)
queued_atTEXTNULLRFC3339 (0027)

Indexes: idx_onboarding_token on token.

Writers: store/onboarding.go (queue mgmt), onbod/main.go:235/:251/:321/:571/:580/:659.

Readers: onbod, dashd (operator queue), gated (admission check).

Migrations: created 0009; 0021 collapsed flow; 0023 dropped sender/world_name/channel; 0024 added token/user_sub; 0027 added gate/queued_at; 0042 rewrote rows to typed JID.

onboarding_gates

Per-gate admission limits.

columntypemeaning
gateTEXT PKgate name
limit_per_dayINTEGER NOT NULLcap
enabledINTEGER NOT NULL DEFAULT 1on/off

Writers: store/onboarding.go:43 (upsert), :52 (DELETE), :62 (enable/disable).

Readers: onbod, dashd.

Migrations: created 0029.

invites

Invite tokens for the onboarding flow. Hard cutover of older invitations table in 0032.

columntypenulldefaultmeaning
tokenTEXT PKNOT NULLopaque token
target_globTEXTNOT NULLfolder glob the invite admits to
issued_by_subTEXTNOT NULLissuer auth_users.sub
issued_atTEXTNOT NULLRFC3339
expires_atTEXTNULLRFC3339
max_usesINTEGERNOT NULL1cap
used_countINTEGERNOT NULL0counter

Writers: store/invites.go:55 (INSERT), :119 (DELETE), :138 (UPDATE used_count).

Readers: onbod, cmd/arizuko (arizuko invite).

Migrations: created 0032 (replaces dropped invitations from 0028).

5. Secrets

Per-folder MCP rule overlays (formerly the grant_rules table, dropped in 0053) now live in acl as (principal=folder:<f>, action=mcp:<tool>, scope=<f>, params, effect) rows. Tier defaults are still derived in code by grants.DeriveRules (used as a fall-back for mcp:* actions with no acl match).

secrets

Folder- and user-scoped secret values. Plaintext at rest (operator trusts disk + FS perms per spec 9/11).

columntypemeaning
scope_kindTEXT NOT NULL'folder' or 'user'
scope_idTEXT NOT NULLfolder path or auth_users.sub
keyTEXT NOT NULLenv-var name
valueBLOB NOT NULLplaintext (renamed from enc_value in 0047)
created_atTEXT NOT NULLRFC3339

PK: (scope_kind, scope_id, key).

Indexes: partial idx_secrets_folder on scope_id WHERE scope_kind='folder'; partial idx_secrets_user on scope_id WHERE scope_kind='user'.

Writers: store/secrets.go:55 (upsert), :119 (DELETE), dashd/me_secrets.go:133 (user-scope INSERT), :174 (UPDATE), :209 (DELETE).

Readers: gated (FolderSecretsResolved, UserSecrets at spawn), dashd.

Migrations: created 0034 (originally enc_value BLOB); 0047 renamed to value.

secret_use_log

Audit log for the secret broker (spec 9/11). One row per tool-call × resolved key. No secret values stored.

columntypedefaultmeaning
tsTEXT NOT NULLRFC3339Nano
spawn_idTEXT NOT NULL''
caller_subTEXT NOT NULL''resolved user sub
folderTEXT NOT NULL''spawn folder
toolTEXT NOT NULLMCP tool name
keyTEXT NOT NULLsecret key name
scopeTEXT NOT NULL'folder' or 'user'
statusTEXT NOT NULL'ok' / 'denied' / 'missing'
latency_msINTEGER NOT NULL0

Indexes: idx_secret_use_log_ts on ts.

Writers: store/secret_use_log.go:25.

Readers: dashd (audit views).

Migrations: created 0048.

6. Egress

network_rules

Per-folder egress allowlist; resolved root-to-folder by store.ResolveAllowlist at container spawn and consumed by crackbox.

columntypedefaultmeaning
folderTEXT NOT NULLfolder path; '' = root seed
targetTEXT NOT NULLhost (e.g. api.github.com)
created_atTEXT NOT NULLRFC3339
created_byTEXT NOT NULL'''system' / sub

PK: (folder, target). Indexes: idx_network_rules_folder on folder.

Seed rows: ('', 'anthropic.com'), ('', 'api.anthropic.com') at migration time.

Writers: store/network.go:18 (INSERT OR IGNORE), :27 (DELETE).

Readers: gated, dashd, cmd/arizuko (arizuko network).

Migrations: created 0037.

7. Scheduling

scheduled_tasks

Cron- and interval-scheduled prompts (driven by timed).

columntypenulldefaultmeaning
idTEXT PKNOT NULL
ownerTEXTNOT NULLowning folder
chat_jidTEXTNOT NULLtyped chat JID (rewritten in 0043)
promptTEXTNOT NULLinjected text
cronTEXTNULLcron expr / interval ms / empty = one-shot
next_runTEXTNULLRFC3339
statusTEXTNOT NULL'active''active' | 'paused' | 'firing' | 'completed'
created_atTEXTNOT NULLRFC3339
context_modeTEXTNOT NULL'group''group' | 'isolated' (0011)

Indexes: idx_tasks_next on (status, next_run).

Writers: store/tasks.go:27 (INSERT), :81 (UPDATE), :86 (DELETE), timed/main.go:96 (status transitions during fire).

Readers: timed, gated, ipc (task MCP tools), dashd.

Migrations: created 0001; 0011 added context_mode; 0043 rewrote chat_jid to typed shape.

task_run_logs

Audit log for every scheduled-task fire.

columntypemeaning
idINTEGER PK AUTOINCREMENT
task_idTEXT NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE
run_atTEXT NOT NULLRFC3339
duration_msINTEGER NULL
statusTEXT NOT NULL'ok' / 'error'
resultTEXT NULL
errorTEXT NULL

Indexes: idx_task_run_logs_task on task_id.

Writers: timed/main.go:86.

Readers: dashd, ipc (list_task_runs).

Migrations: created 0011.

8. Misc

router_state

Generic key-value scratch for the gateway (per-key strings).

columntypemeaning
keyTEXT PK
valueTEXT NOT NULL

Writers: store/sessions.go:45.

Readers: gated.

Migrations: created 0001.

9. Vestigial (declared but unused)

Pre-v0.38.0 the schema also carried grants, user_groups, user_jids, and grant_rules. Migration 0053 dropped all four after back-filling acl + acl_membership.

channels read-only (dashd), not written by live code

Intended persistent registry for channel adapter URL lookup; current implementation keeps the registry in memory (chanreg/). dashd reads counts/rows from this table for the operator dashboard, but no production code writes it.

columntypemeaning
nameTEXT PKadapter name
urlTEXT NOT NULLbackend URL
jid_prefixesTEXT NULL
capabilitiesTEXT NULL

Migrations: created 0009.

email_threads declared in messages.db but unused (emaid uses its own emaid.db)

0001 defined this table in the shared DB, but the live emaid adapter maintains its own email_threads in emaid.db with a different shape (emaid/store.go:26). The shared-DB row is dead.

columntypemeaning
thread_idTEXT PK
chat_jidTEXT NOT NULL
subjectTEXT NULL
last_message_idTEXT NULL

Migrations: created 0001.

10. Tables removed by migrations

11. Audit of writers

Schema owner: gated (the //go:embed migrations/*.sql in store/store.go:13 runs at boot). Every other daemon opens the same DB read/write but never applies migrations. Per-table writer mapping above lists the canonical INSERT/UPDATE/DELETE call sites; multiple readers per table is the norm and noted as "many" where widespread.