@zoink-dev/zoink-cli
v0.4.10
Published
AI Agent Swarm Orchestration CLI
Downloads
755
Maintainers
Readme
Zoink CLI
Multi-Framework AI Agent Swarm Orchestration
Zoink is an open-source CLI tool for orchestrating swarms of AI agents in isolated Docker containers. v0.3.0 evolves Zoink from a single-framework orchestrator into a multi-framework control plane — run OpenClaw and Hermes agents side-by-side in the same swarm. Define your AI team in a simple YAML file, launch it with one command, and watch your agents collaborate through a built-in terminal dashboard.
Installation
curl -fsSL https://zoink.dev/install.sh | shor
npm install -g @zoink-dev/zoink-cliTo start the swarm
Create a workflow directory and then:
zoink config init # Create minimal config file
zoink up # Bring your AI swarm to lifeFeatures
- Multi-Framework Swarms — Mix OpenClaw and Hermes agents in a single swarm with per-agent
runtimefield - Declarative Configuration — Define agent teams in
swarm.yamlv2.0 with roles, models, runtimes, and workspaces - One-Command Orchestration —
zoink uphandles Docker networking, container lifecycle, and agent coordination - Idempotent Operations — Run
zoink upmultiple times safely without duplicating resources - Persistent Agent State — Workspace-backed bind mounts per agent (
workspace/agents/<name>/state); state surviveszoink down/zoink upcycles. Disable withpersistState: falsefor stateless agents. - Health Checks & Auto-Recovery — Framework-specific Docker health checks with crash-loop detection (3 failures in 60s →
errorstate) - Resource Governance — Per-agent
cpu_limitandmemory_limitapplied as Docker resource constraints - Graceful Config Reload —
zoink config reloadapplies safe changes without container restarts; prompts for critical changes - Starter Templates — Three built-in swarm templates via
zoink config init --template - API Key Redaction — All log output is scrubbed of API keys via
RedactTransform - Dynamic Agent Management — Spawn, remove, pause, unpause, and exec into agents at runtime
- Built-in Kanban Board — Shared
kanban.yamlwith CLI commands for add, move, assign, and watch - PM-Driven Delegation — Send high-level goals to a PM agent that decomposes and assigns work
- Direct Agent Queries — Query any agent role directly via
zoink run query - Autonomous Task Pickup — Workers self-claim cards assigned to them via per-agent
autoPickupcron, clone the task'srepoUrl, and move it toDoneon success - Terminal Dashboard — Full-screen TUI with five tabs (Overview, Kanban, Logs, Agents, Chat) and approval modal
- Web Dashboard — Browser-based SPA with the same five tabs, served by
zoink daemonon a host of your choice - Daemon Mode —
zoink daemon startruns a single-board HTTP service for remote Kanban access, agent control, chat, and SSE board events - Remote Kanban — Opt-in
kanban: { mode: remote }inswarm.yamlroutes allzoink kanbancommands and worker pickup loops through the daemon over HTTP - Chat With Agents — Stream conversations with any gateway-enabled agent from the dashboard (TUI or web), powered by the OpenResponses adapter
- Human-in-the-Loop Approval — Pause before risky actions with configurable approval flow
- Global Configuration — User-level settings for theme, default model, and API key storage
- Role Templates — Built-in and user-created role templates
- Isolated Containers — Each agent runs in its own Docker container with secure networking
- Enhanced Log Streaming — Color-coded logs with
--follow,--tail,--since,--until
See docs/PRODUCT.md for a full feature breakdown and command reference.
Prerequisites
- Docker - Docker Desktop or Docker Engine
- Node.js 20+ - For running the CLI tool
- API Keys - Anthropic/OpenAI API key for your agents (set as environment variables)
Installation
One-line installer (recommended)
The fastest way to install zoink on Linux or macOS is the hosted install script. It detects or installs Docker, installs the CLI binary, prefetches both runtime images, and writes a bearer token to ~/.zoink/daemon.token — all in one shot:
curl -fsSL https://zoink.dev/install.sh | shThe installer never starts the daemon, registers OS services, or opens a browser. Those steps happen in your project workspace, under your control.
Flags (pipe sh -s -- before them):
# Skip Docker detection/install (you already have Docker)
curl -fsSL https://zoink.dev/install.sh | sh -s -- --no-docker
# Skip image prefetch
curl -fsSL https://zoink.dev/install.sh | sh -s -- --no-images
# Force the tarball path even when npm is available
curl -fsSL https://zoink.dev/install.sh | sh -s -- --prefer-tarball
# Pin a specific CLI version (for tarball path)
curl -fsSL https://zoink.dev/install.sh | sh -s -- --version 0.4.2
# Run as root (not recommended)
ZOINK_ALLOW_ROOT=1 curl -fsSL https://zoink.dev/install.sh | shAfter the installer exits:
# Verify the install
zoink --version
# Your daemon token is ready to use:
cat ~/.zoink/daemon.tokennpm global install
If you already have Node.js 20+ and npm:
npm install -g @zoink-dev/zoink-cliFrom source
git clone https://github.com/zoink-dev/zoink-cli.git
cd zoink-cli
npm install
npm linkQuick Start
1. Initialize a Swarm
mkdir my-project
cd my-project
zoink config initThis creates a swarm.yaml template (use --template for a pre-built starter):
version: '2.0'
name: my-swarm
workspace: ./workspace
frameworks:
openclaw:
defaultImage: ghcr.io/openclaw/openclaw:latest
agents:
- name: pm-bot
role: pm
runtime: openclaw
model: gpt-4
provider:
name: openai
keyEnv: OPENAI_API_KEY
- name: coder-1
role: coder
runtime: openclaw
model: gpt-4
provider:
name: openai
keyEnv: OPENAI_API_KEY
cpu_limit: '2.0'
memory_limit: '2GB'
- name: researcher-1
role: researcher
runtime: hermes
model: gpt-4
provider:
name: openai
keyEnv: OPENAI_API_KEYOr pick a ready-made starter:
zoink config init --template default # PM + coder + researcher on OpenClaw
zoink config init --template hybrid # PM + coder on OpenClaw, researcher on Hermes
zoink config init --template coding-team # PM + coder + devops on OpenClaw2. Launch Your Swarm
# Set your API key
export OPENAI_API_KEY="sk-..."
# Start the swarm — daemon auto-starts in the background
zoink upZoink will:
- Pull the OpenClaw Docker image if needed
- Create a dedicated network (
zoink-my-swarm-net) - Start each agent in its own container
- Create a
workspace/kanban.yamlboard for task coordination - Auto-start the workspace daemon (generates a token in
workspace/.zoink-daemon.token)
3. Check Swarm Status
zoink psOutput (includes runtime and health columns):
NAME ROLE RUNTIME STATUS HEALTH CONTAINER ID
pm-bot pm openclaw running healthy a1b2c3d4e5f6
coder-1 coder openclaw running healthy b2c3d4e5f6a1
researcher-1 researcher hermes running healthy c3d4e5f6a1b24. Delegate a Task
zoink run task "Research the best state management library for React and create a proof-of-concept"The PM agent will:
- Decompose the high-level goal into subtasks
- Create cards on the Kanban board
- Assign work to appropriate agents
- Coordinate execution
5. Monitor with the Dashboard
zoink dashboardOpens a full-screen TUI with five tabs:
- Overview — Swarm health, agent status, activity feed, command input
- Kanban — Interactive board with navigation and task management
- Logs — Multi-agent log viewer with filtering and color-coded severity
- Agents — Agent list with pause, logs, and detail views
- Chat — Stream a conversation with any gateway-enabled agent
Prefer a browser? Start the daemon and open the dashboard with one command:
# Foreground (keep the terminal open)
zoink daemon serve --token-file ~/.zoink/daemon.token
# Or background + auto-open browser
zoink daemon start --token-file ~/.zoink/daemon.token --detach
zoink daemon open
# → opens http://localhost:9999/?token=… (auto-logs you in)The daemon serves the web SPA from the same port and exposes the Kanban, agents, chat, and SSE event APIs behind bearer-token auth.
6. Reload Config
Apply changes to swarm.yaml without restarting in-flight agents:
zoink config reloadSafe changes (model, temperature) apply instantly. Critical changes (network, volumes) prompt for confirmation.
7. View Agent Logs
# Stream logs from a specific agent (API keys are automatically redacted)
zoink agent logs pm-bot --follow
# View Kanban board
zoink kanban show8. Teardown
zoink downCleanly stops and removes all containers, networks, and the workspace daemon. Agent state directories in workspace/agents/<name>/state are preserved — state persists for the next zoink up. Use --keep-daemon if you want the daemon to remain running after teardown (e.g. to inspect the web dashboard post-mortem).
Commands
Swarm Lifecycle
zoink up [--dir <path>] [--no-daemon] [--daemon-port <n>] [--daemon-bind <addr>]
# Launch swarm + auto-start daemon (use --no-daemon to skip)
zoink down [--dir <path>] [--keep-daemon]
# Teardown swarm + stop daemon (use --keep-daemon to leave it running)
zoink ps [--dir <path>] # List running agents
zoink dashboard [--dir <path>] # Open terminal dashboardDaemon (Web Dashboard & Remote Kanban API)
# Foreground (dev / tmux session)
zoink daemon serve --token-file ~/.zoink/daemon.token [--port 9999] [--bind 127.0.0.1]
# Background
zoink daemon start --token-file ~/.zoink/daemon.token --detach
zoink daemon open # open dashboard in browser (reads port + token from sidecars)
zoink daemon restart # stop + restart with the same options
zoink daemon status
zoink daemon stopThe daemon serves the browser dashboard (same five tabs as the TUI) and exposes a bearer-authenticated HTTP API for Kanban operations, agent control (pause/unpause/restart/logs), chat, and SSE board events.
--token-file vs --token-env
--token-file <path> reads the bearer token from a file on every auth check — token rotation works without restarting the daemon. The install script writes ~/.zoink/daemon.token for you.
--token-env <VAR> reads the token from an environment variable at startup (legacy behaviour, still fully supported). The two flags are mutually exclusive; passing both or neither exits with an error.
# Using the token file written by install.sh (recommended)
zoink daemon serve --token-file ~/.zoink/daemon.token
# Using an env var (ad-hoc / CI)
export ZOINK_TOKEN="$(openssl rand -hex 32)"
zoink daemon start --token-env ZOINK_TOKEN --detachPass --detach to run the daemon in the background so you can close the terminal:
zoink daemon start --token-file ~/.zoink/daemon.token --detach
# → Daemon started in background (pid=…, logs=<workspace>/.zoink-daemon.log)
zoink daemon open # opens http://localhost:9999/?token=… in your browser
zoink daemon status
zoink daemon stopzoink daemon open constructs the dashboard URL with the token from ~/.zoink/daemon.token (via .zoink-daemon.tokenfile sidecar) so you get a one-click authenticated session. On headless Linux (DISPLAY and WAYLAND_DISPLAY unset) it prints the URL to stdout instead.
Logs are written to <workspace>/.zoink-daemon.log. Files larger than 10 MiB are rotated to .zoink-daemon.log.old at each launch. Override the cap with ZOINK_DAEMON_LOG_MAX_BYTES=<bytes> (set to 0 to disable rotation).
Loopback auto-login
When you point a browser at http://127.0.0.1:9999/ on the same machine that runs the daemon, zoink automatically mints a session cookie for that first request — no token-paste required. The cookie is set on the very first call to /api/v1/auth/me (the SPA's bootstrap probe), so the dashboard loads directly without flashing the login page.
This only applies to connections that arrive on 127.0.0.1, ::1, or ::ffff:127.0.0.1 (the loopback interface). Any request from another host still requires a bearer token or an existing session cookie.
To disable auto-login (e.g. on a shared development machine where other local users should not get automatic access):
# One-off: pass the flag when starting
zoink daemon serve --token-file ~/.zoink/daemon.token --no-loopback-auto-login
# Permanently: set in global config
zoink config set dashboard.loopbackAutoLogin falseSecurity note — shared dev hosts. On a machine with multiple local users, any user with a browser can open the dashboard when loopback auto-login is enabled. The daemon token file's filesystem permissions (written as
0600by the install script) are the only barrier. If you share the host with untrusted users, setdashboard.loopbackAutoLogin: false.
Agent Management
zoink agent spawn <name> # Dynamically add a new agent
zoink agent rm <name> # Remove an agent container
zoink agent logs <name> [-f] # Stream agent logs
zoink agent pause <name> # Suspend an agent
zoink agent unpause <name> # Resume a paused agent
zoink agent exec <name> [cmd] # Execute a command inside a containerConfiguration
zoink config init [--template <name>] # Generate swarm.yaml (use --template for starters)
zoink config validate # Validate swarm.yaml against schema v2.0 (with line numbers)
zoink config reload # Apply safe config changes without container restarts
zoink config set <key> <val> # Set global config value
zoink config get [key] # Get global config value
zoink config role list # List role templates
zoink config role create <name> # Create role template from running agent
zoink config role delete <name> # Delete a role templateKanban Board
zoink kanban show # Display Kanban board
zoink kanban add --title <text> [--repo-url <url>] [--assign <agent>] # Add a new task; --repo-url enables autonomous pickup
[--priority low|medium|high]
zoink kanban move <id> <col> # Move a task to a different column
zoink kanban assign <id> <agent> # Assign a task to an agent
zoink kanban watch [--json] # Watch for board changes in real-timeWhen kanban.mode: remote is set, every subcommand transparently routes to the daemon over HTTP instead of touching kanban.yaml directly.
Task Orchestration
zoink run task "<description>" [--repo-url <url>] # Delegate task to PM agent (repo-url propagates to PM-decomposed cards)
zoink run query "<prompt>" --agent <role> # Direct query to a specific agent roleConfiguration
swarm.yaml v2.0 Structure
version: '2.0' # Schema version
name: my-swarm # Unique identifier for this swarm
workspace: ./workspace # Shared workspace directory
frameworks: # Framework-level defaults
openclaw:
defaultImage: ghcr.io/openclaw/openclaw:latest
hermes:
defaultImage: ghcr.io/nousresearch/hermes:v2026.4.23
orchestration: # Optional human-in-the-loop settings
approval_mode: false
approval_timeout_seconds: 300
kanban: # Optional; defaults to local file-backed mode
mode: local # 'local' (default) or 'remote' — see Remote Kanban Mode below
agents:
- name: pm-bot # Unique agent name
role: pm # Agent role (pm, coder, researcher, devops)
runtime: openclaw # Framework: openclaw (default) or hermes
model: claude-3-5-sonnet-20241022
provider:
name: anthropic
keyEnv: ANTHROPIC_API_KEY
- name: coder-1
role: coder
runtime: openclaw
model: claude-3-5-sonnet-20241022
provider:
name: anthropic
keyEnv: ANTHROPIC_API_KEY
cpu_limit: '2.0' # Docker CPU quota
memory_limit: '2GB' # Docker memory limit
persistState: true # default; set to false for a fully stateless agent
autoPickup: true # opt in to autonomous Kanban pickup
pickupIntervalSeconds: 300 # how often the agent sweeps the board
- name: stateless-helper
role: coder
runtime: openclaw
model: claude-3-5-sonnet-20241022
provider:
name: anthropic
keyEnv: ANTHROPIC_API_KEY
persistState: false # no state directory — ephemeral agent
- name: researcher-1
role: researcher
runtime: hermes # Use Hermes for persistent-memory research
model: claude-3-5-sonnet-20241022
provider:
name: anthropic
keyEnv: ANTHROPIC_API_KEY
profiles: 2 # Hermes: number of isolated profile instancesprovider is the preferred way to select an LLM backend and auth source. v1.0 configs (no version, no runtime) remain fully valid, and apiKeyEnv is still accepted as a deprecated compatibility field for one release.
Supported Runtimes
| Runtime | Image | Best For |
| -------------------- | ---------------------------------------- | ------------------------------------------ |
| openclaw (default) | ghcr.io/openclaw/openclaw:latest | General coding, PM tasks |
| hermes | ghcr.io/nousresearch/hermes:v2026.4.23 | Research, persistent memory, multi-profile |
Supported Roles
- pm - Project manager; decomposes high-level goals and assigns work
- coder - Writes code, implements features, fixes bugs
- researcher - Gathers information, evaluates libraries, reads documentation
- devops - Manages infrastructure, deployment, CI/CD
Per-Agent Personality (SOUL.md)
Both OpenClaw and Hermes support a SOUL.md file that sets an agent's voice, tone, and behavioral rules. Zoink auto-generates one from the soul block in swarm.yaml and seeds it into the container before it starts. zoink config init ships a starter soul block on every agent so you can edit and go:
agents:
- name: pm-agent
role: pm
runtime: openclaw
model: claude-sonnet-4-5
soul:
tone: "blunt, direct — no corporate filler"
style: "concise; one sentence when one sentence is enough"
personality: "opinionated, witty, calls out bad ideas early"
rules:
- "Never open with 'Great question' or 'Absolutely'"
- "Skip preambles; answer the question"
- name: researcher
role: researcher
runtime: hermes
model: anthropic/claude-opus-4.7
soul: false # disable SOUL.md seeding entirely for this agentAll four fields (tone, style, personality, rules) are optional. If soul is omitted entirely, a minimal identity line is seeded. If soul: false, nothing is written.
Where the file lands:
- OpenClaw:
/root/.openclaw/workspace/SOUL.md - Hermes:
/opt/data/SOUL.md(theHERMES_HOMEmount)
Sidecar Config Files
For anything beyond model and soul — channels, custom skills, OpenClaw $include fragments, Hermes .env, memories/, AGENTS.md — use configFiles to drop files into the agent's state mount at first boot:
agents:
- name: pm-agent
role: pm
runtime: openclaw
configFiles:
- source: ./prompts/pm.AGENTS.md # relative to swarm.yaml
target: AGENTS.md # relative to state mount root
- source: ./configs/openclaw/plugins.json5
target: openclaw.d/plugins.json5 # loaded via $include in openclaw.json
- source: ./secrets/openclaw.env
target: .env
mode: "0600" # restrict permissions
- name: researcher
role: researcher
runtime: hermes
configFiles:
- source: ./prompts/researcher.SOUL.md
target: SOUL.md # overrides the auto-seeded SOUL.md
- source: ./configs/hermes/researcher.env
target: .env
mode: "0600"
- source: ./memories/researcher.MEMORY.md
target: memories/MEMORY.mdtarget paths are relative to the per-runtime state mount root (/root/.openclaw for OpenClaw, /opt/data for Hermes). Absolute paths and .. traversal are rejected. If a configFiles entry targets the same path as the auto-seeded SOUL.md, the user-supplied file wins.
When persistState: false, OpenClaw configFiles must target either config/… (routes to the config volume) or workspace/… (routes to the workspace volume); any other prefix is skipped with a warning.
Resource Limits
Per-agent resource constraints are applied directly as Docker container limits:
agents:
- name: coder-1
cpu_limit: '2.0' # 2 CPU cores
memory_limit: '4GB' # 4 GB RAMProvider and API Key Configuration
Zoink now separates the model ID from the provider/auth settings. The recommended config shape is:
agents:
- name: agent-1
role: coder
runtime: openclaw
model: gpt-4o-mini
provider:
name: openai
keyEnv: OPENAI_API_KEYUse the provider block like this:
provider.nameselects the backend. Common values includeopenai,anthropic,openrouter,google, andcustom.provider.keyEnvreads the API key from the named environment variable. This is the preferred option.provider.keystores the key inline inswarm.yaml. It is supported, but not recommended because the secret is stored in plaintext.provider.baseUrloverrides the provider endpoint, which is useful for OpenAI-compatible gateways and self-hosted endpoints.
Examples:
agents:
- name: claude-agent
role: pm
runtime: openclaw
model: claude-sonnet-4-5
provider:
name: anthropic
keyEnv: ANTHROPIC_API_KEY
- name: gateway-agent
role: coder
runtime: hermes
model: gpt-4.1-mini
provider:
name: custom
keyEnv: AI_GATEWAY_API_KEY
baseUrl: https://gateway.example.com/v1If you do not want to pass secrets via shell environment variables, you can store a provider key in Zoink's global config and reference only the provider in swarm.yaml:
zoink config set api_keys.openai sk-...
zoink config set api_keys.anthropic sk-ant-...agents:
- name: agent-1
role: coder
runtime: openclaw
model: gpt-4o-mini
provider:
name: openaiKey resolution order is:
provider.keyEnvprovider.key- deprecated
apiKeyEnv zoink config set api_keys.<provider> ...
Legacy configs that still use apiKeyEnv continue to work during the deprecation window:
agents:
- name: legacy-agent
role: coder
model: claude-3-5-sonnet-20241022
apiKeyEnv: ANTHROPIC_API_KEYAutonomous Task Pickup
Workers can self-claim assigned cards on a recurring interval, clone the task's repository into their persistent state, execute it, and move the card to Done. Enable it by setting autoPickup: true on a worker agent and adding --repo-url when you create the card.
agents:
- name: coder-1
role: coder
runtime: openclaw
autoPickup: true
pickupIntervalSeconds: 300 # board sweep cadencezoink kanban add \
--title "Add health-check endpoint" \
--repo-url https://github.com/your-org/api.git \
--assign coder-1A card is eligible for pickup when it is in To Do, its assignee matches the agent name exactly, repoUrl is set, and it is not already claimed. Claiming is deterministic: if the board changed mid-claim the worker simply skips and retries on the next interval. Each successful claim stamps claimedBy and claimedAt and moves the card to In Progress. Repository checkouts land under workspace/agent-state/<agent>/<runtime>/tasks/<taskId>/.
zoink run task --repo-url <url> "<goal>" propagates the same repoUrl to PM-decomposed cards so the entire downstream slice is pickup-ready.
Remote Kanban Mode
By default the board is a local kanban.yaml file. For distributed setups (one shared board, multiple host machines) start a daemon on the host that owns the board and point your swarm at it:
# On the daemon host (token from install.sh, or ad-hoc via --token-env)
zoink daemon start --token-file ~/.zoink/daemon.token --bind 0.0.0.0 --port 9999# In each consumer swarm.yaml
kanban:
mode: remote
daemonUrl: http://kanban-host:9999
authTokenEnv: KANBAN_TOKEN # the consumer reads its bearer token from this env var
timeoutMs: 30000 # optionalWhen mode: remote is set:
- All
zoink kanbansubcommands (show,add,move,assign,watch) route through the daemon over HTTP. zoink upskips the workspace bind mount and injects daemon-endpoint env vars into agent containers instead of file paths.- The autonomous pickup cron uses
--board-endpointand--board-token-envso workers reach the same board over HTTP. - The daemon also serves the web SPA dashboard and SSE board events on the same port. TLS termination is left to a reverse proxy (nginx, Caddy, etc.).
Uninstalling
zoink uninstall # interactive confirmation
zoink uninstall --yes # skip confirmation prompt
# Or via the install script
curl -fsSL https://zoink.dev/install.sh | sh -s -- --uninstallzoink uninstall (or install.sh --uninstall):
- Stops any running daemon — treating a missing PID file, stale PID,
ESRCH,EPERM, and any signal error as warnings, never errors. - Removes the CLI binary (
npm uninstall -g @zoink-dev/zoink-clifor npm installs,rm -rf ~/.zoink/binfor tarball installs). - Removes
~/.zoink/daemon.token. - Prints what was left untouched:
Containers and `~/.zoink/{config,assets,roles}` left in place.
To remove Docker containers and networks created by zoink:
docker ps --filter "label=zoink.swarm" -q | xargs docker rm -f
docker network ls --filter "label=zoink.swarm" -q | xargs docker network rm
To fully remove ~/.zoink state:
rm -rf "/Users/you/.zoink"Running install.sh again after zoink uninstall produces a clean installation from scratch.
Architecture
Zoink uses a hexagonal architecture (Ports & Adapters) with strict separation of concerns: domain services contain pure business logic, ports define contracts, adapters handle I/O, and the CLI/TUI layers provide the user interface. A centralized event bus decouples components.
See docs/ARCHITECTURE.md for the full architecture documentation.
Development
Setup
git clone https://github.com/zoink-dev/zoink-cli.git
cd zoink-cli
npm installTesting
# Unit tests (fast, no Docker required)
npm test
# Watch mode
npm test:watch
# Coverage report
npm test:coverage
# E2E tests (requires Docker daemon)
npm run e2e:full
# Run specific test file
npx vitest run src/domain/services/SwarmLifecycleService.spec.tsContributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
Development Workflow
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make changes and add tests
- Run tests:
npm test && npm run e2e:full - Commit with descriptive messages
- Push and open a pull request
Roadmap
See docs/PRODUCT.md for the feature overview and roadmap.
License
MIT License - see LICENSE for details.
Made with ❤️ by the Zoink community
