# Your Computer Is Busy When You're Not: Building a Desktop Observability Tool with Electron

You close your laptop.

The screen goes dark.

You assume everything stopped.

But what if it didn't?

Maybe Docker is still transferring data.

Maybe Chrome is still downloading something.

Maybe an application is consuming CPU in the background.

Maybe your computer woke up at 3 AM, performed some background work, and went back to sleep.

Most operating systems already expose pieces of this information.

The interesting part is putting those pieces together.

That's what I wanted to explore:

> **Can we build an observability layer for a desktop computer using Electron?**

Not another CPU/RAM widget.

Something that answers:

> **What is my computer actually doing?**

* * *

# The Idea

Imagine opening your desktop monitoring application and seeing:

```text
TODAY
────────────────────────────────────

Computer active        7h 42m
Idle                   1h 13m
Sleep                  8h 05m

Network
↓ Download             6.8 GB
↑ Upload               1.2 GB

Top applications
────────────────────────────────────

VS Code                3h 42m
Chrome                 2h 18m
Docker                 1h 31m
Slack                  52m

Network-heavy apps
────────────────────────────────────

Docker                 2.4 GB
Chrome                 1.8 GB
VS Code                920 MB
```

Now imagine clicking **"What happened while I was away?"**

```text
18:42  User became idle
18:51  Screen locked
18:53  Docker network activity
19:07  Background sync completed
19:14  System entered sleep
```

That's much more interesting than a CPU percentage.

* * *

# The npm Package That Makes This Easier

Instead of writing native system integrations for everything ourselves, we can start with:

```bash
npm install systeminformation
```

`systeminformation` provides APIs for system information, CPU, memory, battery, filesystem, network, processes, Docker and more. It also supports Linux, macOS and Windows, although individual APIs have platform-specific support.

For example:

```javascript
const si = require("systeminformation");

async function getSystemStats() {
  const [cpu, memory, battery, network] = await Promise.all([
    si.currentLoad(),
    si.mem(),
    si.battery(),
    si.networkStats()
  ]);

  return {
    cpu: cpu.currentLoad,
    memory: memory.active,
    battery: battery.percent,
    network
  };
}

console.log(await getSystemStats());
```

Now our Electron main process can periodically collect system information.

* * *

# Monitoring Network Activity

One of the most useful APIs is:

```javascript
si.networkStats()
```

It provides network statistics including received/transmitted bytes and calculated per-second rates. The package calculates rates based on differences between successive calls, so the first call doesn't provide a meaningful rate yet.

A simple monitor:

```javascript
const si = require("systeminformation");

setInterval(async () => {
  const stats = await si.networkStats();

  for (const network of stats) {
    console.log({
      interface: network.iface,
      download: network.rx_sec,
      upload: network.tx_sec
    });
  }
}, 1000);
```

Now we have something like:

```text
Wi-Fi
Download: 2.4 MB/s
Upload:   180 KB/s
```

But this is only the beginning.

* * *

# Network Connections

We can also inspect active network connections:

```javascript
const connections = await si.networkConnections();

console.log(connections);
```

This gives us information about active TCP/UDP connections.

That means we can potentially build:

```text
Active Connections
────────────────────────────

TCP    443    api.example.com
TCP    443    github.com
TCP    443    google.com
UDP    53     DNS
```

Now the dashboard starts becoming useful for troubleshooting.

* * *

# Which Application Is Active?

Network traffic alone doesn't tell us **who is responsible**.

We also want to know which application the user is interacting with.

One option is a native active-window module such as:

```bash
npm install @jannchie/active-window
```

The package exposes information about the currently selected window and user idle time on supported Windows, macOS and Linux/Xorg environments. Platform coverage isn't identical everywhere, so this should be treated as an OS-dependent component.

Conceptually:

```javascript
const activeWindow = require("@jannchie/active-window");

async function getActiveApp() {
  const window = await activeWindow().getActiveWindow();

  return {
    process: window.windowClass,
    title: window.windowName
  };
}
```

