# Mattermost Proxy Mirror Local read-only Mattermost Desktop mirror for AI workspace context. This is for **raw evidence only**. It writes under `ai/inbox/mattermost-mirror/`; durable project memory still belongs in `project-knowledge/` after normal promotion rules. ## Why this exists Mattermost Team Edition 11.4.2 exposes normal `/api/v4` REST and WebSocket traffic. When Mattermost Desktop is launched with Chromium/Electron's `--proxy-server` flag, `mitmproxy` can capture only that app without changing the macOS system proxy. ## Setup 1. Install `mitmproxy`. 2. Trust the mitmproxy certificate if HTTPS interception is not already working: - Start `scripts/mattermost-proxy/run-mirror.sh` - Open `http://mitm.it` - Install/trust the certificate in Keychain. 3. Optional: copy `.env.example` to `.env` and set `MATTERMOST_MIRROR_HOST_ALLOW` to the exact Mattermost host, for example `mm.all-win-solutions.app`. ## Run day to day Terminal 1: ```bash scripts/mattermost-proxy/run-mirror.sh ``` Terminal 2: ```bash scripts/mattermost-proxy/launch-mattermost.sh ``` This launches Mattermost Desktop through macOS LaunchServices with: ```bash --proxy-server=http://127.0.0.1:8080 ``` No global macOS proxy is required. The helper intentionally uses `open -n /Applications/Mattermost.app --args ...` instead of invoking `/Applications/Mattermost.app/Contents/MacOS/Mattermost` directly. Direct binary launch can crash sandboxed Electron apps with Mach rendezvous errors because their expected app/container parent process is missing. ## Output layout ```text ai/inbox/mattermost-mirror/ latest.jsonl # bounded AI-readable window latest.md # bounded Markdown view state.json # last seen by channel and user cache index.json # date/channel/thread file map refs/ channels.json # channel_id -> channel_name users.json # user_id -> username channels//YYYY/MM/YYYY-MM-DD.jsonl by-date/YYYY/MM/YYYY-MM-DD.jsonl threads/.jsonl raw/YYYY/MM/YYYY-MM-DD-websocket.jsonl # only if MATTERMOST_MIRROR_WRITE_RAW=1 raw/YYYY/MM/YYYY-MM-DD-rest-flows.jsonl # only if MATTERMOST_MIRROR_WRITE_RAW=1 ``` Use `latest.md` or `latest.jsonl` for quick AI context. Use `channels/...` for conversation-focused analysis, `by-date/...` for standups or daily review, and `threads/...` when a single discussion thread is the relevant evidence. This mirrors Slack's export pattern of one folder per conversation with one file per date, while adding Mattermost-specific thread views. For standup generation, prefer the focused reader instead of loading broad `latest.md` directly: ```bash python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD ``` `standup` mode reads only date-bucketed records for the previous workday and today. To avoid spending tokens on unrelated channels or stale global `latest.md` content, configure project-specific context channels in the connector-local `.env` or pass them explicitly. Keep those channel values out of reusable scripts. ```bash AIW_MATTERMOST_CONTEXT_CHANNELS="project-main,project-standup,dm-you--manager" \ python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD \ --channels "project-main,project-standup,dm-you--manager" ``` If no context channel filter is configured, `standup` mode still avoids `latest.md` and reads date-bucketed records only, but it will include all mirrored channels for those dates. Direct-message channels are labeled as `dm---` when the mirror has seen enough user metadata to resolve the Mattermost channel ID. Group DMs use `group-...`. If a DM was first captured before the relevant user metadata arrived, the folder can temporarily use raw IDs; later captures use the readable label and `refs/channels.json` remains the source for resolving channel IDs. The mirror writes any post payload it sees, including older messages returned when the desktop app loads channel history or a thread. It dedupes by `post_id`, so scrolling back through useful history is a safe way to backfill missing local evidence without creating repeated entries. ## Normalized message schema Each line in the normalized JSONL contains: ```json { "source": "websocket|rest", "captured_at": "2026-05-19T...Z", "created_at": "2026-05-19T...Z", "created_at_ms": 1779190000000, "channel_id": "...", "channel_name": "fidelity-preguntas", "post_id": "...", "root_id": "...", "thread_id": "...", "user_id": "...", "username": "jeff", "message": "...", "type": "channel_post|thread_reply", "raw_event": "posted|posts|post" } ``` ## Safety rules - The addon allowlists Mattermost hosts and `/api/v4` traffic only. - Headers such as `Authorization`, `Cookie`, `Set-Cookie`, and CSRF are redacted in optional raw output. - Optional raw output is disabled by default to prevent large files. - Attachments are not downloaded by this mirror. - The mirror is evidence, not canonical memory. ## Useful environment variables - `MATTERMOST_MIRROR_HOST_ALLOW`: exact host or parent domain to capture. - `MATTERMOST_MIRROR_DIR`: output directory, default `ai/inbox/mattermost-mirror`. - `MATTERMOST_MIRROR_LATEST_LIMIT`: number of messages in `latest.*`, default `200`. - `MATTERMOST_MIRROR_CHANNEL_IDS`: optional comma-separated channel ID allowlist. - `MATTERMOST_MIRROR_WRITE_RAW`: set to `1` to save compact raw REST/WebSocket evidence. - `MATTERMOST_APP_PATH`: Mattermost Desktop `.app` bundle path. - `AIW_MATTERMOST_PROJECT_CHANNELS`: optional comma-separated channel names or IDs for focused standup reads. ## Troubleshooting ### TLS certificate warnings Mitmproxy uses a persistent local CA under `~/.mitmproxy`. If the desktop app asks about the certificate after every proxy restart, install and trust that CA in macOS Keychain instead of approving it only in the app prompt: 1. Start `scripts/mattermost-proxy/run-mirror.sh`. 2. Open `http://mitm.it` from a browser on this Mac and download the macOS certificate. 3. Add it to Keychain Access and set it to **Always Trust**. 4. Restart Mattermost Desktop through `launch-mattermost.sh`. Warnings for unrelated hosts such as `releases.mattermost.com` or OpenGraph preview hosts are not required for message capture. The mirror only writes normalized messages from Mattermost `/api/v4` REST/WebSocket payloads. ### Proxy logs show traffic but no `latest.md` The mirror writes files only after it sees a post payload. Startup calls such as `/api/v4/teams`, `/api/v4/users`, `/api/v4/files`, or WebSocket ping/ack events do not create message files. Open a channel, open a thread, scroll slightly in history, or wait for/send a new message. Then check: ```text ai/inbox/mattermost-mirror/latest.md ai/inbox/mattermost-mirror/channels//YYYY/MM/YYYY-MM-DD.jsonl ai/inbox/mattermost-mirror/by-date/YYYY/MM/YYYY-MM-DD.jsonl ```