Agent command surface
The bound device runs a small lifeline core plus a
signed plugin. The most basic agent must always accept
and execute confirmed shell commands natively — shell is
not break-glass and not plugin-optional. Other ops still come from the
signed plugin. This page maps that surface against the control plane.
Modular agent · 0.3.2-lifeline+
Lab: GL-E5800
Shell = core native (confirmed)
Upgrade required for shell on current lab unit
Rule of thumb: trust GET /api/admin/devices/:id
→ device.status.agent.commands. After the shell-capable core
is installed, that list includes shell. Until upgrade,
live lab may still report without shell.
Why this page exists
Earlier modular builds treated shell as local break-glass only. That was the
wrong product direction: the most basic device agent must run shell
(with server confirmation). MQTT and collect_status already worked;
shell was missing from the advertised core surface.
Compare these stories when something fails:
- What the live device advertises (source of truth for enqueue)
- What the modular core implements natively (status, upgrade, reboot, shell)
- What the signed plugin manifest allows
- What the server accepts as a type name (
COMMAND_TYPES)
What runs on the device
Think of three replaceable pieces on A/B devices. Only core + plugin
advertise remote commands.
Core
cloudcontrold
Reach the assigned edge at all costs, durable ledger/outbox,
recovery, signed updates, minimal status, and shell.
Native:
collect_status, agent_upgrade, reboot,
shell (confirmed)
Guardian
cloudcontrol-guard
Independent rollback deadlines for connectivity and updates.
Not a command agent — no remote command types.
Plugin
cloudcontrol-legacy
Out-of-process ops (set_*, network tests, WG/IPsec, scans).
Signed manifest is the allowlist. Shell stays on the core so the
basic agent works even with zero plugins.
Optional lock-down
Disable shell
Deployments may set CLOUDCONTROL_DISABLE_SHELL=1 on the
device to turn shell off. Default is on.
Lab snapshot · GL-E5800
Captured 2026-07-19 against the live lab unit via admin API only.
| Online | live · MQTT via edge-nyc3-01 |
| Agent | cloudcontrold · 0.3.2-lifeline · profile full · protocol 3 |
| Core SHA-256 | d211e2646e5fef36f32667d31c75aad074c40023899e3dd1e6bc2ed8b1f6ce87 |
| Core size | 2,000,748 bytes (UPX) |
| Plugin | cloudcontrol-legacy · same version |
| Shell (pre-upgrade lab) | Was not advertised on 0.3.2-lifeline without native shell; code now treats shell as core native — rebuild/upgrade to pick up |
| Proven path | collect_status succeeded (delivered → acked → completed ≈ 553 ms) |
Advertised commands on that device
agent_upgrade, block_client, bt_scan, cell_scan, collect_clients, collect_status,
firmware_check, ipsec_join, ipsec_leave, network_iperf, network_path_test,
network_port_test, network_speedtest, reboot, set_firewall, set_lan, set_system,
set_wifi, wireguard_onboard, wireguard_remove
Full command grid
Compare each command type across the modular core, the legacy plugin,
the server type allowlist, and the live lab advertisement.
Rows highlighted in warm tint are mismatches
(server still names the type; modular device does not advertise it).
Y present / allowed
N absent / refused when advertised set exists
BG break-glass only
E edge-control, not device agent
| Command type |
Core |
Plugin |
Server |
Live |
Notes |
Control-plane mismatches
These sources still describe an older or broader surface than modular lifeline ships.
src/routes/admin.js · COMMAND_TYPES
Claims: historical full set including shell, wifi_*, reset, firmware_upgrade, cell_at…
Reality: type name accepted, then capability gate filters per device advertisement.
src/routes/info.js · commandTypes
Claims: short outdated list with shell; missing WG/IPsec/agent_upgrade.
Reality: not a reliable live catalog.
src/agent-command-policy.js
Claims: comment says modular bundle does not export WG/IPsec/agent_upgrade/shell.
Reality: wrong for WG/IPsec/agent_upgrade (plugin/core export them). Shell correctly absent.
docs/autonomous-operations.md (pre-fix)
Claims: “Full profile on GL-E5800 advertises shell.”
Reality: false for 0.3.2-lifeline. Ops doc corrected; use this page.
agent/cmd/cloudcontrold/capabilities.go
Claims: monolithic defaultFullAgentCapabilities (wifi, firmware_upgrade, …).
Reality: shipped lifeline follows coreagent + plugin, not that list alone.
How enqueue is gated
POST /api/admin/devices/:id/commands with type in server COMMAND_TYPES.
- If the device has reported
agent.commands or agent.capabilities, the type must appear in their union (case-insensitive).
- Otherwise:
409 device_capability_not_advertised.
- Confirmation / risk bits (
agent-command-policy.js) are separate from advertise/deny.
Do not use SQL to invent command capability. Inspect and control via admin API only.
Device traffic is edge-only; the UI host is not on the device contact path.
Example: uname -a
| Path | Result |
| Lab unit on pre-shell core |
device_capability_not_advertised — expected until upgrade |
| After shell-capable core upgrade |
Admin enqueue shell with payload.command: "uname -a" (confirmed) → stdout |
collect_status inventory (always) |
Kernel / hostname / arch without full uname string |
How to refresh this page’s data
- Admin login →
GET /api/admin/devices/:id
- Read
status.agent.commands, .capabilities, .plugins[]
- Compare to
public/downloads/cloudcontrol-plugin-legacy-*.plugin.json
- Compare to
COMMAND_TYPES in src/routes/admin.js
- Update the
COMMANDS array in this HTML (and the snapshot pills) when the surface changes
Machine-oriented twin (markdown): docs/agent-command-surface.md in the repo.
Prefer this HTML page for operators.