New service: services/matrix-bridge-dagi/ - app/config.py: BridgeConfig dataclass, load_config() with full env validation (MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, MATRIX_USER_ID, SOFIIA_ROOM_ID, DAGI_GATEWAY_URL, SOFIIA_CONSOLE_URL, SOFIIA_INTERNAL_TOKEN, rate limits) - app/main.py: FastAPI app with lifespan, GET /health, GET /metrics (prometheus) health returns: ok, node_id, homeserver, bridge_user, sofiia_room_id, allowed_agents, gateway, uptime_s; graceful error state when config missing - requirements.txt: fastapi, uvicorn, httpx, prometheus-client, pyyaml - Dockerfile: python:3.11-slim, port 7030, BUILD_SHA/BUILD_TIME args docker-compose.matrix-bridge-node1.yml: - standalone override file (node1 network, port 127.0.0.1:7030) - all env vars wired: MATRIX_*, SOFIIA_ROOM_ID, DAGI_GATEWAY_URL, SOFIIA_CONSOLE_URL, SOFIIA_INTERNAL_TOKEN, rate limit policy - healthcheck, restart: unless-stopped DoD: config validates, health/metrics respond, imports clean Made-with: Cursor
69 lines
2.4 KiB
Python
69 lines
2.4 KiB
Python
"""
|
|
matrix-bridge-dagi — configuration and validation
|
|
"""
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
from typing import FrozenSet
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BridgeConfig:
|
|
# Matrix homeserver
|
|
matrix_homeserver_url: str
|
|
matrix_access_token: str
|
|
matrix_user_id: str # e.g. @dagi_bridge:daarion.space
|
|
|
|
# Room → agent mapping (M1: single room)
|
|
sofiia_room_id: str # e.g. !abcdef:daarion.space
|
|
|
|
# DAGI backend
|
|
dagi_gateway_url: str # e.g. http://dagi-gateway-node1:9300
|
|
default_node_id: str # e.g. NODA1
|
|
|
|
# Sofiia Console (audit write)
|
|
sofiia_console_url: str # e.g. http://dagi-sofiia-console-node1:8002
|
|
sofiia_internal_token: str # X-Internal-Service-Token for audit ingest
|
|
|
|
# Policy
|
|
bridge_allowed_agents: FrozenSet[str]
|
|
rate_limit_room_rpm: int # max messages per room per minute
|
|
rate_limit_sender_rpm: int # max messages per sender per minute
|
|
|
|
# Service identity
|
|
node_id: str
|
|
build_sha: str
|
|
build_time: str
|
|
|
|
|
|
def load_config() -> BridgeConfig:
|
|
"""Load and validate config from environment variables."""
|
|
|
|
def _require(key: str) -> str:
|
|
v = os.getenv(key, "").strip()
|
|
if not v:
|
|
raise RuntimeError(f"Required env var {key!r} is not set")
|
|
return v
|
|
|
|
def _optional(key: str, default: str = "") -> str:
|
|
return os.getenv(key, default).strip()
|
|
|
|
allowed_raw = _optional("BRIDGE_ALLOWED_AGENTS", "sofiia")
|
|
allowed = frozenset(a.strip() for a in allowed_raw.split(",") if a.strip())
|
|
|
|
return BridgeConfig(
|
|
matrix_homeserver_url=_require("MATRIX_HOMESERVER_URL").rstrip("/"),
|
|
matrix_access_token=_require("MATRIX_ACCESS_TOKEN"),
|
|
matrix_user_id=_require("MATRIX_USER_ID"),
|
|
sofiia_room_id=_require("SOFIIA_ROOM_ID"),
|
|
dagi_gateway_url=_require("DAGI_GATEWAY_URL").rstrip("/"),
|
|
default_node_id=_optional("DEFAULT_NODE_ID", "NODA1"),
|
|
sofiia_console_url=_optional("SOFIIA_CONSOLE_URL", "").rstrip("/"),
|
|
sofiia_internal_token=_optional("SOFIIA_INTERNAL_TOKEN", ""),
|
|
bridge_allowed_agents=allowed,
|
|
rate_limit_room_rpm=int(_optional("RATE_LIMIT_ROOM_RPM", "20")),
|
|
rate_limit_sender_rpm=int(_optional("RATE_LIMIT_SENDER_RPM", "10")),
|
|
node_id=_optional("NODE_ID", "NODA1"),
|
|
build_sha=_optional("BUILD_SHA", "dev"),
|
|
build_time=_optional("BUILD_TIME", "local"),
|
|
)
|