> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Gateway Config Reload

> Hot-reload gateway.yaml without dropping in-flight turns — SIGHUP, watchdog, drain-coordinated restart

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Gateway Config Reload"
        Request[📋 User Request] --> Process[⚙️ Gateway Config Reload]
        Process --> Result[✅ Result]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Request input
    class Process process
    class Result output
```

<Note>
  The gateway now ships in the `praisonai-bot` package. `praisonai serve gateway` still works exactly as documented here; for a standalone install see [praisonai-bot Migration](/docs/guides/praisonai-bot-migration).
</Note>

Edit `gateway.yaml` while the gateway is running and the change applies automatically — no restart, no dropped connections.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
pip install "praisonai[gateway]"
praisonai gateway start --config gateway.yaml
# Edit gateway.yaml and save — reload applies within seconds
```

The operator edits `gateway.yaml`; the gateway diffs and hot-reloads without dropping in-flight turns.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Gateway Config Reload"
        In[📝 Edit gateway.yaml] --> Watch[🔍 Watchdog / SIGHUP]
        Watch --> Reload[⚙️ Diff + Drain]
        Reload --> GW[🤖 Gateway]
        GW --> Out[✅ Reload Applied]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class In input
    class Watch,Reload process
    class GW agent
    class Out output
```

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Op as Operator
    participant FS as gateway.yaml
    participant GW as Gateway
    participant Ch as Channel (bot)
    Op->>FS: edit gateway.yaml
    FS-->>GW: watchdog event (or mtime poll)
    Op->>GW: kill -HUP pid (equivalent)
    GW->>GW: diff config
    GW->>Ch: drain(reload_drain_timeout)
    Ch-->>GW: in-flight turns finished
    GW->>Ch: restart with new config
    GW-->>Op: log "reload applied: agents; restart[telegram]"
```

## Quick Start

<Steps>
  <Step title="Install the gateway extra">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    pip install "praisonai[gateway]"
    ```

    This installs `watchdog>=3.0.0` for event-driven file watching. The gateway falls back to mtime polling when `watchdog` is not installed.
  </Step>

  <Step title="Edit gateway.yaml — auto-detected">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # gateway.yaml
    agents:
      assistant:
        instructions: "You are a helpful assistant."

    gateway:
      reload_drain_timeout: 10.0   # seconds to drain a channel on reload
    ```

    Save the file. The running gateway detects the change within the watchdog debounce window and reloads automatically.
  </Step>

  <Step title="Trigger a reload manually via SIGHUP">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    systemctl reload praisonai-gateway
    # or
    kill -HUP $(pgrep -f 'praisonai gateway start')
    ```

    `SIGHUP` runs the same `reload_config` path as a file change — no shutdown. Best-effort on Windows (SIGHUP unavailable, see fallback below).
  </Step>

  <Step title="Tune the drain window">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    gateway:
      reload_drain_timeout: 10.0   # seconds to drain a channel on reload
      # If unset, falls back to gateway.drain_timeout
      drain_timeout: 5.0
    ```

    `reload_drain_timeout` controls how long a channel restart waits for in-flight turns to finish. If unset, it falls back to `drain_timeout`.
  </Step>

  <Step title="Watch the audit log">
    ```
    reload applied: agents; restart[telegram]
    ```

    Each line tells you exactly what changed and which channels were restarted.
  </Step>
</Steps>

***

## What Triggers a Reload

Two paths trigger the same `reload_config` routine:

| Trigger   | Mechanism                                                             |
| --------- | --------------------------------------------------------------------- |
| File edit | `watchdog` filesystem event (if installed) or mtime polling every 5 s |
| Manual    | `kill -HUP <pid>` or `systemctl reload praisonai-gateway`             |

Both paths debounce rapid consecutive writes before applying — a burst of saves from an editor doesn't cause multiple reloads.

<Tabs>
  <Tab title="File-based (event-driven)">
    With `watchdog` installed, filesystem events (inotify on Linux, FSEvents on macOS, ReadDirectoryChangesW on Windows) trigger a reload within the debounce window (default 1 s) automatically — no signal required.
  </Tab>

  <Tab title="File-based (polling fallback)">
    Without `watchdog`, the gateway falls back to mtime polling every 5 s automatically. Zero config change needed to opt in or out — the gateway chooses the best available mechanism at startup.
  </Tab>

  <Tab title="Operator-triggered (SIGHUP)">
    Send SIGHUP to the gateway process to force an immediate reload:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    kill -HUP <gateway-pid>
    # or via systemd
    systemctl reload praisonai-gateway
    ```

    Concurrent SIGHUPs are serialized — the second waits for the first to finish.

    <Warning>
      SIGHUP does not exist on Windows. The gateway skips the SIGHUP handler silently on Windows and falls back to polling or file-event watching only.
    </Warning>
  </Tab>
</Tabs>

***

## What Gets Restarted

| Config change                            | Action                                   |
| ---------------------------------------- | ---------------------------------------- |
| Agent `instructions` / `model` / `tools` | Recreate affected agents in-place        |
| Channel `token` or platform args         | Drain + restart that single channel      |
| Gateway `host` / `port`                  | Full restart (WebSocket server stays up) |
| Scheduler / routes / guardrails          | Full channel restart                     |

The WebSocket server itself keeps running throughout — connected clients are not disconnected unless their channel is restarting.

***

## Drain-Coordinated Restart

Channel restarts drain in-flight turns before bouncing, using the same `drain_timeout` coroutine as shutdown.

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# gateway.yaml
gateway:
  drain_timeout: 30         # shutdown drain budget
  reload_drain_timeout: 10  # reload-specific budget (wins over drain_timeout for reloads)
```

