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

# Gateway boot sequence

> What happens inside the gateway container from entrypoint to OpenClaw start.

`gateway/entrypoint.sh` is the container's entrypoint. It runs on every container start and is fully idempotent — steps that already completed on a prior boot are skipped.

On shutdown (SIGTERM/SIGINT), the entrypoint runs `gateway_backup.py backup` to save gateway state to Supabase Storage before forwarding the signal to child processes. This ensures auth tokens, agent configs, and secrets survive container recreation.

***

## Boot sequence

### Step 0 — Resolve Supabase credentials

Three credential paths are tried in order:

**Path A: environment variables** — `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` already set in env. Used immediately, no waiting.

**Path B: gateway token exchange** — For remote gateways installed via the gateway installer (`install.yourhq.ai/gateway`). If `GATEWAY_TOKEN` is set and `$OPENCLAW_HOME/.token-consumed` does not exist, the entrypoint calls `consume_gateway_token()` to exchange the one-time token for a gateway slug and UUID.

* Requires: `GATEWAY_TOKEN`, `SUPABASE_URL`, `SUPABASE_ANON_KEY`
* On success: writes the assigned slug to `.gateway-slug` and marks the token consumed with `.token-consumed`
* One-shot — the marker file prevents re-exchange on subsequent boots
* If it fails, the container exits (restart with a fresh token)

**Path C: workspace registry** — For co-located installs where the UI writes creds to the shared `ui-config` volume. The entrypoint polls `/config/workspaces.json` + `/config/secrets.json` every 5 seconds until creds appear. This lets the gateway start before the user has completed browser onboarding — it will wait indefinitely, logging a status line every 30 seconds.

On every subsequent boot, the pinned slug from `.gateway-slug` (if present) is restored into `GATEWAY_ID` so the gateway always registers as the same row in Supabase.

***

### Step 0b — Restore from backup (conditional)

If the `$OPENCLAW_HOME/agents` directory does not exist (indicating a fresh boot or recreated container) and Supabase credentials are available:

