# MsgLib.db copied read-only schema extraction Date: 2026-07-10 ## Question Can copied `MsgLib.db` files be opened read-only with the wrapper password recovered in C25, and which schema fields should drive the next business read implementation? ## Evidence checked Ignored C26 artifacts: - `runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c26-schema-probe-x86/` - `runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c26-msglib-schema-probe-x86.json` Probe method: - referenced the bundled `System.Data.SQLite.dll` from the extracted iSphere client files; - opened copied `MsgLib.db` files only; - used the C25 recovered password `123`; - forced read-only mode in the SQLite connection string; - queried only `sqlite_master`, `PRAGMA table_info(...)`, and safe target-table row counts; - did not dump message bodies, contact values, file paths, or raw rows. ## Runtime finding - A 64-bit probe failed at CLR/SQLite mixed-mode loading. - A 32-bit .NET Framework probe succeeded. - Successful provider: `System.Data.SQLite`, version observed through the opened DB runtime as SQLite `3.6.23.1`. This means the production path should not assume Go can open this DB directly. The practical route is a dedicated x86 .NET read-only sidecar/helper that Go MCP calls through a bounded JSON contract. ## Open result All three copied `MsgLib.db` candidates opened successfully with `Password=123` in read-only mode. | Copied DB | Open result | Table count | Notable safe counts | | --- | --- | ---: | --- | | DB A, about 12.2 MB | ok | 13 | `tblMsgGroupPersonMsg=2884`, `tblPersonMsg=2132`, `tblRecent=52`, `TD_SystemMessageRecord=469` | | DB B, about 2.84 MB | ok | 23 | `tblMsgGroupPersonMsg=1048`, `tblPersonMsg=1209`, `tblRecent=80`, `tblChatLevel=105`, `TD_WorkGroupAuth=2`, `TD_SystemMessageRecord=314` | | DB C, about 2.78 MB | ok | 13 | `tblMsgGroupPersonMsg=796`, `tblPersonMsg=1932`, `tblRecent=51`, `TD_SystemMessageRecord=228` | The counts above are schema-validation counts only. No row values were extracted into committed docs. ## Confirmed business schema map ### Contacts and display names Validated tables/columns: - `TD_Roster`: `JId`, `Domain`, `Nick`, `Ask`, `Subscription`, `Show`, `Status`, `GroupName`. - `TD_CustomEffigy`: `PersonJid`, `PersonName`, `PersonSex`, `HeadImageSize`, `LocalPath`, `ServerUpdateTime`. - `tblRecent`: `Id`, `PersonId`, `PersonName`, `ActionTime`, `FType`. - `tblChatLevel`: present in the richer DB and already known from C25 model fields as `PersonId`, `PersonName`. - `tblPersonMsg`: `SndPerson`, `SndPersonId`, `RecvPerson`, `RecvPersonId`, `ObjPerson`, `ObjPersonId`, plus message metadata fields. Recommended contact display-name priority for a future read source: 1. `TD_Roster.Nick` by `TD_Roster.JId` when present. 2. `TD_CustomEffigy.PersonName` by `PersonJid` when present. 3. `tblRecent.PersonName` by `PersonId` for recent conversations. 4. `tblPersonMsg` sender/receiver/object display fields as fallback. 5. JID-derived fallback when DB display fields are absent. ### Groups and member names Validated tables/columns: - `tblMsgGroupPersonMsg`: `MsgGroupId`, `MsgGroupName`, `SessionTitle`, `SessionPersonId`, `SessionPersonName`, `GroupType`, plus message metadata fields. - `TD_WorkGroupAuth`: `SessionID`, `SessionStatus`, `SessionType`, `ApplyInfo`, `GroupJID`, `GroupName`, `SendPersonSex`, `ApplyDateTime`; richer DB also has `ChildGroupJid`. - `tblRecent`: stores group recent entries through triggers using `PersonId = MsgGroupId`, `PersonName = MsgGroupName`, and `FType = GroupType`. Recommended group display/member priority: 1. `tblMsgGroupPersonMsg.MsgGroupName` by `MsgGroupId`. 2. `tblRecent.PersonName` by `PersonId` where `FType` indicates group/discussion. 3. `TD_WorkGroupAuth.GroupName` by `GroupJID` for work-group authorization records. 4. `tblMsgGroupPersonMsg.SessionPersonId/SessionPersonName` for member-name enrichment. 5. JID-derived fallback when DB display fields are absent. ### Receive messages Validated tables/columns: - `tblPersonMsg`: one-to-one message table, including ids, sender/receiver/object identities, text/body/content/version/read fields, timestamps, and receipt state. - `tblMsgGroupPersonMsg`: group/discussion message table, including ids, sender identities, group ids/names, session title/member fields, message body/content/version/read fields, and timestamps. - `TD_SystemMessageRecord`: system/auth message metadata. C27 should not read message bodies yet. The next code loop should first add a schema/display-name sidecar and only later add DB-backed message listing with strict limit/redaction controls. ### File metadata Validated tables/columns: - `TD_ReceiveFileRecord`: `ReceiveMsgGuid`, `FileName`, `FileSize`, `FilePath`, `SourceID`, `SourceName`, `FileType`, `SendPersonJid`, `SendPersonName`, `SendTime`. - `TD_SendFileRecord`: `SendMsgGuid`, `FileName`, `FileSize`, `FilePath`, `SourceID`, `SourceName`, `FileType`, `ReceivePersonJid`, `ReceivePersonName`, `ReceiveTime`. File download remains blocked until cache/download mapping is separately validated. ## Decision C26 validates the DB path: > `MsgLib.db` can be opened from copied evidence with the bundled 32-bit `System.Data.SQLite.dll`, `Password=123`, and read-only mode. The project can now move from log-only read support toward DB-backed contact/group display-name enrichment. Because the working provider path is 32-bit .NET, the next implementation should be a bounded x86 .NET sidecar/helper rather than direct Go SQLite access. ## Next slice Loop C27 should implement the first production-shaped DB bridge as a narrow helper/sidecar contract, not as broad message extraction: 1. Build a committed contract for a read-only `MsgLib.db` schema/display-name sidecar. 2. Start with safe operations such as `msglib_self_check` and `msglib_list_display_sources` or equivalent. 3. Return only table availability, column availability, and display-name candidate maps with strict limits. 4. Keep message-body reads, file downloads, sends, writes, and DB mutation out of scope.