# goclaw [GoClaw](https://docs.goclaw.sh) is a multi-tenant AI agent gateway in Go: LLM providers, messaging channels (Telegram, Discord, Slack, Zalo, Feishu, WhatsApp), agent teams, and a knowledge vault, behind one binary with a built-in web dashboard. Two containers: `goclaw`, and `postgres` — pgvector, which holds tenants, sessions, encrypted provider keys and the semantic memory. ## Setup 1. Generate the two secrets: ```sh openssl rand -hex 16 # GOCLAW_GATEWAY_TOKEN openssl rand -hex 32 # GOCLAW_ENCRYPTION_KEY ``` 2. Map the domain to port `18790` and deploy. Migrations run on start. 3. Open the domain and use the setup wizard to add an LLM provider key. Health check: `GET /health`. ## Authentication `GOCLAW_GATEWAY_TOKEN` is the bearer token for the API and the dashboard, and the only thing between the domain and a gateway whose agents run tools. It fails fast if unset rather than defaulting to blank: upstream's own compose allows an empty token for loopback-only development, which behind a public domain would publish the gateway. `GOCLAW_ENCRYPTION_KEY` encrypts the provider API keys at rest in PostgreSQL (AES-256-GCM). It is required for the same reason, and it must not change once keys are stored — every one of them becomes unreadable. Both are the two values upstream's `prepare-env.sh` generates; the script itself is not used here. ## Environment | Variable | Default | Purpose | | --- | --- | --- | | `GOCLAW_GATEWAY_TOKEN` | — | Bearer token for the API and dashboard | | `GOCLAW_ENCRYPTION_KEY` | — | AES-256-GCM key for stored provider keys | | `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | `goclaw` | Database credentials, read by both containers | | `GOCLAW_TRACE_VERBOSE` | `0` | Log full LLM request and response bodies | `GOCLAW_HOST`, `GOCLAW_PORT`, `GOCLAW_CONFIG` and `GOCLAW_SKILLS_DIR` are set in `compose.yml` rather than here — they are properties of this layout, not of whoever deploys it. The two paths override image defaults that point outside any volume (`/app/config.json`, `/app/skills`), which would drop the configuration and every installed skill on redeploy. Provider API keys are not variables: they are entered in the dashboard and stored encrypted in PostgreSQL. ## Storage | Volume | Mount | Holds | | --- | --- | --- | | `goclaw-data` | `/app/data` | `config.json`, skills, Claude CLI credentials, runtime packages | | `goclaw-workspace` | `/app/workspace` | Files the agents work on | | `postgres-data` | `/var/lib/postgresql` | The database | Upstream's PostgreSQL overlay adds a third `goclaw-skills` volume at `/app/skills`; it is not here, because `GOCLAW_SKILLS_DIR` points at `/app/data/skills` and the volume would mount empty over a path nothing reads. Skills persist in `goclaw-data` instead. `postgres-data` mounts `/var/lib/postgresql`, not `/var/lib/postgresql/data`. PostgreSQL 18 images moved `PGDATA` down a level into a version directory, so the parent is now the mount point. The image's own user owns `/app` — `goclaw`, uid 1000, created with `/app` as its home. `/app/.claude` is a symlink into `/app/data`, which is how CLI credentials survive a recreate. ## Networking Listens on `18790`. `extra_hosts` maps `host.docker.internal` to the host gateway, so an agent can reach a service running on the host itself. ## Image `ghcr.io/nextlevelbuilder/goclaw:full`, the variant with every runtime and skill dependency pre-installed — Python, Node, pandoc, poppler, the GitHub CLI. The plain tag ships Python only and installs the rest on demand into `/app/data/.runtime`, which means the first use of a skill waits on a package install and a wiped data volume repeats it. Paying for the size once is the better trade here. The tag is moving, tracking the newest full build. The variant tags are only published as `full` and `.-full` — there is no `-full` — so `full` is the moving tag to follow. ## Hardening `cap_drop: ALL` with `SETUID`/`SETGID`/`CHOWN` added back, `no-new-privileges`, a `noexec` tmpfs on `/tmp`, and memory, CPU and pid limits — all as upstream ships them. The three capabilities are what the entrypoint needs to install persisted packages as root and then drop to the `goclaw` user via `su-exec`. This is a service whose whole purpose is running model-chosen tools, so the limits are worth keeping.