# Temps réel

Le temps réel pousse des messages à vos utilisateurs connectés sans qu'ils interrogent le serveur : la diffusion de client à client, la présence (qui est en ligne), et la diffusion des changements d'une table. Il parle le protocole de `supabase-js`. Cette page dit comment l'activer, comment s'en servir, ce qui n'est pas servi, et les quotas de chaque palier.

## Ce qui est servi

- **La diffusion** (`broadcast`) : un client envoie un message sur un canal, les autres abonnés le reçoivent. Elle passe par la WebSocket, ou par REST (`POST /realtime/v1/api/broadcast`), le repli de `supabase-js`.
- **La présence** (`presence`) : chaque client annonce son état, et tous voient qui est là.
- **La diffusion depuis la base** : une fonction pose un déclencheur sur une table, et chaque insertion, modification ou suppression part en diffusion sur un canal.
- **Les canaux privés**, jugés par les politiques que vous écrivez sur `realtime.messages`.

La WebSocket est `wss://<identifiant>.donnees.bastide.app/realtime/v1/websocket`, protocoles 1.0.0 et 2.0.0 (voir [Base de données](database.md) pour l'adresse).

> **Attention.** L'écoute des changements de la base par `postgres_changes` n'est pas servie : un tel abonnement est refusé à la jonction du canal (statut `error`), et le refus nomme la diffusion depuis la base (LIM-08). Aucune réplication logique n'est exposée.

## Activer le temps réel

Le temps réel est désactivé par défaut. Tant qu'il l'est, `/realtime/v1` répond 403, code `temps_reel_inactif`.

- **Console** : ouvrez **Données**, section « Temps réel », puis « Activer le temps réel ». La carte affiche les quotas de votre palier.
- **Agent** : `backend_realtime_configure` avec `active: true` (rang `maintainer`, portée `db:write`).
- **Ligne de commande** : `bastide backend realtime configure --project-id <projet> --active true`.
- **API** : `PUT /v1/projects/{projectId}/backend/realtime`.

```json
{
  "projectId": "prj_01JQ8ZK4M7NP2RSTVWXYZ34567",
  "active": true,
  "idempotencyKey": "temps-reel-2026-10-28"
}
```

L'activation et la désactivation prennent effet en moins de 30 secondes. Désactiver ferme les connexions ouvertes du projet, sans toucher aux autres projets. L'état et les quotas se relisent par `backend_read` (champ `tempsReel`).

## Diffuser et suivre la présence

```js
const canal = supabase.channel("salon-42");

canal
  .on("broadcast", { event: "curseur" }, ({ payload }) => afficher(payload))
  .on("presence", { event: "sync" }, () => console.log(canal.presenceState()))
  .subscribe(async (statut) => {
    if (statut === "SUBSCRIBED") await canal.track({ nom: "Léa" });
  });

await canal.send({ type: "broadcast", event: "curseur", payload: { x: 10, y: 20 } });
```

Un sujet de canal commence par `realtime:` dans le protocole ; `supabase-js` l'ajoute pour vous.

## Diffuser les changements d'une table

À la place de `postgres_changes`, posez le déclencheur une fois, dans une migration ou l'éditeur SQL :

```sql
select realtime.diffuser_les_changements('public.messages');
```

Le canal `public:messages` reçoit alors chaque insertion, modification et suppression de la table, en diffusion. Les messages venus de la base comptent dans le quota de messages par seconde du projet ; au-delà, ils sont écartés.

## Les canaux privés

Un canal privé n'admet que les clients que vos politiques admettent. Écrivez-les sur la table `realtime.messages`, avec `realtime.topic()` (le sujet du canal) et `auth.uid()` :

- la politique `SELECT` décide qui peut **rejoindre et lire** le canal ;
- la politique `INSERT` décide qui peut **y envoyer**.

La plateforme éprouve la politique dans une transaction toujours annulée, sous le rôle de l'appelant : c'est PostgreSQL qui juge, telle que vous l'avez écrite. Voir [Sécurité des données](data-security.md).

## Les quotas par palier

| Palier | Messages par seconde | Connexions simultanées |
| --- | --- | --- |
| Découverte | 100 | 200 |
| Standard | 500 | 2 000 |
| Pro | 2 500 | 10 000 |
| Souverain | négociés, sans plafond opposé | négociées, sans plafond opposé |

Sur tous les paliers : 100 canaux par connexion, une charge utile de 3 000 Ko au plus par message, et 5 appels de présence par client par tranche de 30 secondes.

- **Connexions** : la place est réservée avant la poignée de main. Au-delà du quota, la WebSocket reçoit 429, code `connexions_simultanees_depassees`, sans qu'aucune connexion s'ouvre.
- **Messages** : le quota se compte par seconde pleine, pour tout le projet (diffusions, présence et messages de la base). Au-delà, l'envoi reçoit une erreur qui nomme le quota ; par REST, 429.
- **Canaux** : au-delà de 100 sur une même connexion, la jonction est refusée ; quittez-en un, ou ouvrez une autre connexion.

> **Note.** Le temps réel tient, par instance de la plateforme, une connexion en mode session à votre base tant qu'un client est branché. Elle compte dans le plafond de connexions serveur de votre palier (trois en Découverte ; voir [Base de données](database.md)).

## Erreurs fréquentes

- **403 `temps_reel_inactif`** : activez le temps réel du projet.
- **Abonnement refusé, raison citant la diffusion depuis la base** : vous avez demandé `postgres_changes` ; employez `realtime.diffuser_les_changements`.
- **429 `connexions_simultanees_depassees`** : fermez les connexions inutilisées, ou changez de palier.
- **« jeton d'accès refusé »** à la jonction : le jeton de session a expiré ; reconnectez le client.
- **Rien n'arrive sur un canal privé** : la politique `SELECT` de `realtime.messages` n'admet pas cet utilisateur.

## Voir aussi

- [Base de données](database.md), [Sécurité des données](data-security.md), [Authentification](auth.md), [Limites par palier](limits.md)
- Référence : [backend.realtime.configure](../reference/operations/backend.realtime.configure.md)
