Clusters are multi-role agent teams. You define a set of roles, each with its own prompt and trigger configuration, and the system launches ephemeral Docker containers to execute them. Roles within a cluster share a workspace directory, allowing agents to collaborate by reading and writing to shared folders.
Create a cluster from the web UI. Each cluster has:
- Name — Display label for the cluster.
- System Prompt — Shared context appended to every role's system prompt. Use this for mission, goals, and shared instructions. Defaults to the template in
event-handler/clusters/SYSTEM.md. - Shared Folders — Named subdirectories under
shared/that all roles can access (e.g.,docs,output). - Enabled/Disabled — Toggle the cluster on or off. Disabling stops all running containers and prevents new triggers from firing.
Each role within a cluster represents one agent type. A role has:
| Field | Description |
|---|---|
| Role Name | Display name (e.g., "Researcher", "Writer") |
| Role Instructions | Markdown prompt describing what this role does. Defaults to event-handler/clusters/ROLE.md. |
| Prompt | The user-facing task prompt passed to the agent (default: "Execute your role.") |
| Max Concurrency | Maximum number of simultaneous containers for this role (default: 1) |
| Trigger Config | How this role gets activated (see Triggers below) |
| Folders | Role-specific subdirectories within its own directory |
| Cleanup Worker Dir | If enabled, the ephemeral worker directory is deleted after the container exits |
Both the system prompt and role prompt support {{PLACEHOLDER}} template variables that are resolved at container launch time:
| Variable | Value |
|---|---|
{{CLUSTER_HOME}} |
Container workspace root (/home/claude-code/workspace) |
{{CLUSTER_SHARED_DIR}} |
Path to cluster shared directory |
{{CLUSTER_SHARED_FOLDERS}} |
JSON array of shared folder paths |
{{SELF_ROLE_NAME}} |
This role's name |
{{SELF_WORKER_ID}} |
Unique ID for this worker instance |
{{SELF_WORK_DIR}} |
This worker's ephemeral working directory |
{{SELF_TMP_DIR}} |
This worker's temp directory |
{{WORKSPACE}} |
JSON manifest of the full workspace layout |
{{DATETIME}} |
ISO timestamp at launch time |
{{WEBHOOK_PAYLOAD}} |
JSON payload from a webhook trigger (only present for webhook-triggered runs) |
Each role can have multiple triggers configured simultaneously. All triggers respect the role's concurrency limit — if the maximum number of containers is already running, the trigger is skipped.
Always available. Click the run button in the UI to launch a role container on demand.
Always-on once the role exists. Send a POST request to trigger execution:
POST /api/cluster/{clusterId}/role/{roleId}/webhook
Header: x-api-key: YOUR_API_KEY
Body: { "key": "value" }
The request body is passed to the role as {{WEBHOOK_PAYLOAD}}. You can also include a prompt field in the body to override the role's default prompt for that run.
Responses:
| Status | Meaning |
|---|---|
| 200 | Container launched successfully |
| 403 | Cluster is disabled |
| 404 | Role or cluster not found |
| 429 | Max concurrency reached |
Schedule a role to run on a recurring basis using a cron expression.
Configure in the role's trigger settings:
{
"cron": {
"enabled": true,
"schedule": "0 */6 * * *"
}
}Uses standard cron syntax (powered by node-cron). The schedule is registered at server boot and reloaded whenever trigger config changes.
Trigger a role when files change in the cluster's data directory.
{
"file_watch": {
"enabled": true,
"paths": "shared/input,shared/data"
}
}Paths are comma-separated, relative to the cluster's data directory. Changes are debounced (5-second window) so rapid edits produce a single trigger. The logs/ directory is always excluded from watching. Requires chokidar to be installed.
Each cluster gets a data directory on disk under data/clusters/:
data/clusters/
cluster-{shortId}/
shared/ # Cluster-wide shared directory
docs/ # Example shared folder
output/ # Example shared folder
role-{roleShortId}/
shared/ # Role-specific shared directory
worker-{uuid}/ # Ephemeral per-container directory
tmp/ # Worker temp directory
logs/
role-{roleShortId}/
2025-01-15_14-30-00_{uuid}/
system-prompt.md # Resolved system prompt for this run
user-prompt.md # Resolved user prompt for this run
meta.json # Run metadata (role name, timestamps)
trigger.json # Trigger info (type, payload)
stdout.jsonl # Container stdout
stderr.txt # Container stderr
Short IDs are the first 8 characters of the UUID with dashes removed.
Each role has a maxConcurrency setting (default: 1). Before any trigger launches a container, the system counts running containers for that role. If the count meets or exceeds the limit, the trigger is rejected.
This applies uniformly across all trigger types — manual, webhook, cron, and file watch all go through the same canRunRole() gate.
Set higher concurrency for roles that handle parallel workloads (e.g., a webhook-triggered role that processes independent requests). Keep it at 1 for roles that should run sequentially.
The cluster console page provides real-time visibility into running containers via Server-Sent Events. It shows:
- Container status per role — which workers are running, idle, or stopped
- Resource usage — CPU, memory, and network stats per container
- Live logs — Streaming stdout and stderr from each running container
- Prompts — View the resolved system and user prompts for any worker
The console polls every 3 seconds, automatically discovers new containers, and cleans up tailing when containers stop.
Every container run creates a log session under logs/ in the cluster data directory. The logs page in the UI shows historical runs grouped by role, with access to:
stdout.jsonl— Structured stdout outputstderr.txt— Raw stderr outputsystem-prompt.md— The full system prompt as sent to the agentuser-prompt.md— The user prompt as sent to the agenttrigger.json— What triggered the run and any payload
Each worker container receives two prompts:
-
System Prompt (
--append-system-prompt) — Composed from the cluster's system prompt plus the role's instructions, with all template variables resolved. This provides shared context about the cluster mission and the role's responsibilities. -
User Prompt (
-p) — The role'spromptfield (or a webhook-provided override). This is the actual task instruction.
The default role template (event-handler/clusters/ROLE.md) introduces the agent to the cluster concept and provides the workspace layout via {{WORKSPACE}}. The default system prompt template (event-handler/clusters/SYSTEM.md) has placeholders for mission, vision, goals, and values.
Customize these templates in your project's event-handler/ directory to set defaults for new clusters and roles.