-
Notifications
You must be signed in to change notification settings - Fork 12
discovery detailed analysis
This document proposes a unified solution for three related features in the Device Management Toolkit (DMT). The common goal is to give operators greater visibility into their AMT-capable device fleet — covering what devices are present on the network, what their firmware and platform state is, and whether key services such as Intel LMS are running. The proposal defines the required data model changes, API impact across Console, MPS, RPS, and rpc-go, and a design for the discovery agent that collects and reports device state.
| Issue | Summary |
|---|---|
| sample-web-ui#1265 — SKU Type & Config Mode in Devices UI | Display SKU type (vPro / ISM / Non-vPro) and Configuration mode (ACM / CCM / Pre-Prov) in the Devices list of the OpenAMT UI. Use the existing deviceInfo JSON column in MPS; add the same column to Console if not already present. |
| console#858 — Device Discovery | Add a discovery capability so customers gain visibility into AMT-capable devices across their network. A lightweight agent (likely extending rpc-go) collects AMT/ME details (FW version, SKU, control mode, TLS mode, network config, UPID, cert hashes) and platform details (OS, LMS, CPU, hostname, IP, ethernet adapters, monitor presence) and reports them back to Console. Console ingests, stores, and visualises the data via a dashboard with filtering, sorting, capability breakdowns, and export support. |
| rpc-go#1246 — Detect LMS During Orchestration | Detect whether Intel LMS (Local Manageability Service) is installed on a device during the v3 orchestration/registration process. rpc-go reports LMS presence in the registration payload; Console captures and persists it in a new is_lms_available field in the device information table. UI enhancements based on LMS presence are out of scope for this story. |
The table below maps each data field required by the three issues against what is currently stored.
| Required Field | Required By | Currently in MPS/Console? | Notes |
|---|---|---|---|
| CSME FW Version | Discovery, SKU/Mode UI | ✅ deviceInfo.fwVersion
|
Already stored in deviceInfo JSON |
| SKU type label (vPro / ISM / Non-vPro) | Discovery, SKU/Mode UI | deviceInfo.fwSku string |
SKU is stored as a raw number; decoded label (features field) exists but is not a structured enum |
| Control Mode (ACM / CCM / Pre-Prov) | Discovery, SKU/Mode UI | ✅ deviceInfo.currentMode
|
Stored as "0"/"1"/"2"; UI label decoding needed |
| Provisioned State | Discovery | ✅ Derivable from currentMode
|
0 = pre-provisioned, 1/2 = provisioned |
| TLS Mode | Discovery | ❌ Not stored | Not in deviceInfo or any column |
| DHCP vs Static | Discovery | deviceInfo.ipAddress only |
No DHCP flag, gateway, or subnet stored |
| 802.1x / wireless config | Discovery | ❌ Not stored | Available via Console AMT API (/amt/networkSettings) but not persisted |
| AMT certificate hashes | Discovery | ❌ Not stored | Available during activation payload but not persisted in deviceInfo
|
| UPID | Discovery | ❌ Not stored | Not collected anywhere today |
| AMT enabled in BIOS | Discovery | ❌ Not stored | Collectable via rpc-go but not stored |
| ME Interface Version | Discovery | ❌ Not stored | Not in deviceInfo
|
| LMS version / installed | Discovery, LMS Orchestration | ❌ Not stored | Not collected today; #1246 requires detection at registration time — stored as lmsInstalled in discoveryInfo (partial write at registration; enriched with lmsVersion during a full discovery scan) |
| OS details (distro, version) | Discovery | ❌ Not stored | Not collected today |
| CPU details | Discovery | ❌ Not stored | Available via Console CIM API (/amt/hardwareInfo) but not persisted |
| OS IP address | Discovery | deviceInfo.ipAddress is AMT IP |
OS IP may differ from AMT IP |
| Number of ethernet adapters | Discovery | ❌ Not stored | Not collected today |
| Monitor connected | Discovery | ❌ Not stored | Not collected today |
| OS hostname | Discovery | ✅ hostname column |
Already a top-level column |
| DNS suffix (AMT) | Discovery | ✅ dnsSuffix column |
Already a top-level column |
| Last reported/discovered time | Discovery (NFR) | ✅ deviceInfo.lastUpdated
|
Exists in deviceInfo JSON |
| Export support | Discovery (NFR) | Console has an export usecase but not wired for discovery data |
-
MPS is the system of record for CIRA connection state and the
deviceInfoJSON blob. It does not store credentials. -
Console mirrors
deviceInfo, adds credentials/TLS fields, and is the entry point for all AMT management actions. It has live APIs to query most of the missing fields (network, hardware, features) but does not persist them back to the database. - The
deviceInfoJSON column is the natural extension point for adding new discovery fields without a disruptive schema change. Fields that are queried live today (network settings, hardware info, features) should be persisted into this blob (or a newdiscoveryInfocolumn) by the discovery agent on each report cycle. - Fields not yet collected anywhere (UPID, LMS version, OS distro, monitor detection, 802.1x state) require new collection logic — AMT-level fields (UPID) via
rpc amtinfo --syncin rpc-go; OS-level fields (LMS, OS distro, monitor, 802.1x) via therpc-discoveryOS collector on the device.
The three features require extending the existing devices table with additional persisted data. The proposal is to extend the existing deviceInfo JSON column for fields that relate to the managed/activated device, and introduce a new discoveryInfo JSON column for fields that are relevant only in the discovery context (OS-level data, platform checks, LMS state). This avoids a disruptive schema migration while keeping the two concerns clearly separated.
| Concern | Storage Location | Rationale |
|---|---|---|
| AMT/ME firmware state (SKU, mode, TLS, UPID) | Extend deviceInfo JSON — both MPS and Console |
Consistent with today's write path; RPS and rpc amtinfo --sync already populate this blob |
| OS and platform state (LMS, OS details, network adapters) | New discoveryInfo JSON column — Console only |
Discovery-specific; not relevant for managed-device operations; Console is the primary consumer |
LMS presence at registration (lmsInstalled) |
discoveryInfo JSON — Console only, partial write at registration |
Consolidates all LMS state; discoveryInfo written partially at registration, enriched fully at discovery scan |
| Credentials and TLS connection config | Existing Console-only columns — no change | Already correctly separated |
Live connection state (connectionStatus, lastConnected) |
Existing MPS columns — no change | Updated at CIRA runtime; not a discovery concern |
The diagram below shows the current entity structure and the proposed additions. Each field is annotated with its presence — M = MPS, C = Console, M+C = both — and new fields are marked with <<new>>.
erDiagram
DEVICES {
uuid guid PK "M+C"
string hostname "M+C"
string dnsSuffix "M+C"
boolean connectionStatus "M+C"
string mpsusername "M+C"
string mpsinstance "M+C"
string tenantId "M+C"
string friendlyName "M+C"
string tags "M+C"
timestamp lastConnected "M+C"
timestamp lastSeen "M+C"
timestamp lastDisconnected "M+C"
json deviceInfo "M+C"
json discoveryInfo "C <<new>>"
}
DEVICE_INFO {
string fwVersion "M+C"
string fwBuild "M+C"
string fwSku "M+C"
string currentMode "M+C"
string features "M+C"
string ipAddress "M+C"
timestamp lastUpdated "M+C"
string tlsMode "M+C <<new>>"
string upid "M+C <<new>>"
boolean amtEnabledInBIOS "M+C <<new>>"
string meInterfaceVersion "M+C <<new>>"
boolean dhcpEnabled "M+C <<new>>"
string[] certHashes "M+C <<new>>"
}
DISCOVERY_INFO {
string lmsVersion "C <<new>>"
boolean lmsInstalled "C <<new>>"
string osName "C <<new>>"
string osVersion "C <<new>>"
string osDistro "C <<new>>"
string cpuModel "C <<new>>"
string osIpAddress "C <<new>>"
int ethernetAdapterCount "C <<new>>"
boolean monitorConnected "C <<new>>"
boolean ieee8021xEnabled "C <<new>>"
timestamp lastDiscovered "C <<new>>"
}
DEVICES ||--|| DEVICE_INFO : "deviceInfo (JSON)"
DEVICES ||--|| DISCOVERY_INFO : "discoveryInfo (JSON)"
These fields relate to the AMT/ME state of an already-managed or activatable device. They extend the existing deviceInfo blob, keeping the same write path (RPS activation + rpc amtinfo --sync).
| Field | Type | Feature | Collection Method | Presence |
|---|---|---|---|---|
tlsMode |
string | Discovery |
AMT_TLSProtocolEndpointCollection via WSMAN (/amt/tls/:guid) |
M + C |
upid |
string | Discovery |
AMT_SetupAndConfigurationService.GetUuid — already in rpc-go InfoResult
|
M + C |
amtEnabledInBIOS |
boolean | Discovery |
AMT_BootSettingData via WSMAN or rpc-go amtinfo |
M + C |
meInterfaceVersion |
string | Discovery |
CIM_SoftwareIdentity (ME version) — via GET /amt/version/:guid in Console |
M + C |
dhcpEnabled |
boolean | Discovery |
AMT_EthernetPortSettings.DHCPEnabled — already in GET /amt/networkSettings/:guid
|
M + C |
certHashes |
string[] | Discovery | Sent in rpc-go activation payload (MessagePayload.CertificateHashes) — already collected, not persisted |
M + C |
Key: M = MPS · C = Console · M + C = both
These fields describe the OS environment and platform state rather than AMT configuration. They are only meaningful in the discovery context and do not belong in the managed-device deviceInfo blob.
Key: for Presence M = MPS · C = Console · M + C = both
| Field | Type | Feature | Collection Method | Presence |
|---|---|---|---|---|
lmsVersion |
string | Discovery, LMS Orchestration | rpc-discovery OS collector — OS package query or LMS API | C |
lmsInstalled |
boolean | Discovery, LMS Orchestration | rpc-discovery OS collector — check process/service presence; partial write at registration (#1246), enriched at discovery scan | C |
osName |
string | Discovery | rpc-discovery OS collector — runtime.GOOS (no rpc needed) |
C |
osVersion |
string | Discovery | rpc-discovery OS collector — OS version API (no rpc needed) |
C |
osDistro |
string | Discovery | rpc-discovery OS collector — Linux: /etc/os-release; Windows: WMI (no rpc needed) |
C |
cpuModel |
string | Discovery | rpc-discovery OS collector — Linux: /proc/cpuinfo; Windows: WMI Win32_Processor (no WSMAN; works on non-AMT devices) |
C |
osIpAddress |
string | Discovery | rpc-discovery OS collector — net.InterfaceAddrs() (no rpc needed) |
C |
ethernetAdapterCount |
int | Discovery | rpc-discovery OS collector — net.Interfaces() (no rpc needed) |
C |
monitorConnected |
boolean | Discovery | rpc-discovery OS collector — Windows: EnumDisplayMonitors; Linux: EDID/DRM check (no rpc needed) |
C |
ieee8021xEnabled |
boolean | Discovery | rpc-discovery OS collector — Linux: nmcli/wpa_supplicant config; Windows: WMI Win32_NetworkAdapterConfiguration (no WSMAN; works on non-AMT devices) |
C |
lastDiscovered |
timestamp | Discovery (NFR) | Set by rpc-discovery agent on each report cycle | C |
rpc-go#1246 explicitly calls for storing LMS presence in the "device information table". However, this requires a deliberate placement decision given the two-blob design.
Arguments for deviceInfo
- The issue author explicitly targets the device information table.
- The write event is registration time (v3 orchestration), not a discovery scan cycle — a device that is registered but never scanned would otherwise have no LMS data.
Arguments for discoveryInfo
- LMS is an OS-level service, not an AMT/ME firmware concept. The design principle is: AMT/ME state →
deviceInfo, OS/platform state →discoveryInfo. -
discoveryInfoalready defineslmsInstalled(boolean) andlmsVersion(string). AddingisLmsAvailabletodeviceInfosplits LMS data across two blobs. - Consolidating all LMS state in one location simplifies queries and UI display.
Decision: discoveryInfo with partial writes at registration
discoveryInfo is the correct home. The write path for discoveryInfo must be extended to accept partial writes at registration time, not only during full discovery scans. Concretely:
- The boolean from rpc-go#1246 maps to the existing
lmsInstalledfield indiscoveryInfo— no new field is needed. - The Console registration handler writes just
lmsInstalledintodiscoveryInfowhen the device registers, leaving all other fields null until a discovery scan runs. - A subsequent discovery scan enriches the same blob with
lmsVersion,osName, and the remaining platform fields.
This avoids splitting LMS data, respects the design principle, and ensures LMS presence is captured immediately at registration even for devices that never undergo a full discovery scan.
This section maps the existing API surface against the three features, identifying which endpoints need modification and which new endpoints must be introduced.
rpc-go already supports two mechanisms for managing a device's presence in Console. The three new features extend both paths.
Path A — Local activation (direct to Console as part of next branch):
rpc activate --local --profile <profile.yaml> \
--auth-endpoint <console>/api/v1/authorize \
--auth-username <user> --auth-password <pass>- Authenticate with Console:
POST /api/v1/authorize→ JWT token - Register device in Console before AMT orchestration:
POST /api/v1/deviceswith aDevicePayloadstruct carrying GUID, hostname, AMT/MEBx/MPS credentials, and TLS configuration flags - Run orchestrator (CCM/ACM activation, optional CIRA configuration)
- If CIRA configuration fails: clear MPS password via
PATCH /api/v1/devices { "guid": "...", "mpspassword": "" }
Path A — Local deactivation (direct to Console as part of next branch):
rpc deactivate --local --auth-endpoint <console>/api/v1/authorize \
--auth-username <user> --auth-password <pass>- Resolve device GUID from AMT (before unprovisioning — AMT is unavailable afterwards)
- Locally unprovision AMT
- Delete device from Console:
DELETE /api/v1/devices/{guid}
Path B — Remote activation via RPS (unchanged):
rpc activate -u wss://rps.example.com --profile <profile>rpc-go sends a MessagePayload to RPS over WebSocket. This path does not call POST /api/v1/devices directly from rpc-go.
The new LMS detection (rpc-go#1246) must be propagated through both paths, and Console's registration handler must persist the flag into discoveryInfo.lmsInstalled.
| # | Endpoint | Service | Type | Feature |
|---|---|---|---|---|
| 1 | POST /api/v1/devices |
Console only | Modify — accept isLMSAvailable in registration body |
LMS Orchestration |
| 2 | POST /api/v1/devices/discovery |
Console only | New | Discovery |
| 3 | GET /api/v1/devices[/:guid] |
Console only | Modify — include discoveryInfo in response |
Discovery, SKU/Mode UI |
| 4 | GET /api/v1/devices |
Console only | Modify — add filter/sort query params | Discovery |
| 5 | GET /api/v1/devices/export |
Console only | New | Discovery |
| 6 | PATCH /api/v1/devices |
Console + MPS | Modify — extend deviceInfo fields |
Discovery, SKU/Mode UI |
| 7 | PATCH /api/v1/devices |
Console only | Modify — accept discoveryInfo partial payload |
Discovery, LMS Orchestration |
| 8 |
rpc amtinfo --sync payload |
rpc-go | Modify — extend syncDeviceInfo + add discoveryInfo
|
Discovery |
| 9 | rpc-go registration payloads | rpc-go | Modify — add isLMSAvailable (Path A DevicePayload) + lmsInstalled (Path B MessagePayload) |
LMS Orchestration |
Feature: LMS Orchestration (rpc-go#1246)
Change: The existing device registration endpoint must accept a new isLMSAvailable boolean field in the request body and persist it as discoveryInfo.lmsInstalled (a partial write into the discoveryInfo column; all other discoveryInfo fields remain null until a discovery scan runs).
This is a Console-side change that receives the value sent by rpc-go Path A (local activation). The DevicePayload struct in rpc-go internal/device/api.go must be extended with the field:
IsLMSAvailable bool `json:"isLMSAvailable"`| JSON field | Maps to | Type | Write event |
|---|---|---|---|
isLMSAvailable |
discoveryInfo.lmsInstalled |
boolean | Device registration via Path A |
Console handler must read isLMSAvailable from the POST body and write { "lmsInstalled": <value> } as a partial upsert into the discoveryInfo column. No equivalent change is needed in MPS.
Feature: Device Discovery
Change: A dedicated endpoint for the discovery agent to submit a full discovery report for a device (identified by UUID or hostname). This is distinct from the PATCH /api/v1/devices used for device management, allowing cleaner separation of concerns and a different auth scope if needed.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/devices/discovery |
Accepts a full discovery payload; upserts discoveryInfo and merges new deviceInfo fields |
Request body:
{
"guid": "...",
"deviceInfo": {
"tlsMode": "...",
"upid": "...",
"amtEnabledInBIOS": true,
"meInterfaceVersion": "...",
"dhcpEnabled": true,
"certHashes": ["..."]
},
"discoveryInfo": {
"lmsInstalled": true,
"lmsVersion": "2025.1.0",
"osName": "Windows",
"osVersion": "11",
"osDistro": "Windows 11 Pro",
"cpuModel": "Intel Core Ultra 7 165H",
"osIpAddress": "192.168.1.100",
"ethernetAdapterCount": 2,
"monitorConnected": true,
"ieee8021xEnabled": false,
"lastDiscovered": "2026-04-24T10:00:00Z"
}
}Feature: Device Discovery, SKU/Mode UI
Change: Add discoveryInfo to the response DTO alongside the existing deviceInfo. The field should be omitted (omitempty) when null.
MPS's equivalent GET /api/v1/devices endpoints are not affected — discoveryInfo is a Console-only column.
Feature: Device Discovery
Change: The discovery dashboard requires server-side filtering and sorting on the new fields. Extend the existing GET /api/v1/devices to support:
| Parameter | Example | Purpose |
|---|---|---|
filter[skuType] |
vPro |
Filter by decoded SKU label |
filter[currentMode] |
ACM |
Filter by control mode |
filter[lmsInstalled] |
true |
Filter by LMS presence |
filter[osName] |
Windows |
Filter by OS |
sort |
hostname, fwVersion
|
Sort column |
order |
asc / desc
|
Sort direction |
These map to new SQL query clauses against the deviceInfo and discoveryInfo JSON columns.
Feature: Device Discovery (NFR) Change: Add a new endpoint that returns all discovery data as a downloadable file (CSV or JSON).
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/devices/export |
Returns all devices with deviceInfo + discoveryInfo as CSV/JSON |
Query parameters: format=csv|json, same filter parameters as the list endpoint.
Feature: Discovery, SKU/Mode UI
Change: The request body's deviceInfo object must accept the new fields added to the deviceInfo blob. No new endpoint is needed — the existing PATCH is extended.
Fields to add to the accepted deviceInfo payload:
| Field | Type | Populated By |
|---|---|---|
tlsMode |
string |
rpc amtinfo --sync (reads AMT_TLSProtocolEndpointCollection) |
upid |
string |
rpc amtinfo --sync (reads AMT_SetupAndConfigurationService.GetUuid) |
amtEnabledInBIOS |
boolean |
rpc amtinfo --sync (reads AMT_BootSettingData) |
meInterfaceVersion |
string |
rpc amtinfo --sync (reads CIM_SoftwareIdentity) |
dhcpEnabled |
boolean |
rpc amtinfo --sync (reads AMT_EthernetPortSettings.DHCPEnabled) |
certHashes |
string[] | RPS activation payload (MessagePayload.CertificateHashes) |
Console must deserialise these into the DeviceInfo struct and persist them into the deviceInfo JSON column. No schema migration is required — deviceInfo is already a TEXT/JSON column.
The rpc-go syncDeviceInfo struct and MessagePayload struct must also be extended to carry these fields.
Feature: Device Discovery, LMS Orchestration
Change: The existing PATCH must additionally accept an optional discoveryInfo JSON object and merge it into the new discoveryInfo column (Console only). The merge must be additive — fields present in the payload overwrite the stored value; absent fields are left unchanged, enabling partial writes.
This endpoint handles post-registration write events only:
-
At
amtinfo --sync: rpc-go can include a partialdiscoveryInfoobject with any fields collected during a sync run. -
At discovery scan (
console#858): the discovery agent sends a fulldiscoveryInfoobject.
Note: The initial
lmsInstalledwrite at device registration time goes viaPOST /api/v1/devices(item #1 above), not this PATCH. For Path B (remote via RPS), RPS forwards the value to Console through its own registration flow.
Fields accepted in discoveryInfo:
| Field | Type | Write Event |
|---|---|---|
lmsInstalled |
boolean |
amtinfo --sync + Discovery scan |
lmsVersion |
string | Discovery scan |
osName |
string | Discovery scan |
osVersion |
string | Discovery scan |
osDistro |
string | Discovery scan |
cpuModel |
string | Discovery scan |
osIpAddress |
string | Discovery scan |
ethernetAdapterCount |
int | Discovery scan |
monitorConnected |
boolean | Discovery scan |
ieee8021xEnabled |
boolean | Discovery scan |
lastDiscovered |
timestamp | Discovery scan |
Feature: Discovery, LMS Orchestration
Change: Extend the syncDeviceInfo struct to include the new deviceInfo fields (tlsMode, upid, amtEnabledInBIOS, meInterfaceVersion, dhcpEnabled, certHashes) and add a new discoveryInfo struct carrying OS-level fields. The SyncDeviceInfo function collects and includes these in the PATCH body.
Feature: LMS Orchestration (rpc-go#1246)
Change: rpc-go detects LMS presence via TCP probe to localhost:16992 (or :16993 for AMT 19+ with local TLS) before sending any registration payload. The LMS flag must be added to both registration paths:
Path A — Local activation (DevicePayload → Console): Add IsLMSAvailable bool json:"isLMSAvailable" to the DevicePayload struct in internal/device/api.go. Console receives it via POST /api/v1/devices and stores it as discoveryInfo.lmsInstalled (see item #1).
Path B — Remote activation (MessagePayload → RPS): Extend the MessagePayload struct in internal/rps/message.go to include LmsInstalled bool json:"lmsInstalled". RPS receives this over WebSocket and must forward it to Console when registering the device (RPS → Console write path).
A discovery agent must run persistently in the background, survive reboots, and operate without a logged-in user session. The platform-native service mechanisms are:
Windows
| Technology | Description | Considerations |
|---|---|---|
| Windows Service (SCM) | Registered with the Service Control Manager; starts at boot, restarts on failure, runs as SYSTEM or a dedicated account | Standard approach; well-supported by Go (golang.org/x/sys/windows/svc) and by frameworks such as kardianos/service
|
| Task Scheduler | Periodic task triggered on schedule or at login | Simpler to deploy but less reliable; no automatic restart on failure |
| COM Out-of-Process Server | Background activation through COM | High complexity; not appropriate for a device agent |
Recommended for Windows: Windows Service via the kardianos/service library, which provides a single cross-platform service registration API.
Ubuntu / Linux
| Technology | Description | Considerations |
|---|---|---|
systemd unit (.service) |
Standard on Ubuntu 18.04+; Type=simple or Type=notify; supports Restart=on-failure and WantedBy=multi-user.target
|
Preferred; fully scriptable; integrates with journald for logging |
| SysV init script | Legacy /etc/init.d/ scripts |
Only needed for very old distributions; Ubuntu ships systemd by default |
| cron / at | Scheduled execution | Periodic only; cannot run continuously; no failure recovery |
| Docker container | Containerised agent with --restart=always
|
Adds a Docker dependency; suitable for server deployments, not typical AMT-capable end devices |
Recommended for Linux: systemd service unit, distributed as a .service file alongside the binary.
Two architectural approaches are feasible. Both are written in Go (consistent with rpc-go) and produce a standalone binary.
A new, dedicated binary — e.g. rpc-discovery — that runs as a long-lived service. Rather than importing rpc-go packages as a library, it invokes the rpc binary as a subprocess to perform AMT data collection and device sync, and handles only the service harness, scheduling loop, and OS-level collection itself.
rpc-discovery (new binary)
├── service harness (kardianos/service)
├── scheduler (periodic scan interval)
├── AMT collector → shells out: rpc amtinfo --all --json
│ rpc amtinfo --sync --url <console>/api/v1/devices
├── OS collector (new Go code: LMS probe, OS details, adapters, monitor)
└── Console reporter → shells out or direct HTTP:
POST <console>/api/v1/devices/discovery (discoveryInfo payload)
The agent locates the rpc binary either alongside itself in the same directory (co-deployed) or via a configurable path in agent-config.yaml.
| Pros | Cons |
|---|---|
| No rpc-go package imports — no build-time coupling or circular dependency risk | Requires the rpc binary to be present on the device alongside rpc-discovery
|
The rpc binary can be updated independently without rebuilding the agent |
Subprocess invocation adds process-spawn overhead per scan cycle |
| DLL consumers of rpc-go are completely unaffected | Parsing JSON output of rpc amtinfo --json creates a soft API contract that can break on rpc-go output changes |
| Service harness, scheduling, and OS collection are fully self-contained | Two binaries to deploy and keep in sync on the end device |
| Versioned independently from rpc-go | Error handling across process boundaries is more complex |
Add an agent subcommand to the existing rpc binary. When invoked as rpc agent --start, the process forks into the background (or registers itself as a service) and begins periodic discovery reporting.
rpc agent start → installs and starts the service
rpc agent stop → stops the service
rpc agent status → reports current state
| Pros | Cons |
|---|---|
Single binary to distribute — consistent with current rpc.exe packaging |
rpc.exe is already used as a DLL (via go build -buildmode=c-shared); a service harness in the same binary adds complexity to the DLL surface |
| Authentication, Console URL, and profile config are shared with existing flags | Running as a long-lived Windows Service requires the binary to handle SCM control signals, which conflicts with the current short-lived CLI lifecycle |
| Reuses all existing internal packages directly — no factoring needed | Agent start/stop/status commands add scope to an already large binary |
| One fewer artifact to manage in CI/CD | Versioning the agent and the CLI together may create undesirable coupling |
Interactive rpc commands and the background agent would share the same process namespace if launched from the same binary instance |
Approach A (standalone agent) is preferred for production use. The DLL usage of rpc-go (buildmode=c-shared) makes embedding a persistent service harness in the same binary architecturally risky. Because rpc-discovery shells out to the rpc binary as a subprocess rather than importing rpc-go packages, there is no build-time coupling — both binaries are simply co-deployed on the device.
As a short-term option for prototyping, Approach B is acceptable if the DLL build target is excluded from the agent subcommand (i.e. the agent command is compiled out of the shared library variant).
A key deployment challenge is getting the discovery agent binary — and the credentials it needs to report back to Console — onto each managed or to-be-managed device without manual steps.
Console exposes two endpoints that an operator (or an IT automation tool) calls in sequence: one to download the platform-appropriate agent binary and one to download a pre-populated configuration file containing Console credentials. Both steps are authenticated; the operator deploys the binary and config together onto the device.
New Console endpoints:
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/discovery/agent?platform=windows|linux |
Returns the rpc-discovery binary for the requested platform as an application/octet-stream download |
GET |
/api/v1/discovery/agent/config |
Returns a signed agent-config.yaml (YAML) containing the Console URL, a scoped API token, and the reporting interval — generated per request for the authenticated operator |
Bootstrap flow:
sequenceDiagram
actor Operator
participant Console
participant Automation as GitHub Release
participant Device as Target Device
participant Agent as rpc-discovery (service)
Operator->>Console: Authenticate (browser or CLI)
Console-->>Operator: Session established
Operator->>Console: GET /api/v1/discovery/agent?platform=windows|linux
Console-->>Operator: rpc-discovery binary (application/octet-stream)
Operator->>Console: GET /api/v1/discovery/agent/config
Console-->>Operator: agent-config.yaml (Console URL, scoped JWT, interval)
Operator->>Device: Deploy binary and config file
Note over Device: Windows: rpc-discovery install && rpc-discovery start<br/>Linux: systemctl enable --now rpc-discovery
Device->>Agent: Service starts on boot
loop Every reportInterval seconds
Agent->>Device: subprocess: rpc amtinfo --all --json
Device-->>Agent: AMT JSON data
Agent->>Device: OS collection (LMS probe, adapters, OS details)
Device-->>Agent: OS data
Agent->>Console: POST /api/v1/devices/discovery (Bearer JWT)
Console-->>Agent: 200 OK
end
Configuration file (agent-config.yaml) structure:
consoleURL: https://console.example.com
token: <scoped-JWT> # Console issues a long-lived discovery-scoped token
reportInterval: 3600 # seconds between scan cycles
skipCertCheck: falseSecurity considerations:
- The token issued in the config file should be scoped to the
discovery:writepermission only — it cannot manage devices or read credentials. - Console should support token revocation per device or per batch to allow decommissioning agents without rotating the global credential.
- The binary served from
/api/v1/discovery/agentshould be the official release artifact (checksummed and optionally code-signed); Console stores it as a static asset and does not build it on demand. - The config download endpoint (
/api/v1/discovery/agent/config) requires authentication so that only authorised operators can generate agent credentials.
Alternative delivery mechanisms (for environments without direct Console access from end devices):
- Agent config can be baked into a GPO/MDM deployment package alongside the binary (using the standalone
/api/v1/discovery/agent/configendpoint to generate credentials). - For air-gapped environments, the binary and a manually populated
agent-config.yamlcan be distributed together via standard software deployment tooling (SCCM, Ansible, etc.).