7.1 KiB
Mattermost Proxy Mirror
Local read-only Mattermost Desktop mirror for AI workspace context.
This is for raw evidence only. By default it writes under the active profile inbox at <inbox_dir>/mattermost-mirror/; durable project memory still belongs in the profile's canonical project knowledge vault 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
- Install
mitmproxy. - 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.
- Start
- Optional: copy
.env.exampleto.envand setMATTERMOST_MIRROR_HOST_ALLOWto the exact Mattermost host, for examplemm.all-win-solutions.app.
Run day to day
Terminal 1:
scripts/mattermost-proxy/run-mirror.sh
Terminal 2:
scripts/mattermost-proxy/launch-mattermost.sh
This launches Mattermost Desktop through macOS LaunchServices with:
--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
<profile 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/<channel-name>/YYYY/MM/YYYY-MM-DD.jsonl
by-date/YYYY/MM/YYYY-MM-DD.jsonl
threads/<root-or-post-id>.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:
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.
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-<user-a>--<user-b> 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:
{
"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/v4traffic 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: optional output directory. If omitted,run-mirror.shuses<inbox_dir from profiles/<profile>/workspace.json>/mattermost-mirror.MATTERMOST_MIRROR_LATEST_LIMIT: number of messages inlatest.*, default200.MATTERMOST_MIRROR_CHANNEL_IDS: optional comma-separated channel ID allowlist.MATTERMOST_MIRROR_WRITE_RAW: set to1to save compact raw REST/WebSocket evidence.MATTERMOST_APP_PATH: Mattermost Desktop.appbundle 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:
- Start
scripts/mattermost-proxy/run-mirror.sh. - Open
http://mitm.itfrom a browser on this Mac and download the macOS certificate. - Add it to Keychain Access and set it to Always Trust.
- 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:
<profile inbox>/mattermost-mirror/latest.md
<profile inbox>/mattermost-mirror/channels/<channel-name>/YYYY/MM/YYYY-MM-DD.jsonl
<profile inbox>/mattermost-mirror/by-date/YYYY/MM/YYYY-MM-DD.jsonl