- 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
362 lines
8.8 KiB
Markdown
362 lines
8.8 KiB
Markdown
# 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 показує створені кімнати
|
||
|