Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
**/node_modules
**/dist
**/.turbo
**/.astro
.git
.github
.env
.env.*
!.env.example
*.log
coverage
archives
rapport
sprints
70 changes: 34 additions & 36 deletions apps/demo-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
# ============================================================
# DomOS Demo Server — multi-stage build
# Build context MUST be the repo root (pnpm monorepo).
# docker build -f apps/demo-server/Dockerfile -t domos-demo-server .
# ============================================================

# ---- Stage 1: build the workspace ----
FROM node:20-alpine AS builder

RUN npm install -g pnpm@9.15.9
# syntax=docker/dockerfile:1.7

FROM node:20-alpine AS base
ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
WORKDIR /app

# Copy workspace manifests first (better layer caching)
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml turbo.json tsconfig.base.json ./

# Copy the full source (packages + apps). A .dockerignore keeps this lean.
COPY packages ./packages
COPY plugins ./plugins
COPY apps/demo-server ./apps/demo-server

# Install all workspace dependencies
FROM base AS deps
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.base.json ./
COPY apps/demo-server/package.json apps/demo-server/package.json
COPY packages/core/package.json packages/core/package.json
COPY packages/server/package.json packages/server/package.json
COPY packages/adapter-livekit/package.json packages/adapter-livekit/package.json
COPY packages/adapter-google/package.json packages/adapter-google/package.json
COPY packages/adapter-openai/package.json packages/adapter-openai/package.json
COPY packages/adapter-anthropic/package.json packages/adapter-anthropic/package.json
COPY packages/audio/package.json packages/audio/package.json
RUN pnpm install --frozen-lockfile

# Build the demo-server and everything it depends on
FROM deps AS builder
COPY apps/demo-server apps/demo-server
COPY packages/core packages/core
COPY packages/server packages/server
COPY packages/adapter-livekit packages/adapter-livekit
COPY packages/adapter-google packages/adapter-google
COPY packages/adapter-openai packages/adapter-openai
COPY packages/adapter-anthropic packages/adapter-anthropic
COPY packages/audio packages/audio
RUN pnpm --filter @domos/demo-server... build
RUN pnpm deploy --filter @domos/demo-server --prod /prod/demo-server

# ---- Stage 2: runtime ----
FROM node:20-alpine AS runtime

RUN npm install -g pnpm@9.15.9

WORKDIR /app
ENV NODE_ENV=production

# Copy built workspace from the builder
COPY --from=builder /app ./

# The DomOS server listens on PORT (HTTP + WebSocket on the same port)
EXPOSE 3001

WORKDIR /app/apps/demo-server

# start = node dist/server.js
CMD ["pnpm", "start"]
ENV PORT=4001
WORKDIR /app
RUN addgroup -S domos && adduser -S domos -G domos
COPY --from=builder --chown=domos:domos /prod/demo-server ./
USER domos
EXPOSE 4001
HEALTHCHECK --interval=30s --timeout=4s --start-period=15s --retries=3 \
CMD node -e "const n=require('node:net');const s=n.connect(Number(process.env.PORT||4001),'127.0.0.1',()=>{s.end();process.exit(0)});s.setTimeout(3000,()=>process.exit(1));s.on('error',()=>process.exit(1))"
CMD ["node", "dist/server.js"]
139 changes: 139 additions & 0 deletions apps/docs-site/src/content/docs/livekit/web-deployment.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
title: Déployer DomOS avec LiveKit
description: Mise en production du serveur, du widget web et du canal audio LiveKit.
---

# Déployer DomOS avec LiveKit

DomOS utilise **deux canaux complémentaires**. ADTP transporte la session, le contexte, les tools et les approvals. LiveKit transporte l'audio temps réel du navigateur. Couper LiveKit ne doit donc pas supprimer la conversation texte.

## Architecture cible

```text
Navigateur HTTPS
├─ Widget DomOS ── WSS ───────────────┐
└─ livekit-client ── WebRTC ───────┐ │
│ │
LiveKit Cloud
ou self-hosted
Internet ── TLS/Caddy ── DomOS Server ───┘
├─ ADTP /domos
└─ POST /domos/livekit/token
```

Les valeurs `LIVEKIT_API_KEY`, `LIVEKIT_API_SECRET` et `GOOGLE_API_KEY` restent dans le serveur. Le navigateur reçoit uniquement un token de room court, après validation de l'API key DomOS et de la session.

## Choisir votre LiveKit

| Option | À choisir quand | Ce que vous gérez |
| --- | --- | --- |
| LiveKit Cloud | premier déploiement, petite équipe | projet, région, clés et quotas |
| Self-hosted | contrainte réseau, souveraineté ou contrôle infra | TLS, TURN, UDP/TCP, Redis, montée en charge et supervision |

