Skip to content

Security model

The agent opens and maintains a WebSocket connection to the control plane (/agent/ws) with bearer credentials. The control plane never connects to the machine, so no inbound ports are required and the machine can stay behind NAT. During enroll, a single-use token creates agent credentials on the machine, stored in /etc/nythronix/agent.json (on Windows, %ProgramData%\Nythronix\agent.json) with permissions 0600.

AI workers and the supervisor follow the same model: workers call the control plane over HTTP with narrowly scoped tokens, and the supervisor polls it with no inbound ports.

End-to-end SSH encryption through the relay

Section titled “End-to-end SSH encryption through the relay”

The relay lets agent A reach agent B’s loopback port through the control plane, for example sshd at 127.0.0.1:22. The control plane only copies bytes between two WebSocket connections. SSH itself handles authentication and encryption between the client and sshd, so the control plane cannot read session contents. Relay is disabled by default and enabled per machine in Agents. Only agents in the same organization can connect.

Unprivileged execution: nyx-worker and just-in-time sudo

Section titled “Unprivileged execution: nyx-worker and just-in-time sudo”

The agent runs worker commands (cmd.exec) through runuser as the unprivileged nyx-worker user, provided the agent runs as root and that user exists. Otherwise, commands run under the agent’s UID with a warning. Agents shows the actual execution user and whether Docker socket access exists. Windows does not yet have this privilege-reduction model.

When a worker needs root, an administrator grants it temporarily using Grant root, from 15 minutes to 8 hours. The control plane sends a policy; the agent renders /etc/sudoers.d/nyx-worker, validates it with visudo -cf, and replaces it atomically. The server revokes the grant at expiry. A local agent timer also removes the file if the control plane is unavailable. Policies are none, list (specified commands), and all. Every change is audited as admin.agent.sudo_policy_set.

The server uses a positive allowlist to decide whether a command can run without a human. Every pipeline stage must be a recognized read command (ls, cat, grep, ps, df, journalctl, systemctl status, docker ps/logs, kubectl get, git log, and similar), without redirection, ;, &, $(...), or backticks. SQL clients may use inline queries with psql -c / mysql -e if they contain no modifying keywords. Everything outside the list, including obfuscated commands, docker exec, ssh, and secret reads (.env, keys, /etc/shadow, env), requires channel approval. Workers cannot bypass this check: it runs on the server before forwarding to the agent.

The machine owner can restrict control plane actions independently of dashboard settings. The agent reads /etc/nythronix/agent-policy.json at startup; the control plane cannot see or modify it:

{
"allow_remote_sudo": false,
"max_sudo_policy": "list",
"terminal_login_lock": "svc-ops"
}
  • allow_remote_sudo: false: reject root grants from the dashboard; revocation is always allowed.
  • max_sudo_policy: cap incoming policies at none < list < all.
  • terminal_login_lock: always open operator terminals as this local user.

A missing file means no local restrictions; a malformed file denies remote sudo. The same principle applies to skills: the agent starts only MCP servers listed by the owner in /etc/nythronix/mcp-catalog.json. See Skills through MCP.

Agent, NX endpoint, and CLI releases are signed with Ed25519 on the build machine. The public key is registered on the control plane and pinned in agent configuration, automatically during enroll or manually with NYTHRONIX_TRUSTED_KEYS. On an update offer, the agent downloads the bundle, checks SHA-256, and verifies the manifest signature against pinned keys. A mismatch rejects the update. Without trusted keys, only SHA-256 is checked and a warning is logged. With signature verification in place, even a compromised control plane cannot substitute a foreign binary. See Administration for publishing details.

A modifying command runs only after an operator authorized for its category clicks Approve. Approval becomes a single-use token bound to the exact command and machine, valid for 10 minutes and verified before execution. Tokens exist only in process memory; a control plane restart requires fresh approval. A worker’s narrow token permits only its channels, memory, heartbeats, and cmd-exec within its organization. Administrative and foreign endpoints return 401.

Each machine command is audited as agent.cmd_exec before forwarding, each executed MCP tool as agent.mcp.call, and each operator decision as chat.approval.decided. See Workers and channels.

The supervisor launches workers with a fixed specification: --cap-drop ALL, no-new-privileges, a read-only root filesystem with a small tmpfs, memory and CPU limits, and no host mounts. It injects LLM keys from its own configuration; they are not on the control plane. An optional isolated network (NYX_WORKER_NETWORK + deploy/host/worker-egress.sh) uses SNI filtering to allow only control plane and LLM traffic. Skill MCP servers run as separate nyx-worker subprocesses, with optional domain-based outbound restrictions.

Stored: operator accounts (Argon2 hashes with pepper), hashed agent and NX endpoint credentials, desired state (sudo policies, skills, update policies), channels, messages and worker memory, releases and public signing keys, audit logs, and beta requests.

Not stored: LLM keys (held by the supervisor), the private release signing key (on the build machine), the owner’s local policy and skill launch catalog (on the machine), SSH relay session contents, or approval tokens after a process restart. In SaaS, NX endpoints are unavailable and their APIs return 404.