feat: Implement Matrix Rooms Bridge
- MATRIX_ROOMS_BRIDGE_SPEC.md documentation - Migration 012: Add matrix_room_id/alias to city_rooms - Matrix Gateway service (port 7025) - City-service: auto-create Matrix rooms on room creation - Backfill endpoint for existing rooms - API returns matrix_room_id/alias in room responses
This commit is contained in:
361
docs/matrix/MATRIX_ROOMS_BRIDGE_SPEC.md
Normal file
361
docs/matrix/MATRIX_ROOMS_BRIDGE_SPEC.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# MATRIX ROOMS BRIDGE — DAARION.city
|
||||
|
||||
Version: 1.0.0
|
||||
|
||||
## 0. PURPOSE
|
||||
|
||||
Звʼязати City Rooms у DAARION з Matrix-кімнатами так, щоб:
|
||||
|
||||
- кожна `city_room` мала свій `matrix_room_id` / `matrix_room_alias`,
|
||||
- UI для `/city/[slug]` працював поверх справжньої Matrix-кімнати,
|
||||
- подальший presence/typing/реальний чат використовували Matrix як єдине джерело істини.
|
||||
|
||||
Це база для:
|
||||
|
||||
- живих чатів у City,
|
||||
- спільної присутності,
|
||||
- інтеграції агентів як Matrix-botʼів.
|
||||
|
||||
---
|
||||
|
||||
## 1. ARCHITECTURE OVERVIEW
|
||||
|
||||
### Сервіси
|
||||
|
||||
- **city-service** (7001)
|
||||
- зберігає список кімнат (rooms)
|
||||
- розширюється полями для Matrix
|
||||
|
||||
- **matrix-gateway** (новий сервіс, 7025)
|
||||
- проксі до Synapse REST API
|
||||
- хендлить auth до Matrix від імені DAARION
|
||||
|
||||
- **auth-service** (7020)
|
||||
- вже створює Matrix акаунти для юзерів (auto-provisioning)
|
||||
|
||||
- **synapse** (8018)
|
||||
- Matrix homeserver
|
||||
|
||||
### Мапінг
|
||||
|
||||
Кожна City Room має:
|
||||
|
||||
| Поле | Опис | Приклад |
|
||||
|------|------|---------|
|
||||
| `room_id` | внутрішній DAARION id | `room_city_general` |
|
||||
| `slug` | URL/імена кімнати | `general` |
|
||||
| `matrix_room_id` | Matrix ID | `!abc123xyz:daarion.space` |
|
||||
| `matrix_room_alias` | Matrix alias | `#city_general:daarion.space` |
|
||||
|
||||
---
|
||||
|
||||
## 2. DATA MODEL CHANGES (PostgreSQL)
|
||||
|
||||
### 2.1. Таблиця city_rooms
|
||||
|
||||
Додати поля:
|
||||
|
||||
```sql
|
||||
ALTER TABLE city_rooms
|
||||
ADD COLUMN IF NOT EXISTS matrix_room_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS matrix_room_alias TEXT;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS city_rooms_matrix_room_id_uq
|
||||
ON city_rooms (matrix_room_id)
|
||||
WHERE matrix_room_id IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS city_rooms_matrix_room_alias_uq
|
||||
ON city_rooms (matrix_room_alias)
|
||||
WHERE matrix_room_alias IS NOT NULL;
|
||||
```
|
||||
|
||||
### 2.2. Invariants
|
||||
|
||||
* `matrix_room_id` → або `NULL`, або валідний Matrix room id (`!....:domain`)
|
||||
* `matrix_room_alias` → або `NULL`, або вигляду `#city_<slug>:daarion.space`
|
||||
* Один Matrix room = одна City room
|
||||
|
||||
---
|
||||
|
||||
## 3. NAMING CONVENTION
|
||||
|
||||
Для Matrix-кімнат:
|
||||
|
||||
* **room alias**:
|
||||
* формат: `#city_<slug>:daarion.space`
|
||||
* приклад: `#city_general:daarion.space`
|
||||
|
||||
* **room name**:
|
||||
* `"DAARION City — <Room Name>"`
|
||||
* приклад: `"DAARION City — General"`
|
||||
|
||||
Ці назви видно в Matrix-клієнтах (Element Web).
|
||||
|
||||
---
|
||||
|
||||
## 4. FLOWS
|
||||
|
||||
### 4.1. Створення нової City Room
|
||||
|
||||
Коли в `city-service` створюється новий room:
|
||||
|
||||
1. Генерується `slug` (як є зараз).
|
||||
|
||||
2. Викликається Matrix Gateway:
|
||||
`POST /internal/matrix/rooms/create`:
|
||||
```json
|
||||
{
|
||||
"slug": "energy",
|
||||
"name": "Energy",
|
||||
"visibility": "public"
|
||||
}
|
||||
```
|
||||
|
||||
3. Matrix Gateway:
|
||||
* викликає Synapse API `POST /_matrix/client/v3/createRoom`
|
||||
* задає:
|
||||
* `name: "DAARION City — Energy"`
|
||||
* `room_alias_name: "city_energy"`
|
||||
* `preset: "public_chat"`
|
||||
* повертає:
|
||||
```json
|
||||
{
|
||||
"matrix_room_id": "!abc123:daarion.space",
|
||||
"matrix_room_alias": "#city_energy:daarion.space"
|
||||
}
|
||||
```
|
||||
|
||||
4. `city-service` зберігає ці значення в `city_rooms`.
|
||||
|
||||
**Якщо Matrix недоступний:**
|
||||
* MVP: fail створення кімнати цілком, щоб не було "неповних" кімнат.
|
||||
* TODO: retry-механізм для production.
|
||||
|
||||
---
|
||||
|
||||
### 4.2. Синхронізація існуючих кімнат (backfill)
|
||||
|
||||
Для вже створених `city_rooms`:
|
||||
|
||||
1. Endpoint: `POST /internal/city/matrix/backfill`:
|
||||
* бере всі кімнати, де `matrix_room_id IS NULL`.
|
||||
* для кожної:
|
||||
* пробує знайти Matrix room по alias `#city_<slug>:daarion.space` через Matrix Gateway:
|
||||
* якщо знайдено → зберігає `matrix_room_id` / `matrix_room_alias`;
|
||||
* якщо не знайдено → створює нову Matrix room, як у п. 4.1.
|
||||
|
||||
---
|
||||
|
||||
### 4.3. Архівація / деактивація Room
|
||||
|
||||
Коли Room в DAARION позначається як "archived" / "inactive":
|
||||
|
||||
MVP:
|
||||
* не видаляти Matrix room,
|
||||
* показувати статус в DAARION UI як `archived`.
|
||||
|
||||
Future:
|
||||
* змінити power levels,
|
||||
* закрити можливість писати,
|
||||
* додати `m.room.tombstone`.
|
||||
|
||||
---
|
||||
|
||||
## 5. MATRIX GATEWAY SERVICE
|
||||
|
||||
### 5.1. Конфігурація
|
||||
|
||||
```env
|
||||
MATRIX_GATEWAY_PORT=7025
|
||||
SYNAPSE_URL=http://daarion-synapse:8008
|
||||
SYNAPSE_ADMIN_TOKEN=<admin_access_token>
|
||||
MATRIX_SERVER_NAME=daarion.space
|
||||
```
|
||||
|
||||
### 5.2. API Endpoints
|
||||
|
||||
#### `POST /internal/matrix/rooms/create`
|
||||
|
||||
Викликається тільки з `city-service`.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"slug": "energy",
|
||||
"name": "Energy",
|
||||
"visibility": "public"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"matrix_room_id": "!abc123:daarion.space",
|
||||
"matrix_room_alias": "#city_energy:daarion.space"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (500):**
|
||||
```json
|
||||
{
|
||||
"error": "matrix_unavailable",
|
||||
"detail": "Failed to create Matrix room"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /internal/matrix/rooms/find-by-alias`
|
||||
|
||||
**Request:**
|
||||
`GET /internal/matrix/rooms/find-by-alias?alias=%23city_energy%3Adaarion.space`
|
||||
|
||||
**Response (200, exists):**
|
||||
```json
|
||||
{
|
||||
"matrix_room_id": "!abc123:daarion.space",
|
||||
"matrix_room_alias": "#city_energy:daarion.space"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (404, not found):**
|
||||
```json
|
||||
{
|
||||
"error": "not_found"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /healthz`
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"synapse": "connected"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. CITY-SERVICE API CHANGES
|
||||
|
||||
### 6.1. `GET /api/city/rooms`
|
||||
|
||||
Додати у відповідь:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "room_city_general",
|
||||
"slug": "general",
|
||||
"name": "General",
|
||||
"description": "Головна кімната міста",
|
||||
"is_default": true,
|
||||
"members_online": 42,
|
||||
"last_event": "2025-11-26T20:00:00Z",
|
||||
"matrix_room_id": "!abc123:daarion.space",
|
||||
"matrix_room_alias": "#city_general:daarion.space"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 6.2. `GET /api/city/rooms/{slug}`
|
||||
|
||||
Також включити `matrix_room_id` / `matrix_room_alias`.
|
||||
|
||||
### 6.3. `POST /api/city/rooms`
|
||||
|
||||
При створенні кімнати автоматично створювати Matrix room.
|
||||
|
||||
---
|
||||
|
||||
## 7. FRONTEND INTEGRATION
|
||||
|
||||
На сторінці `/city/[slug]`:
|
||||
|
||||
* при завантаженні room:
|
||||
* отримати `matrix_room_id` / `matrix_room_alias`,
|
||||
* передати їх у чат-Layout.
|
||||
|
||||
Чат-шар (`ChatRoom`) повинен:
|
||||
* використовувати Matrix-клієнт для:
|
||||
* приєднання до кімнати,
|
||||
* отримання історії,
|
||||
* відправки повідомлень.
|
||||
|
||||
---
|
||||
|
||||
## 8. SECURITY
|
||||
|
||||
* `matrix-gateway` endpoint'и `/internal/matrix/*`:
|
||||
* тільки internal auth (service-to-service)
|
||||
* перевірка internal token або Docker network
|
||||
|
||||
* Кінцеві користувачі не повинні напряму бачити Matrix admin-credentials.
|
||||
|
||||
---
|
||||
|
||||
## 9. DEPLOYMENT
|
||||
|
||||
### Docker Compose addition
|
||||
|
||||
```yaml
|
||||
matrix-gateway:
|
||||
build: ./services/matrix-gateway
|
||||
container_name: daarion-matrix-gateway
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MATRIX_GATEWAY_PORT=7025
|
||||
- SYNAPSE_URL=http://daarion-synapse:8008
|
||||
- SYNAPSE_ADMIN_TOKEN=${SYNAPSE_ADMIN_TOKEN}
|
||||
- MATRIX_SERVER_NAME=daarion.space
|
||||
ports:
|
||||
- "7025:7025"
|
||||
networks:
|
||||
- dagi-network
|
||||
depends_on:
|
||||
- synapse
|
||||
```
|
||||
|
||||
### Nginx routing
|
||||
|
||||
```nginx
|
||||
location /internal/matrix/ {
|
||||
# Internal only - block external access
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://127.0.0.1:7025/internal/matrix/;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. ROADMAP AFTER BRIDGE
|
||||
|
||||
Після реалізації цього SPEC:
|
||||
|
||||
1. **Presence & Typing:**
|
||||
* читати `m.presence`, `m.typing`, `m.receipt` з Matrix,
|
||||
* транслювати в фронт (через WebSocket).
|
||||
|
||||
2. **Agents як Matrix Bot:**
|
||||
* окремі Matrix accounts для агентів,
|
||||
* відповіді агентів у тій же кімнаті, що й користувачі.
|
||||
|
||||
3. **City Map:**
|
||||
* візуалізація активності кімнат на 2D/2.5D мапі.
|
||||
|
||||
4. **PWA/Mobile:**
|
||||
* manifest.json,
|
||||
* service worker,
|
||||
* offline кеш.
|
||||
|
||||
---
|
||||
|
||||
## 11. ACCEPTANCE CRITERIA
|
||||
|
||||
- [ ] `city_rooms` має поля `matrix_room_id` / `matrix_room_alias`
|
||||
- [ ] При створенні City Room автоматично створюється Matrix room
|
||||
- [ ] Backfill endpoint синхронізує існуючі кімнати
|
||||
- [ ] API `/api/city/rooms` повертає matrix поля
|
||||
- [ ] Matrix Gateway працює і відповідає на healthcheck
|
||||
- [ ] Element Web показує створені кімнати
|
||||
|
||||
Reference in New Issue
Block a user