58fcb04ce4
Second pass on docs/ cleanup (item #15 in STATUS.md): - pfi/chromadb-setup.md: deleted. References configs/pfi-ana/... and scripts/setup-chromadb.sh, neither of which exist in this repo (artifacts of an earlier project layout). ChromaDB is already live per docker-stack.md; the operational truth lives there. - pfi/docker-stack.md: removed the cross-link to the deleted file and pulled the bit of useful content from it (auth-token generation + client Settings example) into the inline ChromaDB section. - pfi/vm-102-matrix-{synapse,appservice}.md: stripped the YAML frontmatter. The `path:` values pointed at docs/pfi-ana/... which doesn't exist in this repo; no toolchain consumed the metadata. - README.md: tree updated to reflect the deletion. VM-102 docs kept separate by design — each is right-sized; merging would push past the ~500-line guideline.
247 lines
8.1 KiB
Markdown
247 lines
8.1 KiB
Markdown
# VM 102 — Matrix Appservice Configuration
|
|
|
|
## Overview
|
|
|
|
The AIPA Matrix bridge registers with Synapse as a Matrix Application Service. This means:
|
|
- Synapse **pushes** all relevant events to the bridge (no polling)
|
|
- The bridge manages **virtual users** for each agent — no real accounts needed
|
|
- The bridge authenticates to Synapse with a shared `as_token`
|
|
- Synapse authenticates its pushes to the bridge with a shared `hs_token`
|
|
|
|
> Prerequisite: [VM 102 — Matrix Synapse Deployment](vm-102-matrix-synapse.md) must be complete.
|
|
|
|
---
|
|
|
|
## Application Service Model
|
|
|
|
```
|
|
Synapse ──── push events ────► AIPA Bridge (port 8009)
|
|
│
|
|
routes to agent
|
|
│
|
|
posts response ◄──── Synapse ◄──── Element ◄──── User
|
|
```
|
|
|
|
### Agent Virtual Users
|
|
|
|
| Agent | Matrix ID | Display Name |
|
|
|----------|----------------------------|--------------|
|
|
| Atlas | `@atlas:matrix.pfi.local` | Atlas |
|
|
| Linus | `@linus:matrix.pfi.local` | Linus |
|
|
| Hermione | `@hermione:matrix.pfi.local` | Hermione |
|
|
|
|
These are **virtual** — managed entirely by the bridge. Do not register them as real Synapse accounts.
|
|
|
|
---
|
|
|
|
## Step 1 — Generate Tokens
|
|
|
|
Two random tokens are needed:
|
|
|
|
```bash
|
|
python3 -c "import secrets; print(secrets.token_hex(32))" # as_token (bridge → Synapse)
|
|
python3 -c "import secrets; print(secrets.token_hex(32))" # hs_token (Synapse → bridge)
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2 — Create Appservice Registration File
|
|
|
|
File: `/opt/docker/data/synapse/aipa_appservice.yaml`
|
|
|
|
```yaml
|
|
id: aipa-bridge
|
|
url: http://<BRIDGE_HOST_IP>:8009 # IP/hostname the bridge is reachable from Synapse container
|
|
as_token: "<AS_TOKEN_FROM_STEP_1>"
|
|
hs_token: "<HS_TOKEN_FROM_STEP_1>"
|
|
sender_localpart: aipa-bot # @aipa-bot:matrix.pfi.local (unused fallback sender)
|
|
namespaces:
|
|
users:
|
|
- exclusive: true
|
|
regex: "@(atlas|linus|hermione):.*"
|
|
rooms: []
|
|
aliases: []
|
|
rate_limited: false
|
|
```
|
|
|
|
- **`exclusive: true`** — only the bridge can act as those users; no one can register `@atlas` as a real account.
|
|
- **`url`** — must be reachable from inside the Synapse container. If the bridge runs on the VM host, use the host IP or Docker gateway IP (e.g., `172.17.0.1`).
|
|
|
|
### Restart Synapse to Load
|
|
|
|
```bash
|
|
cd /opt/docker/compose/synapse && docker compose restart synapse
|
|
```
|
|
|
|
---
|
|
|
|
## Step 3 — Configure AIPA Environment
|
|
|
|
### env.sh additions
|
|
|
|
```bash
|
|
# ── Matrix bridge ──────────────────────────────────────────────────────
|
|
export MATRIX_HOMESERVER_URL="http://localhost:8008"
|
|
export MATRIX_SERVER_NAME="matrix.pfi.local"
|
|
export MATRIX_AS_TOKEN="<as_token from Step 1>"
|
|
export MATRIX_HS_TOKEN="<hs_token from Step 1>"
|
|
# Optional: room to send agent notifications when no channel is specified
|
|
# export MATRIX_DEFAULT_ROOM="!roomid:matrix.pfi.local"
|
|
```
|
|
|
|
### providers.yaml matrix section
|
|
|
|
```yaml
|
|
matrix:
|
|
homeserver_url: "${MATRIX_HOMESERVER_URL}"
|
|
server_name: "${MATRIX_SERVER_NAME}"
|
|
as_token: "${MATRIX_AS_TOKEN}"
|
|
hs_token: "${MATRIX_HS_TOKEN}"
|
|
bridge_host: "0.0.0.0"
|
|
bridge_port: 8009
|
|
default_notification_room: "${MATRIX_DEFAULT_ROOM}"
|
|
agents:
|
|
atlas:
|
|
display_name: "Atlas"
|
|
avatar_url: ""
|
|
linus:
|
|
display_name: "Linus"
|
|
avatar_url: ""
|
|
hermione:
|
|
display_name: "Hermione"
|
|
avatar_url: ""
|
|
```
|
|
|
|
---
|
|
|
|
## Step 4 — Start the Bridge
|
|
|
|
```bash
|
|
source .venv/bin/activate && source env.sh
|
|
python -m core.matrix_bridge
|
|
```
|
|
|
|
Options:
|
|
|
|
```
|
|
python -m core.matrix_bridge --host 0.0.0.0 --port 8009
|
|
python -m core.matrix_bridge --no-profile-setup # skip display name / avatar init
|
|
```
|
|
|
|
On first start, the bridge:
|
|
1. Registers display names for each agent user
|
|
2. Starts the appservice HTTP server on port 8009
|
|
3. Listens for Matrix transactions from Synapse
|
|
4. Accepts invitations to rooms
|
|
5. Begins routing messages
|
|
|
|
---
|
|
|
|
## Step 5 — First Use
|
|
|
|
1. Open Element at `http://10.250.50.70:8080`
|
|
2. Log in with your admin account (server: `matrix.pfi.local`)
|
|
3. Open a DM with `@atlas:matrix.pfi.local`
|
|
4. Send a message — the bridge routes it to Atlas and posts the response
|
|
|
|
To start a room with a specific agent, invite them:
|
|
- New room → Invite `@linus:matrix.pfi.local` → Linus joins automatically
|
|
- All messages in that room go to Linus
|
|
|
|
---
|
|
|
|
## Room Routing
|
|
|
|
1. User invites an agent user to a room (or opens a DM)
|
|
2. Bridge receives the `m.room.member` invite event, agent auto-joins
|
|
3. Room is permanently mapped to that agent in `sessions/matrix_rooms.json`
|
|
4. All subsequent messages in that room are routed to the mapped agent
|
|
5. If multiple agent users are in the same room, the first invite wins for session purposes;
|
|
subsequent agents each get their own session (multi-agent collaboration rooms)
|
|
|
|
---
|
|
|
|
## Operational Notes
|
|
|
|
### Room Session Persistence
|
|
|
|
The bridge persists the room→agent→session mapping to `sessions/matrix_rooms.json`
|
|
under `AIPA_ROOT`. If the bridge restarts, existing rooms continue their sessions.
|
|
|
|
To reset a room's conversation history:
|
|
- Ask the agent `/reset`, or
|
|
- Delete the entry from `matrix_rooms.json` and restart the bridge.
|
|
|
|
### Formatting
|
|
|
|
The bridge converts markdown in agent responses to Matrix HTML
|
|
(`format: org.matrix.custom.html`). Code blocks, bold, and inline code are
|
|
rendered correctly in Element.
|
|
|
|
### Typing Indicators
|
|
|
|
The bridge sends a typing indicator (`m.typing`) while the agent is processing,
|
|
so users see the animated dots while waiting.
|
|
|
|
### Proactive Notifications
|
|
|
|
Agents can push messages to Matrix rooms via `send_notification`:
|
|
|
|
```
|
|
send_notification(
|
|
content="KB rebuild complete — 1,247 chunks indexed.",
|
|
channel="!roomid:matrix.pfi.local"
|
|
)
|
|
```
|
|
|
|
- If `channel` is set, it must be a Matrix room ID (`!roomid:server`)
|
|
- If `channel` is omitted, the notification goes to `MATRIX_DEFAULT_ROOM` if configured
|
|
|
|
### Agent Users Always Show as Offline
|
|
|
|
Expected behavior — virtual users don't have presence. This is normal for appservice users.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Likely Cause | Fix |
|
|
|---------|-------------|-----|
|
|
| Bridge starts but Synapse doesn't push events | Appservice URL wrong in registration YAML | Verify `url:` is reachable from the Synapse container; check `docker inspect synapse` network |
|
|
| Agent joins room but doesn't respond | `hs_token` mismatch | Verify token in `aipa_appservice.yaml` matches `MATRIX_HS_TOKEN` env var |
|
|
| 401 errors from Synapse | `as_token` mismatch | Verify token in `aipa_appservice.yaml` matches `MATRIX_AS_TOKEN` env var |
|
|
| Agent user shows as offline always | Expected — virtual users don't have presence | Normal for appservice users; no fix needed |
|
|
| Messages loop (agent replies to itself) | Bridge not filtering its own messages | Check `SENDER_LOCALPART` filter in bridge; bridge ignores all managed agent users as senders |
|
|
| Element can't connect to homeserver | Wrong `base_url` in element-config.json | Must be the IP/hostname Element's browser can reach, not the Docker container name |
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
| Dependency | Version | Purpose |
|
|
|------------|---------|---------|
|
|
| `markdown` | any | Markdown→HTML rendering for Matrix responses |
|
|
| `fastapi` | any | Appservice HTTP server |
|
|
| `httpx` | any | Client-Server API calls to Synapse |
|
|
|
|
Install: `pip install markdown` (or `pip install -r requirements.txt`)
|
|
|
|
---
|
|
|
|
## Security Notes
|
|
|
|
- The `as_token` and `hs_token` are secrets equivalent to admin credentials. Store
|
|
them in `env.sh` (gitignored) and never commit them.
|
|
- The bridge runs with `MATRIX_HS_TOKEN` to authenticate Synapse's push requests. Any
|
|
request without this token in the `Authorization` header is rejected (403).
|
|
- Restrict port 8009 to internal access only (firewall or bind to `127.0.0.1` if bridge and Synapse are on the same host).
|
|
|
|
---
|
|
|
|
## Sources
|
|
|
|
- Source deployment guide: `projects/matrix/matrix-deployment.md` (2026-04-11)
|
|
- AIPA bridge code: `core/matrix_bridge.py`
|
|
- AIPA bridge KB entry: `KB/projects/aipa/matrix-bridge.md`
|
|
- Matrix Synapse appservice docs: https://element-hq.github.io/synapse/latest/application_services.html
|