showcase/shell-docs/src/content/docs/channels/deploy-and-operate.mdx
A managed Channel is a long-running worker with an outbound realtime connection. It is not a serverless request handler. Deploy it like a queue consumer or WebSocket worker.
npm install --save-exact @copilotkit/[email protected] @copilotkit/[email protected]
Run Node.js 22 or later. The managed launcher relies on the global WebSocket
available in Node.js 22+.
Upgrade Channels and Runtime together, then run the same real-provider smoke test used for the initial quickstart.
Creating the Node listener starts the managed connection — a declared Channel
connects because it was declared, with no start call to forget. Await ready()
to observe that activation settling, then inspect status():
const channels = listener.channels;
await channels.ready({ timeoutMs: 30_000 });
const initial = channels.status();
if (initial.overall !== "online") {
throw new Error(`Channels not online: ${JSON.stringify(initial)}`);
}
ready() can resolve in setup_required; it means activation settled, not
that delivery is healthy. Treat only status().overall === "online" as a
healthy connected activation.
The runtime status values are:
| Runtime status | Meaning |
|---|---|
connecting | Activation has not settled |
online | The managed connection is healthy and can claim delivery invitations |
setup_required | The Intelligence provider setup is incomplete |
reconnecting | The gateway connection dropped and is retrying |
error | Activation failed, or reconnect give-up (~60s default) marked the session not-sendable |
stopped | The Channels control was stopped |
Treat post-online error carefully: after reconnect give-up the client marks
error while Phoenix keeps retrying underneath. A successful rejoin
restores online without a process restart. Activation-time error (bad key,
unreachable gateway, config) is different — fix configuration and restart the
worker.
The Intelligence UI translates provider and runtime details into its own operator-facing states such as Waiting for runtime, Conflict, Offline, Delivery failing, and Online.
channels.status().overall is online.Do not restart immediately for a short reconnecting episode; the realtime
client owns bounded reconnect and rejoin (default reconnect give-up window is
about 60 seconds, then status becomes error while retries continue). Alert on
error; restart only when your own outage budget is exceeded or activation
failed before ever going online.
Poll status() from your health endpoint or monitoring loop. Do not cache the
startup result forever because an online session can later become reconnecting
or error.
Register signal handlers before creating the listener, not just before
ready(). Creating it begins activation, so a signal that arrives during the
connect window must already have somewhere to land — otherwise it hits Node's
default handler and leaves a live gateway session behind:
let teardown: (() => Promise<void>) | undefined;
const shutdown = async () => {
await teardown?.();
};
process.once("SIGINT", shutdown);
process.once("SIGTERM", shutdown);
const listener = createCopilotNodeListener({ runtime });
const server = createServer(listener);
teardown = async () => {
await listener.channels.stop();
if (server.listening) server.close();
};
await listener.channels.ready({ timeoutMs: 30_000 });
Give the worker enough termination grace to stop its managed sessions. Avoid
cutting power before channels.stop() has released ownership when a graceful
shutdown is possible.
Keep these server-side:
INTELLIGENCE_API_KEY=<project-runtime-key>
CHANNEL_CODE=<exact-intelligence-code>
# Optional paired overrides for self-hosted or non-production Intelligence:
# INTELLIGENCE_API_URL=https://intelligence.example.com
# INTELLIGENCE_GATEWAY_WS_URL=wss://realtime.intelligence.example.com
Hosted Intelligence supplies the managed endpoints by default. For a
self-hosted or non-production deployment, override both bases together. Do not
derive one from the other or append /api, /socket, /runner, /client, or
/channels.
Provider credentials stay in Intelligence. The runner should not contain Slack tokens or the Teams client secret. Rotate the project runtime key through your secret manager, restart the listener with the new value, and verify it returns to Online.
Intelligence offers each prepared delivery to connected Runtimes that declare the matching Channel. One Runtime claims that delivery; other replicas can claim different deliveries at the same time.
Run the same build and Channel declarations on every replica. Set
maxConcurrentDeliveries to a bound each process can sustain. A full Runtime
declines an invitation before it claims the delivery, which leaves another
eligible replica free to claim it.
At minimum, record:
reconnecting, error, and back to onlinemessage.eventId, message.turnId, and message.deliveryId for correlationthread.postFile() failuresDo not log message bodies, hydrated files, API keys, Slack tokens, Teams secrets, or raw provider payloads by default.
online.reconnecting recovery.SIGTERM and confirm graceful shutdown.Use the Intelligence walkthrough for control-plane statuses and persistence and scaling for restart-safe application state.