1. Locates `gateway_backup.py` (from `/opt/yourhq/daemons/` or the entrypoint's sibling `daemons/` directory)
2. Runs `gateway_backup.py restore` to check Supabase Storage for previous backups
3. Downloads and extracts the newest available backup (falls back to older backups if the newest is corrupted)
4. Restores auth tokens, agent configs, secrets, and Telegram pairing data
5. If a VNC password is found in the restored backup (`$OPENCLAW_HOME/.vnc-password`), exports it as `VNC_PASSWORD` so the VNC setup step reuses it instead of generating a random one

If the `agents` directory already exists (returning container with existing state), this step is skipped entirely.

Retention: 3 backups per gateway, 7-day max age. The backup excludes `sessions` and `codex-home` directories.

***

### Step 1 — Seed git repo (first boot only)

If `$OPENCLAW_HOME/repo.git` does not exist:

1. Creates a bare git repo with `default` as the HEAD branch
2. Seeds it with agent template branches:
   * From `TEMPLATES_SOURCE` (if set, format `git+<url>`)
   * Otherwise from `/opt/templates` bundled in the image
3. Each template directory becomes a git branch: `default` or `template/<name>`

This step is skipped on every subsequent boot.

***

### Step 2 — Attach git remote (conditional)

If `GIT_REMOTE_URL` is set (or synthesized from `GITHUB_TOKEN` + `GITHUB_REPO_OWNER` + `GITHUB_REPO_NAME`):

1. Adds/updates the `origin` remote on the bare repo
2. Installs a `post-commit` hook that async-pushes every commit to origin
3. Fetches from origin to fast-forward any branches that moved while the gateway was down

This step runs on every boot so the remote URL stays current (tokens rotate).

***

### Step 3 — OpenClaw onboard (first boot only)

If `$OPENCLAW_HOME/openclaw.json` does not exist, runs:

```bash theme={null}
openclaw onboard --non-interactive --flow quickstart \
  --auth-choice skip --accept-risk --skip-health \
  --gateway-port 18789 --gateway-bind loopback
```

Creates `openclaw.json` with baseline config. If it exits non-zero, the entrypoint logs a warning and continues — the next boot will retry.

***

### Step 4 — Patch `openclaw.json`

Every boot (idempotent via `//=` defaults in `jq`):

* Sets `tools.profile = "full"`
* Sets Chrome executable and profile paths
* Enables Telegram channel with `pairing` DM policy
* Enables the `hq-bootstrap` plugin and adds its path to `plugins.load.paths`

***

### Step 5 — Install hq-bootstrap plugin

Copies `*.json` and `*.ts` files from `/opt/openclaw-plugins/hq-bootstrap` into `$OPENCLAW_HOME/plugins/hq-bootstrap/`. Runs every boot — safe to repeat.

***

### Step 6 — VNC password

If `$HOME/.vnc/passwd` does not exist, generates a VNC password:

* Uses `VNC_PASSWORD` env if set (this is automatically populated from a backup restore in Step 0b, so restored gateways keep their original VNC password)
* Otherwise generates a random 12-character password
* Saves the vncpasswd-encoded hash to `$HOME/.vnc/passwd`
* Saves plaintext to `$OPENCLAW_HOME/.vnc-password` (readable inside the `gateway-state` volume)

***

### Step 7 — Start Xtigervnc + XFCE

Starts the virtual display and desktop:

1. Removes stale X lock files (`/tmp/.X1-lock`, `/tmp/.X11-unix/X1`) from a crashed prior run
2. Starts Xtigervnc on `:1` — combined X server + VNC server (RFB on port 5901, localhost-only)
3. Starts `autocutsel` twice (CLIPBOARD → PRIMARY sync for noVNC clipboard passthrough)
4. Starts `dbus-daemon` session bus with a deterministic socket path
5. Starts `startxfce4`

The D-Bus socket is started explicitly (not via `dbus-launch`) because `dbus-launch` double-forks in a way that leaves XFCE components unable to find the bus address in container environments.

***

### Step 8 — Start websockify (noVNC)

If `NOVNC_BIND != "off"`, starts:

```bash theme={null}
websockify --web=/usr/share/novnc 0.0.0.0:6901 localhost:5901
```

Inside the container websockify always binds `0.0.0.0:6901`. Port 6901 is not mapped to the host — the UI proxies noVNC through its own `/api/novnc` route over Docker's internal network (`gateway:6901`).

Set `NOVNC_BIND=off` to skip noVNC entirely (e.g. on a headless host where you never need remote desktop).

***

### Step 9 — Register in Supabase

If Supabase creds are available, upserts a row in the `gateways` table with:

* `slug` = `GATEWAY_ID`
* `label` = `GATEWAY_LABEL`
* `status` = `ready`
* `last_seen_at` = now
* `meta.reachable_urls` = URLs built from `HOST_REACHABLE_URL` + port config
* `meta.networking_mode` = `NETWORKING_MODE`
* `tenant_id` = `TENANT_ID`

Conflict key is `(tenant_id, slug)`, so re-runs update the existing row.

Also resolves `GATEWAY_DB_ID` (the row's UUID) and exports it so the hq-bootstrap plugin can tag `agent_usage` rows with the gateway that ran them.

***

### Step 10 — Start files-API (conditional)

If `GATEWAY_AUTH_TOKEN` is available (from env or `/config/gateway-auth-token`) AND `FILES_API_BIND != "off"`:

```bash theme={null}
FILES_API_BIND=docker FILES_API_PORT=18790 python3 /usr/local/bin/files_api.py
```

Serves the agent worktrees to the HQ UI file browser over HTTP with bearer-token auth.

If no token is available, the step is skipped silently — the token file is created by the UI the first time someone opens an agent's Files tab. The next gateway restart picks it up.

***

### Step 11 — Clear Chrome Singleton locks

Sweeps `$HOME/.openclaw/browser/` for `Singleton*` files left behind by a crashed browser session. Without this, Chrome aborts launch on next boot reporting it's already running.

***

### Step 12 — Source gateway secrets

```bash theme={null}
SECRETS_ENV="$OPENCLAW_HOME/secrets/gateway.env"
[ -f "$SECRETS_ENV" ] && set -a && . "$SECRETS_ENV" && set +a
```

If the `secrets_sync` daemon (running in the runner container) has written gateway-level secrets to disk, the entrypoint sources them into the environment before starting OpenClaw. This makes gateway-scoped credentials (e.g. shared API keys) available to the main process and all agents.

The secrets `.env` files are generated by `secrets_sync.py`, which runs inside the runner container on the shared `gateway-state` volume. The file at `~/.openclaw/secrets/gateway.env` contains gateway-scoped keys; per-agent files at `~/.openclaw/secrets/agents/<slug>.env` contain merged (gateway + agent-specific) keys. All files are `chmod 0600`.

***

### Step 13 — Exec OpenClaw gateway

```bash theme={null}
exec openclaw gateway run
```

Replaces the entrypoint shell process with the OpenClaw v2026.6.6 gateway as the container's foreground process. All X11/XDG/DBUS env vars are exported first so OpenClaw's browser launcher can find the Xtigervnc display.

***

## State directory layout

`$OPENCLAW_HOME` defaults to `~/.openclaw` and is persisted in the `gateway-state` Docker volume.

| Path                             | Created                      | Purpose                                                                                 |
| -------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------- |
| `openclaw.json`                  | First boot (onboard)         | OpenClaw runtime config                                                                 |
| `repo.git`                       | First boot                   | Bare git repo for agent branches                                                        |
| `plugins/hq-bootstrap/`          | Every boot                   | HQ plugin files                                                                         |
| `secrets/gateway.env`            | By secrets\_sync daemon      | Decrypted gateway-scoped secrets (chmod 0600)                                           |
| `secrets/agents/<slug>.env`      | By secrets\_sync daemon      | Decrypted per-agent secrets — gateway defaults merged with agent overrides (chmod 0600) |
| `shared-auth/auth-profiles.json` | On first auth success        | Shared auth profiles synced across agents                                               |
| `agents/`                        | On agent provision           | Per-agent state directories (presence triggers skip of backup restore)                  |
| `.vnc-password`                  | First boot or backup restore | Plaintext VNC password                                                                  |
| `.token-consumed`                | First boot (token exchange)  | Prevents re-exchange of `GATEWAY_TOKEN`                                                 |
| `.gateway-slug`                  | First boot (token exchange)  | Pinned gateway slug restored on subsequent boots                                        |
| `Desktop/`                       | First boot                   | XFCE desktop shortcuts (agent launch icons)                                             |

`$HOME/.vnc/passwd` is the vncpasswd-encoded password hash read by Xtigervnc.
