Add rate-limit tests
cover validation edge cases
Control plane for coding agents
Nothing lands until you review the diff. Switchyard stores boards and tasks, claims them with a single dispatcher, runs them on the machine that owns your code, and holds every result in review until you inspect and approve it.
cover validation edge cases
executor unavailable
Your repo is on a Mac or Windows box, not on the server running the scheduler.
A passing run still needs a human look at the diff.
Two schedulers claiming the same task is a bug, not a feature.
The VPS runs the control plane. Node-agent runs the execution plane on every host that owns source code. Remote workspaces are never used as cwd by local VPS processes.
POST /api/dispatch to the node-agent server.POST /api/nodes/:id/result.Paths like /Users/… and C:\… are routed to their registered host, never executed on the VPS.
Every executor goes through the same gate.
Agent mutates the working tree but does not commit or push.
Board fetches the diff from the workspace host.
You pick Commit or Commit & Push.
Board runs approval over SSH.
Status moves from review to done.
GET /api/boards/{slug}/tasks/{id}/diff
POST /api/boards/{slug}/tasks/{id}/approve
{"action": "commit", "message": "optional commit message"}Tasks store human intent as a title and description. Choose an executor only to force a specific runtime. Every executor below runs through node-agent on the workspace host.
Hermes on the workspace host.
Codex on the workspace host.
Claude CLI on the workspace host.
CommandCode on the workspace host.
DSH CLI on the workspace host.
Direct remote commands; command is the only executed input.
Hermes on the VPS, file access over SSH. Kept for backward compatibility.
--yolo, which lets the worker edit files and run shell commands. Use it only on trusted nodes.Node capabilities
hermes · codex · claude · commandcode · dsh · shellRequest
dshThe server picks a node by workspace prefix plus executor capability. A node without that executor is rejected withexecutor unavailable.Node-agent prefers gRPC and falls back to HTTP long-poll when the stream drops. Operators can see which path a task took.
grpcgRPC streamactiveHTTP long-pollfallbackClient-side illustration only. No network calls.
| Setting | Meaning |
|---|---|
NODE_AGENT_TRANSPORT=auto | gRPC preferred, HTTP fallback |
NODE_AGENT_TRANSPORT=grpc | Fail-closed when gRPC is unavailable |
NODE_AGENT_TRANSPORT=http | Forces the compatibility lane |
8789 private on the tailnet. Tailscale connects VPS and workers.Three layers keep agent prompts and shell output small.
Structural index of the codebase on the workspace host.
Used for hermes, codex and commandcode.Shortens verbose shell commands and output within bounded timeouts.
800 ms hook check/rewrite · 2 s --ultra-compact capOptional compact output for shell over 8 KiB, with fail-open behavior.
NODE_AGENT_SHELL_CAVEMAN=1Shell tasks skip AGENTS/README/codegraph prompt injection by default.
A compact surface for boards, tasks, workspaces, flow and remote dispatch.
| Method | Path | Purpose |
|---|---|---|
| GET / POST | /api/boards | Boards and tasks |
| PATCH | /api/boards/{slug}/tasks/{id}/status | Status transitions |
| PATCH | /api/boards/{slug}/tasks/{id}/assignee | Change assignee |
| GET | /api/boards/{slug}/tasks/{id}/diff | Workspace diff |
| POST | /api/boards/{slug}/tasks/{id}/approve | Commit or push |
| GET/POST/PUT/DELETE | /api/workspaces* | Workspaces and health |
| GET | /api/flow/active | Active flow tasks |
| POST | /api/remote/dispatch | Manual dispatch |
| GET | /api/nodes | Node status |
All /api/* routes require the kanban_session HttpOnly cookie except the four /api/auth/* routes.
{
"task_id": "t1",
"board": "saas",
"message": "Fix login validation",
"workspace": "/Users/<user>/Development/saas",
"executor": "dsh"
}Mac and Windows agents keep running with their previous capabilities until you upgrade them.
Build and restart node-agent server on the VPS (HTTP :8788, gRPC :8789).
Build and restart kanban-board (Switchyard).
Cross-build the worker binary (GOOS=darwin GOARCH=arm64 for Apple Silicon).
Reinstall the agent on Mac or Windows and restart the LaunchAgent or service.
Confirm node is idle and capability and transports show at /api/nodes.
Run a dispatch canary: expect success=true, a delivery_id, and transport grpc (or fallback http).
go vet ./...
go test ./...
go build -o bin/kanban-board ./cmd/server
cd web && pnpm build
pm2 restart kanban-boardweb/dist from the Go binary; no Node or Bun runtime stays alive.
Control plane in Go, execution plane in node-agent.