# MsgLib read-only sidecar contract Date: 2026-07-10 ## Purpose `MsgLibReadSidecar` is the bounded 32-bit .NET process used by the Go MCP layer to access copied or operator-local `MsgLib.db` files through the client-compatible `System.Data.SQLite` provider. It exists because C26 proved the database opens with the bundled 32-bit SQLite provider and password `123`, while a 64-bit probe fails at provider loading. Go should coordinate the process; the sidecar owns only the narrow Windows/.NET DB read boundary. ## Protocol - Protocol id: `isphere.msglib.v1` - Transport: one JSON request on stdin, one JSON response on stdout. - Process architecture: x86. - Database mode: read-only only. - Raw rows: never returned. - Message bodies: returned only when a future caller explicitly sets `list_messages.include_body=true`; current verification and MCP defaults keep it `false`. - DB mutation: never allowed. Request envelope: ```json { "protocol": "isphere.msglib.v1", "request_id": "example-1", "op": "self_check", "args": {} } ``` Response envelope: ```json { "protocol": "isphere.msglib.v1", "request_id": "example-1", "op": "self_check", "ok": true, "data": {}, "error": null, "warnings": [] } ``` ## Operations ### `self_check` Returns sidecar identity, protocol, runtime, process bitness, and capability flags. Key response fields: - `sidecar_name` - `sidecar_version` - `protocol` - `runtime` - `process_bits` - `requires_process_bits` - `db_mutation_allowed=false` - `message_body_reads_allowed=true` for explicit `list_messages.include_body` opt-in; verification keeps it disabled. ### `schema_summary` Validates provider load and read-only DB open, then returns schema metadata only. Required args: - `sqlite_dll_path`: path to bundled `System.Data.SQLite.dll`. - `db_path`: path to a copied/operator-local `MsgLib.db`. Optional args: - `password`: default `123`. - `include_counts`: default `true`; returns row counts only for target tables. - `max_tables`: default `128`, clamped to `1..256`. - `target_tables`: optional list; defaults to known display/message/file metadata tables. Returned data: - `metadata_only=true` - `read_only=true` - `message_body_values_returned=false` - `sqlite_version` - `tables`: table/view names only. - `columns`: table column names/types only. - `target_row_counts`: safe row counts for target tables only. ### `display_sources` Same safe provider/DB validation as `schema_summary`, but additionally maps schema availability to product-facing display source candidates. Returned data adds: - `display_sources[]` entries with: - `source` - `table` - `available` - `required_columns` - `present_columns` Initial display sources: - `contacts_roster` from `TD_Roster`. - `contacts_effigy` from `TD_CustomEffigy`. - `recent_display` from `tblRecent`. - `person_message_names` from `tblPersonMsg`. - `group_message_names` from `tblMsgGroupPersonMsg`. - `work_group_auth` from `TD_WorkGroupAuth`. - `received_file_metadata` from `TD_ReceiveFileRecord`. - `sent_file_metadata` from `TD_SendFileRecord`. ### `display_entities` Same safe provider/DB validation as `display_sources`, but returns bounded normalized contact/group display metadata. Required args: - `sqlite_dll_path` - `db_path` - `entity_type`: `contacts` or `groups` Optional args: - `password`: default `123`. - `query`: optional string matched against allowlisted jid/name columns. - `limit`: bounded to `1..100`; default `25`. Returned data adds: - `metadata_only=true` - `read_only=true` - `message_body_values_returned=false` - `raw_rows_returned=false` - `entities[]` entries with: - `entity_type` - `source_table` - `jid` - `display_name` - `confidence` - `matched_columns` Initial allowlisted identity sources: - Contacts: `TD_Roster(JId,Nick)`, `TD_CustomEffigy(PersonJid,PersonName)`, `tblRecent(PersonId,PersonName)`, `tblChatLevel(PersonId,PersonName)`, selected `tblPersonMsg` id/name pairs only. - Groups: `TD_WorkGroupAuth(GroupJID,GroupName)`, `TD_WorkGroupAuth(ChildGroupJid,GroupName)`, selected `tblMsgGroupPersonMsg` group/session id/name pairs only. ### `message_sources` Same safe provider/DB validation as `display_sources`, but reports whether candidate message tables are ready for a future DB-backed `isphere_receive_messages` source. Required args: - `sqlite_dll_path` - `db_path` Optional args: - `password`: default `123`. Returned data adds: - `metadata_only=true` - `read_only=true` - `message_body_values_returned=false` - `raw_rows_returned=false` - `file_paths_returned=false` - `message_sources[]` entries with: - `source` - `table` - `role` - `available` - `required_columns` - `present_columns` - `row_count` Initial message source candidates: - `direct_messages` from `tblPersonMsg`. - `group_messages` from `tblMsgGroupPersonMsg`. - `system_messages` from `TD_SystemMessageRecord`. - `group_receipts` from `TD_GroupReceiptMessage`. - `received_file_metadata` from `TD_ReceiveFileRecord`. - `recent_conversation_index` from `tblRecent`. ### `list_messages` Same safe provider/DB validation as `message_sources`, but returns a bounded normalized message list for future DB-backed receive-message work. C38 keeps this at sidecar/Go-wrapper level only; it is not wired into MCP receive by default. Required args: - `sqlite_dll_path` - `db_path` Optional args: - `password`: default `123`. - `conversation_id`: optional metadata filter. - `conversation_type`: `auto`, `direct`, `group`, or `system`; default `auto`. - `query`: optional metadata filter. Body columns are searched only when `include_body=true`. - `since`: optional timestamp lower bound using the DB timestamp column representation. - `cursor`: unsupported initially; any non-empty value is rejected. - `limit`: bounded to `1..50`; default `20`. - `include_body`: default `false`. - `include_attachment_metadata`: default `false`; returns filename/size metadata only, never path values or download refs. Returned data adds: - `read_only=true` - `message_body_values_returned`: equals `include_body`. - `raw_rows_returned=false` - `file_paths_returned=false` - `source_tables[]` - `messages[]` entries with normalized fields: - `message_id` / `id` - `conversation_id` - `conversation_type` - `sender_id` - `sender_name` - `receiver_id` - `text` / `content_text` (empty when `include_body=false`) - `content_type` - `timestamp` / `created_at` - `subject` - `read` - `source=local_readonly` - `raw_ref=msglib: