{"slug": "show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents", "title": "Show HN: TaskHandoff – Self-hosted control plane for containerized AI agents", "summary": "TaskHandoff launched as a self-hosted control plane for running, managing, and collaborating with containerized AI agents across local and remote machines, with Docker as its primary isolated runtime. The open-source project, currently in beta and warning that breaking changes may land between releases, connects Codex and other AI development sessions through three layers — a Control Plane for Web/API management, a Node Agent on each managed machine, and Controlled Instances hosting workspaces and AI sessions. TaskHandoff routes messages and approvals from Telegram, DingTalk, WeChat, and Feishu/Lark to selected instances, offers iOS and Android clients, and deploys as a desktop or mobile application or as systemd services on Debian and Ubuntu.", "body_md": "**A unified control plane for running, managing, and collaborating with AI workspaces across local and remote machines**\n\n**English** | [简体中文](https://github.com/edgestorage/task-handoff/blob/main/README_CN.md)\n\nTaskHandoff brings Codex and other AI development work into one control plane. It connects AI sessions spread across machines, workspaces, and chat platforms while managing node enrollment, instance lifecycles, sessions, applications, and message routing.\n\n**Beta:** Task Handoff is under active development. Breaking changes may land between releases.\n\nLight mode:\n\nDark mode, with a Story session and a browser running in the container:\n\nFor more interface screenshots, see the [Interface and Workbench guide](https://docs.thandoff.com/en/guide/workbench).\n\n- **Multi-node management** — Connect local and remote nodes and inspect their resources and managed instances from one place.\n- **Managed workspaces** — Create, start, stop, and restore isolated workspaces, with Docker as the primary runtime today.\n- **Image market and custom images** — Choose from a read-only built-in catalog or separately managed custom images through one instance creation flow.\n- **Environment templates** — Save a Docker instance's installed tools and container configuration as a node-local reusable environment, then combine it with any project or local-folder workspace.\n- **AI session center** — View and control sessions across instances with real-time state delivered over WebSocket.\n- **Repository workflows** — Inspect files, changes, branches, and worktrees, with conservative remote delivery for Git repositories.\n- **Managed Git credentials** — Scope HTTPS tokens or pinned SSH keys to remotes, use them for one-time provisioning, or retain them for Agent, Terminal, App, and Repository Git commands.\n- **Chat integrations** — Route messages, approvals, and actions from Telegram, DingTalk, WeChat, and Feishu/Lark to a selected instance.\n- **Application management** — Install, remove, and run applications on target instances through a trusted built-in catalog.\n- **Mobile client** — Connect an iOS or Android device directly to a user-managed Control Plane for AI sessions, instance operations, applications, and terminals.\n- **Desktop and server deployment** — Run TaskHandoff as a mobile or desktop application, or as systemd services on Debian and Ubuntu.\n- **English and Chinese UI** — Switch languages instantly or follow the browser language automatically.\n\n```\nBrowser / Desktop / Mobile / Chat platforms\n                 │\n                 ▼\n          Control Plane\n       UI, API, and chat gateway\n                 │\n                 ▼\n            Node Agent\n   Node resources and instance lifecycle\n                 │\n                 ▼\n       Controlled Instance\n Workspace, applications, and AI sessions\n```\n\nTaskHandoff is organized into three runtime layers:\n\n- **Control Plane** provides the Web/API management surface and owns the node inventory, instance board, chat gateway, and cross-instance AI session views.\n- **Node Agent** runs on each managed machine and owns node-local configuration, runtime resources, folder inventory, and controlled instance lifecycles. Instances continue running when the control plane is stopped or restarted.\n- **Controlled Instance** hosts a workspace, applications, AI sessions, triggers, and metadata. It can run standalone; in a managed deployment, its lifecycle and access are owned by the Node Agent.\n\nChat and AI Session state form a cross-layer path: the Control Plane owns chat credentials, bindings, command parsing, and routing, while each target AI Session remains the source of truth for conversation state.\n\nDocker is the primary isolated runtime and supports multiple instances on one node. A built-in Local Runtime is also available on supported non-Windows nodes for one controlled instance per host user. Runtime capabilities and adapters keep the same model extensible to Kubernetes without creating a separate UI flow.\n\nAn environment template is a node-local Docker image created from an existing instance with `docker commit`. Registry images and environment templates are peer environment sources in the instance creation flow. Workspace selection remains independent, so either source can be combined with a Git project or a local-folder workspace.\n\nTemplates capture only the container writable layer, such as installed system packages and tools. They exclude `/workspace`, `/data`, `/home/agent`, every other bind mount or volume, memory, processes, and network state. Derived instances always receive a new identity, registration token, port, and managed volumes. The node agent briefly pauses the source container during commit and rejects a template if Docker Config contains instance-private credentials.\n\nEvery Docker instance has managed volumes for `/data` and `/home/agent`; Git workspaces also have a managed `/workspace` volume, while local folders use an external bind mount. The instance deletion dialog uses one option, selected by default, to delete all managed data. Clearing it retains every managed volume and reports its name; retained volumes are never attached automatically to another instance.\n\nThe source node owns both the template record and its Docker image, so a template can be used only on that node while it is ready. Deleting a template removes its internal template tag. A content-addressed internal lease keeps the image recoverable while derived instances reference it, and the image is garbage-collected after the final reference is removed.\n\n- Node.js `>= 24.15.0 < 25`\n- pnpm `9.15.3`\n- Docker, when using Docker Runtime, building container images, or running the standalone Compose profile\n\n```\npnpm install\npnpm run build:all\npnpm cli help\n```\n\nStart the Control Plane API and development UI in separate terminals. The disabled authentication mode is intended only for loopback development:\n\n```\npnpm cli control-plane --auth-mode disabled\npnpm run control-plane-ui:dev\n```\n\nCommon development commands:\n\n```\n# Start the control-plane UI\npnpm run control-plane-ui:dev\n\n# Type-check and build\npnpm run typecheck\npnpm run web:typecheck\npnpm run build:all\n\n# Run tests\npnpm test\n\n# Inspect the npm package contents\npnpm run pack:dry\n```\n\nTo run a standalone Browser-profile controlled instance instead of the Control Plane development stack:\n\n```\ndocker compose up -d --build\n```\n\nThe current directory is mounted at `/workspace` by default. Set `TASK_HANDOFF_WORKSPACE_HOST` to mount a different host directory. This Compose service is a standalone controlled instance, not a Control Plane and Node Agent deployment.\n\nServer deployments install the Control Plane and the server-local Node Agent as independent systemd services. The control plane can stop or restart without terminating instances managed by the agent.\n\nOn a Debian or Ubuntu server running systemd, run the latest stable installer as root:\n\n```\ncurl -fsSL https://github.com/edgestorage/task-handoff/releases/latest/download/install-server.sh | sudo sh\n```\n\nThe script checks the host, installs Node.js 24 and Docker when needed, installs the latest stable `@task-handoff/server` package from npm, and then creates and starts the Control Plane and Node Agent systemd services. The default `auto` source profile uses Tsinghua APT mirrors and npmmirror for Chinese locale or timezone environments, and also falls back to those mirrors when the official Node.js source is unreachable. Its temporary APT source list does not overwrite the host's source configuration. By default, the control plane listens on port `8081` with password authentication enabled. Installer options can change the port, authentication mode, release channel, and other service settings.\n\nUse `--install-source china` or `--install-source official` to select a source profile explicitly:\n\n```\ncurl -fsSL https://github.com/edgestorage/task-handoff/releases/latest/download/install-server.sh | sudo sh -s -- --install-source china\nsudo npm install -g @task-handoff/server@latest\nsudo task-handoff install\n```\n\nManage services and updates:\n\n```\nsudo task-handoff start\nsudo task-handoff stop\nsudo task-handoff restart\n\ntask-handoff check\nsudo task-handoff update\n```\n\nThe installation creates:\n\n```\ntask-handoff-node-agent.service\ntask-handoff-control-plane.service\n```\n\nSee [`scripts/install-server.sh`](https://github.com/edgestorage/task-handoff/blob/main/scripts/install-server.sh) for supported installer options.\n\nRemote machines only need the Node Agent. Generate a one-time join token in the Control Plane and prefer the exact installation command shown there. Its package version is resolved from the running Control Plane release. The equivalent form is:\n\n```\ncurl -fsSL https://CONTROL_PLANE_HOST/install-node-agent.sh | sudo sh -s -- \\\n  --control-plane https://CONTROL_PLANE_HOST \\\n  --join-token JOIN_TOKEN \\\n  --npm-package @task-handoff/node-agent \\\n  --controlled-instance-package @task-handoff/controlled-instance \\\n  --version RELEASE_VERSION\n```\n\nOn Debian and Ubuntu, the remote-node installer bootstraps the required Node.js\n24 and npm on a fresh host. It uses the same automatic source selection; append\n`--install-source china` to force Chinese mirrors. The selected npm registry is\npreserved in the Node Agent service environment for subsequent managed updates.\n\nReplace `RELEASE_VERSION` with the Control Plane's runtime package version so the Node Agent and controlled-instance runtime use the same release.\n\nAn installed Node Agent can also generate a one-time invitation directly on the node:\n\n```\nsudo task-handoff-node-agent invite --ipc-path /run/task-handoff/node-agent.sock\n```\n\nAdd `--json` for automation-friendly output. Remote TCP access still requires an invitation and paired HMAC authentication.\n\nTo remove a standalone Node Agent installation:\n\n```\nsudo task-handoff-node-agent uninstall\n```\n\nThe command removes the systemd service and runtime packages, then asks whether to delete the Node Agent data directory. The default is No. Use `--keep-data` or `--delete-data` for non-interactive execution. Managed Docker volumes are preserved.\n\n`@task-handoff/server` provides the unified `task-handoff` command:\n\n```\ntask-handoff control-plane\ntask-handoff node-agent\ntask-handoff node-agent-invite\ntask-handoff web\ntask-handoff help\n```\n\nUse `pnpm cli help` during development. Chat adapters, bindings, AI session messages, queues, and approvals are managed by the control plane.\n\nThe control-plane UI supports English (`en-US`) and Simplified Chinese (` zh-CN`). Open **Settings → Appearance → Language** to follow the system language or choose a language explicitly. The interface updates without reloading control-plane data.\n\nThe preference is stored only in the current browser. Terminal output, logs, AI messages, repository content, and other user- or provider-supplied data are never translated.\n\n```\napps/cli/                         CLI entry point\napps/desktop-shell/               Electron desktop shell\napps/mobile/                      Expo iOS and Android client\npackages/control-plane/           Control plane, Node Agent, and chat gateway\npackages/control-plane-client/    Shared Control Plane API and realtime client\npackages/control-plane-ui/        Control-plane Vue UI\npackages/controlled-instance/     Controlled-instance HTTP/WebSocket API\npackages/controlled-instance-ui/  Frozen controlled-instance Vue UI\npackages/ai-session-runtime/      AI session runtime\npackages/app-runtime/             Managed application runtime and catalog\npackages/protocol/                Cross-component protocols and data models\npackages/core/                    Shared capabilities, diagnostics, and storage\npackages/web-theme/               Web theme and Markdown rendering\nscripts/                          Installation, build, and runtime scripts\n```\n\nA semantic version tag such as `v1.2.3` builds the controlled-instance runtime artifacts, publishes `@task-handoff/control-plane`, `@task-handoff/node-agent`, `@task-handoff/controlled-instance`, and `@task-handoff/server`, and attaches the installer and immutable artifacts to the GitHub Release. `alpha` and `beta` versions use their matching npm dist-tags; stable versions update `latest`.\n\nThe six public base images and their independent `docker-vX.Y.Z` release\nworkflow are maintained in the\n[TaskHandoff Images repository](https://github.com/edgestorage/task-handoff-images).\nThey contain system dependencies and developer tools, but not the\ncontrolled-instance runtime. Node Agent remains the authority for mounting the\nbootstrap bundle and installing the desired runtime artifact.\n\nThe Control Plane image market uses the bundled snapshot by default; once a\nverification key is configured it pulls its catalog from\n[https://images.thandoff.com/market/v1/catalog.json](https://images.thandoff.com/market/v1/catalog.json) and degrades through\nremote, local cache, then the bundled snapshot; the cache lives at\n`market/catalog-cache.json` inside the data directory. The issued catalog is\nauthoritative for the repositories it references: remote responses must pass\nschema validation and mandatory ed25519 verification, and remote loading stays\ndisabled while no public key is configured. Configuration:\n\n- `TASK_HANDOFF_MARKET_CATALOG_URL` overrides the catalog URL (official URL by\ndefault); set it to`off` /`0` to disable remote loading.\n- `TASK_HANDOFF_MARKET_REFRESH_INTERVAL` sets the refresh interval in seconds\n(six hours by default);`0` keeps manual refresh only.\n- `TASK_HANDOFF_MARKET_CATALOG_PUBLIC_KEY` is required to enable remote\nloading: the ed25519 key (PEM or base64 SPKI).`TASK_HANDOFF_MARKET_CATALOG_KEY_ID` optionally pins the key identifier carried by the catalog signature.\n- `TASK_HANDOFF_MARKET_ALLOWED_REPOSITORIES` is an optional comma-separated\nrepository allowlist (matched on repository path boundaries) for deployments\nthat want to restrict the catalog to their own registry.\n\nSemantic version tags build macOS arm64/x64, Windows arm64/x64, and Linux x64 installers and publish them to GitHub Releases. Versions with an `alpha` or `beta` suffix are marked as prereleases. macOS artifacts are signed, notarized, stapled, and verified with Gatekeeper. Windows code signing is not enabled yet.\n\nClosing the Desktop control-panel window keeps TaskHandoff running in the system tray. The tray shows the current Control Plane and Node Agent service status and can reopen the existing window without restarting either service. Choose **Quit TaskHandoff** from the tray or the platform application menu to stop the Desktop services. A graceful Node Agent shutdown stops Local Runtime controlled instances so they can be restored on the next launch; Docker Runtime controlled instances keep running and are rediscovered when the Node Agent returns.\n\nA stable tag in the exact form `mobile-vX.Y.Z` runs the mobile release checks and starts independent Android and iOS release jobs. GitHub-hosted Linux and macOS runners build and sign both native applications. Android attaches an APK to the corresponding GitHub Release; after approval through the `ios-production` environment, iOS uploads directly to App Store Connect/TestFlight. Final App Store review remains a manual action, and Android is not submitted to Google Play by this workflow.\n\nSee [`apps/mobile/README.md`](https://github.com/edgestorage/task-handoff/blob/main/apps/mobile/README.md) for the client boundary and development commands, and [`apps/mobile/RELEASE.md`](https://github.com/edgestorage/task-handoff/blob/main/apps/mobile/RELEASE.md) for credentials, first-build setup, and release operations.\n\nTaskHandoff is licensed under the [Apache License 2.0](https://github.com/edgestorage/task-handoff/blob/main/LICENSE). See [NOTICE](https://github.com/edgestorage/task-handoff/blob/main/NOTICE) for attribution information.", "url": "https://wpnews.pro/news/show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents", "canonical_source": "https://github.com/edgestorage/task-handoff", "published_at": "2026-10-09 17:09:06+00:00", "updated_at": "2026-10-09 17:24:57.356666+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-infrastructure", "mlops"], "entities": ["TaskHandoff", "Codex", "Docker", "Telegram", "DingTalk", "WeChat", "Feishu/Lark", "Debian"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents", "markdown": "https://wpnews.pro/news/show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents.md", "text": "https://wpnews.pro/news/show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents.txt", "jsonld": "https://wpnews.pro/news/show-hn-taskhandoff-self-hosted-control-plane-for-containerized-ai-agents.jsonld"}}