Skip to content
ChannelDesk
Features

A control desk for every agent, flow and device you run.

ChannelDesk is an NX monorepo of cooperating services. Here's what each of them gives you out of the box.

Orchestration

Real Claude Code, remote-driven.

ChannelDesk spawns genuine claude-code subprocesses on your machine via the SessionSpawnerService. Sessions have full tool access, persistent context, and stream their output live back to any subscribed channel.

  • • Per-flow session lifecycle via spawnForFlow()
  • • Flow-tools MCP automatically wired into each session
  • • Resume, cancel, and fork sessions from any device
/sessions/:id · a real claude-code subprocess, streamed live
ChannelDesk Session — a live claude-code subprocess streaming markdown output, route badges and tool calls back into the chat dock
One chat shell

Five surfaces. One shell.

Every chat in ChannelDesk — agent session, cluster assistant, Quick Actions panel, Dashboard panel, Flow editor tab — is the same component. Auto-revive, Stop button, message queue while busy, per-session model + effort, attachments, slash commands — every chat behaves identically, no matter which page it lives on.

  • /agents/:id/sessions/:sid — agent session view
  • /cluster — cluster assistant dock
  • /actions — Quick Actions chat panel
  • /dashboards/:id — dashboard chat panel
  • /flows/:id — flow editor chat tab
mobile · same shell, same gestures, every page
ChannelDesk PWA on mobile — Agent chat surface with Stop button and queued message badge
/agents/diskwatch · session from Tuesday
OK, retry the cleanup with --no-locks this time.
last message: 3 days ago
spawning · reading ~/.claude/projects/…/diskwatch.jsonl · resumed.

Re-running the cleanup with --no-locks. The orchestrator pod restarted twice since we last spoke; my context is intact.
no Continue button — just send
Auto-revive

Pick up where you left off. Days later.

Open a session you closed last week, type a message, hit send. ChannelDesk spawns a fresh claude-code subprocess and resumes from the JSONL transcript Claude wrote to disk — no Continue button, no copy-paste warm-up.

  • • JSONL lives on an NFS share so every pod sees the same state
  • • If the file's gone, falls back to transcript-prepend so context survives
  • • Model + effort persisted on the session row, not the agent
  • • Works on every chat surface — agent, cluster, actions, dashboard, flow
One graph, every surface

Every primitive composes with every other.

Enroll an SSH host once. It's now callable from a Quick Action button, a chat agent's ssh-command, a flow's ssh-command node, and a sub-flow. Same for integrations. Same for MCP tools. The graph is the product.

🖥️

SSH hosts

Enrolled once via OTP, stored as an ed25519 key. Reachable from quick actions, agents, flow ssh-command nodes, dashboard widgets — and any custom MCP tool you write.

🔌

Integrations

UniFi, Hue, P1 — and anything you wire up — register typed capabilities. Quick actions, agents and flows dispatch the same verb. integration-command is a node type.

🧰

MCP tools

Eight ship by default; bring your own via Claude CLI's --mcp-config. Agents pick which ones to expose; flows can call any of them through an agent node.

🎯

Quick Actions

One-click buttons that run flows, fire integration commands, or spawn an agent. Every quick action is a flow trigger — same plumbing, different surface.

💬

Chat agents

Claude sessions with the SSH hosts, integrations, MCP tools and skills you selected for that agent. Drop the same agent into a flow as an agent node and it brings its toolkit with it.

🪢

Flows & sub-flows

A flow can call another flow as a node — composition without copy-paste. Build a vacuum-journald flow once, then call it from disk-pressure, weekly-cleanup, and post-deploy.

One SshHostEntity, one IntegrationEntity, one MCP server — referenced from every surface. No copies, no drift, no "which version is the real one."

/agents/disk-pressure · chat
<channel source="web-chat" user="brian">
Why is worker-02 paging?
/var/log on worker-02 is at 89%. Top offender: journald at 4.2 GB. I'll vacuum to 200 MB — OK?
reply routed back via the same web-chat channel
Channels

MCP-native inboxes.

Every inbound channel is an MCP server. Drop a transport in your config, ChannelDesk registers its reply tool, and Claude can now talk back to that channel — no glue code, no custom webhooks.

  • • Messages arrive tagged with <channel source=…>
  • • Responses are always sent via the channel's reply tool — never stdout
  • • Works with any MCP-capable transport you write
Approval gates

Allow. Deny. Read first.

Every write tool — cluster_cordon, posture_fix_sshd_directive, anything with side effects — fires a permission relay. The chat dock pops an Allow / Deny prompt with the exact tool, the exact arguments, and the agent's reasoning. The tool does not run until you click Allow.

  • • Read tools (kubectl_get, memory_list) skip the gate — the agent only asks before it acts
  • • Permission requests route to the chat surface that triggered the agent
  • • Mobile push relays the prompt to your phone — Allow / Deny from the kitchen
  • • Every approval lands in /activity with source = agent, distinguishable from human actions
/cluster · cluster_cordon awaiting approval
worker-02 is at 91 % disk on /var/log. I want to cordon it so nothing new schedules there while we vacuum journald.
🔒 cluster_cordon  ·  args: { "node": "worker-02" }
Allow Deny Read first →
Skills & MCP, per-agent

You decide what each agent can touch.

