177 lines
7.1 KiB
Markdown
177 lines
7.1 KiB
Markdown
# 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
|
|
|
|
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
|
|
<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:
|
|
|
|
```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-<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:
|
|
|
|
```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`: optional output directory. If omitted, `run-mirror.sh` uses `<inbox_dir from profiles/<profile>/workspace.json>/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
|
|
<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
|
|
```
|