***

## Windows / No-Watchdog Fallback

| Environment                          | File watching                | SIGHUP        |
| ------------------------------------ | ---------------------------- | ------------- |
| Linux / macOS + `watchdog` installed | Event-driven (instant)       | Supported     |
| Linux / macOS without `watchdog`     | mtime polling (5 s interval) | Supported     |
| Windows + `watchdog` installed       | Event-driven (instant)       | Not available |
| Windows without `watchdog`           | mtime polling (5 s interval) | Not available |

***

## YAML Reference

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
gateway:
  drain_timeout: 30           # shutdown drain window (seconds)
  reload_drain_timeout: 10    # reload drain window; falls back to drain_timeout if unset
```

***

## Rotating the shared secret during reload

The reload path also picks up changes to `gateway.auth_token`. When the reloaded config carries a new secret, the gateway adopts it inline and — by default — revokes every live session still on the old secret.

Adopting a new secret has three side effects:

1. `self.config.auth_token` is updated to the new value.
2. `GATEWAY_AUTH_TOKEN` in the environment is updated so all auth paths (HTTP, magic-link, WS) read the same value.
3. Stale live sessions are force-closed with WebSocket close code `4001` and reason `credentials_rotated` — unless `revoke_on_secret_rotation: false`.

| Situation in reloaded `gateway.yaml`                             | Outcome                                                                               |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `auth_token` absent                                              | Full no-op. Running secret + live sessions untouched.                                 |
| `auth_token` present, resolves to empty (`""` or unset `${VAR}`) | Running secret kept. A warning is logged.                                             |
| `auth_token` present, same as running secret                     | No-op (0 revoked).                                                                    |
| `auth_token` present, differs                                    | Adopt new secret, export to env, revoke stale sessions (unless opted out).            |
| `auth_token` not a string (e.g. `12345`)                         | Coerced to `str`, avoiding a mid-reload `TypeError`.                                  |
| `revoke_on_secret_rotation: false`                               | Secret adopted for new connections only; live sessions stay on the old secret.        |
| `revoke_on_secret_rotation: "false"` / `"0"` / `"no"` / `"off"`  | Also disables revocation.                                                             |
| `revoke_on_secret_rotation` truthy (default)                     | Every session on the old secret is force-closed with `(4001, "credentials_rotated")`. |

<Card title="Gateway Credential Rotation" icon="key" href="/docs/features/gateway-credential-rotation">
  Client-side recovery, the rotation mermaid, and the full behaviour matrix
</Card>

***

## Optional Dependency

<Info>
  `pip install "praisonai[gateway]"` adds `watchdog>=3.0.0` for event-driven watching. Without it the gateway uses mtime polling (5 s interval). Both modes apply the same reload logic — the only difference is detection latency.
</Info>

On Windows, use the file-watch path only. `kill -HUP` is not available; trigger reloads by saving the config file.

***

## Reading the Log Line

A concise summary is logged after each reload:

```
reload applied: agents; restart[telegram]
```

| Part                        | Meaning                                             |
| --------------------------- | --------------------------------------------------- |
| `agents`                    | Agent definitions were updated (no channel restart) |
| `restart[telegram]`         | The `telegram` channel was restarted with drain     |
| `restart[telegram,discord]` | Multiple channels were restarted                    |
| *(empty)*                   | Config was identical; no changes applied            |

***

## Best Practices

<AccordionGroup>
  <Accordion title="Install watchdog for instant detection">
    Without `watchdog`, the gateway polls every 5 seconds. With `watchdog` installed (`pip install "praisonai[gateway]"`), changes are detected within milliseconds via filesystem events.
  </Accordion>

  <Accordion title="Test reloads on a canary channel first">
    Apply config changes to one channel (e.g. a staging Telegram bot) before rolling to all channels. The per-channel restart scope makes this safe — the reload touches only what changed.
  </Accordion>

  <Accordion title="Set reload_drain_timeout shorter than drain_timeout">
    A short reload drain (5s–10s) keeps channel restarts fast. A longer shutdown drain (15s–30s) gives in-flight conversations more time to complete. Keep them separate.
  </Accordion>

  <Accordion title="Validate before saving">
    A bad config (YAML syntax error, missing required field) is rejected at reload time — the gateway keeps the last-known-good config and logs an error. Test with `praisonai gateway validate gateway.yaml` before saving to the watched path.
  </Accordion>

  <Accordion title="Grep the audit trail after every reload">
    Log lines like `reload applied: agents; restart[telegram]` are your audit trail. Ship them to your log aggregator and alert on unexpected full restarts.
  </Accordion>

  <Accordion title="Use SIGHUP in CI/CD pipelines">
    After updating `gateway.yaml` in a deploy pipeline, send `kill -HUP $(cat /var/run/praisonai.pid)` to trigger reload without downtime. No restart, no new process.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Gateway" icon="tower-broadcast" href="/docs/features/gateway">
    WebSocket control plane overview and full YAML reference
  </Card>

  <Card title="Gateway CLI" icon="terminal" href="/docs/features/gateway-cli">
    CLI commands for starting, stopping, and managing the gateway
  </Card>

  <Card title="Gateway Reliability" icon="shield-check" href="/docs/features/gateway-reliability">
    Graceful drain and admission control presets
  </Card>

  <Card title="Gateway Graceful Drain" icon="hourglass" href="/docs/features/gateway-graceful-drain">
    Drain-only knob — full reference and sequence diagram
  </Card>

  <Card title="Credential Rotation" icon="key" href="/docs/features/gateway-credential-rotation">
    Revoke live sessions when the shared secret rotates
  </Card>
</CardGroup>
