Files
isphere-ai-bridge/docs/msglib-read-sidecar-contract.md
2026-07-10 09:11:22 +08:00

14 KiB

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:

{
  "protocol": "isphere.msglib.v1",
  "request_id": "example-1",
  "op": "self_check",
  "args": {}
}

Response envelope:

{
  "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:<table>
    • attachments[] with file_id, file_name, size_bytes, and empty download_ref
  • next_cursor=null

Initial allowlisted message sources:

  • Direct: tblPersonMsg.
  • Group: tblMsgGroupPersonMsg.
  • System: TD_SystemMessageRecord.
  • Attachment metadata: TD_ReceiveFileRecord, with FilePath/path-like values intentionally excluded.

Security and scope rules

  • The sidecar must open DB files with read-only connection settings.
  • db_path must point to a file named MsgLib.db.
  • The schema/display-source operations return schema, columns, counts, and display-source availability only.
  • display_entities may return bounded contact/group jid and display_name metadata only.
  • message_sources may return message-source table names, required/present column names, roles, availability flags, and row counts only.
  • list_messages may return bounded normalized message metadata. It returns text/content_text only when include_body=true; the standard verification path uses include_body=false.
  • It does not return file path values, attachment contents, raw row sets, sends, downloads, hooks, injection, or DB writes.
  • It does not implement send, file download, mark-read, login, hooks, injection, or DB writes.
  • The Go MCP layer remains the coordinator; this process is a bounded local reader.

Current implementation

Committed C27 implementation:

  • Source: native/MsgLibReadSidecar/.
  • Build script: scripts/build-msglib-sidecar.ps1.
  • Verification script: scripts/verify-msglib-sidecar.ps1.
  • Default verification builds an x86 executable and checks self_check.
  • Evidence-backed verification with C26 paths checked provider load/read-only DB open and returned 8 display-source candidates.

Go client wrapper

Committed C28 implementation:

  • Source: internal/msglib/.
  • ConfigFromEnv reads ISPHERE_MSGLIB_SIDECAR_EXE, ISPHERE_MSGLIB_SQLITE_DLL, ISPHERE_MSGLIB_DB, and optional ISPHERE_MSGLIB_PASSWORD.
  • NewClient creates a process-backed client for protocol isphere.msglib.v1.
  • SelfCheck calls self_check and decodes sidecar identity, protocol, bitness, and read-only/no-mutation flags.
  • DisplaySources calls display_sources and decodes display-source availability metadata.
  • The client uses stdin/stdout JSON, request ids, context.Context, per-call timeout, stderr capture, and typed SidecarError values.
  • Unit tests use a fake sidecar process; normal tests do not require real System.Data.SQLite.dll or copied MsgLib.db.
  • C28 does not register a new MCP tool and does not return message bodies, file paths, raw rows, contact row values, or group row values.

Committed C29 implementation:

  • Sidecar op: display_entities.
  • Go wrapper: DisplayEntities(ctx, DisplayEntitiesOptions).
  • Verification script calls display_entities only when copied DB env paths are supplied, and prints only sanitized entity_count plus source_tables summaries.
  • Evidence-backed C29 verification against the copied DB returned contact/group entity counts and source-table names without printing entity values.
  • C31 adds optional scripts/verify-msglib-mcp-enrichment.ps1 to prove MCP-level contact/group enrichment through the real sidecar while printing only counts and msglib:<source_table> refs. C33 extends the same optional script to create a synthetic encrypted PacketReader fixture from internally selected MsgLib contact/group metadata and verify receive-message sender_name plus conversation.display_name enrichment; the printed result remains limited to counts, booleans, and source refs.

Committed C36 implementation:

  • Sidecar op: message_sources.
  • Go wrapper: MessageSources(ctx).
  • Verification script calls message_sources only when copied DB env paths are supplied, and prints only sanitized message_source_count plus message-source summaries containing source/table/role/availability/row-count metadata.
  • Evidence-backed C36 verification against the copied DB returned six message-source summaries without printing message body values, file path values, raw rows, or copied DB contents.

Committed C38 implementation:

  • Sidecar op: list_messages.
  • Go wrapper: ListMessages(ctx, ListMessagesOptions).
  • The op reads only allowlisted direct/group/system message tables and optional received-file metadata from a copied MsgLib.db, clamps limit to 1..50, rejects non-empty cursor, and never returns raw rows or file path values.
  • scripts/verify-msglib-sidecar.ps1 calls list_messages only when copied DB env paths are supplied, with include_body=false, and prints only message_list_count, message_list_sources, safety booleans, and omission booleans.
  • Evidence-backed C38 smoke returned normalized message count/source-table evidence without printing message text, body values, file paths, download refs, raw rows, sends, downloads, hooks, injection, or DB writes.

Committed C39 implementation:

  • Go adapter: internal/isphere.MsgLibMessageSource.
  • The adapter uses an injected ListMessages provider and maps sidecar-normalized messages into the existing receive-message domain model.
  • It preserves explicit source/raw_ref values and bounded attachment metadata while rejecting provider results that report non-read-only mode, raw rows, or file path values.
  • It is not wired into MCP default receive behavior; auto remains PacketReader/log-backed until an explicit source-selection loop is completed.

Committed C40 implementation:

  • Tool-layer selector: RegisterISphereReadToolsWithReceiveSources.
  • source_preference="", auto, and local_readonly continue to use the primary PacketReader/log-backed source.
  • source_preference="msglib_readonly" can select an explicitly configured MsgLib receive source and is rejected when that source is absent.
  • Server env wiring is deferred to C41, so standard MCP startup and verification remain deterministic.

Committed C41 implementation:

  • Server env wiring now constructs one MsgLib sidecar client for both display enrichment and explicit receive selection when all MsgLib env vars are present.
  • isphere_receive_messages with source_preference="msglib_readonly" uses internal/isphere.MsgLibMessageSource; default auto still uses PacketReader/log-backed source.
  • scripts/verify-msglib-mcp-enrichment.ps1 includes an optional copied-DB explicit DB receive smoke and prints only counts, msglib:<source_table> refs, and booleans.
  • Standard scripts/verify-go-mcp.ps1 continues to clear MsgLib env and remains deterministic.

Verification

Default verification builds and checks self_check only:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-msglib-sidecar.ps1

Evidence-backed provider verification is opt-in through environment variables and should use copied DB files only. The script also verifies display_sources and sanitized display_entities summaries when these variables are present:

$env:ISPHERE_MSGLIB_SQLITE_DLL = "<path-to-System.Data.SQLite.dll>"
$env:ISPHERE_MSGLIB_DB = "<path-to-copied-MsgLib.db>"
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-msglib-sidecar.ps1

Optional MCP enrichment smoke, including receive-message display enrichment, is also opt-in through the same copied DB environment variables:

$env:ISPHERE_MSGLIB_SQLITE_DLL = "<path-to-System.Data.SQLite.dll>"
$env:ISPHERE_MSGLIB_DB = "<path-to-copied-MsgLib.db>"
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-msglib-mcp-enrichment.ps1

Expected printed evidence is sanitized: contact/group counts, msglib:<source_table> refs, message_source_count, message-source table/role/row-count summaries, receive_display_smoke=true, populated-field booleans, and no entity values, message bodies, file path values, or raw rows.

C38 provider verification additionally prints message_list_count, message_list_sources, message_list_body_values_returned=false, message_list_raw_rows_returned=false, message_list_file_paths_returned=false, message_list_content_values_omitted=true, and message_list_download_refs_omitted=true.