# GoClaw Gateway PostgreSQL multi-tenant AI agent gateway with WebSocket RPC + HTTP API. ## Tech Stack **Backend:** Go 1.26, Cobra CLI, gorilla/websocket, pgx/v5 (database/sql, no ORM), golang-migrate, go-rod/rod, telego (Telegram) **Web UI:** React 19, Vite 6, TypeScript, Tailwind CSS 4, Radix UI, Zustand, React Router 7. Located in `ui/web/`. **Use `pnpm` (not npm).** **Database:** PostgreSQL 18 with pgvector. Raw SQL with `$1, $2` positional params. Nullable columns: `*string`, `*time.Time`, etc. ## Project Structure ``` cmd/ CLI commands, gateway startup, onboard wizard, migrations internal/ ├── agent/ Agent loop (think→act→observe), router, resolver, input guard ├── bootstrap/ System prompt files (SOUL.md, IDENTITY.md) + seeding + per-user seed ├── bus/ Event bus system ├── cache/ Caching layer ├── channels/ Channel manager: Telegram, Feishu/Lark, Zalo, Discord, WhatsApp ├── config/ Config loading (JSON5) + env var overlay ├── crypto/ AES-256-GCM encryption for API keys ├── cron/ Cron scheduling (at/every/cron expr) ├── gateway/ WS + HTTP server, client, method router │ └── methods/ RPC handlers (chat, agents, sessions, config, skills, cron, pairing) ├── hooks/ Hook system for extensibility ├── http/ HTTP API (/v1/chat/completions, /v1/agents, /v1/skills, etc.) ├── i18n/ Message catalog: T(locale, key, args...) + per-locale catalogs (en/vi/zh) ├── knowledgegraph/ Knowledge graph storage and traversal ├── mcp/ Model Context Protocol bridge/server ├── media/ Media handling utilities ├── memory/ Memory system (pgvector) ├── oauth/ OAuth authentication ├── permissions/ RBAC (admin/operator/viewer) ├── providers/ LLM providers: Anthropic (native HTTP+SSE), OpenAI-compat (HTTP+SSE), DashScope (Alibaba Qwen), Claude CLI (stdio+MCP bridge), ACP (Anthropic Console Proxy), Codex (OpenAI) ├── sandbox/ Docker-based code sandbox ├── scheduler/ Lane-based concurrency (main/subagent/cron) ├── sessions/ Session management ├── skills/ SKILL.md loader + BM25 search ├── store/ Store interfaces + pg/ (PostgreSQL) implementations ├── tasks/ Task management ├── tools/ Tool registry, filesystem, exec, web, memory, subagent, MCP bridge ├── tracing/ LLM call tracing + optional OTel export (build-tag gated) ├── tts/ Text-to-Speech (OpenAI, ElevenLabs, Edge, MiniMax) ├── upgrade/ Database schema version tracking pkg/protocol/ Wire types (frames, methods, errors, events) pkg/browser/ Browser automation (Rod + CDP) migrations/ PostgreSQL migration files ui/web/ React SPA (pnpm, Vite, Tailwind, Radix UI) ``` ## Key Patterns - **Store layer:** Interface-based (`store.SessionStore`, `store.AgentStore`, etc.) with pg/ (PostgreSQL) implementations. Uses `database/sql` + `pgx/v5/stdlib`, raw SQL, `execMapUpdate()` helper in `pg/helpers.go` - **Agent types:** `open` (per-user context, 7 files) vs `predefined` (shared context + USER.md per-user) - **Context files:** `agent_context_files` (agent-level) + `user_context_files` (per-user), routed via `ContextFileInterceptor` - **Providers:** Anthropic (native HTTP+SSE), OpenAI-compat (HTTP+SSE), DashScope (Alibaba Qwen), Claude CLI (stdio+MCP bridge), ACP (Anthropic Console Proxy), Codex (OpenAI). All use `RetryDo()` for retries. Loads from `llm_providers` table with encrypted API keys - **Agent loop:** `RunRequest` → think→act→observe → `RunResult`. Events: `run.started`, `run.completed`, `chunk`, `tool.call`, `tool.result`. Auto-summarization at >75% context - **Context propagation:** `store.WithAgentType(ctx)`, `store.WithUserID(ctx)`, `store.WithAgentID(ctx)`, `store.WithLocale(ctx)` - **WebSocket protocol (v3):** Frame types `req`/`res`/`event`. First request must be `connect` - **Config:** JSON5 at `GOCLAW_CONFIG` env. Secrets in `.env.local` or env vars, never in config.json - **Security:** Rate limiting, input guard (detection-only), CORS, shell deny patterns, SSRF protection, path traversal prevention, AES-256-GCM encryption. All security logs: `slog.Warn("security.*")` - **Telegram formatting:** LLM output → `SanitizeAssistantContent()` → `markdownToTelegramHTML()` → `chunkHTML()` → `sendHTML()`. Tables rendered as ASCII in `
` tags
- **i18n:** Web UI uses `i18next` with namespace-split locale files in `ui/web/src/i18n/locales/{lang}/`. Backend uses `internal/i18n` message catalog with `i18n.T(locale, key, args...)`. Locale propagated via `store.WithLocale(ctx)` — WS `connect` param `locale`, HTTP `Accept-Language` header. Supported: en (default), vi, zh. New user-facing strings: add key to `internal/i18n/keys.go`, add translations to all 3 catalog files. New UI strings: add key to all 3 locale dirs. Bootstrap templates (SOUL.md, etc.) stay English-only (LLM consumption).

## Running

```bash
go build -o goclaw . && ./goclaw onboard && source .env.local && ./goclaw
./goclaw migrate up                 # DB migrations
go test -v ./tests/integration/     # Integration tests

cd ui/web && pnpm install && pnpm dev   # Web dashboard (dev)
```

## Post-Implementation Checklist

After implementing or modifying Go code, run these checks:

```bash
go fix ./...                        # Apply Go version upgrades (run before commit)
go build ./...                      # Compile check
go vet ./...                        # Static analysis
go test -race ./tests/integration/  # Integration tests with race detector
```

Go conventions to follow:
- Use `errors.Is(err, sentinel)` instead of `err == sentinel`
- Use `switch/case` instead of `if/else if` chains on the same variable
- Use `append(dst, src...)` instead of loop-based append
- Always handle errors; don't ignore return values
- **Migrations:** When adding a new SQL migration file in `migrations/`, bump `RequiredSchemaVersion` in `internal/upgrade/version.go` to match the new migration number
- **i18n strings:** When adding user-facing error messages, add key to `internal/i18n/keys.go` and translations to `catalog_en.go`, `catalog_vi.go`, `catalog_zh.go`. For UI strings, add to all locale JSON files in `ui/web/src/i18n/locales/{en,vi,zh}/`
- **SQL safety:** When implementing or modifying SQL store code (`store/pg/*.go`), always verify: (1) All user inputs use parameterized queries (`$1, $2, ...`), never string concatenation — prevents SQL injection. (2) Queries are optimized — no N+1 queries, no unnecessary full table scans. (3) WHERE clauses, JOINs, and ORDER BY columns use existing indices — check migration files for available indexes

## Mobile UI/UX Rules

When implementing or modifying web UI components, follow these rules to ensure mobile compatibility:

- **Viewport height:** Use `h-dvh` (dynamic viewport height), never `h-screen`. `h-screen` causes content to hide behind mobile browser chrome and virtual keyboards
- **Input font-size:** All ``, `