Agent API
Controller endpoints for remote agent status and logs, plus the Agent's own internal API.
For agents that cannot accept inbound connections and dial out to drydock instead, see the Portwing Edge API.
Controller endpoints
These endpoints are served by the Controller for remote agent status, logs and credential management.
List all agents
Returns all configured agents with connection status, system info, and container stats.
curl http://drydock:3000/api/v1/agents
{
"data": [
{
"name": "agent1",
"host": "192.168.1.50",
"port": 3000,
"connected": true,
"version": "1.8.0",
"build": "1.8.0-rc.1",
"os": "linux",
"arch": "amd64",
"cpus": 4,
"memoryGb": 8,
"uptimeSeconds": 86400,
"lastSeen": "2026-08-12T10:00:00.000Z",
"logLevel": "info",
"pollInterval": "0 */6 * * *",
"containers": { "total": 5, "running": 4, "stopped": 1 },
"images": 5
}
],
"total": 1
}Response fields
| Field | Type | Description |
|---|---|---|
data[].name | string | Agent name |
data[].host | string | Agent hostname or IP |
data[].port | integer | Agent port |
data[].connected | boolean | Whether the agent is currently connected |
data[].version | string | Agent drydock version, with any prerelease suffix removed |
data[].build | string | Agent's full build identity. See version and build. Equals version when the agent reports no prerelease suffix. The controller derives both fields from the version a drydock agent or a Portwing edge agent reports, so agents that predate build get one too |
data[].os | string | Operating system |
data[].arch | string | CPU architecture |
data[].cpus | integer | Number of CPU cores |
data[].memoryGb | number | Total memory in GB |
data[].uptimeSeconds | number | Agent process uptime |
data[].lastSeen | string | ISO 8601 timestamp of last heartbeat |
data[].logLevel | string | Agent's configured log level |
data[].pollInterval | string | Agent's watcher cron schedule |
data[].containers | object | Container count summary (total, running, stopped) |
data[].images | integer | Number of distinct images across agent containers |
Rotate a Portwing signing key (Drydock RC, Portwing development)
Drydock v1.8.0-rc.1 adds opt-in, staged Ed25519 rotation with compatible Portwing development builds. Published Portwing v1.0.0-rc.1 does not include the companion feature; no compatible published Portwing version is assigned yet. See Agent configuration for storage and recovery requirements.
GET /api/v1/agents/:name/key-rotation returns public credential status for a
known agent, or { "enabled": false } when rotation is unavailable. Status is
the controller's current view, not an independent remote proof.
POST /api/v1/agents/:name/key-rotation accepts exactly one property:
{ "action": "rotate" } or { "action": "abort" }. Existing session access
policy applies; API keys are rejected. Recovery-assurance sessions cannot
perform these writes. A completed Standard rotation returns 200 with the new
current_key_id. Edge activation returns 202, phase: "awaiting-reconnect"
and the pending_key_id. Read status after reconnection to confirm the new
current key. A 202 alone does not mean the old key has been retired.
Invalid actions return 400, missing agents 404, disabled rotation 409,
and an unconfirmed remote operation 502. After a 502, preserve managed
storage and check status before retrying. Retrying reuses the staged replacement.
Abort cancels an uncommitted replacement, keeping the current key. Responses
never include private keys. Normal Docker proxy requests cannot access the
reserved credential-management namespace.
Get agent log entries
Returns application log entries from a connected agent, proxied through the controller. See Log API for query parameters.
curl "http://drydock:3000/api/v1/agents/agent1/log/entries?level=warn&tail=50"Returns 404 if the agent is not found, or 503 if the agent is not connected.
Agent internal API
The Agent exposes specific endpoints for the Controller to synchronize state and receive events. These are generally internal APIs used by the Controller but are documented here for reference or advanced debugging.
All endpoints require the X-Dd-Agent-Secret header, except the unauthenticated GET /health check.
Get State
Returns a snapshot of the Agent's current state (containers, watchers, triggers).
# Get Containers
curl -H "X-Dd-Agent-Secret: <SECRET>" http://agent:3000/api/containers
# Get Watchers
curl -H "X-Dd-Agent-Secret: <SECRET>" http://agent:3000/api/watchers
# Get Triggers
curl -H "X-Dd-Agent-Secret: <SECRET>" http://agent:3000/api/triggersWatch Resources
Trigger a manual watch on a specific watcher or container hosted by the Agent.
# Watch a specific watcher (discovery)
curl -X POST \
-H "X-Dd-Agent-Secret: <SECRET>" \
http://agent:3000/api/watchers/:type/:name
# Watch a specific container (discovery)
curl -X POST \
-H "X-Dd-Agent-Secret: <SECRET>" \
http://agent:3000/api/watchers/:type/:name/container/:idDelete a Container
Delete a container from the Agent's state.
Note: This operation requires
DD_SERVER_FEATURE_DELETEto be enabled on the Agent.
curl -X DELETE \
-H "X-Dd-Agent-Secret: <SECRET>" \
http://agent:3000/api/containers/:idReal-time Events (SSE)
Subscribes to real-time updates from the Agent using Server-Sent Events (SSE).
The Agent pushes events when containers are added, updated, or removed.
Endpoint
curl -N -H "X-Dd-Agent-Secret: <SECRET>" -H "Accept: text/event-stream" http://agent:3000/api/eventsProtocol
Events are sent as JSON objects with the following structure:
data: {
"type": "event_type",
"data": { ...payload... }
}Supported Events
dd:ack
Sent immediately upon connection to confirm the handshake.
{
"type": "dd:ack",
"data": {
"version": "1.8.0",
"build": "1.8.0-rc.1",
"os": "linux",
"arch": "amd64",
"cpus": 4,
"memoryGb": 8,
"uptimeSeconds": 86400,
"lastSeen": "2026-08-12T10:00:00.000Z",
"logLevel": "info",
"pollInterval": "0 */6 * * *",
"containers": { "total": 5, "running": 4, "stopped": 1 },
"images": 5
}
}The version, build, logLevel and pollInterval fields correspond to the same-named fields in the GET /agents response.
dd:container-added
Sent when a new container is discovered.
{
"type": "dd:container-added",
"data": { ...container_object... }
}dd:container-updated
Sent when an existing container is updated (e.g. status change, new image tag).
{
"type": "dd:container-updated",
"data": { ...container_object... }
}dd:container-removed
Sent when a container is removed (e.g. stopped and pruned).
{
"type": "dd:container-removed",
"data": {
"id": "container_id"
}
}Container Logs
Returns stdout/stderr output from a container running on the Agent.
curl -H "X-Dd-Agent-Secret: <SECRET>" \
"http://agent:3000/api/containers/:id/logs?tail=100"Query parameters are the same as the controller container logs endpoint.
Application Log Entries
Returns application log entries from the Agent's in-memory ring buffer.
curl -H "X-Dd-Agent-Secret: <SECRET>" \
"http://agent:3000/api/log/entries?level=warn&tail=50"Query parameters
| Parameter | Type | Description |
|---|---|---|
level | string | Filter by minimum log level |
component | string | Filter by component name |
tail | integer | Number of entries to return from the end |
since | integer | Unix timestamp (milliseconds) -- only entries after this time |
Execute Remote Trigger
Executes a specific trigger on the Agent (e.g., to update a container).
curl -X POST \
-H "X-Dd-Agent-Secret: <SECRET>" \
-H "Content-Type: application/json" \
-d '{ ...container_json... }' \
http://agent:3000/api/triggers/:type/:nameBatch execution
Execute a trigger for multiple containers in a single request.
curl -X POST \
-H "X-Dd-Agent-Secret: <SECRET>" \
-H "Content-Type: application/json" \
-d '[{ ...container_json... }, { ...container_json... }]' \
http://agent:3000/api/triggers/:type/:name/batchHealth Check
Returns the Agent's uptime once at least one watcher is registered. This endpoint is unauthenticated and intended for Docker HEALTHCHECK directives.
curl http://agent:3000/health
{
"uptime": 3600.123
}If the agent starts but no watchers register, it returns 503:
{
"status": "unhealthy",
"reason": "no watchers registered"
}