Network requirements
This page introduces the connection model: which side initiates the connection, which ports are involved, and what an agent must be able to reach. The short version β the agent dials the studio, and you do not have to open a single inbound port on a bench.
Who talks to whom
βββββββββββββββββββββββββ
β DK Studio β :3000
β UI Β· REST Β· Socket.IOβ
βββββββββββββ²ββββββββββββ
β outbound HTTP + Socket.IO from the agent
βββββββββββββ΄ββββββββββββ
β DK Studio Agent β :3001 (its own page, local)
β bench-01 β
βββββββββββββββββββββββββ
Every connection is initiated by the agent. The studio never dials an agent: there is no inbound command channel, no port forwarding and no reverse tunnel in the product. The studio is the only server you expose.
The studio's REST API answers anything that can reach port 3000, so the install belongs on the lab network. If it has to be reachable from beyond that, front it with a proxy that authenticates callers. See Security and privacy.
An agent uses two channels to the studio, both outbound:
- Socket.IO at
/api/socket.ioon the studio's port, for the push path:auth,command,agent_status,device_event,ping. - REST on the same origin and port, for registration, authentication and the polling fallback.
If the socket cannot be established, the agent falls back to polling GET /api/agent/commands?agentId=
every 5000 ms and posts results to POST /api/agent/commands/result. Both paths write the same
command rows, so a blocked websocket degrades latency rather than breaking function.
Ports
| Port | Who listens | Configured by | Notes |
|---|---|---|---|
| 3000 | The studio | PORT | The UI, the REST API and the Socket.IO endpoint, all on one port and one origin. |
| 3001 | The agent | AGENT_PORT | The agent's own page, on the bench. |
| β | The agent | AGENT_HOST | The interface the agent binds to. Defaults to 127.0.0.1, so the page is reachable only from the bench itself unless you change it. |
Socket.IO is not a separate port. It is mounted on the studio's port under the path
/api/socket.io, with transports ["websocket","polling"].
The agent's page on port 3001 is a local convenience: a registration form, a serial console and a
status badge. It talks to http://localhost:3001 from the browser regardless of where the
browser is. You do not need to expose it, and nothing in the studio depends on it.
Outbound access
From each bench, the agent must be able to reach the studio's base URL over http or https.
Those are the only schemes supported β the agent builds its Socket.IO URL and its REST calls from
the URL you type into the registration form.
What the agent needs to reach, at the studio's host and port:
| Path | Used for |
|---|---|
/api/socket.io | The Socket.IO handshake and the push channel. |
/api/agent/register | Claiming the licence. |
/api/agent/commands | The polling fallback. |
/api/agent/commands/result | Returning command results over polling. |
Nothing on the studio needs to reach the bench.
The Socket.IO server offers both websocket and polling. A proxy that only forwards
/api/socket.io/ as an ordinary HTTP route, or that blocks upgrade requests, will break the
push path. If you terminate TLS in front of the studio, forward websocket upgrades.
Proxies and TLS
The studio serves plain HTTP and does not terminate TLS itself. Terminate TLS at a reverse proxy β nginx, Caddy, HAProxy β and forward to the studio's port on the private network.
The session cookie is marked secure when NODE_ENV=production, so a production deployment
without TLS will not keep sessions.
server {
listen 443 ssl http2;
server_name studio.lab.internal;
ssl_certificate /etc/ssl/studio.crt;
ssl_certificate_key /etc/ssl/studio.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
}
}The long proxy_read_timeout matters: the socket sits idle between commands, and a proxy that
closes idle connections will push agents onto the polling path.
The Socket.IO server is configured with CORS origin: "*" and credentials: true. The studio is
meant to sit on a trusted network. If it is reachable beyond your own network, put an authenticating
proxy in front of it, and see Security and privacy.
Connectivity checklist
Run these from the bench, with the studio running.
- The studio answers HTTP
curl -s http://studio.lab.internal:3000/api/system/statusExpect
{"status":"ready","databaseConfigured":true,"adminCreated":true,β¦}. - The Socket.IO endpoint answers
curl -s "http://studio.lab.internal:3000/api/socket.io/?EIO=4&transport=polling"Expect an Engine.IO payload beginning with
0{. - Registration is reachable
curl -s -X POST http://studio.lab.internal:3000/api/agent/license?code=dk_l_β¦Expect
"success":truewith the licence'stype. This does not consume the licence β use the agent's own registration form to register with it. - The agent shows as connected
In the studio, open Agents. The agent appears as online only while its socket is live. If the list is empty, the agent has not completed the handshake β check the studio's log and the agent's own console on the bench.
If the socket is blocked but REST works, the agent registers and executes commands through the polling path. Commands arrive on the next 5-second poll. See Agent offline.
Related pages
- Self-hosted deployment β running the studio and its database.
- Agent registration and entitlements β issuing licences.
- Socket protocol β the wire format.
- Configuration β the environment variables behind these ports.