Then we can record:

```text
10:32:14  VS Code
10:32:15  VS Code
10:32:16  VS Code
...
10:45:02  Chrome
10:45:03  Chrome
```

Instead of storing every second individually, we can aggregate this into sessions.

```text
VS Code
10:32 → 10:45
Duration: 13 minutes
```

* * *

# Sleep and Wake

This is where Electron itself becomes useful.

Electron provides the `powerMonitor` module for monitoring system power-state changes, including suspend/resume, AC/battery changes, idle state and lock/unlock events on supported platforms.

In the Electron main process:

```javascript
const { powerMonitor } = require("electron");

powerMonitor.on("suspend", () => {
  console.log("System is going to sleep");
});

powerMonitor.on("resume", () => {
  console.log("System woke up");
});
```

We can turn those events into database records:

```javascript
function recordEvent(type) {
  db.insert({
    type,
    timestamp: new Date()
  });
}

powerMonitor.on("suspend", () => {
  recordEvent("system_sleep");
});

powerMonitor.on("resume", () => {
  recordEvent("system_wake");
});
```

Now our timeline knows when the machine slept.

* * *

# Idle Detection

Electron also provides:

```javascript
powerMonitor.getSystemIdleTime()
```

and:

```javascript
powerMonitor.getSystemIdleState(60)
```

The latter can return states such as `active`, `idle`, or `locked` on supported systems.

For example:

```javascript
const state = powerMonitor.getSystemIdleState(60);
const idleSeconds = powerMonitor.getSystemIdleTime();

console.log({
  state,
  idleSeconds
});
```

We can therefore distinguish:

```text
Active
   ↓
Idle
   ↓
Locked
   ↓
Sleep
```

That's much more meaningful than simply measuring application runtime.

* * *

# Putting Everything Together

Now imagine collecting events from four sources:

```text
                 Electron App
                      │
       ┌──────────────┼──────────────┐
       │              │              │
  powerMonitor   systeminformation   active-window
       │              │              │
       │         CPU / RAM / Net     Active App
       │              │              │
       └──────────────┼──────────────┘
                      ▼
                Event Collector
                      │
                      ▼
                Local Database
                      │
                      ▼
              Activity Timeline
```

A simple event model could be:

```javascript
{
  timestamp: Date.now(),
  type: "network",
  app: "docker",
  downloadBytes: 1824000,
  uploadBytes: 420000
}
```

Or:

```javascript
{
  timestamp: Date.now(),
  type: "power",
  state: "sleep"
}
```

Or:

```javascript
{
  timestamp: Date.now(),
  type: "application",
  app: "Code",
  window: "server.js - Visual Studio Code"
}
```

Now everything becomes queryable.

* * *

# The Interesting Part: Correlation

This is where a normal system monitor becomes a **desktop observability tool**.

Suppose we see:

```text
18:20  User idle
18:23  CPU 65%
18:24  Network 18 MB/s
18:26  Docker active
18:31  420 MB downloaded
18:42  Screen locked
18:45  Network still active
19:02  System sleep
```

We can generate an explanation:

> Docker continued running after the user became idle and transferred approximately 420 MB before the system entered sleep.

That's an insight.

Not just a metric.

* * *

# A Simple Collector

We could start with something surprisingly small:

```javascript
const { powerMonitor } = require("electron");
const si = require("systeminformation");

async function collect() {
  const [cpu, memory, network] = await Promise.all([
    si.currentLoad(),
    si.mem(),
    si.networkStats()
  ]);

  return {
    timestamp: Date.now(),

    system: {
      idle: powerMonitor.getSystemIdleTime(),
      idleState: powerMonitor.getSystemIdleState(60),
      battery: powerMonitor.isOnBatteryPower()
    },

    cpu: cpu.currentLoad,

    memory: {
      active: memory.active,
      total: memory.total
    },

    network: network.map(n => ({
      interface: n.iface,
      rxPerSecond: n.rx_sec,
      txPerSecond: n.tx_sec
    }))
  };
}

setInterval(async () => {
  console.log(await collect());
}, 5000);
```

