Move adolf config to agap_git root (kb#65)
Relocate the OpenClaw gateway config from openai/adolf/ to adolf/ at the repo root, since it's shared config rather than part of the openai/ compose project's own tree. Update the docker-compose.yml bind-mount path (./adolf/openclaw.json -> ../adolf/openclaw.json) and comments, plus README.md references, to match. Verified: adolf container recreated healthy with the new bind-mount source resolving to /home/alvis/agap_git/adolf/openclaw.json, and a fresh openclaw.json.last-good snapshot confirms the config was accepted.
This commit is contained in:
111
adolf/README.md
Normal file
111
adolf/README.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 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`
|
||||
|
||||
## 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.
|
||||
148
adolf/openclaw.json
Normal file
148
adolf/openclaw.json
Normal file
@@ -0,0 +1,148 @@
|
||||
{
|
||||
// Adolf P6 — OpenClaw gateway config for the "adolf" container.
|
||||
// Lives in the adolf-state VOLUME (mounted at /home/node/.openclaw), not
|
||||
// in the openai/ git repo. Secrets referenced below (${VAR}) are resolved
|
||||
// from this container's process env, itself sourced from openai/.env
|
||||
// (gitignored) via docker-compose.yml — never inlined here.
|
||||
|
||||
gateway: {
|
||||
mode: "local",
|
||||
auth: {
|
||||
// Compose already binds "lan" (0.0.0.0) and publishes 18789/18790 to
|
||||
// the host, so this is a non-loopback bind and auth is mandatory.
|
||||
// Shared-secret token auth also gives the openclaw-tools bridge
|
||||
// (which calls POST /tools/invoke with the same token) full
|
||||
// trusted-operator scope, which is what lets gateway.tools.allow
|
||||
// below actually unlock cron/nodes for it.
|
||||
mode: "token",
|
||||
token: "${OPENCLAW_GATEWAY_TOKEN}",
|
||||
},
|
||||
tools: {
|
||||
// cron and nodes are owner-only and hard-denied on the HTTP
|
||||
// /tools/invoke surface by default. The openclaw-tools MCP bridge
|
||||
// (P5) calls that surface for cron_create/cron_list/nodes_invoke, so
|
||||
// without this allow-list those tools 404 even with a valid token.
|
||||
allow: ["cron", "nodes"],
|
||||
},
|
||||
},
|
||||
|
||||
// Model provider: adolf-llm (P2/P4), the Kimi-CLI OpenAI-compatible
|
||||
// wrapper on :8010. Its HTTP server (openai/adolf-llm/server.js) performs
|
||||
// NO api-key/Authorization validation at all -- ADOLF_KEY's value is
|
||||
// functionally irrelevant to adolf-llm itself. It's still wired through
|
||||
// env (not hardcoded) because OpenClaw's custom-provider schema requires
|
||||
// a non-empty apiKey field and ${VAR} substitution fails closed on an
|
||||
// empty/missing var.
|
||||
models: {
|
||||
mode: "merge",
|
||||
providers: {
|
||||
"adolf-llm": {
|
||||
baseUrl: "http://adolf-llm:8010/v1",
|
||||
apiKey: "${ADOLF_KEY}",
|
||||
api: "openai-completions",
|
||||
// Margin above the server.js SSE heartbeat cadence (empty-content
|
||||
// keepalive delta every ~25s once idle) so the idle watchdog never
|
||||
// fires on long thinking/tool/MCP phases even if a heartbeat tick
|
||||
// is delayed (kb #71).
|
||||
timeoutSeconds: 300,
|
||||
models: [
|
||||
{ id: "adolf", name: "Adolf", input: ["text", "image"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
agents: {
|
||||
defaults: {
|
||||
model: "adolf-llm/adolf",
|
||||
},
|
||||
},
|
||||
|
||||
ui: {
|
||||
assistant: { name: "Adolf" },
|
||||
},
|
||||
|
||||
// Matrix channel (P6). Deliberately no accessToken/userId/password here:
|
||||
// MATRIX_HOMESERVER / MATRIX_USER_ID / MATRIX_PASSWORD / MATRIX_DEVICE_NAME
|
||||
// are config-key-backed env vars OpenClaw reads automatically when the
|
||||
// matching config key is unset, so real creds never touch this file or
|
||||
// git. Password auth (not the raw access token) mints Adolf its own fresh
|
||||
// Matrix device instead of reusing the existing matrixbot (Python/nio)
|
||||
// device -- see the P6 report for why that separation matters.
|
||||
//
|
||||
// Conservative defaults on purpose: dm "pairing" (owner must approve
|
||||
// unknown senders) and groupPolicy "disabled" (no room handling yet).
|
||||
// Revisit once the legacy matrixbot adapter is retired.
|
||||
channels: {
|
||||
matrix: {
|
||||
enabled: true,
|
||||
encryption: true,
|
||||
dm: { policy: "allowlist", allowFrom: ["@admin:mtx.alogins.net", "@elizaveta:mtx.alogins.net"] },
|
||||
groupPolicy: "disabled",
|
||||
autoJoin: "always",
|
||||
// mtx.alogins.net is deliberately mapped to the host-gateway (private)
|
||||
// IP via extra_hosts in docker-compose.yml to dodge a hairpin-NAT dead
|
||||
// end on the public route -- not an actual SSRF exposure, so opt out
|
||||
// of the private-network block for this trusted, self-owned homeserver.
|
||||
network: { dangerouslyAllowPrivateNetwork: true },
|
||||
},
|
||||
},
|
||||
|
||||
// MCP registry (P6) -- same servers as openai/shared-mcp.json,
|
||||
// expressed in OpenClaw's own mcp.servers schema. `type: "http"` is
|
||||
// OpenClaw's documented CLI-native alias for transport: "streamable-http".
|
||||
mcp: {
|
||||
servers: {
|
||||
cognee: {
|
||||
type: "http",
|
||||
url: "http://cognee-mcp:8000/mcp",
|
||||
},
|
||||
"openclaw-tools": {
|
||||
type: "http",
|
||||
url: "http://openclaw-tools:8020/mcp",
|
||||
},
|
||||
// kanboard-mcp-adolf (kb task #58) -- standalone kanboard-mcp image
|
||||
// (source /home/alvis/kanboard/mcp), second instance on :3104,
|
||||
// authenticated as the Kanboard "adolf" user via its own personal API
|
||||
// access token (KANBOARD_AUTH_USER=adolf in that instance's
|
||||
// .env.adolf) -- a genuinely distinct credential from the "claude"
|
||||
// instance on :3103 (app-wide jsonrpc token). Not part of the openai
|
||||
// compose network, so reached via host.docker.internal (already
|
||||
// extra_hosts-mapped for this container) rather than a service name.
|
||||
kanboard: {
|
||||
type: "http",
|
||||
url: "http://host.docker.internal:3104/mcp",
|
||||
},
|
||||
// marketplace-mcp (kb task #61) -- the SAME shared marketplace-mcp
|
||||
// instance Claude Code and OpenWebUI use (single service, port 3101,
|
||||
// network_mode: host on the Agap host), not a second copy. It can
|
||||
// place real orders on live marketplace accounts, so it's gated by a
|
||||
// shared bearer token (MARKETPLACE_MCP_TOKEN in Vaultwarden / this
|
||||
// container's env, injected via openai/.env -> docker-compose.yml).
|
||||
// Reached via host.docker.internal, same reasoning as kanboard above.
|
||||
marketplace: {
|
||||
type: "http",
|
||||
url: "http://host.docker.internal:3101/mcp",
|
||||
headers: {
|
||||
Authorization: "Bearer ${MARKETPLACE_MCP_TOKEN}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Cognee memory plugin (P8) — installed external plugin under
|
||||
// .openclaw/extensions/cognee-memory. Activation entry is required for the
|
||||
// gateway to load it at startup (discovery alone is not enough).
|
||||
plugins: {
|
||||
entries: {
|
||||
"cognee-memory": {
|
||||
enabled: true,
|
||||
// External (non-bundled) plugins must opt in to conversation + prompt-injection
|
||||
// hook access explicitly. before_prompt_build => allowPromptInjection;
|
||||
// agent_end => allowConversationAccess.
|
||||
hooks: { allowConversationAccess: true, allowPromptInjection: true },
|
||||
config: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -121,12 +121,17 @@ services:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
|
||||
# Adolf — OpenClaw fork (Matrix-first personal assistant). Runtime config
|
||||
# (Matrix channel, SOUL.md, model provider -> adolf-llm:8010, MCP registry,
|
||||
# gateway.tools.allow for cron/nodes) is wired in P6 and lives in the
|
||||
# adolf-state VOLUME (openclaw.json + SOUL.md), not in this repo. Matrix
|
||||
# creds and ADOLF_KEY come from openai/.env (gitignored, never committed).
|
||||
# Source: /home/alvis/adolf.
|
||||
# Adolf — OpenClaw fork (Matrix-first personal assistant). The OpenClaw
|
||||
# gateway config (Matrix channel + allow-list, model provider ->
|
||||
# adolf-llm:8010, MCP registry, gateway.tools.allow for cron/nodes) is
|
||||
# version-controlled at agap_git/adolf/openclaw.json (repo root, alongside
|
||||
# this openai/ project, not nested inside it) and bind-mounted read-only
|
||||
# over the adolf-state volume (see volumes below), so git is the single
|
||||
# source of truth — not a hand-edited volume file. The volume still
|
||||
# holds runtime state only (Matrix crypto/devices, credentials, sessions,
|
||||
# workspace/SOUL.md, logs). Matrix creds and ADOLF_KEY come from
|
||||
# openai/.env (gitignored, never committed). Source tree: /home/alvis/adolf.
|
||||
# To change config: edit ../adolf/openclaw.json + restart adolf.
|
||||
adolf:
|
||||
build:
|
||||
context: ../../adolf
|
||||
@@ -149,9 +154,22 @@ services:
|
||||
- MATRIX_USER_ID=${MATRIX_USER_ID:-}
|
||||
- MATRIX_PASSWORD=${MATRIX_PASSWORD:-}
|
||||
- MATRIX_DEVICE_NAME=${MATRIX_DEVICE_NAME:-Adolf OpenClaw Gateway}
|
||||
# marketplace-mcp bearer token (kb task #61) -- referenced by
|
||||
# openclaw.json's mcp.servers.marketplace.headers.Authorization via
|
||||
# ${MARKETPLACE_MCP_TOKEN} substitution; never inlined into that file.
|
||||
- MARKETPLACE_MCP_TOKEN=${MARKETPLACE_MCP_TOKEN:-}
|
||||
- TZ=Europe/Riga
|
||||
volumes:
|
||||
# Runtime state only (Matrix crypto/devices, credentials, sessions,
|
||||
# workspace, logs). The gateway config file itself is overlaid below.
|
||||
- adolf-state:/home/node/.openclaw
|
||||
# Version-controlled OpenClaw gateway config, mounted read-only on top
|
||||
# of the state volume so it is the single source of truth. The gateway
|
||||
# reads this JSONC file and snapshots its own .last-good/.rejected
|
||||
# copies into the volume dir (writable) — it never rewrites this file,
|
||||
# so read-only is safe. Edit the tracked file + restart to change config;
|
||||
# runtime/UI edits are intentionally disabled by the ro mount.
|
||||
- ../adolf/openclaw.json:/home/node/.openclaw/openclaw.json:ro
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
# mtx.alogins.net's public A record can't hairpin-NAT back through the
|
||||
|
||||
Reference in New Issue
Block a user