Pour un premier environnement public, **LiveKit Cloud est le meilleur choix**. L'auto-hébergement ajoute beaucoup d'exploitation sans améliorer le widget.

## 1. Préparer les variables

Copiez l'exemple sans le committer :

```bash
cp deploy/.env.example .env
```

Renseignez au minimum :

```ini
DOMOS_DOMAIN=api.example.com
DOMOS_LIVEKIT_ALLOWED_ORIGINS=https://app.example.com
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=...
LIVEKIT_API_SECRET=...
GOOGLE_API_KEY=...
ADMIN_PASSWORD=...
```

`DOMOS_LIVEKIT_ALLOWED_ORIGINS` contient l'origine exacte du site qui héberge le widget. N'utilisez pas `*` en production.

## 2. Construire et lancer

Sans reverse proxy, utile pour vérifier localement :

```bash
docker compose build server
docker compose up -d server
docker compose ps
```

Avec HTTPS automatique via Caddy :

```bash
docker compose --profile proxy up -d --build
```

Le DNS de `DOMOS_DOMAIN` doit pointer vers la machine avant le démarrage de Caddy. Les ports 80 et 443 doivent être accessibles publiquement.

## 3. Configurer le widget

Le canal DomOS doit viser le WebSocket public :

```ini
VITE_DOMOS_ENDPOINT=wss://api.example.com/domos
VITE_DOMOS_LIVEKIT_TOKEN_ENDPOINT=https://api.example.com/domos/livekit/token
```

Le widget ouvre d'abord une session DomOS. Une fois le `sessionId` disponible, `useDomOSLiveKitRoom` demande un token puis rejoint la room. Les tools continuent à passer par ADTP.

## 4. Vérifier le parcours

Effectuez ces contrôles dans l'ordre :

1. `docker compose ps` indique que `server` est healthy.
2. Le widget ouvre son WebSocket en `wss://`.
3. Une session DomOS est créée avec une API key valide.
4. `POST /domos/livekit/token` retourne un token pour cette session.
5. Le navigateur demande l'autorisation microphone.
6. La room passe à l'état connecté.
7. L'audio utilisateur est publié.
8. Un tool DomOS s'exécute via ADTP.
9. Couper LiveKit laisse le chat texte utilisable.

## 5. Lire les erreurs rapidement

| Symptôme | Cause probable | Vérification |
| --- | --- | --- |
| `origin_not_allowed` | origine du widget absente | valeur exacte de `DOMOS_LIVEKIT_ALLOWED_ORIGINS` |
| `livekit_not_configured` | variable LiveKit manquante | les trois variables `LIVEKIT_*` côté serveur |
| room bloquée en connexion | URL, TURN ou réseau | console navigateur et région LiveKit |
| microphone silencieux | permission refusée ou piste mute | permission du site et état `muteMicrophone` |
| chat indisponible aussi | panne ADTP, pas LiveKit | connexion WSS DomOS et API key |

## 6. Sécurité production

- Servez le widget et DomOS uniquement en HTTPS/WSS.
- Restreignez les origines du token endpoint.
- Gardez les tokens de room courts.
- Faites tourner le conteneur en utilisateur non-root.
- Placez les secrets dans le gestionnaire de secrets de votre plateforme.
- Ne loggez jamais les tokens, le contexte brut, les arguments ou résultats de tools.
- Appliquez un rate limit au token endpoint au niveau proxy ou plateforme.

## 7. Mise à jour et rollback

Avant chaque mise à jour :

```bash
docker compose build server
docker compose run --rm server node --version
docker compose up -d server
```

Conservez le tag de l'image précédente dans `DOMOS_IMAGE_TAG`. Pour revenir en arrière, remettez ce tag puis relancez :

```bash
docker compose up -d --no-build server
```

## Limites actuelles

Le dépôt fournit maintenant le packaging de référence. Il reste à valider l'image dans la CI, exécuter le smoke test depuis un vrai domaine, ajouter une readiness applicative plus fine et confirmer les navigateurs cibles. La téléphonie SIP n'est pas requise pour le widget web et reste hors scope.
102 changes: 102 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: domos

