Connect Your First Agent
An agent joins the studio in two steps: somebody issues a licence, and the agent claims that licence to register. The licence says what the machine will be — a personal machine or a bench in the lab — and it is spent the moment the machine registers. When both steps are done, the bench appears as online in the studio and accepts remote commands.
1. Get a licence
Every machine needs one licence, and there are two kinds.
| Kind | Where you get it | What the machine becomes |
|---|---|---|
| A personal licence | Personal Center in the studio — you apply for your own | A machine that belongs to you |
| A lab licence | Admin → Licences, issued by an administrator | A bench that belongs to the shared lab |
Sign in to the studio and open Personal Center. The Apply for a license action issues a personal licence and shows its code once. Copy the code — it is the only way into the studio.
A lab licence is issued by an administrator for a bench in the lab, and it carries the location and a label for the humans.
A licence authorises exactly one agent registration, and it is spent on that registration. Issue one licence per machine. See Agent Management.
2. Register the agent
On the machine under test, install the agent as a service and open http://localhost:3001:
./install.shThe installer builds the agent, registers it with the operating system — launchd on macOS,
systemd on Linux — so it comes back after every reboot, and opens its page for you. To run it
in the foreground instead:
pnpm --filter @dkstudio/agent devThe agent's home page shows a registration form with three fields:
| Field | Value |
|---|---|
| Studio URL | The base URL of your studio, e.g. http://localhost:3000. |
| Agent name | How this bench appears in the studio's agent list. |
| Licence code | The code from the licence. |
Press Check licence and the agent asks the studio what kind of licence it is. The answer appears before anything is registered:
- Personal licence — this machine will be registered as yours.
- Lab licence — this machine will be registered as part of the shared lab.
Submit the form. The agent calls POST /api/agent/register on the studio:
{
"code": "dk_l_…40 hex characters…",
"name": "Bench 01",
"studioUrl": "http://localhost:3000",
"workstation": { "hostname": "bench-01", "platform": "linux", "arch": "x64" },
"capabilities": []
}The studio checks the licence, registers the machine in the scope the licence type dictates, and answers with the agent's permanent identity:
{
"success": true,
"agentId": "0f2c…",
"apiKey": "…32 alphanumeric characters…",
"agentSecret": "…64 hex characters…",
"licenseType": "personal",
"scope": "personal",
"ownerUserId": "9a1c…",
"location": null,
"reRegistered": false
}The agent stores these in data/agent.json on its own machine and marks itself registered. It then
opens its connection to the studio, so the bench comes online without a further step.
| Credential | Purpose |
|---|---|
| Agent id | The identifier the agent sends in the socket handshake and on every polling request. |
| API key | The secret presented to POST /api/agent/auth when the agent authenticates over the REST API. |
| Agent secret | The credential presented over the socket. |
The licence's status flips from pending to consumed at the moment the agent row is created, and
the machine now belongs to whoever the licence belonged to.
3. The agent authenticates on the socket
The agent opens a Socket.IO connection to the studio at /api/socket.io and sends an auth
request with its identity:
{
"v": 1,
"type": "auth",
"kind": "request",
"id": "…",
"timestamp": "2026-01-01T09:30:00.000Z",
"payload": {
"agentId": "0f2c…",
"agentSecret": "…64 hex characters…",
"protocolVersion": 1
}
}The studio validates the pair against the registered agent and answers on the same auth type:
{
"v": 1,
"type": "auth",
"kind": "response",
"id": "…",
"timestamp": "2026-01-01T09:30:00.010Z",
"payload": {
"ok": true,
"data": {
"agentId": "0f2c…",
"name": "Bench 01",
"capabilities": [],
"protocolVersion": 1
}
}
}If the pair does not match, the studio answers with ok: false and the code AUTH_FAILED, then
closes the connection.
4. Confirm the connection
From that moment the studio shows the agent as online in the agent list. That status is derived from the live socket, not from a stored flag, so it is accurate as soon as the handshake completes.
The studio's agent list also shows the agent's name, its last-seen timestamp and its registration
count. While the agent is connected the studio keeps that timestamp fresh from the agent's presence
on the socket, and GET /api/agents returns the same picture for scripts.
An agent that restarts reconnects on its own, using the credentials in data/agent.json. If the
studio is not up yet, the agent keeps retrying, so restarting the bench does not need anyone at
the keyboard.
Where the machine shows up
A machine registered with a personal licence appears under My Computer in the studio, and only its owner sees it. A machine registered with a lab licence appears under Lab, where the whole team sees it.
The licence type decides this, not where the machine sits. A personal licence used on a bench in the lab still gives you a personal machine.
Re-registering
A licence is spent on the first registration, so it cannot be used to register a second time. To
bring an agent back after a reinstall, register with the credentials the agent already holds —
its agentId and agentSecret:
{
"agentId": "0f2c…",
"agentSecret": "…64 hex characters…",
"name": "Bench 01",
"studioUrl": "http://localhost:3000",
"workstation": { "hostname": "bench-01", "platform": "linux", "arch": "x64" },
"capabilities": []
}The studio updates the agent in place rather than creating a second one:
- the agent id and API key stay the same,
- the name, workstation and capabilities are updated,
- the response carries
reRegistered: true.
This is the intended way to move a bench to a new machine, or to re-point an agent after reinstalling it, without leaving a stale agent behind in the list.
A licence authorises one machine and is spent on it. If a second machine presents the same code, the studio refuses it. Issue a second licence instead.
If registration fails
The studio answers with 403 and one of these messages:
| Message | Cause |
|---|---|
invalid_code | The code does not match any licence. Check for a truncated paste. |
used | The licence was already spent on another machine. Apply for a new one. |
revoked | The licence was revoked. Apply for a new one. |
expired | The licence had an expiry date and it has passed. Apply for a new one. |
To retire an agent, revoke its licence with POST /api/licenses/{id}/revoke. A revoked licence
blocks further registrations. A licence that has already been consumed cannot be revoked — delete
the agent itself instead.
Next steps
- Your First Remote Command — run something on the bench from the browser.
- Personal Center — where your licences and machines live.
- Agent Management — licences, revocation and presence.