Every agent has its own selected set of skills and MCP tools. A skill is a folder (SKILL.md + supporting files) that ships into the session via Claude CLI's --add-dir. An MCP tool is a server Claude can call. Both are checkboxes on the agent — flip one on, it's available next session; flip it off, it's gone.

  • Author skills by chat — the /skills page has a chat dock backed by the skill-author MCP. Describe what you want; Claude drafts the skill, validates it, and fills in the form.
  • Per-agent toolboxskillIds[], allowedTools, sshHostIds[], integrationIds[] all live on the agent. Different agents get different surfaces.
  • Bring your own MCP — drop a binary in the config and it's a checkbox like the rest. No fork required.
  • Skills travel with the agent — call that agent from a flow node and it brings its skills + MCP set with it.
/skills/new · authoring a skill via chat
I want a skill that pushes a branch to GitHub and opens a PR — should refuse to push to main.
Drafted github-commit-push. Frontmatter:
name: github-commit-push · whenToUse: "Commit local changes and push to GitHub" · allowedTools: Bash
Body covers preflight (verify github remote), staging by name (no git add -A), commit style match, and a force-push-to-main refusal. Validated with validate_skill_draft. Ready to save?
Saves to skills table · attach to any agent with one click.
Quick Actions

Your runbook, as buttons.

Per-host catalogs of one-click commands — k3s, Proxmox, NAS, NPM registry, network. Each button is also an MCP tool, so Claude can run the same actions from inside a session and a human operator clicks them from the desk.

  • • Color-coded by namespace, grouped by host
  • • Inline chat dock — ask Claude on the same page
  • • Versioned, parameterized, sharable across operators
/actions
ChannelDesk Quick Actions with chat dock expanded — categorized buttons (Dev / K3s / NAS / NPM / Network / Proxmox) and recent chat threads
/cluster
ChannelDesk Cluster control — node cards with CPU/Mem and Wake/Shutdown buttons
Cluster control

Wake, sleep, restart — your nodes, your call.

Talks Wake-on-LAN to spare workers, ssh to masters, and the k8s API to everything else. Pull up live CPU/Mem on every node, see the 30-min event rollup, and walk a worker into & out of the pool from your phone.

  • • Card or grid view across multiple clusters
  • • Per-node label badges (ci-runner, dev-worker, …)
  • • Drill into a single namespace or see the full service map
Security posture

Hardening checks, on a cron.

A scheduled audit runs across hosts and the cluster — SSH config, kubelet baseline, cert expiry, listening ports, backup recency. One score, a 6-run trend line, and a per-check table you can filter by category and severity.

  • • Host hardening · Kubernetes posture · Certs & secrets · Exposure · Backup & recovery
  • • Re-run on demand from the UI, or wire it into a flow
/cluster/posture
ChannelDesk Security Posture — score 57, 6-run trend chart, category breakdown and per-check audit table

And a lot more

Settings service

Per-user and per-flow settings with model validation against a CLAUDE_MODELS allow-list — the UI always has live values, no restarts.

Flow Engine v2

Triggers, conditions, LLM steps and device actions. Manual, cron, webhook and event triggers — composed visually, no YAML.
Learn more →

Capability-based integration graph

Every device, MCP server, agent, action and SSH host registers a capability — a typed verb that flows and chat can dispatch. Sync adapters per integration type (UniFi, Philips Hue, custom).
Learn more →

Command adapters

Integration-agnostic device control. Write once, dispatch to any adapter that implements the capability.

Optimistic UI

Room-centric views update immediately and reconcile with the real state. No "waiting for server" spinners.

PWA + offline shell

Installable on iOS/Android. Service worker keeps the shell cached; registered via app.config.ts.

Kaniko image builds

Ships with a GitHub Actions pipeline that builds images in-cluster via Kaniko. Zero external CI dependency.

Observable

Prometheus metrics, structured logs, Grafana-friendly. Plays nicely with kube-prometheus-stack.

Activity audit

Every flow run, cluster action and Claude session lands in /activity with status pills you can filter on. Same trail powers the per-page chat dock.

Multi-cluster

Switch between clusters from the sidebar; each integration scopes to the active cluster by default, or lift to shared with one toggle.

Wake-on-LAN built in

ChannelDesk speaks WoL natively — schedule a worker to sleep at midnight and wake the moment a flow needs it.
Positioning

If you've used n8n.

n8n composes API calls. ChannelDesk composes Claude sessions, automations, and your homelab's machines. Same category, different scope.

n8n

  • • Wire HTTP nodes, transform JSON, send notifications
  • • If you want intelligence in a step, you call an LLM API and parse the JSON yourself
  • • Workflows over APIs and webhooks
  • • Excellent at integrating SaaS

ChannelDesk

  • • Spawns real claude-code subprocesses as first-class flow nodes
  • • Each agent has full filesystem, git, SSH and per-agent MCP/skill access
  • • Approval gates and the chat dock are wired into the same surface that triggers the work
  • • Excellent at driving your homelab's actual machines

Short version: n8n with a Claude agent on every node — and your homelab's machines hanging off it.

Self-host ChannelDesk in 10 minutes

Clone the repo, drop your SSH keys, and chat to your fleet from your own box. No SaaS account, no metered bills, no telemetry.