services:
server:
build:
context: .
dockerfile: apps/demo-server/Dockerfile
image: domos-server:${DOMOS_IMAGE_TAG:-local}
restart: unless-stopped
init: true
environment:
NODE_ENV: production
PORT: 4001
DOMOS_REQUIRE_API_KEY: ${DOMOS_REQUIRE_API_KEY:-true}
DOMOS_LIVEKIT_ALLOWED_ORIGINS: ${DOMOS_LIVEKIT_ALLOWED_ORIGINS:-http://localhost:5173}
LIVEKIT_URL: ${LIVEKIT_URL:-}
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-}
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-}
GOOGLE_API_KEY: ${GOOGLE_API_KEY:-}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-}
expose:
- "4001"
ports:
- "${DOMOS_PORT:-4001}:4001"
healthcheck:
test:
- CMD
- node
- -e
- "const n=require('node:net');const s=n.connect(4001,'127.0.0.1',()=>{s.end();process.exit(0)});s.setTimeout(3000,()=>process.exit(1));s.on('error',()=>process.exit(1))"
interval: 30s
timeout: 4s
start_period: 15s
retries: 3
security_opt:
- no-new-privileges:true
cap_drop:
- ALL

redis:
image: redis:7-alpine
profiles: ["livekit-selfhosted"]
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${LIVEKIT_REDIS_PASSWORD}"]
volumes:
- livekit_redis_data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli -a \"$$LIVEKIT_REDIS_PASSWORD\" ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
environment:
LIVEKIT_REDIS_PASSWORD: ${LIVEKIT_REDIS_PASSWORD}
security_opt:
- no-new-privileges:true

livekit:
image: livekit/livekit-server:${LIVEKIT_SERVER_TAG:-v1.13.1}
profiles: ["livekit-selfhosted"]
restart: unless-stopped
command: ["--config", "/etc/livekit.yaml"]
depends_on:
redis:
condition: service_healthy
environment:
LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
LIVEKIT_REDIS_PASSWORD: ${LIVEKIT_REDIS_PASSWORD}
volumes:
- ./deploy/livekit.yaml:/etc/livekit.yaml:ro
ports:
- "${LIVEKIT_HTTP_PORT:-7880}:7880/tcp"
- "${LIVEKIT_RTC_TCP_PORT:-7881}:7881/tcp"
- "${LIVEKIT_RTC_UDP_PORT:-7882}:7882/udp"
security_opt:
- no-new-privileges:true

proxy:
image: caddy:2.8-alpine
profiles: ["proxy"]
restart: unless-stopped
depends_on:
server:
condition: service_healthy
environment:
DOMOS_DOMAIN: ${DOMOS_DOMAIN:-localhost}
LIVEKIT_DOMAIN: ${LIVEKIT_DOMAIN:-livekit.localhost}
ACME_EMAIL: ${ACME_EMAIL:-}
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
security_opt:
- no-new-privileges:true

volumes:
caddy_data:
caddy_config:
livekit_redis_data:
44 changes: 25 additions & 19 deletions deploy/.env.example
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
# ============================================================
# DomOS deployment stack — environment variables
# Copy to .env and fill in real values before `docker compose up`.
# ============================================================
# Image et exposition locale
DOMOS_IMAGE_TAG=local
DOMOS_PORT=4001
DOMOS_REQUIRE_API_KEY=true

# --- Google Gemini (LLM + native audio + STT/TTS) ---
GOOGLE_API_KEY=your_gemini_api_key_here
# Domaines publics, sans https://
DOMOS_DOMAIN=api.example.com
LIVEKIT_DOMAIN=livekit.example.com
ACME_EMAIL=ops@example.com

# --- DomOS client / admin auth ---
DOMOS_API_KEY=pk_demo_local
DOMOS_ADMIN_API_KEY=pk_admin_local
DOMOS_HOME_API_KEY=pk_home_local
DOMOS_REQUIRE_API_KEY=true
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change_me
# Origines web autorisees a demander un token LiveKit
DOMOS_LIVEKIT_ALLOWED_ORIGINS=https://app.example.com

# LiveKit Cloud ou profil Compose self-hosted
LIVEKIT_URL=wss://livekit.example.com
LIVEKIT_API_KEY=replace-with-a-long-random-key
LIVEKIT_API_SECRET=replace-with-a-long-random-secret
# Version stable verifiee le 2026-07-13. Mettre a jour volontairement apres lecture des release notes.
LIVEKIT_SERVER_TAG=v1.13.1
LIVEKIT_HTTP_PORT=7880
LIVEKIT_RTC_TCP_PORT=7881
LIVEKIT_RTC_UDP_PORT=7882
LIVEKIT_REDIS_PASSWORD=replace-with-a-long-random-password

# --- LiveKit (self-hosted) ---
# These MUST match the keys block in livekit.yaml.
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=change_me_to_a_long_random_secret
# Provider realtime actuellement implemente
GOOGLE_API_KEY=

# Comma-separated list of origins allowed to request LiveKit room tokens.
DOMOS_LIVEKIT_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
# Administration DomOS
ADMIN_PASSWORD=
Loading
Loading