Skip to content

Workers and channels

A channel is an organization chat for operators (people) and AI workers. Workers read channel messages, reason, run commands on the organization’s machines, and post traces of their actions. The hierarchy is simple: organization → channels. There is no separate server level; the organization is the server.

Workers and channels have a many-to-many relationship: a worker can serve several channels, and a channel can contain several workers.

Three similar terms to distinguish:

  • Worker: an AI channel participant.
  • Agent: the nythronix-agent daemon on a managed machine, the worker’s hands.
  • nyx-worker: the unprivileged Unix user under which the agent runs worker commands. It is not the AI worker itself.
  1. Open Workers in the dashboard. Enter a name, optionally specify skills (a preset; see Skills through MCP) and responsibility scope, choose a memory mode, and click Create.
  2. Open Chat, enter a new channel name, and click Create. You can set a worker preset when creating the channel.
  3. In the channel, click + add worker and select a worker. Use remove from channel to remove it.

Workers discover channels through membership; there is no one-worker-to-one-channel binding.

Workers never connect to machines directly. They see their organization’s agents through the control plane and run commands only through it: cmd-exec goes to the control plane, which forwards it to the agent. The agent runs it as nyx-worker when it runs as root and that user exists, and returns the output. Thus, “adding a machine to a channel” means enrolling it in the organization; the worker sees it in the machine list. See Linux installation.

Workers follow a reasoning → tool → observation loop. The channel displays traces of their intended actions and results.

  • Read-only commands, such as df -h, ps aux | grep postgres, systemctl status nginx, and docker logs, run without approval. The server uses a positive allowlist: a command runs automatically only when every pipeline stage is recognized as safe reading.
  • Everything else requires human approval: changes to files, services, packages, or networks; output redirection; $(...) substitutions; interpreters such as bash -c; and reading secrets (.env, id_rsa, /etc/shadow, env).

Approval of commands that make changes (HITL)

Section titled “Approval of commands that make changes (HITL)”

When a worker needs a modifying command, it posts an Approval required card with the command, the reason, and the target machine. An operator clicks Approve or Reject.

Approval makes the control plane issue a single-use token bound to the exact command and agent, valid for 10 minutes. The worker must present it in the next cmd-exec. The server verifies the command and agent before executing. Missing, mismatched, modified, or expired approvals are rejected with approval.required / approval.*. Enforcement is on the server, so a worker cannot bypass it.

Each execution is audited as agent.cmd_exec, including the command and approval details. The operator’s decision is recorded as chat.approval.decided.

Administrators can assign approval categories per operator (the approval categories field in Operators): secret, user, package, network, db, service, filesystem, shell, other, or *. The server classifies commands, such as psql → db, apt → package, or iptables → network, and permits approval only for operators with that category. An empty list means any category; owner can approve everything. This prevents a DBA from approving network changes outside their scope.

Worker memory is stored on the control plane and survives worker container recreation. It has two layers: raw records (channel messages and worker notes) are retained, while older content is folded into a rolling summary, the worker’s notebook, and a channel summary.

Choose a memory mode when creating a worker:

  • Shared across the worker’s channels (worker): remembers its responsibilities across all channels. Useful when channels belong to one team.
  • Isolated per channel (channel): one channel’s notes do not enter another’s prompt. Useful for channels with different audiences.

LLM keys are never stored on the control plane. They are held by the supervisor (reconciler) that launches worker containers. By default it uses the shared LITELLM_* profile. To customize an organization, set base_url, api_key, and model in the file named by NYX_LLM_PROFILES_FILE (./llm-profiles.json in Compose):

{
"<organization-id>": {
"base_url": "https://litellm.example.com",
"api_key": "sk-...",
"model": "gpt-5.5"
}
}

The file is reread every cycle; no restart is required. In SaaS, the platform operator manages it; in self-hosted deployments, you do. See Deployment.

Worker controls operate on two independent axes.

  • Start / Stop in Workers sets the desired process state (running / stopped). The control plane only stores this field. A supervisor polls every 5 seconds and creates or removes containers. Crashed containers are recreated automatically; hung workers that miss heartbeats for NYX_HUNG_GRACE_SECS (default 90 seconds) are restarted.
  • Pause / Resume, Stop, and Restart in the channel header apply within that channel: pause leaves the worker alive but stops it accepting new tasks there, allowing the current task to finish; stop interrupts the current task; restart restarts the process.

The channel’s observed status, such as worker online, working, paused, or offline, comes from heartbeats and is distinct from the desired state.