For maintainers. Using T3 Code? See docs/user.
Status: implemented
Resource telemetry replaces recurring ps, PowerShell, ioreg, and pmset
subprocess probes with two persistent, direct data sources:
sysinfo;The native monitor owns bounded in-memory history. The server only merges and summarizes that history when diagnostics requests it. Telemetry history is not persisted to disk or continuously copied into Node.
The monitor is intentionally not a Node native addon.
ffi-rs, or dynamic-library ABI is loaded into the server process.The cost is one persistent child process and NDJSON serialization. That is a better failure boundary than repeatedly spawning shell utilities or loading native code into Node.
| 1 | Electron main |
| 2 | ├─ powerMonitor |
| 3 | ├─ app.getAppMetrics() while diagnostics is open |
| 4 | ├─ inherited fd 4, telemetry NDJSON ─────────────┐ |
| 5 | └─ inherited fd 5, demand-control NDJSON ◀──────┤ |
| 6 | ▼ |
| 7 | Node server ── stdin/stdout NDJSON ── Rust resource monitor |
| 8 | │ |
| 9 | ├─ ResourceTelemetry Effect service |
| 10 | ├─ background power policy projection |
| 11 | └─ WebSocket RPC/subscription ── diagnostics UI |
Electron telemetry is unavailable. The native monitor still runs beside the
server and tracks the server process tree. Power fields degrade to unknown
instead of invoking platform shell commands.
Windows desktop packages currently ship the Windows resource-monitor executable.
That executable cannot run inside the Linux WSL backend, so a WSL-only backend
does not receive resourceMonitorPath and reports native process telemetry as
unavailable. Electron host-power telemetry remains available over the inherited
desktop pipe. Supporting native WSL process telemetry requires publishing a
Linux sidecar for each supported architecture in the Windows artifact and
converting its packaged path into the selected distro; the configuration
deliberately does not pass the Windows .exe into WSL as a false fallback.
The executable lives in native/resource-monitor.
It receives schema-compatible commands on stdin and emits one JSON object per line on stdout:
configuresetExternalProcessessetSampleIntervalsetStreamingsampleNowreadHistoryshutdownhellosnapshothistoryChunkerrorThe protocol version is defined by
RESOURCE_MONITOR_PROTOCOL_VERSION in
packages/contracts/src/resourceTelemetry.ts.
The monitor keeps one sysinfo::System instance and refreshes it at the
power-adaptive interval selected by the server. It collects:
On Linux, task/thread enumeration is disabled. Command lines are refreshed with
each sample so process replacements remain visible. Disabling task enumeration
avoids walking every /proc/<pid>/task/<tid> directory on each refresh.
Each sample scans the accessible process table, builds the PID/PPID graph, and retains:
node, tsgo, language servers, and other tools;Process identity is (pid, startTimeMs), not PID alone. Electron and native
start times are matched with a two-second tolerance because native start times
can have coarser platform resolution.
The process list is emitted in depth-first tree order so renderer collapse and expansion preserves complete subtrees.
Every native sample is appended to a one-hour in-memory ring bounded to 3,600
snapshots, 20,000 retained process rows, and 64 MiB of retained history bytes.
History stays in the sidecar until a readHistory request and is returned in
bounded chunks. The first bound reached wins, so high process counts or large
process names and command lines shorten the effective history window.
Periodic snapshot streaming is disabled by default. The server enables it only
while at least one diagnostics subscription is retained. sampleNow remains
available for explicit refreshes and identity validation.
The server adjusts native sampling without restarting the sidecar:
This is counter sampling, not syscall tracing.
Those deeper tracing systems can be added later as opt-in diagnostic modes
without changing the public ResourceTelemetry model.
The monitor preserves platform semantics instead of presenting all counters as equivalent:
sysinfo.The UI therefore labels these values as I/O reads and writes and exposes the
per-process ioSemantics value.
Group totals are observed deltas since telemetry startup. Per-process total columns are the operating system's cumulative counters for that process.
Electron main owns DesktopTelemetryPublisher.
Power events trigger an immediate snapshot. While diagnostics is closed, the server sends the configured active and idle host-power intervals to Electron (30 seconds and 2 minutes in the balanced profile). During those heartbeats Electron reads:
powerMonitor.isOnBatteryPower();powerMonitor.getSystemIdleTime();powerMonitor.getSystemIdleState();powerMonitor.getCurrentThermalState().app.getAppMetrics() is only called while diagnostics demand is active. Its
live cadence is 1 second on AC, 5 seconds on battery, and 15 seconds while
locked, suspended, or thermally constrained.
It also listens for:
Suspension stays latched across event-driven snapshots. An explicit resume clears it immediately; a periodic heartbeat also clears a stale latch so a missed resume event cannot leave telemetry permanently constrained.
Electron does not expose a cross-platform low-power-mode getter, so that field
remains unknown.
The desktop backend is spawned with:
These are private Electron-main/server pipes. They do not use the renderer WebSocket and are recreated for every backend restart.
The implementation is under apps/server/src/resourceTelemetry.
ResourceMonitorBinaryResolves an executable from:
T3CODE_RESOURCE_MONITOR_PATH;Unsupported platforms, missing binaries, and non-executable binaries use schema-backed tagged errors with descriptive messages.
NativeTelemetryClientOwns the resource-monitor process and protocol.
sampleNow;Snapshot sequence numbers are scoped to a monitor generation. Server ingestion uses the monitor restart count as the generation key, so sequence reset after a restart cannot freeze telemetry.
DesktopTelemetryReceiverReads fd 4, decodes schema-validated messages, stores the latest Electron snapshot, and publishes desktop health. It writes diagnostics demand to fd 5 and gives the first sample a 90-second startup deadline. Once samples arrive, the stale deadline stays beyond the slower configured host-power interval with 30 seconds of scheduling grace, so intentional 2–10 minute idle polling does not oscillate the policy between constrained and unconstrained states. Decode errors, protocol mismatch, control-write failure, stream failure, stale input, and normal stream closure are represented explicitly.
ResourceTelemetryMerges native and Electron data and owns public telemetry semantics.
(pid, startTimeMs) before process signaling;Electron and monitor processes are visible but are not valid targets for the existing process-signal RPC.
ResourceTelemetryHistory is a pure on-demand projection. It replays raw native
snapshots to derive rates, lifecycle counters, buckets, and process summaries.
Current Electron process metrics are intentionally excluded from historical
replay so they cannot overwrite older native CPU or memory samples.
ResourceAttributionTracks known logical application I/O separately from OS counters. Current integration points record successful writes for:
Entries contain component, operation, logical bytes, count, and elapsed time.
Future persistence paths should call ResourceAttribution.record rather than
adding diagnostics-specific counters.
HostPowerMonitor consumes DesktopTelemetryReceiver directly; observing host
power does not retain live resource diagnostics or invoke shell probes.
The monitor updates its latest timestamp on every Electron sample but only publishes semantic state changes. Increasing idle seconds alone does not cause a background-policy broadcast every second.
The WebSocket RPC surface provides:
The diagnostics page displays:
Legacy process diagnostics RPCs are projected from the same service so they no longer start recurring process-table commands.
Desktop artifact builds compile the Rust target, stage it as
resources/resource-monitor/t3-resource-monitor[.exe], and pass its path to the
backend bootstrap.
CLI release jobs upload each active platform monitor artifact and copy it into:
| 1 | apps/server/dist/resource-monitor/<platform>-<arch>/ |
The published server package already includes dist, so those executables ship
with the CLI. Missing platform artifacts degrade native telemetry to
unavailable; the server continues running.
Steady state uses:
app.getAppMetrics() calls while diagnostics is closed;The diagnostics page exposes the monitor's own process resource usage and collection duration so the observer's cost is measurable.
Failures are isolated:
High-value follow-up work can use the existing service boundaries:
ResourceAttribution instrumentation for databases, checkpoints,
caches, and file synchronization;These additions should preserve the current rules: direct platform APIs, schema-validated boundaries, explicit metric semantics, bounded retention, and no mandatory telemetry persistence.