# Console

> Le ticket, le WebSocket, les événements qui circulent.

La console ne passe pas par l'API : elle passe par un WebSocket **directement sur le
nœud** qui héberge le service. L'API ne te donne qu'un laissez-passer.

## 1. Demander un ticket

```http
GET /api/resources/{id}/console
```

```json
{
  "url": "wss://gra-01.arkya.gg:443/api/servers/9a17d0a6-.../ws",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2026-09-17T16:16:33.000Z",
  "port": 25565,
  "user": null
}
```

Le ticket vit **dix minutes**. Il ne vaut que pour ce service et pour toi.

## 2. Ouvrir le WebSocket

Connecte-toi à `url`, puis authentifie-toi avec `token` :

```js
const ws = new WebSocket(ticket.url);

ws.addEventListener("open", () => {
  ws.send(JSON.stringify({ event: "auth", args: [ticket.token] }));
});
```

## 3. Écouter

Chaque message est un JSON `{ event, args }`.

| Événement reçu | Ce qu'il veut dire |
| --- | --- |
| `auth success` | Tu es authentifié. C'est le moment de demander les journaux. |
| `console output` | Une ligne écrite par le processus. |
| `install output` | Une ligne du script d'installation. |
| `status` | L'état du processus : `running`, `starting`, `stopping`, `offline`. |
| `stats` | Un relevé de consommation, en JSON dans `args[0]`. |
| `daemon message`, `daemon error` | Un mot du démon, pas du processus. |
| `token expiring` | Le ticket va expirer. Redemande-en un et renvoie `auth`. |
| `token expired` | Il a expiré. Même geste. |
| `jwt error` | Le ticket a été refusé. Inutile d'insister. |

## 4. Parler

| Événement envoyé | Effet |
| --- | --- |
| `auth` | S'authentifier, avec le ticket dans `args[0]`. |
| `send logs` | Rejouer le tampon des dernières lignes. |
| `send stats` | Demander un relevé de consommation. |
| `send command` | Envoyer une commande au processus, dans `args[0]`. |
| `set state` | `start`, `stop`, `restart` ou `kill` dans `args[0]`. |

```js
ws.send(JSON.stringify({ event: "send command", args: ["say bonjour"] }));
```

## Un client minimal

```js
const ticket = await fetch(`${API}/api/resources/${id}/console`, {
  headers: { authorization: `Bearer ${token}` },
}).then((r) => r.json());

const ws = new WebSocket(ticket.url);

ws.addEventListener("open", () =>
  ws.send(JSON.stringify({ event: "auth", args: [ticket.token] })),
);

ws.addEventListener("message", (message) => {
  const { event, args = [] } = JSON.parse(String(message.data));

  if (event === "auth success") {
    ws.send(JSON.stringify({ event: "send logs" }));
  }
  if (event === "console output") {
    console.log(args[0]);
  }
  if (event === "token expiring") {
    // redemander un ticket et renvoyer « auth »
  }
});
```

## Renouveler sans couper

Quand `token expiring` arrive, redemande un ticket à l'API et renvoie un `auth` sur la
**même** connexion. Elle ne se coupe pas : c'est ce que fait `ark console` pour tenir
une session de plusieurs heures.

## Envoyer une seule commande, sans WebSocket

Pour un geste ponctuel, l'API suffit :

```http
POST /api/resources/{id}/command
```

```json
{ "command": "say bonjour" }
```

```json
{ "sent": true }
```

Elle envoie et rend la main : elle n'attend pas la réponse du processus. C'est ce que
fait [`ark exec`](/cli/console#ark-exec).
