Phase 0: Foundation & Infrastructure
Timeline: Week 1-2
Objective: Wire GoClaw to Supabase and establish the base configuration.
0.1 — GoClaw Managed Mode + Supabase
Tasks
- [x] Set GoClaw
database.mode: "managed"and pointGOCLAW_POSTGRES_DSNat the Supabase Postgres connection string - [x] Create new Supabase tables for GoClaw multi-tenant state (auto-created by managed mode migrations into the
botschema — 38 tables total) - [x] Enable the
pgvectorextension in Supabase - [x] Create a unified
docker-compose.dev.ymlat the repo root that brings up GoClaw + API + Supabase together - [x] Install GoClaw UI (
ai-marketing-goclaw-uiDocker image built fromgoclaw/ui/web)
Database Setup (Automated via Supabase Migration)
The setup that was previously a manual SQL editor step is now fully automated. The migration front-end/supabase/migrations/20260308000000_create_bot_schema.sql runs as part of supabase db reset (local) or supabase db push (cloud) and handles everything:
-- Enables extensions
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Dedicated schema for all GoClaw tables
CREATE SCHEMA IF NOT EXISTS bot;
-- Dedicated role — password stored in GOCLAW_POSTGRES_DSN
CREATE ROLE goclaw_bot WITH LOGIN PASSWORD 'goclaw_bot_dev'; -- change in production!
-- Pin search_path at the role level so golang-migrate always targets bot
ALTER ROLE goclaw_bot SET search_path TO bot, public;
-- Privileges
GRANT USAGE, CREATE ON SCHEMA bot TO goclaw_bot;
GRANT USAGE ON SCHEMA public TO goclaw_bot;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO goclaw_bot;
Why a dedicated role and schema?golang-migrate (used by GoClaw's auto-upgrade) respects the connection's search_path. By pinning search_path TO bot, public on the goclaw_bot role, all 38 GoClaw tables (agents, llm_providers, sessions, schema_migrations, etc.) are created in the bot schema automatically — no schema-qualification needed in queries and no risk of colliding with the app's public schema.
⚠️
GOCLAW_POSTGRES_DSNmust connect asgoclaw_bot— notpostgresor the Supabase service-role user — otherwise tables will be created inpublicinstead ofbot.
For full step-by-step instructions (including Supabase cloud production setup, password rotation, and troubleshooting), see docs/SUPABASE_BOT_SCHEMA_SETUP.md.
Resetting the Database
Use the script at scripts/reset-and-migrate.sh to reset Supabase and re-run GoClaw migrations in one step:
bash scripts/reset-and-migrate.sh
This script:
- Runs
supabase db reset --yesfromfront-end/(applies all Supabase migrations) - Runs
docker run ai-marketing-goclaw:latest migrate upto apply GoClaw's own migrations as thegoclaw_botuser - Prints the
botschema and all tables to confirm success
GoClaw Config (bot/data/config.json)
{
"database": {
"mode": "managed"
},
"channels": {
"telegram": {
"enabled": true,
"token": "${GOCLAW_TELEGRAM_TOKEN}",
"dm_policy": "pairing",
"group_policy": "disabled",
"require_mention": false,
"stream_mode": "partial",
"history_limit": 50,
"media_max_bytes": 20971520
}
},
"bindings": [
{
"agentId": "liaison-agent",
"match": { "channel": "telegram" }
}
]
}
GOCLAW_POSTGRES_DSN is never stored in config.json — it is injected via the environment only.
Connection String Format
The connection string must use the
goclaw_botrole (notpostgres).
Thegoclaw_botrole hassearch_pathpinned tobot, public, which causes GoClaw'sgolang-migrateto create all tables in thebotschema. Using any other role will silently create tables inpublic.
# Local dev (port 54322 = local Supabase direct connection)
# Password 'goclaw_bot_dev' is set by the Supabase migration — do not change for local.
GOCLAW_POSTGRES_DSN=postgresql://goclaw_bot:[email protected]:54322/postgres?sslmode=disable
# Production (Supabase cloud, Session mode port 5432)
# Replace [PASSWORD] with the value set via: ALTER ROLE goclaw_bot PASSWORD '...';
GOCLAW_POSTGRES_DSN=postgresql://goclaw_bot:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres?sslmode=disable
See docs/SUPABASE_BOT_SCHEMA_SETUP.md for the complete cloud production setup walkthrough including password rotation, verification, and troubleshooting.
Docker Compose
docker-compose.dev.yml at repo root brings up GoClaw + GoClaw UI + API together. Key GoClaw service settings:
goclaw:
environment:
- GOCLAW_MODE=managed
- GOCLAW_AUTO_UPGRADE=true # runs migrate up automatically on start
- GOCLAW_POSTGRES_DSN=${GOCLAW_POSTGRES_DSN}
volumes:
- ./bot/data:/app/data # config.json lives here
- ./bot/workspace:/app/workspace
GOCLAW_AUTO_UPGRADE=true means the container will run goclaw upgrade before starting the gateway, so migrations stay in sync whenever the image is rebuilt.
0.2 — Telegram Bot Setup
Tasks
- [x] Create a Telegram bot via BotFather → get
GOCLAW_TELEGRAM_TOKEN - [x] Configure GoClaw
channels.telegramin config.json - [x] Set up GoClaw
bindingsto route Telegram messages to theliaison-agent - [ ] Test basic Telegram ↔ GoClaw message round-trip
Create Telegram Bot
- Open Telegram and message @BotFather
- Send
/newbot - Follow prompts to choose name and username
- Copy the API token provided
- Store in
.env:GOCLAW_TELEGRAM_TOKEN=your_token_here
Configure GoClaw Telegram Channel
Update goclaw/data/config.json:
{
"channels": {
"telegram": {
"enabled": true,
"token": "${GOCLAW_TELEGRAM_TOKEN}",
"dm_policy": "pairing",
"group_policy": "disabled",
"require_mention": false,
"stream_mode": "partial",
"history_limit": 50,
"media_max_bytes": 20971520
}
}
}
Note: dm_policy: "pairing" means users must pair with a client ID before the bot responds in DMs.
Set Up Agent Bindings
Add to goclaw/data/config.json:
{
"bindings": [
{
"agentId": "liaison-agent",
"match": {
"channel": "telegram"
}
}
]
}
This routes all Telegram messages to the liaison-agent (which we'll create in Phase 1).
Test Telegram Connection
- Restart GoClaw with the new config
- Message your bot on Telegram:
/start - Check GoClaw logs for incoming message
- Bot should respond with "Agent not found" (expected — Phase 1 creates the agent)
0.3 — Environment & Config Unification
Tasks
- [x] Create a root-level
.env.exampledocumenting all env vars across services - [x] Add
GOCLAW_API_URLtoapi/src/config/env.tsfor API→GoClaw communication - [x] Add
API_BASE_URLto GoClaw env for GoClaw→API tool calls - [x] Set up shared Docker network between GoClaw and API containers
Root .env.example
Create /code/ai-marketing/.env.example:
# ===== Supabase =====
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-role-key
# ===== API Service =====
API_KEY=your-internal-api-key
WORKER_API_JWT_SECRET=your-worker-secret
PORT=8000
HOST=0.0.0.0
# LLM Provider (ollama or gemini)
LLM_PROVIDER=gemini
GOOGLE_API_KEY=your-google-api-key
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama2
# Unsplash
UNSPLASH_ACCESS_KEY=your-unsplash-key
# Ghost CMS
GHOST_API_URL=https://your-ghost-site.com
GHOST_ADMIN_API_KEY=your-ghost-key
# GoClaw API URL (for API → GoClaw calls)
GOCLAW_API_URL=http://goclaw:18790
# ===== GoClaw Service =====
GOCLAW_HOST=0.0.0.0
GOCLAW_PORT=18790
GOCLAW_POSTGRES_DSN=postgresql://user:[email protected]:5432/postgres
# GoClaw LLM Providers
GOCLAW_PROVIDER=anthropic
GOCLAW_MODEL=claude-sonnet-4-20250514
GOCLAW_ANTHROPIC_API_KEY=your-anthropic-key
GOCLAW_GEMINI_API_KEY=your-gemini-key
# GoClaw Channels
GOCLAW_TELEGRAM_TOKEN=your-telegram-bot-token
# API Base URL (for GoClaw → API tool calls)
API_BASE_URL=http://api:8000
Update API Config
Edit api/src/config/env.ts:
export const env = {
API_KEY: process.env.API_KEY,
LLM_PROVIDER: process.env.LLM_PROVIDER || 'ollama',
NODE_ENV: process.env.NODE_ENV || 'development',
PORT: parseInt(process.env.PORT || '8000', 10),
HOST: process.env.HOST || '0.0.0.0',
// Supabase configuration
SUPABASE_URL: process.env.SUPABASE_URL || '',
SUPABASE_KEY: process.env.SUPABASE_KEY || '',
// Google Gemini configuration
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || '',
// Ollama configuration
OLLAMA_BASE_URL: process.env.OLLAMA_BASE_URL || 'http://localhost:11434',
OLLAMA_MODEL: process.env.OLLAMA_MODEL || 'llama2',
// SerpAPI configuration
SERPAPI_API_KEY: process.env.SERPAPI_API_KEY || '',
// Unsplash configuration
UNSPLASH_ACCESS_KEY: process.env.UNSPLASH_ACCESS_KEY || '',
// Ghost CMS configuration
GHOST_API_URL: process.env.GHOST_API_URL || '',
GHOST_ADMIN_API_KEY: process.env.GHOST_ADMIN_API_KEY || '',
// GoClaw API (NEW)
GOCLAW_API_URL: process.env.GOCLAW_API_URL || 'http://localhost:18790',
// API Base URL for GoClaw tools (NEW)
API_BASE_URL: process.env.API_BASE_URL || 'http://localhost:8000'
};
Verify Docker Network
The docker-compose.dev.yml created in step 0.1 already defines ai-marketing-network. Verify services can reach each other:
docker-compose -f docker-compose.dev.yml up -d
docker-compose -f docker-compose.dev.yml exec api curl http://goclaw:18790/
# Should return GoClaw gateway response
New Tooling Required
docker-compose.dev.yml— Multi-service orchestration- Supabase migration for pgvector extension
- Root
.envfile with all service configurations
Verification Checklist
- [x] GoClaw starts in managed mode and connects to Supabase
- [x] pgvector extension is enabled in Supabase
- [x] Telegram bot receives and logs messages
- [x] Docker network allows API ↔ GoClaw communication
- [x] All environment variables are documented in
.env.example - [x]
.envfile exists locally (not committed) with actual values
Next Steps
Proceed to Phase 1: Agent Team Architecture to define the three-agent team and create their skill files.