OpenDots
Self-hosted AI coworkers with shared documents, chat and calls
Deploy NowREADME
Self-hosted AI coworkers with shared documents, chat and calls.
Overview
OpenDots is a personal-agent workspace from the CopilotKit team. A Space is a home for working documents: a searchable library of pages you edit in a focused editor with formatting, slash commands and autosave. A Dot is a specialist agent with a name, a role, instructions and a set of permitted tools, which you talk to in text or on a call, can give MCP connections to, and can ask to draft a page that you approve before it is saved. It is built with CopilotKit and AG-UI, and upstream describes it as an alpha template rather than a finished product.
Upstream publishes no container image: the repository ships a Dockerfile but nothing builds or
pushes it, and there is no tag or release. This template builds the app target of that Dockerfile
from a pinned commit (see ./Dockerfile). It is the upstream application behind an HTTPS face, not
a reimplementation.
Two parts of upstream are deliberately not here, and both are additions rather than repairs:
- The isolated browser worker (upstream's
browserDockerfile target, a Playwright Chromium service used byWEB_SEARCH_PROVIDER=browser). A template directory builds one image, and the defaultparallelsearch provider needs no second service. - Dot computers. Each Dot's own computer is an OpenBot
container supervisor that creates and
execs into containers, which needs a Docker engine a compute machine does not provide.COMPUTER_SUPERVISOR_URLis left unset, so the Computer panel stays unconfigured.
What you get by hosting it
- The web app behind one HTTPS URL, with a single owner token in front of its whole API.
- Spaces and pages: a document workspace with nested subpages, grid and list views, a visual editor, Markdown source mode and autosave. This half works with no third-party key at all.
- Dots: specialist agents with per-Dot instructions and tool permissions, once you supply an Intelligence key and a model.
- Optional Slack: mention a Dot through a managed CopilotKit Channels connection, with an explicit workspace and user allowlist.
- A persistent volume at
/dataholding the SQLite database: Spaces, pages, Dots, settings, memories and scheduled tasks, so a restart keeps your work. Conversation history lives in CopilotKit Threads rather than on the volume. - The scheduled-task runner in-process, firing saved tasks on their own interval, which is why the service is always-on.
What you need before deploying
- An owner access token of your choosing, at least 24 characters. OpenDots' sign-in screen asks for it; it is the app's only password, and the server refuses to start with a shorter one.
- For conversations, calls and scheduled tasks: a CopilotKit Intelligence project key, plus an
OpenAI-compatible API key and a model id. Run
npx copilotkit@latest loginand thennpx copilotkit@latest project selectfor the first. Without all three the app still opens and the document side works; it reports the missing ones on its own setup screen. - For calls: a realtime speech provider key and model, on top of the three above.
- For Slack: a managed Channels declaration name, your Slack workspace id, and the Slack user ids allowed to mention your Dots. See upstream's Slack setup.
Configuration
| Variable | Required | What it does |
|---|---|---|
OWNER_TOKEN | yes | The owner access token OpenDots' sign-in screen asks for, at least 24 characters. It is the whole of the app's auth: every /api/* call is compared against it timing-safely, and the server refuses to start with a shorter one. You type it into the app. |
CPK_INTELLIGENCE_API_KEY | no | CopilotKit Intelligence project key, from npx copilotkit@latest project select. Conversations, threads, calls and Slack need it. Server-only; the browser never sees it. |
OPENAI_API_KEY | no | Key for the OpenAI-compatible provider the Dots run on. |
OPENAI_MODEL | no | Model id the Dots run, for example gpt-5. The app reports this and OPENAI_API_KEY as missing until both are set. |
OPENAI_BASE_URL | no | OpenAI-compatible endpoint when the provider is not OpenAI itself. Defaults to https://api.openai.com/v1. |
WEB_SEARCH_PROVIDER | no | parallel (the default, anonymous MCP for light use), browser (needs a browser service this template does not deploy), or disabled. |
PARALLEL_API_KEY | no | Bearer key for Parallel, for production rate limits. |
VOICE_API_KEY / VOICE_MODEL / VOICE_NAME | no | Realtime speech for calls. Calls stay off until the key and the model are both set. VOICE_NAME defaults to marin. |
SLACK_CHANNEL_NAME / SLACK_TEAM_ID / SLACK_USER_IDS / SLACK_DOT_ID | no | Managed Slack connection and its allowlist. SLACK_CHANNEL_NAME is the Channels declaration name, not a Slack #channel name. |
COPILOTKIT_TELEMETRY_DISABLED | no | Set to true to switch off the CopilotKit SDK's usage telemetry, which is on by default and is separate from conversation data. |
Set by the template, not by you: HOST=0.0.0.0 (the platform proxy reaches the service over the
network, which is also what makes OWNER_TOKEN mandatory upstream),
DATABASE_PATH=/data/opendots.sqlite, OWNER_ID=opendots-owner, and APP_ORIGIN resolved to the
service's own HTTPS URL. That last one matters: left unset, the server falls back to the origin it
reconstructs from the request, which behind the platform's TLS terminator is the http:// form of
the same host, and every browser call would be refused as cross-origin.
The service is always-on. The scheduled-task runner fires from inside the process, and no inbound request would wake a stopped machine for it.
After deploy
- Open the service URL. The sign-in screen asks for the owner access token; paste
OWNER_TOKEN. - The workspace opens. Pick a Space, create a page, and write in it. Autosave reports its progress, and the page is on the volume, so it survives a restart.
- If you set the Intelligence key, the model key and the model id, the Dots answer: open a Dot, ask it something, and it can draft a page for you to approve before it is saved. Without them the app's setup screen names exactly which of the three is missing.
- For Slack, add the Channels declaration name, your workspace id and the user ids allowed to mention a Dot, then restart the service.
Links
- Architectures:
linux/amd64only. Nothing in the build is architecture-specific, but the arm64 leg has not been run; see the architectures table in the registry README. - Upstream: https://github.com/CopilotKit/OpenDots, built from commit
625452e06cde74cb25b0ce319e2c1be0488f5a5f. - Image:
ghcr.io/insforge/insta-oss/templates/opendots, built from./Dockerfilein this directory. - License: MIT (upstream
CopilotKit/OpenDots).
Services & Specs
- Image
- ghcr.io/insforge/insta-oss/templates/opendots:0.1.0
- Port
- 4310
- Healthcheck
- /
Variables
You supply 1 variable before the first deploy.
Required
OWNER_TOKENOwner access token, at least 24 characters. You paste this into the OpenDots sign-in screen; it is the app's only password, and the server refuses to start with a shorter one
Optional (14)
CPK_INTELLIGENCE_API_KEYCopilotKit Intelligence project key, for conversations, threads and calls. Run `npx copilotkit@latest login` then `npx copilotkit@latest project select`. Without it the app opens in setup state and only the document side works
OPENAI_API_KEYKey for the OpenAI-compatible provider the Dots run on. Needed together with OPENAI_MODEL before any Dot will answer
OPENAI_MODELModel id the Dots run, for example gpt-5. Needed together with OPENAI_API_KEY; the app reports both as missing until they are set
OPENAI_BASE_URLOpenAI-compatible endpoint, when the provider is not OpenAI itself. Defaults to https://api.openai.com/v1
WEB_SEARCH_PROVIDERPublic-web search for the Dots: `parallel` (the default, anonymous MCP unless PARALLEL_API_KEY is set), `browser` (needs a browser service this template does not deploy), or `disabled`
PARALLEL_API_KEYBearer key for Parallel, for production rate limits. The default provider works anonymously for light use
VOICE_API_KEYRealtime speech provider key, for calls. Calls also need the Intelligence key above
VOICE_MODELRealtime speech model id. Calls stay off until this and VOICE_API_KEY are both set
VOICE_NAMERealtime speech voice. Defaults to marin
SLACK_CHANNEL_NAMEManaged CopilotKit Channels declaration name for Slack, not a Slack #channel name. Slack needs this plus SLACK_TEAM_ID and SLACK_USER_IDS
SLACK_TEAM_IDSlack workspace id allowed to mention your Dots
SLACK_USER_IDSComma-separated Slack user ids allowed to mention your Dots. The integration answers nobody else
SLACK_DOT_IDWhich Dot answers in Slack. Defaults to the initial Dot
COPILOTKIT_TELEMETRY_DISABLEDSet to true to switch off the CopilotKit SDK's usage telemetry, which is on by default and is separate from conversation data