# Configuration — `config.json`

The client is configured at **runtime** by `config.json`. It is
fetched on startup (before the app renders) and deep-merged over the built-in
defaults, so you can **re-point the client at another IRC network and fully
re-brand it without rebuilding** — just edit the deployed file and reload.

- **Source of truth:** `public/config.json` (copied into the build).
- **Served at:** `/app/config.json` (e.g. `https://tchatou.fr/app/config.json`).
- **Edit live:** the service worker serves it **network-first**, so changes to
  the deployed `/app/config.json` take effect on the next reload — no rebuild.
- **Defaults/fallbacks:** `src/config.ts` (`DEFAULT_CONFIG`). Anything you omit
  from `config.json` falls back to these, so a missing/invalid file still boots.

Merge rules: objects merge key-by-key; **arrays and scalars replace** wholesale
(e.g. `startup.channels` is replaced, not concatenated).

## Options

| Key | Type | Default | What it does |
|-----|------|---------|--------------|
| `server.url` | string | `wss://www.swaygo.fr/irc/` | WebSocket URL of the IRCv3 server (uses the `text/binary.ircv3.net` subprotocols). The server's Origin allowlist must include where the client is served. |
| `server.guestIdent` | string | `Invité` | Ident shown for **guests** (not logged in), e.g. `Foo!Invité@…`. Folded to ASCII since IRC idents are `[A-Za-z0-9._-]` (so `Invité`→`Invite`). Logged-in members use their own nick as the ident. |
| `startup.channels` | string[] | `["#accueil"]` | Channels auto-joined on connect (first = active). A `?channel=` URL param overrides this. |
| `branding.name` | string | `Tchatou` | App/network name shown in the UI, the console title, and CTCP VERSION. |
| `branding.icon` | string | tchatou favicon URL | Logo on the connect screen + default network icon. |
| `branding.url` | string | `https://tchatou.fr` | Homepage; also used in CTCP VERSION/SOURCE replies. |
| `branding.tagline` | string | `Le tchat français,` | Connect screen — first title line. |
| `branding.taglineEm` | string | `en direct.` | Connect screen — emphasised second line. |
| `branding.subtitle` | string | … | Connect screen — paragraph under the title. |
| `branding.projectUrl` | string | `https://orbit.tchatou.fr` | Link to the Orbit project/source page (shown in Settings → About). |
| `turnstile.enabled` | bool | `true` | Render the anti-bot challenge inline (Cloudflare Turnstile). `false` = show the server's verification step as a link, no Cloudflare script. Whether a challenge is *required* is decided server-side. |
| `turnstile.sitekey` | string | `0x4AAA…` | Public Turnstile site key. |
| `report.service` | string | `ReportServ` | Services pseudo-client that receives reports (e.g. `ReportServ`). Preferred over `report.target` — a service isn't blocked by a `+n` staff channel the reporter isn't in. Empty = fall back to `report.target`. |
| `report.target` | string | `#staff` | Channel that user reports (the 🚩 button) are sent to when `report.service` is empty. |
| `defaults.theme` | string | `light` | Default theme for **new** users: `light` \| `dark` \| `yomirc` \| `yomirc-dark`. |
| `defaults.compact` | bool | `false` | Default to denser message rows. |
| `defaults.sound` | bool | `true` | Default sound-on-mention/PM. |
| `defaults.hideJoinQuit` | bool | `false` | Default to hiding join/part/quit noise. |
| `defaults.clock24` | bool | `true` | Default to 24-hour timestamps. |
| `defaults.lang` | string | `""` | Force a default UI language (`en`, `es`, `de`, `it`, `nl`, `tr`, `ru`, `ne`, `pt-BR`, `pt-PT`, `fr`). Empty = auto-detect from the browser. Only seeds users who haven't picked a language. |
| `features.push` | bool | `true` | Show the Web Push notifications setting (and re-assert the subscription on connect). |
| `features.imageUpload` | bool | `true` | Enable the composer image button + paste/drag-drop upload. |
| `features.register` | bool | `true` | Account self-service: shows the "Create account" button, the "Forgot password" link, and their FAQ entries. `false` hides all of them. |
| `plugins` | string[] | `[]` | Plugin script URLs loaded at startup (operator-controlled). See **[PLUGINS.md](./docs/PLUGINS.md)**. |
| `builtins` | string[] | `[]` | Built-in **sandboxed** features to enable by name (opt-in). Currently `"dice"` (a 🎲/🪙 footer widget). See **[docs/SANDBOX.md](./docs/SANDBOX.md)**. |

> `defaults.*` only seed a user's preferences the **first** time — once someone
> changes a setting it's stored in their browser and the config no longer
> overrides it.

## Example

```json
{
  "server":   { "url": "wss://irc.example.org/ws/", "guestIdent": "Guest" },
  "startup":  { "channels": ["#lobby", "#help"] },
  "branding": {
    "name": "ExampleChat",
    "icon": "https://example.org/logo.svg",
    "url": "https://example.org",
    "tagline": "Chat with",
    "taglineEm": "everyone.",
    "subtitle": "Public rooms, private messages, no signup required.",
    "projectUrl": "https://orbit.tchatou.fr"
  },
  "turnstile": { "enabled": false, "sitekey": "" },
  "report":   { "service": "ReportServ", "target": "#staff" },
  "defaults": { "theme": "dark", "compact": true, "sound": true, "hideJoinQuit": true, "clock24": true, "lang": "" },
  "features": { "push": true, "imageUpload": true, "register": false },
  "plugins":  [],
  "builtins": ["dice"]
}
```

## Deploy

```bash
npm run build
# copy the build (incl. config.json) to the web root served at /app/
sudo rm -rf /var/www/tchatou/app/assets/*
sudo cp -r dist/* /var/www/tchatou/app/
```

To tweak config only (no rebuild): edit `/var/www/tchatou/app/config.json` and
reload — the service worker fetches it network-first.
