Files
microdao-daarion/docs/realtime/GLOBAL_PRESENCE_AGGREGATOR_SPEC.md
Apple 78849cc108 feat: Add Global Presence Aggregator system
- GLOBAL_PRESENCE_AGGREGATOR_SPEC.md documentation
- matrix-presence-aggregator service (Python/FastAPI)
  - Matrix sync loop for presence/typing
  - NATS publishing for room presence
- city-service: presence_gateway for WS broadcast
- Frontend: real-time online count in room list
  - useGlobalPresence hook
  - Live typing indicators
  - Active room highlighting
2025-11-26 14:22:34 -08:00

10 KiB
Raw Blame History

GLOBAL PRESENCE AGGREGATOR — DAARION.city

Version: 1.0.0 Location: docs/realtime/GLOBAL_PRESENCE_AGGREGATOR_SPEC.md


0. PURPOSE

Зробити єдиний центр правди про присутність (presence) та активність у місті:

  • збирати Matrix presence/typing/room-activity на сервері,
  • агрегувати їх на рівні кімнат (city_room),
  • публікувати у NATS як події,
  • транслювати у фронтенд через WebSocket з city-service.

Результат: DAARION має "живе місто":

  • список кімнат /city показує:
    • скільки людей онлайн,
    • активність у реальному часі,
  • майбутня City Map (2D/2.5D) живиться цими даними.

1. ARCHITECTURE OVERVIEW

┌─────────────────────────────────────────────────────────────────────────┐
│                           DAARION PRESENCE SYSTEM                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────┐     ┌──────────────────────┐     ┌─────────────────┐   │
│  │   Matrix    │────▶│ matrix-presence-     │────▶│      NATS       │   │
│  │   Synapse   │     │ aggregator           │     │   JetStream     │   │
│  │             │     │ (sync loop)          │     │                 │   │
│  └─────────────┘     └──────────────────────┘     └────────┬────────┘   │
│                                                            │            │
│                                                            ▼            │
│  ┌─────────────┐     ┌──────────────────────┐     ┌─────────────────┐   │
│  │   Browser   │◀────│   city-service       │◀────│   NATS Sub      │   │
│  │   (WS)      │     │   /ws/city/presence  │     │                 │   │
│  └─────────────┘     └──────────────────────┘     └─────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Компоненти

  1. matrix-presence-aggregator (новий сервіс)

    • читає Matrix sync (presence, typing, room activity),
    • тримає у пам'яті поточний стан присутності,
    • публікує агреговані події в NATS.
  2. NATS JetStream

    • канал для presence/events:
      • city.presence.room.*
      • city.presence.user.*
  3. city-service (розширення)

    • підписується на NATS події,
    • тримає WebSocket з'єднання з фронтендом,
    • пушить presence/room-activity у браузер.
  4. web (Next.js UI)

    • сторінка /city:
      • показує N online по кожній кімнаті,
      • highlight "active" кімнати.

2. MATRIX SIDE — ЗВІДКИ БРАТИ ПОДІЇ

2.1. Окремий Matrix-юзер для агрегації

Спец-акаунт:

  • @presence_daemon:daarion.space
  • права:
    • читати presence/typing у всіх city_* кімнатах,
    • бути учасником цих кімнат.

2.2. Sync-loop на сервері

Сервіс matrix-presence-aggregator:

  • використовує /sync Matrix (як клієнт),
  • фільтр:
{
  "presence": {
    "types": ["m.presence"]
  },
  "room": {
    "timeline": { "limit": 0 },
    "state": { "limit": 0 },
    "ephemeral": {
      "types": ["m.typing", "m.receipt"]
    }
  }
}
  • робить long-polling з since + timeout,
  • парсить:
    • presence.eventsm.presence,
    • rooms.join[roomId].ephemeral.eventsm.typing, m.receipt.

3. DATA MODEL (IN-MEMORY AGGREGATOR)

3.1. Room presence state

from dataclasses import dataclass
from typing import Dict, List, Set, Optional
from datetime import datetime

@dataclass
class UserPresence:
    user_id: str               # "@user:domain"
    status: str                # "online" | "offline" | "unavailable"
    last_active_ts: float      # timestamp

@dataclass
class RoomPresence:
    room_id: str               # "!....:daarion.space"
    alias: Optional[str]       # "#city_energy:daarion.space"
    city_room_slug: Optional[str]  # "energy"
    online_count: int
    typing_user_ids: List[str]
    last_event_ts: float

