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

151 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 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:
```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`.