mirror of
https://github.com/tiennm99/composes.git
synced 2026-10-11 03:13:16 +00:00
feat(goclaw): add multi-tenant AI agent gateway with pgvector postgres
This commit is contained in:
1 parent
c2b508c171
commit
9da8546d19
4 files changed
+194
No files matched your search
@@ -62,6 +62,7 @@ Each links to its own README for variables, ports, and storage.
|
||||
| [couchbase](couchbase/README.md) | Couchbase Server |
|
||||
| [diun](diun/README.md) | Image-update notifier, reading the Docker API through a read-only proxy |
|
||||
| [gitea-mirror](gitea-mirror/README.md) | Gitea + PostgreSQL + gitea-mirror, mirroring GitHub repos |
|
||||
| [goclaw](goclaw/README.md) | Multi-tenant AI agent gateway, with pgvector PostgreSQL |
|
||||
| [opencode-web](opencode-web/README.md) | opencode coding agent, served as a browser UI |
|
||||
| [paseo](paseo/README.md) | Paseo coding-agent daemon and web UI |
|
||||
| [traffmonetizer](traffmonetizer/README.md) | TraffMonetizer bandwidth-sharing client |
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copy to .env and fill in. Never commit .env.
|
||||
#
|
||||
# cp .env.example .env
|
||||
|
||||
# Bearer token for the gateway API and dashboard. MUST NOT be blank -- a blank
|
||||
# token serves the agent gateway, and the tools its agents can run, to anyone.
|
||||
# Generate one with: openssl rand -hex 16
|
||||
GOCLAW_GATEWAY_TOKEN=
|
||||
|
||||
# AES-256-GCM key encrypting the LLM provider keys stored in PostgreSQL.
|
||||
# Changing it makes every stored key unreadable.
|
||||
# Generate one with: openssl rand -hex 32
|
||||
GOCLAW_ENCRYPTION_KEY=
|
||||
|
||||
# PostgreSQL credentials. Used by the database and by the DSN GoClaw connects
|
||||
# with, so both sides move together.
|
||||
POSTGRES_USER=goclaw
|
||||
POSTGRES_PASSWORD=goclaw
|
||||
POSTGRES_DB=goclaw
|
||||
|
||||
# Set to 1 to log full LLM request and response bodies.
|
||||
GOCLAW_TRACE_VERBOSE=0
|
||||
@@ -0,0 +1,109 @@
|
||||
# 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 and set them, with the rest of `.env.example`, in
|
||||
Coolify or Dokploy:
|
||||
|
||||
```sh
|
||||
openssl rand -hex 16 # GOCLAW_GATEWAY_TOKEN
|
||||
openssl rand -hex 32 # GOCLAW_ENCRYPTION_KEY
|
||||
```
|
||||
|
||||
2. Point the domain at 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 1:1 with what `prepare-env.sh` generates upstream; that script is not
|
||||
used here because the platform owns the environment.
|
||||
|
||||
## 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`, published nowhere — the platform maps the domain to it. See
|
||||
the [root README](../README.md) for why.
|
||||
|
||||
`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 `<major>.<minor>-full` — there is no `<major>-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 even though nothing else here sets them.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Required env vars (set in Coolify/Dokploy, or a sibling .env):
|
||||
# GOCLAW_GATEWAY_TOKEN, GOCLAW_ENCRYPTION_KEY
|
||||
|
||||
services:
|
||||
goclaw:
|
||||
image: ghcr.io/nextlevelbuilder/goclaw:full
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- GOCLAW_GATEWAY_TOKEN=${GOCLAW_GATEWAY_TOKEN:?required}
|
||||
- GOCLAW_ENCRYPTION_KEY=${GOCLAW_ENCRYPTION_KEY:?required}
|
||||
- GOCLAW_POSTGRES_DSN=postgres://${POSTGRES_USER:-goclaw}:${POSTGRES_PASSWORD:-goclaw}@postgres:5432/${POSTGRES_DB:-goclaw}?sslmode=disable
|
||||
- GOCLAW_HOST=0.0.0.0
|
||||
- GOCLAW_PORT=18790
|
||||
- GOCLAW_CONFIG=/app/data/config.json
|
||||
- GOCLAW_SKILLS_DIR=/app/data/skills
|
||||
- GOCLAW_TRACE_VERBOSE=${GOCLAW_TRACE_VERBOSE:-0}
|
||||
volumes:
|
||||
- goclaw-data:/app/data
|
||||
- goclaw-workspace:/app/workspace
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
extra_hosts:
|
||||
- host.docker.internal:host-gateway
|
||||
init: true
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SETUID
|
||||
- SETGID
|
||||
- CHOWN
|
||||
tmpfs:
|
||||
- /tmp:rw,noexec,nosuid,size=256m
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
cpus: '2.0'
|
||||
pids: 200
|
||||
|
||||
# PostgreSQL 18 with pgvector, for agent memory and the knowledge vault.
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg18
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-goclaw}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-goclaw}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-goclaw}
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-goclaw} -d ${POSTGRES_DB:-goclaw}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
volumes:
|
||||
goclaw-data:
|
||||
goclaw-workspace:
|
||||
postgres-data:
|
||||
Reference in new issue
Block a user