class PresenceState:
    users: Dict[str, UserPresence]
    rooms: Dict[str, RoomPresence]
    room_members: Dict[str, Set[str]]  # room_id -> set of user_ids

3.2. Мапінг Room → City Room

matrix-presence-aggregator має знати matrix_room_idcity_room.slug.

Pull-mode (MVP):

  • при старті сервісу:
    • GET /internal/city/rooms
    • зчитати всі matrix_room_id / matrix_room_alias / slug,
    • зібрати мапу roomId → slug.
  • періодично (кожні 5 хвилин) оновлювати.

4. NATS EVENTS

4.1. Room-level presence

Subject:

city.presence.room.<slug>

Event payload:

{
  "type": "room.presence",
  "room_slug": "energy",
  "matrix_room_id": "!gykdLyazhkcSZGHmbG:daarion.space",
  "matrix_room_alias": "#city_energy:daarion.space",
  "online_count": 5,
  "typing_count": 1,
  "typing_users": ["@user1:daarion.space"],
  "last_event_ts": 1732610000000
}

4.2. User-level presence (опційний)

Subject:

city.presence.user.<localpart>

Payload:

{
  "type": "user.presence",
  "matrix_user_id": "@user1:daarion.space",
  "status": "online",
  "last_active_ts": 1732610000000
}

5. EVENT GENERATION LOGIC

5.1. Обробка m.presence

При кожному m.presence:

  • оновити PresenceState.users[userId],
  • для всіх кімнат, де є цей юзер — перерахувати onlineCount,
  • якщо onlineCount змінився — публікувати нову подію.

5.2. Обробка m.typing

При m.typing:

  • content.user_ids → список typing у кімнаті.
  • Зберегти в RoomPresence.typing_user_ids.
  • Згенерувати івент city.presence.room.<slug>.

5.3. Throttling

  • подію публікувати тільки якщо onlineCount змінився,
  • або не частіше ніж раз на 3 секунди на кімнату.

6. CITY REALTIME GATEWAY (WEBSOCKET)

6.1. WebSocket endpoint

GET /ws/city/presence

Auth: JWT токен у query param або header.

6.2. Формат повідомлень

Snapshot (при підключенні):

{
  "type": "snapshot",
  "rooms": [
    { "room_slug": "general", "online_count": 3, "typing_count": 0 },
    { "room_slug": "welcome", "online_count": 1, "typing_count": 0 }
  ]
}

Incremental update:

{
  "type": "room.presence",
  "room_slug": "energy",
  "online_count": 5,
  "typing_count": 1
}

7. FRONTEND INTEGRATION

7.1. Список кімнат /city

State:

type RoomPresenceUI = {
  onlineCount: number;
  typingCount: number;
};

const [presenceBySlug, setPresenceBySlug] = useState<Record<string, RoomPresenceUI>>({});

WebSocket handler:

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  
  if (data.type === 'snapshot') {
    const presence: Record<string, RoomPresenceUI> = {};
    data.rooms.forEach(r => {
      presence[r.room_slug] = {
        onlineCount: r.online_count,
        typingCount: r.typing_count
      };
    });
    setPresenceBySlug(presence);
  }
  
  if (data.type === 'room.presence') {
    setPresenceBySlug(prev => ({
      ...prev,
      [data.room_slug]: {
        onlineCount: data.online_count,
        typingCount: data.typing_count
      }
    }));
  }
};

7.2. UI

  • Room card: X online, typing badge
  • Active room: glow effect
  • Typing animation

8. CONFIG / ENV

matrix-presence-aggregator

MATRIX_HS_URL=https://app.daarion.space
MATRIX_ACCESS_TOKEN=<presence_daemon_token>
MATRIX_USER_ID=@presence_daemon:daarion.space
CITY_SERVICE_INTERNAL_URL=http://city-service:7001
NATS_URL=nats://nats:4222
ROOM_PRESENCE_THROTTLE_MS=3000

city-service (realtime gateway)

NATS_URL=nats://nats:4222
JWT_SECRET=<secret>

9. ACCEPTANCE CRITERIA

  • matrix-presence-aggregator запущений і синхронізується з Matrix
  • NATS отримує події city.presence.room.*
  • city-service має endpoint /ws/city/presence
  • При підключенні WS клієнт отримує snapshot
  • При зміні presence клієнт отримує update
  • UI /city показує online count для кожної кімнати
  • Typing indicator відображається

10. FUTURE ENHANCEMENTS

  1. Agent presence — окремі статуси для AI-агентів
  2. City Map — візуалізація presence на 2D карті
  3. Push notifications — сповіщення про активність
  4. Historical analytics — статистика активності