📝(doc) create complete documentation website for Meet#1436
Conversation
Build a structured documentation website that centralizes guidance for users, administrators, and contributors. This documentation replaces the previous documentation structure Highlights include: Product overview and feature documentation Self-hosting guides (Docker Compose and Kubernetes) Configuration and integration references API, security, and troubleshooting documentation Development workflows and contribution guides FAQ and operational best practices Remove of outdated and duplicate documentation sources
|
There was a problem hiding this comment.
This asset seems to have a broken layout in the chat
There was a problem hiding this comment.
the layout in the participant side panel seems different than the default one.
There was a problem hiding this comment.
same comment about the participant side panel
There was a problem hiding this comment.
if configured, the "my backgrounds" one can be persisted accross session
There was a problem hiding this comment.
I guess this banner is used in the readme
| docker compose up -d app-dev | ||
|
|
||
| # Django shell | ||
| docker compose exec app-dev python manage.py shell | ||
|
|
||
| # Apply migrations | ||
| docker compose exec app-dev python manage.py migrate | ||
|
|
||
| # Create superuser | ||
| docker compose exec app-dev python manage.py createsuperuser |
There was a problem hiding this comment.
I guess, overall, across La Suite, we prefer to encourage developers to use make commands. That's a team practice, though, and it could be challenged.
| docker compose up -d app-dev | |
| # Django shell | |
| docker compose exec app-dev python manage.py shell | |
| # Apply migrations | |
| docker compose exec app-dev python manage.py migrate | |
| # Create superuser | |
| docker compose exec app-dev python manage.py createsuperuser | |
| make run-backend | |
| # Django shell | |
| docker compose exec app-dev python manage.py shell (we might add a command for this one if missing) | |
| # Apply migrations | |
| make migrate | |
| # Create superuser | |
| make superuser |
| - `Development` - debug mode, relaxed security | ||
| - `Demo` - production-like with demo data | ||
| - `Production` - full production settings |
There was a problem hiding this comment.
we also have a test one
| docker compose exec app-dev python manage.py makemigrations | ||
| docker compose exec app-dev python manage.py migrate |
There was a problem hiding this comment.
same we do offer make commands
| # core/models.py | ||
|
|
||
| class Room(models.Model): | ||
| id = models.UUIDField(primary_key=True, default=uuid.uuid4) | ||
| slug = models.SlugField(unique=True) | ||
| name = models.CharField(max_length=255) | ||
| access_level = models.CharField(...) # public, authenticated, restricted | ||
| configuration = models.JSONField(default=dict) | ||
| created_by = models.ForeignKey(User, on_delete=models.CASCADE) | ||
|
|
||
| class Recording(models.Model): | ||
| id = models.UUIDField(primary_key=True, default=uuid.uuid4) | ||
| room = models.ForeignKey(Room, on_delete=models.CASCADE) | ||
| status = models.CharField(...) # initiated, active, stopped, saved | ||
| mode = models.CharField(...) # screen_recording, transcript | ||
| key = models.CharField(...) # S3 object key (path in bucket) | ||
| ``` | ||
|
|
||
| After model changes, create and apply migrations: |
There was a problem hiding this comment.
Won't it be difficult to keep this duplicated code up to date? I'm concerned the documentation may drift over time, as developers are unlikely to remember to keep both in sync.
| | Framework | React 18 | | ||
| | Language | TypeScript | | ||
| | Build tool | Vite | | ||
| | WebRTC | `@livekit/components-react` | |
There was a problem hiding this comment.
livekit-client is the dependency encapsulating the webrtc logic. https://docs.livekit.io/reference/client-sdk-js/
| | Language | TypeScript | | ||
| | Build tool | Vite | | ||
| | WebRTC | `@livekit/components-react` | | ||
| | Accessible UI | React Aria (Adobe) | |
There was a problem hiding this comment.
React aria are only Headless UI Components !
| | Accessible UI | React Aria (Adobe) | | ||
| | State management | Zustand | | ||
| | i18n | i18next | | ||
| | Testing | Vitest + React Testing Library | |
There was a problem hiding this comment.
I guess we don't have any test (yet)
| npm run dev | ||
| ``` | ||
|
|
||
| Dev server runs at http://localhost:5173 with hot module replacement. |
| Meet uses React Aria for accessible component primitives. Rules: | ||
|
|
||
| - All interactive elements must have an accessible name (`aria-label` or visible text) | ||
| - State changes (mute/unmute, recording) must be announced via ARIA live regions | ||
| - Focus management: opening dialogs moves focus in; closing returns focus to the trigger | ||
| - Test every new interactive element with keyboard navigation | ||
|
|
There was a problem hiding this comment.
Hey I would be a bit more specific like :
Accessibility & React Aria
Meet builds on React Aria Components via styled primitives in src/frontend/src/primitives/. Prefer these over raw HTML or direct RAC imports.
Rules
-
Accessible names : Every interactive element needs a name via
aria-label,aria-labelledby, or visible text. Use i18n (t(...)) for all strings. Icon-only buttons must havearia-label; tooltips are supplementary, not a substitute. -
State announcements : Use
useScreenReaderAnnounce()for programmatic screen reader feedback (e.g. recording state, effects). Use the toast system (@react-aria/toast) for room events. Do not addaria-liveto visual-only UI : announce separately to avoid duplication. -
Focus management : RAC
Dialoghandles focus trap and return-to-trigger. Side panels and custom toolbars may need explicit focus (autoFocus,ref.focus(), orFocusScopefrom@react-aria/focus). -
Keyboard & screen reader testing : Test Tab navigation, arrow keys in menus/toolbars, and verify with a screen reader before merging.
-
PiP / cross-document contexts : Use
VisualOnlyTooltipinstead ofTooltipWrapperto prevent duplicate SR announcements.
| 1. Add the key to `en.json` and `fr.json` (minimum) | ||
| 2. Use in components: | ||
|
|
||
| ```typescript | ||
| const { t } = useTranslation(); | ||
| <span>{t('controls.mute')}</span> | ||
| ``` | ||
|
|
||
| Other languages are managed via Crowdin. |
There was a problem hiding this comment.
We're not using crowdin, it's installed but not in use, people need to add the key for all lang.
| make test-front | ||
|
|
||
| # Or directly | ||
| cd src/frontend | ||
| npm test | ||
| npm run test:coverage | ||
| ``` |
|
|
||
| LiveKit integration has two sides: | ||
|
|
||
| 1. **Backend (Python)**: Generates JWT tokens, calls LiveKit API for room management and Egress |
There was a problem hiding this comment.
Egress, Agent, Sip etc.
| LiveKit integration has two sides: | ||
|
|
||
| 1. **Backend (Python)**: Generates JWT tokens, calls LiveKit API for room management and Egress | ||
| 2. **Frontend (TypeScript)**: Connects to LiveKit for media using the LiveKit React SDK |
There was a problem hiding this comment.
I guess we connect using the JS SDK they provide, and the React components.
| ```toml | ||
| # pyproject.toml | ||
| [project.dependencies] | ||
| livekit-api = ">=0.7" |
There was a problem hiding this comment.
Hmm, what additional information does this provide? We usually pin dependencies, so I'm not sure this adds much.
| return Response({ | ||
| "token": token, | ||
| "url": settings.LIVEKIT_CONFIGURATION["url"], | ||
| }) |
There was a problem hiding this comment.
Hmm, actually, I think this could be misleading. They are indeed returned to the client through the API (via a viewset, de facto), but the core logic still lives in the room serializer.
Overall, I'm in favor of documenting how the backend interacts with LiveKit, with pointers to the relevant code. However, I'm not a big fan of pseudo-code that doesn't accurately reflect how the backend actually works. Why not point directly to the implementation and complement it with a few sequence diagrams instead? I think that would be easier to maintain and less likely to become outdated.
| ## Frontend integration | ||
|
|
||
| ### Connection flow |
There was a problem hiding this comment.
I feel we're documenting how the livekit js sdk works instead of the logic we wrote
| docker compose exec app-dev python manage.py shell | ||
|
|
||
| # Create superuser | ||
| docker compose exec app-dev python manage.py createsuperuser |
| npm run dev | ||
| ``` | ||
|
|
||
| The frontend dev server runs at http://localhost:5173. |
| ## Before you start | ||
|
|
||
| - Read the [developer handbook](https://suitenumerique.gitbook.io/handbook) for team best practices | ||
| - Check the [roadmap](https://github.com/orgs/suitenumerique/projects/3/views/2) to avoid duplicating in-progress work |
There was a problem hiding this comment.
this link is closed, we should remove it
| # Pull Requests | ||
|
|
||
| ## Before you start | ||
|
|
There was a problem hiding this comment.
I would add an initial step, "connect on matrix, present yourself"
| 1. Fork the repository | ||
| 2. Create a branch: `git checkout -b feat/my-feature` or `fix/my-bug` | ||
| 3. Make your changes with tests | ||
| 4. Run `make test-back test-front` and `make lint-back lint-front` |
There was a problem hiding this comment.
there is no test-front
| | `agents` | LiveKit agents | | ||
| | `summary` | Transcription/summary service | | ||
| | `docker` | Docker configuration | | ||
| | `deps` | Dependency updates | |
There was a problem hiding this comment.
we usually use the scope related to the dependency update
| ## PR checklist | ||
|
|
||
| - [ ] Tests added for new functionality or the bug fix | ||
| - [ ] All tests pass (`make test-back test-front`) |
| ## Known security fixes (by version) | ||
|
|
||
| | Version | Component | Issue | | ||
| |---|---|---| | ||
| | v1.5.0 | Frontend | XSS on the recording download page | | ||
| | v1.6.0 | Backend | Object-level permission bypass on room endpoints | | ||
| | v1.6.0 | Backend | Application validation bypass for external JWTs | | ||
| | v1.13.0 | Backend | Email disclosure in room invitation endpoint | | ||
| | v1.14.0 | Backend | Participant metadata update permission enforcement | | ||
| | v1.14.0 | Multiple | CVEs: aiohttp, Vite, Django, Pillow | | ||
| | v1.15.0 | Backend | Room configuration validation | | ||
| | v1.16.0 | Frontend | Room IDs generated with non-cryptographic RNG (fixed) | |
There was a problem hiding this comment.
should we list them? Why not rely on GitHub's security advisories feature?
|
|
||
| ### Object storage (MinIO/S3) | ||
|
|
||
| - Do not expose MinIO/S3 ports publicly |
There was a problem hiding this comment.
I guess we do expose the MinIO publicly for the recording download feature, isn't it?
| @@ -0,0 +1,248 @@ | |||
| # TURN Server | |||
|
|
|||
| TURN (Traversal Using Relays around NAT) is a relay protocol that routes WebRTC media through a well-known port when direct UDP is blocked. Without TURN, participants on corporate networks, hotel Wi-Fi, or VPNs may connect to a meeting but have no audio or video. | |||
There was a problem hiding this comment.
| TURN (Traversal Using Relays around NAT) is a relay protocol that routes WebRTC media through a well-known port when direct UDP is blocked. Without TURN, participants on corporate networks, hotel Wi-Fi, or VPNs may connect to a meeting but have no audio or video. | |
| TURN (Traversal Using Relays around NAT) is a relay protocol that routes WebRTC media through a well-known port when direct UDP is blocked. Without TURN, participants on corporate networks, hotel Wi-Fi, or VPNs may not connect to a meeting. |
| | Public instance, mixed networks | TURN recommended | | ||
| | Enterprise | TURN required - corporate firewalls routinely block non-standard UDP | | ||
|
|
||
| **How to tell if TURN is needed:** participants who connect (room appears joined, participants list shows them) but have no audio or video, and this resolves when they switch to a phone hotspot - that is a UDP-blocked network. TURN solves it. |
There was a problem hiding this comment.
LiveKit is not only about UDP. I think this part of the documentation is a bit oversimplified.
LiveKit offers a range of connection candidates, some over UDP and others over TCP, depending on what is available.
It might be related to how the LiveKit server is configured.
| |---|---| | ||
| | Internal team, controlled network | TURN optional - direct media (7882/UDP) works | | ||
| | Public instance, mixed networks | TURN recommended | | ||
| | Enterprise | TURN required - corporate firewalls routinely block non-standard UDP | |
There was a problem hiding this comment.
think we could be a bit more precise here.
Some corporate firewalls block all UDP traffic, forcing clients to fall back to TURN over TCP (or TLS).
TURN is also required when the client is behind a symmetric NAT, where STUN alone cannot establish a direct peer-to-peer connection. For less restrictive NAT types, STUN is usually sufficient. In fact, a STUN server is almost essential for WebRTC; LiveKit uses Google's public STUN server by default if you don't configure your own.
TURN over UDP may still be required depending on the client's NAT and firewall configuration. In general, LiveKit gathers multiple ICE candidates (host, STUN, TURN over UDP/TCP/TLS) and lets ICE negotiate the best available path.
|
|
||
| --- | ||
|
|
||
| ## Option 1: Built-in TURN on 443/UDP (recommended) |
There was a problem hiding this comment.
One thing that could also be worth mentioning is that LiveKit's built-in TURN server doesn't support every TURN transport (e.g. TURN over UDP, TCP, and TLS). In some environments, especially those with restrictive corporate networks, it can be beneficial to deploy a dedicated TURN server such as coturn to provide the transports you need.
| | [visio.numerique.gouv.fr](https://visio.numerique.gouv.fr/) | DINUM | French central administration (ProConnect required) | | ||
| | [visio.suite.anct.gouv.fr](https://visio.suite.anct.gouv.fr/) | ANCT | French territorial administration (ProConnect required) | | ||
| | [visio.lasuite.coop](https://visio.lasuite.coop/) | lasuite.coop | Open demo, accounts reset monthly | | ||
| | [mosa.cloud](https://mosa.cloud/) | mosa.cloud | Dutch commercial instance | |
| @@ -0,0 +1,183 @@ | |||
| # Theming | |||
There was a problem hiding this comment.
Since you moved the documentation, someone added details on how to modify the favicon
|
|
||
| **Known limitations:** | ||
|
|
||
| - Participant identification is not implemented. Speakers are labeled generically (e.g., `SPEAKER_00`, `SPEAKER_01`) |
There was a problem hiding this comment.
Actually now we have a draft
| Backend->>Summary: POST /api/v1/tasks/ (bearer token) | ||
| Note right of Backend: Payload: owner_id, filename, email, sub, room, recording_date, recording_time | ||
|
|
||
| Summary->>Celery: Register task (transcribe-queue) | ||
| Celery->>MinIO: Fetch audio file | ||
| Celery->>STT: Transcribe audio (WhisperX) | ||
| STT-->>Celery: Segmented transcript | ||
| Celery->>Celery: Format transcript | ||
| Celery->>Docs: POST /create-for-owner (title, content, email, sub, api token) | ||
| Docs-->>Celery: Acknowledgement |
There was a problem hiding this comment.
this doc is outdated with recent works from @FloChehab
| @@ -0,0 +1,235 @@ | |||
| # AI Transcription | |||
| | 7882 | UDP | WebRTC media - all audio and video streams multiplexed here | | ||
|
|
||
| !!!info | ||
| `7882/UDP` is the critical port. If it is blocked, participants connect to the room but have no audio or video and fall back to TCP (7881), which increases latency. |
There was a problem hiding this comment.
hum, this not how ICE works, they won't connect to the room until they find a working pair.
They won't connect with no video/audio.
ICE will try every candidate until one works.
| - LiveKit webhook configured to reach the Meet backend | ||
|
|
||
| !!!warning | ||
| An S3-compatible object storage with bucket event notifications is required. As an example we use MinIO but in production MinIO is not recommanded as the open source community version is not maintained anymore. This dependency is planned to be refactored in a future release see [PR #1386](https://github.com/suitenumerique/meet/pull/1386) |
| @@ -0,0 +1,571 @@ | |||
| # Recording | |||
There was a problem hiding this comment.
@cameledev we should update the documentation with your recent changes
|
|
||
| ### Meet frontend | ||
|
|
||
| The frontend container runs an internal nginx on port 8083 that routes traffic between the backend API and the React SPA. Traefik terminates TLS and forwards everything for `meet.example.com` to port 8083: |
There was a problem hiding this comment.
not in the "community" edition we build.
If I'm right, that's the k8s ingress that routes the traffic in our deployment, not the frontend image
lebaudantoine
left a comment
There was a problem hiding this comment.
1st review, let's chat
|
It closes #937 |
| make bootstrap FLUSH_ARGS='--no-input' | ||
| ``` | ||
|
|
||
| This builds the backend image, installs dependencies, runs migrations, creates a default user (`meet` / `meet`), and compiles translations. |
There was a problem hiding this comment.
| This builds the backend image, installs dependencies, runs migrations, creates a default user (`meet` / `meet`), and compiles translations. | |
| This builds the backend image, installs dependencies, runs migrations, creates a default user (`meet` / `meet`), compiles translations, adds demo data, and starts the stack. |
| ## One-liner | ||
|
|
||
| ```bash | ||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" |
There was a problem hiding this comment.
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | |
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main" |
|
|
||
| ```bash | ||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | ||
| curl -fsSL $RAW/docs/docs/install.sh -o install.sh |
There was a problem hiding this comment.
| curl -fsSL $RAW/docs/docs/install.sh -o install.sh | |
| curl -fsSL $RAW/docs/install.sh -o install.sh |
|
In general, references to and to test here : |
| All three stacks share the same domain names. Download the hosts file once and edit it - each stack will copy it in later. | ||
|
|
||
| ```bash | ||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" |
There was a problem hiding this comment.
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | |
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main" |
| ```bash | ||
| mkdir -p ~/docker/keycloak/env.d && cd ~/docker/keycloak | ||
|
|
||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" |
There was a problem hiding this comment.
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | |
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main" |
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/keycloak/compose.yml | ||
| curl -fsSL -o keycloak-realm.json ${RAW}/docs/docs/examples/keycloak/keycloak-realm.json |
There was a problem hiding this comment.
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/keycloak/compose.yml | |
| curl -fsSL -o keycloak-realm.json ${RAW}/docs/docs/examples/keycloak/keycloak-realm.json | |
| curl -fsSL -o compose.yml ${RAW}/docs/examples/keycloak/compose.yml | |
| curl -fsSL -o keycloak-realm.json ${RAW}/docs/examples/keycloak/keycloak-realm.json |
| ```bash | ||
| mkdir -p ~/docker/meet/env.d && cd ~/docker/meet | ||
|
|
||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" |
There was a problem hiding this comment.
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | |
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main" |
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/meet/compose.yml | ||
| curl -fsSL -o livekit-server.yaml ${RAW}/docs/docs/examples/meet/livekit-server.yaml | ||
| curl -fsSL -o nginx-routing.conf ${RAW}/docs/docs/examples/meet/nginx-routing.conf | ||
| curl -fsSL -o generate-secrets.sh ${RAW}/docs/docs/generate-secrets.sh |
There was a problem hiding this comment.
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/meet/compose.yml | |
| curl -fsSL -o livekit-server.yaml ${RAW}/docs/docs/examples/meet/livekit-server.yaml | |
| curl -fsSL -o nginx-routing.conf ${RAW}/docs/docs/examples/meet/nginx-routing.conf | |
| curl -fsSL -o generate-secrets.sh ${RAW}/docs/docs/generate-secrets.sh | |
| curl -fsSL -o compose.yml ${RAW}/docs/examples/meet/compose.yml | |
| curl -fsSL -o livekit-server.yaml ${RAW}/docs/examples/meet/livekit-server.yaml | |
| curl -fsSL -o nginx-routing.conf ${RAW}/docs/examples/meet/nginx-routing.conf | |
| curl -fsSL -o generate-secrets.sh ${RAW}/docs/generate-secrets.sh |
| ```bash | ||
| mkdir -p ~/docker/nginx-proxy && cd ~/docker/nginx-proxy | ||
|
|
||
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" |
There was a problem hiding this comment.
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/main" | |
| RAW="https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main" |
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/nginx-proxy/compose.yml | ||
| curl -fsSL -o .env ${RAW}/docs/docs/examples/nginx-proxy/.env.example |
There was a problem hiding this comment.
| curl -fsSL -o compose.yml ${RAW}/docs/docs/examples/nginx-proxy/compose.yml | |
| curl -fsSL -o .env ${RAW}/docs/docs/examples/nginx-proxy/.env.example | |
| curl -fsSL -o compose.yml ${RAW}/docs/examples/nginx-proxy/compose.yml | |
| curl -fsSL -o .env ${RAW}/docs/examples/nginx-proxy/.env.example |



Build a structured documentation website that centralizes guidance for users, administrators, and contributors.
This documentation replaces the previous documentation structure
Highlights include:
Documentation is rendered on this temp URL: https://documentation-meet.beta.numerique.gouv.fr/self-hosting/compose/quick-start/
In order to test the guides you will need replace var RAW to point to my branch: