diff --git a/docs/notes/endpoint-telemetry-and-ui-phases.md b/docs/notes/endpoint-telemetry-and-ui-phases.md index 4f0f88f3..5537f957 100644 --- a/docs/notes/endpoint-telemetry-and-ui-phases.md +++ b/docs/notes/endpoint-telemetry-and-ui-phases.md @@ -155,11 +155,18 @@ inventory-snapshot plumbing and one migration. ManagementObjectSearcher, not the friendly strings `Get-PhysicalDisk` prints (Health=0 Op=2 Media=4 Bus=17 for a healthy NVMe); the mapping lives in the collector so the wire stays self-describing. -- **NIC link speed and duplex** - collect, pair into the existing Network - interfaces collection, NOT surfaced yet. -- **Boot type** - fast startup vs true cold boot. Explains why uptime lies and - why a "reboot" did not clear a fault. Collect now; UI placement undecided, - candidate for the Operating system or Hardware card. +- **NIC link speed and duplex** - DONE. Paired onto the existing interfaces + entries by MAC from `MSFT_NetAdapter` (`Win32_NetworkAdapterConfiguration` + carries no link state, so it is a second query). Both fields are null when + unreadable: verified on the live box that a DISCONNECTED adapter returns + empty Speed and FullDuplex, so defaulting would have reported every unplugged + NIC as half-duplex. Not surfaced. +- **Boot type** - DONE. Two facts kept separate: `hiberbootenabled` from the + registry (fast startup configured) and `lastboottype` from Kernel-Boot event + 27 (what the last boot did). Neither substitutes for the other - the registry + cannot say what the last boot did, and the event can age out of the log. + Verified live: BootType=0 with HiberbootEnabled=0. Not surfaced; placement + still undecided. - **Windows Update history, last 5** - PINNED, deferred out of Phase 2. The COM update searcher is not cheap: it routinely takes seconds and sometimes tens of seconds, which would make it the most expensive thing in an otherwise @@ -170,20 +177,28 @@ inventory-snapshot plumbing and one migration. interfering with collections - or with the hosted application itself. Needs its own investigation before any implementation, and if it lands it wants its own cadence (hourly/daily), never the every-cycle path. -- **Defender/AV health** - collect and persist, do NOT surface. Known to - produce false positives in this environment; stored for future wiring. +- **Defender/AV health** - DONE, collected and persisted, NOT surfaced, exactly + as specified. Note the ages are in days and a real 0 means updated today, so + the collector sends null rather than 0 when Defender did not report. A box + with third-party AV or a stripped image has no Defender WMI provider at all, + which is normal here and not a fault. Explicitly dropped after discussion, do not collect: certificates (corporate-managed), BitLocker (network join already requires it), display topology (single monitor on the floor, trained users), session type/RDP (RDP-in not permitted on the shop floor), logon server (local users). -**Pending file-rename operations** - CONFIRMED, collect. A stronger -reboot-required signal than the current `pendingreboot` key, and cheap. +**Pending file-rename operations** - DONE, but NOT as a new collection. It was +already being read: `GetPendingReboot()` OR-s it together with the CBS and +Windows Update keys into one bool. The gap was that the bool cannot say WHICH +signal fired, so the "stronger signal" was already being flattened into the +weaker one. Shipped as `rebootreasons`, a list naming the signals that fired, +leaving `pendingreboot` untouched for compatibility. Empty list = checked, +nothing pending; null = the check failed. -Schema: these add columns to `endpointstates`. One migration, `0014`, with -idempotent guards and a real downgrade, shipped in the SAME change as the model -edit (a model referencing an unapplied column 500s every query on the table). +Schema: these add columns to `endpointstates`. This landed as `0015`, not the +`0014` reserved here - `0014` was taken by the disk health column earlier in +Phase 2. ## Phase 3 - Service/Process Monitors tab split diff --git a/plugins/endpoints/AGENT-CONTRACT.md b/plugins/endpoints/AGENT-CONTRACT.md index 0f3e0b9d..64a2ab53 100644 --- a/plugins/endpoints/AGENT-CONTRACT.md +++ b/plugins/endpoints/AGENT-CONTRACT.md @@ -68,6 +68,18 @@ arrays. `adopt` is first-contact only. "osinfo": { /* opaque object: build, version, install date */ }, "domain": "shopfloor.example.com", // domain or workgroup membership "pendingreboot": false, // omit when unknown; absent != false + "rebootreasons": [], // [] = checked, nothing pending; see notes + "bootinfo": { + "hiberbootenabled": false, // fast startup CONFIGURED + "lastboottype": "Cold" // what the last boot DID: Cold|Fast startup|Resume|Unknown + }, + "defender": { // persisted, deliberately not surfaced + "serviceenabled": true, + "realtimeenabled": true, + "signatureagedays": 0, // real 0 = updated today; null = did not report + "antispywareagedays": 0, + "quickscanagedays": 0 + }, "diskhealth": [ // per PHYSICAL device; see notes { "deviceid": "0", @@ -144,6 +156,28 @@ Notes: genuinely empty now"; omit when you simply did not collect it this cycle. - `pendingreboot` follows that rule strictly: omit it when the check failed. A literal `false` asserts the endpoint is confirmed NOT holding a reboot. +- `rebootreasons` is NOT redundant with `pendingreboot`. That key is a single + bool OR-ed from three registry signals and cannot say which fired, and the + difference is operational: `pendingfilerename` means files are staged for + replacement on next boot, so the box is mid-servicing, while a lone + `componentbasedservicing` or `windowsupdate` key can sit set for weeks on a + machine nobody has rebooted. Same boolean, very different urgency. An EMPTY + array means checked and nothing pending; OMIT the key when the check failed. +- `bootinfo` carries two distinct facts that must not be collapsed: + `hiberbootenabled` is whether fast startup is CONFIGURED (registry, always + readable) and `lastboottype` is what the last boot ACTUALLY did (Kernel-Boot + event 27). The registry cannot tell you what the last boot did, and the event + may have aged out of the log, so neither substitutes for the other. This + exists because uptime LIES under fast startup: a box reports days of uptime + after what the operator experienced as a shutdown. +- `defender` is stored but deliberately NOT surfaced. Defender produces known + false positives in this environment, so wiring it to a card now would train + operators to ignore that card. Signature/scan ages are in DAYS; a real `0` + means updated today, so send `null` when Defender did not report rather than + defaulting to zero. +- `interfaces` entries gained `linkspeedbps` and `fullduplex`, paired on by MAC + from `MSFT_NetAdapter`. Both are `null` when link state was unreadable or the + adapter is disconnected - an unknown duplex must not read as half-duplex. - `diskhealth` is per PHYSICAL device and is NOT the same list as `disks`, which is per volume. One physical drive can back several volumes or none the operator sees, so the two do not share a key and are not merged.