Five seconds later, we have the beginning of our own desktop telemetry system.

* * *

# But Don't Collect Everything Every Second

This is an important design decision.

You don't necessarily want:

```text
Every 1 second
    ↓
Everything
    ↓
Database
```

That can generate enormous amounts of useless data.

Instead, use different collection intervals.

```text
Power events        Event-driven
App changes         Event-driven
CPU                 5-10 seconds
Memory              10 seconds
Network             1-5 seconds
Connections         10-30 seconds
Daily aggregates    1 minute+
```

And store **state changes** rather than repeated identical states.

For example, don't write:

```text
10:00 VS Code
10:01 VS Code
10:02 VS Code
10:03 VS Code
10:04 VS Code
```

Store:

```text
VS Code
10:00 → 10:04
Duration: 4 minutes
```

This makes the database dramatically smaller.

* * *

# What About Electron's Network Logging?

If the thing you're monitoring is specifically **your Electron application's own network traffic**, Electron has another useful tool: `netLog`.

```javascript
const { app, netLog } = require("electron");

app.whenReady().then(async () => {
  await netLog.startLogging("/tmp/electron-network-log");
});
```

Electron's `netLog` records network events for a session and supports different capture modes. Be careful with `includeSensitive` and `everything`, because those modes can capture sensitive information or transferred bytes.

This is different from `systeminformation`:

```text
systeminformation
        ↓
Whole-system information

Electron netLog
        ↓
Your Electron application's network activity
```

That distinction is important.

* * *

# The Product I Would Build

I wouldn't call this another "Task Manager."

I'd call it something like:

> **Desktop Observability**

The dashboard could have four major views.

### 1\. Activity

```text
VS Code       4h 12m
Chrome        2h 31m
Slack         58m
Docker        1h 42m
```

### 2\. Network

```text
Download     8.4 GB
Upload       1.3 GB

Docker       3.1 GB
Chrome       2.7 GB
VS Code      1.2 GB
```

### 3\. Power

```text
Active       8h 21m
Idle         1h 08m
Sleep        7h 42m

Battery used 34%
```

### 4\. Timeline

```text
09:02  Wake
09:05  VS Code
10:32  Chrome
12:14  Idle
12:31  Docker network activity
13:02  Active
18:42  Lock
19:01  Sleep
```

And then the killer feature:

### Ask the computer

> Why was my laptop slow today?

> What used the most bandwidth?

> What happened while I was away?

> Which applications are active in the background?

> Did anything continue running after I locked the computer?

* * *

# The Architecture

A practical first version could be:

```text
Electron
│
├── Main Process
│   │
│   ├── powerMonitor
│   ├── systeminformation
│   ├── active-window
│   └── netLog
│
├── Collector
│   │
│   ├── power events
│   ├── app sessions
│   ├── CPU/memory
│   └── network stats
│
├── SQLite
│
└── React Renderer
    │
    ├── Dashboard
    ├── Timeline
    ├── Applications
    ├── Network
    └── Power
```

The key design principle is:

> **Collect locally. Correlate locally. Explain locally.**

You don't need to send a user's entire computer activity to a cloud server just to tell them why their laptop was busy.

* * *

# The Bigger Idea

The interesting thing here isn't `systeminformation`.

It isn't Electron's `powerMonitor`.

It isn't network monitoring.

It's the **combination**.

A computer is already generating thousands of signals:

```text
Applications
Processes
CPU
Memory
Network
Power
Sleep
Wake
Idle
Lock
Unlock
```

Today, these signals are mostly presented independently.

A desktop observability layer could connect them.

And instead of asking:

> "What's my CPU usage?"

we can finally ask:

> **"What happened on my computer while I wasn't looking?"**

That's a much more interesting monitoring problem.
