# Adolf — OpenClaw gateway deployment Adolf is the self-hosted OpenClaw fork that runs as the `adolf` container (Matrix-first personal assistant). This directory holds its **version-controlled gateway configuration**. - Source tree (the OpenClaw fork being built): `/home/alvis/adolf` - Compose service `adolf` lives in: `openai/docker-compose.yml` - This config directory lives at the repo root (`agap_git/adolf/`), not nested inside `openai/`, since it is shared config rather than part of the `openai` compose project's own tree. - Model backend: `adolf-llm` container (Kimi-CLI wrapper) on `:8010` ## Memory Adolf's long-term memory is being **migrated from Cognee to Hindsight** (a single self-hosted container, `:8888` REST + built-in MCP, `:9999` UI). It stays wired in the same two ways: as a **tool** (Hindsight's built-in MCP in `openclaw.json` `mcp.servers.hindsight`) and as **forced hooks** (the `hindsight-memory` OpenClaw plugin: `before_prompt_build`→recall inject, `agent_end`→retain). Authoritative plan and target architecture: **[`HINDSIGHT-MIGRATION.md`](./HINDSIGHT-MIGRATION.md)** (kanboard *Adolf* tasks H1–H5). Until those land, the running stack is still Cognee. ## Config source of truth The gateway config is **`openclaw.json` in this directory**. It is bind-mounted **read-only** over the `adolf-state` volume: ```yaml volumes: - adolf-state:/home/node/.openclaw # runtime state only - ../adolf/openclaw.json:/home/node/.openclaw/openclaw.json:ro # tracked config ``` Previously this file was a hand-edited copy inside the `adolf-state` Docker volume (edited via `docker cp` into the running container). It is now tracked in git and seeded into the container by the mount, so **git is the single source of truth**. - The file is **JSONC** (comments + unquoted keys allowed). - **No secrets live here.** Every credential is a `${VAR}` reference resolved from the container's environment, which is sourced from `openai/.env` (gitignored, never committed): `OPENCLAW_GATEWAY_TOKEN`, `ADOLF_KEY`, `MATRIX_*`, `MARKETPLACE_MCP_TOKEN`. - The gateway reads this file and writes its own `openclaw.json.last-good` and `openclaw.json.rejected.*` snapshots into the volume dir (writable). It does **not** rewrite this file, so the read-only mount is safe. ### Changing the config 1. Edit `agap_git/adolf/openclaw.json`. 2. Restart the container: ```bash env -u HTTPS_PROXY -u HTTP_PROXY -u ALL_PROXY -u https_proxy -u http_proxy -u all_proxy \ docker compose -f /home/alvis/agap_git/openai/docker-compose.yml up -d adolf ``` 3. Verify it came up healthy and the config was accepted: ```bash env -u HTTPS_PROXY -u HTTP_PROXY -u ALL_PROXY -u https_proxy -u http_proxy -u all_proxy \ docker ps --filter name=adolf --format '{{.Names}} {{.Status}}' env -u HTTPS_PROXY -u HTTP_PROXY -u ALL_PROXY -u https_proxy -u http_proxy -u all_proxy \ docker logs --tail 40 adolf ``` A fresh `openclaw.json.rejected.*` file in `/home/node/.openclaw` means the edit failed validation and the previous `.last-good` is still in use. Because the mount is read-only, editing config through the gateway UI/API is intentionally disabled — all changes go through git. ## Granting a Matrix user access to Adolf Adolf only responds to Matrix users on an **allow-list**. This is the `channels.matrix.dm` block in `openclaw.json`: ```jsonc channels: { matrix: { enabled: true, encryption: true, dm: { policy: "allowlist", allowFrom: [ "@admin:mtx.alogins.net", "@elizaveta:mtx.alogins.net", ], }, groupPolicy: "disabled", // no group-room handling yet autoJoin: "always", }, } ``` - **`dm.policy: "allowlist"`** — only users whose full Matrix ID appears in `allowFrom` can DM the bot. Everyone else is ignored. (`policy: "pairing"` is the alternative: the owner must approve each unknown sender interactively. `allowlist` is stricter and declarative.) - **`dm.allowFrom`** — the list of authorized Matrix user IDs. - **`groupPolicy: "disabled"`** — Adolf does not act in group rooms; DM only. ### To grant a new user access 1. Get the user's **full Matrix ID**, e.g. `@ivan:mtx.alogins.net`. 2. Add it to `allowFrom` in `agap_git/adolf/openclaw.json`: ```jsonc allowFrom: [ "@admin:mtx.alogins.net", "@elizaveta:mtx.alogins.net", "@ivan:mtx.alogins.net", ], ``` 3. Restart adolf (see "Changing the config" above). 4. The user can now start a DM with Adolf's Matrix account (`MATRIX_USER_ID` in `openai/.env`). `autoJoin: "always"` means Adolf auto-accepts the DM invite; conversation is end-to-end encrypted (`encryption: true`). To **revoke** access, remove the ID from `allowFrom` and restart. > Matrix accounts themselves are created on the Synapse homeserver > (`mtx.alogins.net`) — see the AgapHost wiki **Matrix** page. The allow-list > here only controls which existing Matrix users Adolf will talk to. ## Matrix device identity (kb#67) Adolf's Matrix login used **password auth with no pinned `device_id`** (`MATRIX_PASSWORD` in `openai/.env`). Every time OpenClaw's own credential cache (in the `adolf-state` volume) was missing — first boot, a lost/rebuilt volume — a fresh password login minted a **brand-new Matrix device** with no cross-signing, leaving dead ghost devices behind and risking new encrypted DMs getting keys shared to a device that no longer exists. Fix: `openai/.env` now also pins `MATRIX_ACCESS_TOKEN` + `MATRIX_DEVICE_ID` to Adolf's current live device (`TIANDTKUZJ`, cross-signed; token in Vaultwarden as `MATRIX_ADOLF_GATEWAY_TOKEN`). OpenClaw's matrix extension prefers a configured access token over password login (`extensions/matrix/src/matrix/client/config.ts` `resolveMatrixAuth`), so as long as that token stays valid, restarts — even after a volume loss — reuse the same device instead of minting a new one. `MATRIX_PASSWORD` stays set as a manual-recovery fallback only (unset `MATRIX_ACCESS_TOKEN` to force a fresh password login if the token is ever revoked). Cross-signing for `@bot` is already bootstrapped automatically by OpenClaw's matrix extension (`extensions/matrix/src/matrix/sdk/crypto-bootstrap.ts`) — no separate setup needed. If the pinned token is ever revoked/rotated, get a fresh one bound to the *same* device by logging in with `device_id` explicitly set to `TIANDTKUZJ` (Synapse reuses an existing device when its id is given in `/login`, instead of creating a new one), then update `MATRIX_ACCESS_TOKEN` in `openai/.env` and Vaultwarden's `MATRIX_ADOLF_GATEWAY_TOKEN`.