Files
bot/compose.local.yml
zooqueenandHanzo Dev 4aaa6bc1e6 compose: a client secret written into a public file is a published credential
The hanzo-bot registration's confidential half sat inline in compose.local.yml,
which is a file in a public repository — so the value was readable by anyone who
opened it, and had been since it was committed. Whoever holds it can redeem an
auth code as this app against production hanzo.id.

It now arrives from the environment like every other secret this file needs, with
the same KMS path the HANZO_API_KEY comment twenty lines below already names.
Removing it here stops the file publishing it; the value itself is in the history
of a public repo and must be treated as compromised and rotated.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 18:48:13 -07:00

155 lines
5.4 KiB
YAML

# Local development stack: Bot Gateway + Playground Control Plane + Postgres
# Usage:
# docker compose -f docker-compose.local.yml up
# UI: http://localhost:8080 (Playground — /nodes page)
# Bot: http://localhost:18789 (Bot Gateway — /healthz)
# Bot Control UI: http://localhost:18789/__bot__/
services:
# ── PostgreSQL (required by Playground) ────────────────────────────
postgres:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_USER: playground
POSTGRES_PASSWORD: playground
POSTGRES_DB: playground
ports:
- "5433:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U playground"]
interval: 5s
timeout: 3s
retries: 10
# ── Playground Control Plane (app.hanzo.bot equivalent) ────────────
playground:
image: ghcr.io/hanzoai/playground:0.1.42
restart: unless-stopped
networks:
- default
- hanzo-network
environment:
# Core
PLAYGROUND_HOME: /data
PLAYGROUND_HTTP_ADDR: 0.0.0.0:8080
PLAYGROUND_MODE: development
GIN_MODE: debug
# Storage
PLAYGROUND_STORAGE_MODE: postgres
PLAYGROUND_POSTGRES_URL: postgres://playground:playground@postgres:5432/playground?sslmode=disable
PLAYGROUND_STORAGE_POSTGRES_URL: postgres://playground:playground@postgres:5432/playground?sslmode=disable
PLAYGROUND_DATABASE_URL: postgres://playground:playground@postgres:5432/playground?sslmode=disable
# UI
AGENTS_UI_ENABLED: "true"
AGENTS_UI_MODE: embedded
# Gateway integration — points to bot-gateway in this compose
HANZO_AGENTS_CLOUD_GATEWAY_URL: ws://bot-gateway:18789
HANZO_AGENTS_CLOUD_GATEWAY_TOKEN: local-dev-token
# Cloud provisioning disabled (no K8s locally)
HANZO_AGENTS_CLOUD_ENABLED: "false"
HANZO_AGENTS_CLOUD_K8S_ENABLED: "false"
# DID/VC
HANZO_AGENTS_DID_ENABLED: "false"
# IAM — UI bundle has https://hanzo.id baked in, so token proxy must
# forward to production IAM (where the auth codes are issued)
HANZO_AGENTS_IAM_ENABLED: "true"
HANZO_AGENTS_IAM_ENDPOINT: https://hanzo.id
HANZO_AGENTS_IAM_PUBLIC_ENDPOINT: https://hanzo.id
HANZO_AGENTS_IAM_CLIENT_ID: hanzo-bot
HANZO_AGENTS_IAM_ORGANIZATION: hanzo
HANZO_AGENTS_IAM_APPLICATION: app-hanzobot
# Never inline a value here: this repo is public. This is the CONFIDENTIAL
# half of the hanzo-bot registration on production hanzo.id — whoever holds
# it can redeem an auth code as this app, so a literal here is a live
# credential published to anyone who reads the file. It can only arrive
# from the environment. Source of truth is KMS: project hanzo, env prod,
# path /bot, key IAM_CLIENT_SECRET.
IAM_CLIENT_SECRET: ${IAM_CLIENT_SECRET:-}
# CORS — allow local dev origins
AGENTS_API_CORS_ALLOWED_ORIGINS: "http://localhost:8080,http://localhost:3000,http://localhost:18789,http://localhost:8000"
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
volumes:
- playground-data:/data
# ── Bot Gateway (bot.hanzo.ai equivalent) ──────────────────────────
bot-gateway:
image: ghcr.io/hanzoai/bot:1.4.2
restart: unless-stopped
environment:
HOME: /home/node
NODE_ENV: development
TERM: xterm-256color
# Auth
BOT_GATEWAY_TOKEN: local-dev-token
# Config file with controlUi.allowedOrigins
BOT_CONFIG_PATH: /etc/bot/config.json
# Model providers — set at least one to enable chat
# Never inline a value here: this repo is public. HANZO_API_KEY is a
# SECRET completions credential — it authenticates as its owner and it
# spends, so it can only arrive from the environment. Source of truth is
# KMS: project hanzo, env prod, path /bot, key HANZO_API_KEY.
HANZO_API_KEY: ${HANZO_API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
# Playground integration
PLAYGROUND_URL: http://playground:8080
HANZO_NODE_ID: local-bot-gateway
HANZO_PUBLIC_URL: http://bot-gateway:18789
ports:
- "18789:18789"
- "18790:18790"
volumes:
- ./local-dev-config.json:/etc/bot/config.json:ro
init: true
command:
[
"node",
"hanzo-bot.mjs",
"gateway",
"--allow-unconfigured",
"--bind",
"lan",
"--port",
"18789",
]
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
# ── WSS Proxy (gw.hanzo.bot → bot-gateway:18789) ──────────────────
gw-proxy:
image: ghcr.io/hanzoai/ingress:1.7.41
restart: unless-stopped
ports:
- "443:443"
volumes:
- ./local-ssl/ingress.yml:/etc/ingress/config.yml:ro
- ./local-ssl/gw.crt:/etc/ingress/ssl/gw.crt:ro
- ./local-ssl/gw.key:/etc/ingress/ssl/gw.key:ro
depends_on:
- bot-gateway
networks:
hanzo-network:
external: true
volumes:
pgdata:
playground-data: