Files
AgapHost/adolf/README.md
alvis a5c625b9b6 adolf: bearer-authenticate the agap MCP server, fix audio config schema
openclaw.json now sends `Authorization: Bearer ${AGAP_MCP_TOKEN}` to the agap
MCP server, which requires it as of kb#180. The token is injected from
openai/.env via docker-compose.yml and only substituted here, never inlined.
It maps to agent id `adolf`, which is also what the kb#147 vault gate reads.

Fixes tools.media.audio, which had been added but never restart-validated:
the per-entry `apiKey: "not-needed"` is rejected by the schema
("tools.media.audio.models.0: Invalid input"), and an invalid config makes the
gateway refuse to start outright -- adolf crash-looped on the first restart
after the block landed. The old comment claimed the schema requires a
non-empty apiKey; it is the opposite, apiKey is not a valid per-entry key at
all. Isolated with `openclaw config validate` against the running image
(2026.6.11): {provider, model} and {provider, model, baseUrl} validate, and
adding apiKey alone reproduces the failure. baseUrl is kept -- that is the
per-entry override pointing the openai-shaped provider at the local
faster-whisper server. Provider auth follows the normal model auth order per
docs/nodes/audio.md, and faster-whisper-server has no auth to satisfy anyway.

Two lessons encoded in the comments: `enabled: false` does NOT exempt an entry
from schema validation, and a config edit is not done until a restart boots
healthy -- this sat invalid but latent because the running gateway still held
an older loaded config. The block stays enabled: false; turning STT on is
still a kb#175/#191 decision (GTX 1070 co-residency).

Also adds the proactive-prioritization and todoist-capture design notes and
the vw-mcp prototype.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 04:41:13 +00:00

6.5 KiB
Raw Permalink Blame History

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 (kanboard Adolf tasks H1H5). 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:

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:
    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:
    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:

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:
    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.