* fix(agent): filter skill slash commands by the agent's grants The inline <available_skills> block was already filtered by visibility and agent grants, but slash activation read the loader's full skill list. A /<slug> command could therefore activate a skill the agent was never granted, and the not-found suggestions disclosed that such skills existed. Resolve slash commands against the agent's allow list via FilterSkills. The list is filtered before matching, so /list-skills, /help and the near-match suggestions are all covered by the same change. The allow-list convention is unchanged: nil means every skill (the fallback when the access store errors), an empty slice means none, and a populated slice is an explicit set of slugs. * fix(agent): split slash commands on any whitespace The tokenizer separated the skill name from the rest of the message with strings.Cut(after, " ") — a literal space. A user who typed the command and pressed Enter before the rest of the message sent "/ck:git\nreview the diff", which parsed to the target "ck:git\nreview" and matched no skill. The reply was "skill not found" followed by near-matches that included the skill they had just named, because the similarity fallback searched the mangled string and still landed beside it. Multi-line messages are ordinary in Slack and Telegram, so this was reachable in normal use. Introduce cutFirstField, which splits on the first run of whitespace, and use it everywhere the literal-space split appeared. The match loop had the same assumption in strings.HasPrefix(raw, value+" "); hasFieldPrefix replaces it and decodes the following rune properly, so a multi-byte skill name is not truncated. That also stops a plain string prefix from matching: a command for "frontend-design-extra" no longer resolves to "frontend-design". * fix(tools): inherit the parent agent's tool policy in spawned subagents buildSubagentToolsRegistry clones the parent registry, then overwrites exec, read_file, write_file and list_files with freshly constructed tools. A fresh tool carries none of the hardening the gateway applies to the parent's instances at startup: exec path denials and their exemptions, the shell deny-group toggles, the command keyword allowlist, and the read/write/list deny prefixes covering config.json, the internal databases and delegate/. Spawning a subagent therefore widened what an agent could reach — the parent was blocked from the data dir, the subagent was not. Verified by disabling the new call: the subagent's exec read config.json out of the denied data dir, read_file did the same, and write_file overwrote it. Copy the policy from the live parent instances rather than re-deriving it, so there is one source of truth and a deny path reloaded later through config pub/sub reaches subagents without a second wiring site that can drift. Allow-prefixes are inherited alongside the denials: they are what make the denied roots usable at all, since the skills store sits under the denied data dir. Copying denials without them would leave a subagent unable to read the skills it is told to use. * fix(agent): use one inline-vs-search decision for prompt and preview The system-prompt preview exists to show the prompt an agent actually gets, but it decided between inline skills and search mode on its own terms: it counted tokens with the fallback counter over the fully rendered XML — tags and <location> paths included, at roughly runes/2 — while the prompt builder estimated name+description at chars/4. On the same eighteen skills that read 3087 against 944, so the preview reported search mode for an agent that was running inline. Extract shouldInlineSkills and call it from both paths. The estimate keeps the prompt builder's rule, mirroring BuildSummary's 200-rune description truncation, so the decision still costs no rendering. The preview's loader interface gains FilterSkills to feed it. Its behaviour on an access-store error is unchanged: an empty allow list yields no skills rather than falling back to showing every skill. * fix(http): keep reference files when importing skills Export archives the whole skill directory, but import recognised only metadata.json, SKILL.md and grants.jsonl. The switch had no default, so every other file was discarded without a log line. A skill whose SKILL.md cites references/*.md arrived without them, the import reported success, and the skill failed at the first read. Collect the remaining entries and write them under the skill directory with their structure intact. Archive entry names are attacker-controlled, so each path goes through sanitizeRelPath: a traversal attempt collapses to a relative path and the write stays inside the skill directory. * test(agent): make the inline-decision table exercise both gates The "100 skills, 200-char descriptions" case claimed to cover the token ceiling, but 100 exceeds the count ceiling so the count check short-circuited and the token branch never ran — deleting that branch would not have failed the test. Split the table so each case isolates one gate: tiny descriptions for the count rows, a count safely under the ceiling for the token rows. Removing the token comparison now fails the over-the-ceiling case and nothing else. * fix(agent): gate slash commands on the managed tier only The allow list comes from a query over the `skills` table, so filesystem-tier skills — the workspace, .agents and ~/.agents directories of the five-tier loader — have no row in it. Filtering every skill against the list made those four tiers unreachable by slash while skill_search still found them unfiltered, which turned a security fix into a functional regression. Apply the list to managed skills only. Builtins are seeded with is_system and returned unconditionally, so they stay reachable either way. This closes slash activation of ungranted managed skills. It does not close skill_search and use_skill, which still read the loader's full list; that is a wider change and wants its own review. * fix(http): stop imported skill files from replacing guarded ones Two ways the auxiliary write could go wrong. GuardSkillContent scans SKILL.md before anything reaches disk, but the switch that routes archive entries compares the raw path. An entry named "./SKILL.md" does not match it, so it fell through to the auxiliary set — where sanitizeRelPath collapses it back to "SKILL.md" and the write replaced the file that had just been scanned. Reject any auxiliary path that resolves to a name the import handles by itself, and log the attempt. Tar directory entries carry a trailing separator and no content. Written as files they took the name a real directory needed, so everything beneath was dropped — and whether that happened depended on map iteration order, making it intermittent. Skip them; MkdirAll creates what is needed. Also cap the number of auxiliary files per skill and log when the cap trims an archive, so a truncated import is visible rather than silent. --------- Co-authored-by: mor-phongdt <phong.dangtuan@mor.com.vn>
Multi-Tenant AI Agent Platform
Multi-agent AI gateway built in Go. 20+ LLM providers. 7 channels. Multi-tenant PostgreSQL.
Single binary. Production-tested. Agents that orchestrate for you.
Documentation • Quick Start • Twitter / X
🌐 Languages: 🇻🇳 Tiếng Việt · 🇨🇳 简体中文 · 🇯🇵 日本語 · 🇰🇷 한국어 · 🇵🇭 Tagalog · 🇪🇸 Español · 🇧🇷 Português · 🇮🇹 Italiano · 🇩🇪 Deutsch · 🇫🇷 Français · 🇸🇦 العربية · 🇮🇳 हिन्दी · 🇷🇺 Русский · 🇧🇩 বাংলা · 🇮🇱 עברית · 🇵🇱 Polski · 🇨🇿 Čeština · 🇳🇱 Nederlands · 🇹🇷 Türkçe · 🇺🇦 Українська · 🇮🇩 Bahasa Indonesia · 🇹🇭 ไทย · 🇵🇰 اردو · 🇷🇴 Română · 🇸🇪 Svenska · 🇬🇷 Ελληνικά · 🇭🇺 Magyar · 🇫🇮 Suomi · 🇩🇰 Dansk · 🇳🇴 Norsk
Core Features
- 8-Stage Agent Pipeline — context → history → prompt → think → act → observe → memory → summarize. Pluggable stages, always-on execution
- 4-Mode Prompt System — Full / Task / Minimal / None with section gating, cache boundary optimization, and per-session mode resolution
- 3-Tier Memory — Working (conversation) → Episodic (session summaries) → Semantic (knowledge graph). Progressive loading L0/L1/L2
- Knowledge Vault — Document registry with wikilinks, hybrid search (FTS + pgvector), filesystem sync
- Agent Teams & Orchestration — Shared task boards, inter-agent delegation (sync/async), 3 orchestration modes (auto/explicit/manual)
- Self-Evolution — Metrics → suggestions → auto-adapt with guardrails. Agents refine their own communication style
- Multi-Tenant PostgreSQL — Per-user workspaces, per-user context files, encrypted API keys (AES-256-GCM), RBAC, isolated sessions
- 20+ LLM Providers — Anthropic (native HTTP+SSE with prompt caching), OpenAI, OpenRouter, Groq, DeepSeek, Gemini, Mistral, xAI, MiniMax, DashScope, Claude CLI, Codex, ACP, and any OpenAI-compatible endpoint
- 7 Messaging Channels — Telegram, Discord, Slack, Zalo OA, Zalo Personal, Feishu/Lark, WhatsApp
- Production Security — 5-layer permission system, rate limiting, prompt injection detection, SSRF protection, AES-256-GCM encryption
- Single Binary — ~25 MB static Go binary, no Node.js runtime, <1s startup, runs on a $5 VPS
- Observability — Built-in LLM call tracing with spans and prompt cache metrics, optional OpenTelemetry OTLP export
Desktop Edition (GoClaw Lite)
A native desktop app for local AI agents — no Docker, no PostgreSQL, no infrastructure.
macOS:
curl -fsSL https://raw.githubusercontent.com/nextlevelbuilder/goclaw/main/scripts/install-lite.sh | bash
Windows (PowerShell):
irm https://raw.githubusercontent.com/nextlevelbuilder/goclaw/main/scripts/install-lite.ps1 | iex
What's Included
- Single native app (Wails v2 + React), ~30 MB
- SQLite database (zero setup)
- Chat with agents (streaming, tools, media, file attachments)
- Agent management (max 5), provider config, MCP servers, skills, cron
- Team tasks with Kanban board and real-time updates
- Auto-update from GitHub Releases
Lite vs Standard
| Feature | Lite (Desktop) | Standard (Server) |
|---|---|---|
| Agents | Max 5 | Unlimited |
| Teams | Max 1 (5 members) | Unlimited |
| Database | SQLite (local) | PostgreSQL |
| Memory | FTS5 text search | pgvector semantic |
| Channels | — | Telegram, Discord, Slack, Zalo, Feishu, WhatsApp |
| Knowledge Graph | — | Full |
| RBAC / Multi-tenant | — | Full |
| Auto-update | GitHub Releases | Docker / binary |
Building from Source
# Prerequisites: Go 1.26+, pnpm, Wails CLI (go install github.com/wailsapp/wails/v2/cmd/wails@latest)
make desktop-build # Build .app (macOS) or .exe (Windows)
make desktop-dmg VERSION=0.1.0 # Create .dmg installer (macOS only)
make desktop-dev # Dev mode with hot reload
Desktop Releases
Desktop uses independent versioning with lite-v* tags:
git tag lite-v0.1.0 && git push origin lite-v0.1.0
# → GitHub Actions builds macOS (.dmg + .tar.gz) + Windows (.zip)
# → Creates GitHub Release with all assets
Architecture
Quick Start
Prerequisites: Go 1.26+, PostgreSQL 18 with pgvector, Docker (optional)
From Source
git clone -b main https://github.com/nextlevelbuilder/goclaw.git && cd goclaw
make build
./goclaw onboard # Interactive setup wizard
source .env.local && ./goclaw
Note: The default branch is
dev(active development). Use-b mainto clone the stable release branch.
With Docker
# Generate .env with auto-generated secrets
chmod +x prepare-env.sh && ./prepare-env.sh
# Add at least one GOCLAW_*_API_KEY to .env, then:
make up
# If Postgres fails to start ("port 5432 already allocated"), set another host
# port in .env, e.g. POSTGRES_PORT=5433 (see .env.example).
# Web Dashboard at http://localhost:18790 (built-in)
# Health check: curl http://localhost:18790/health
# Optional: separate nginx for custom SSL/reverse proxy
# make up WITH_WEB_NGINX=1 → Dashboard at http://localhost:3000
make up creates a Docker network, embeds the correct version from git tags, builds and starts all services, and runs database migrations automatically.
Common commands:
make up # Start all services (build + migrate)
make down # Stop all services
make logs # Tail logs (goclaw service)
make reset # Wipe volumes and rebuild from scratch
Operator CLI:
The main goclaw binary can also inspect local or remote gateways:
goclaw traces list --status error
goclaw traces get <trace-id> -o json
goclaw --server https://goclaw.example.com --token "$GOCLAW_GATEWAY_TOKEN" traces follow --session <session-key>
Optional services — enable with WITH_* flags:
| Flag | Service | What it does |
|---|---|---|
WITH_BROWSER=1 |
Headless Chrome | Enables browser tool for web scraping, screenshots, automation |
WITH_OTEL=1 |
Jaeger | OpenTelemetry tracing UI for debugging LLM calls and latency |
WITH_SANDBOX=1 |
Docker sandbox | Isolated container for running untrusted code from agents |
WITH_TAILSCALE=1 |
Tailscale | Expose gateway over Tailscale private network |
WITH_REDIS=1 |
Redis | Redis-backed caching layer |
Flags can be combined and work with all commands:
# Start with browser automation and tracing
make up WITH_BROWSER=1 WITH_OTEL=1
# Stop everything including optional services
make down WITH_BROWSER=1 WITH_OTEL=1
When GOCLAW_*_API_KEY environment variables are set, the gateway auto-onboards without interactive prompts — detects provider, runs migrations, and seeds default data.
Docker image variants:
Image Description latestBackend + embedded web UI + Python (recommended) latest-baseBackend API-only, no web UI, no runtimes latest-fullAll runtimes + skill dependencies pre-installed latest-otelLatest + OpenTelemetry tracing goclaw-webStandalone nginx + React SPA (for custom reverse proxy) For custom builds (Tailscale, Redis):
docker build --build-arg ENABLE_TSNET=true ...See the Deployment Guide for details.
Updating
Docker
docker compose pull && docker compose up -d
Binary (with embedded web UI)
goclaw update --apply # Downloads, verifies SHA256, swaps binary, restarts
Web Dashboard
Open About dialog → click Update Now (admin only). The update includes both backend and web dashboard when using the default latest image.
Multi-Agent Orchestration
Each agent runs with its own identity, tools, LLM provider, and context files.
Agent Links define outbound, inbound, or bidirectional permission edges.
Delegation can run synchronously or asynchronously and exchanges files through
an isolated delegation workspace; validated outputs are published back under
the caller's .delegations/<delegation-id>/ directory.
Details: Agent Teams docs
Knowledge Vault
Document registry with [[wikilinks]] for bidirectional linking. Hybrid search combines full-text (BM25) and semantic (pgvector) for precise retrieval. Filesystem sync keeps vault in sync with on-disk files.
Self-Evolution
Agents improve themselves through a 3-stage guardrailed pipeline: metrics collection → suggestion analysis → auto-adaptation. Can refine communication style and domain expertise (CAPABILITIES.md) — but never change identity, name, or core purpose.
Provider Adapters
20+ LLM providers unified through a single adapter interface. Capability-based routing, encrypted API keys (AES-256-GCM), extended thinking support per-provider, and prompt caching for Anthropic + OpenAI.
Event-Driven Architecture
Typed domain events power the consolidation pipeline — session summaries, knowledge graph extraction, and dreaming promotion all run asynchronously via worker pools with dedup and retry.
Built-in Tools
30+ tools across 8 categories:
| Category | Tools | Description |
|---|---|---|
| Filesystem | read_file, write_file, edit_file, list_files, search, glob |
File operations with virtual FS routing |
| Runtime | exec, browser |
Shell commands (approval workflow) + browser automation |
| Web | web_search, web_fetch |
Search (Brave, DuckDuckGo) + content extraction |
| Memory | memory_search, memory_get, knowledge_graph_search |
3-tier memory + KG traversal |
| Media | create_image, create_audio, create_video, read_*, tts |
Generation + analysis (multi-provider) |
| Skills | skill_search, use_skill, skill_manage |
BM25 + semantic hybrid search |
| Teams | team_tasks, spawn, delegate, message |
Task board + orchestration + messaging |
| Automation | cron, heartbeat, sessions_* |
Scheduling + session management |
Full tool reference at docs.goclaw.sh
Webhook API
Trigger agents or send channel messages from external systems without the gateway token.
# Bearer auth — sync LLM call
curl -X POST https://example.com/v1/webhooks/llm \
-H "Authorization: Bearer wh_..." \
-H "Content-Type: application/json" \
-d '{"input":"Summarize today metrics","mode":"sync"}'
# HMAC auth — sign with hmac_signing_key from create response
TS=$(date +%s); BODY='{"input":"hi","mode":"sync"}'
SIG=$(echo -n "${TS}.${BODY}" | openssl dgst -sha256 -mac HMAC \
-macopt "hexkey:${WEBHOOK_HMAC_KEY}" | awk '{print $2}')
curl -X POST https://example.com/v1/webhooks/llm \
-H "Content-Type: application/json" \
-H "X-Webhook-Id: ${WEBHOOK_ID}" \
-H "X-GoClaw-Signature: t=${TS},v1=${SIG}" \
-d "$BODY"
See docs/webhooks.md for the full reference: auth, async callbacks, retry schedule, HMAC examples, channel matrix.
Documentation
Full documentation at docs.goclaw.sh — or browse the source in goclaw-docs/
| Section | Topics |
|---|---|
| Getting Started | Installation, Quick Start, Configuration, Web Dashboard Tour |
| Core Concepts | Agent Loop, Sessions, Tools, Memory, Multi-Tenancy |
| Agents | Creating Agents, Context Files, Personality, Sharing & Access |
| Providers | Anthropic, OpenAI, OpenRouter, Gemini, DeepSeek, +15 more |
| Channels | Telegram, Discord, Slack, Feishu, Zalo, WhatsApp, WebSocket |
| Agent Teams | Teams, Task Board, Messaging, Delegation & Handoff |
| Advanced | Custom Tools, MCP, Skills, Cron, Sandbox, Hooks, RBAC |
| Deployment | Docker Compose, Database, Security, Observability, Tailscale |
| Reference | CLI Commands, REST API, WebSocket Protocol, Environment Variables |
Testing
go test ./... # Unit tests
go test -v ./tests/integration/ -timeout 120s # Integration tests (requires running gateway)
Project Status
See CHANGELOG.md for detailed feature status including what's been tested in production and what's still in progress.
Acknowledgments
GoClaw was originally inspired by the OpenClaw project architecture.
License
CC BY-NC 4.0 — Creative Commons Attribution-NonCommercial 4.0 International








