---
title: Heap snapshot diagnostics (memory bloat investigation via CLI)
---

# Heap snapshot diagnostics (memory bloat investigation via CLI)

## What it is

`POST /diagnostics/heap-snapshot` is a CLI endpoint on Omniscio's local control server (port 19519) that captures a V8 heap snapshot of the renderer process, parses it, and returns a small JSON summary describing where the memory is going. It's the supported way for an agent — Claude Code, the in-app chat, an external tool reading `~/.amc/cli-token` — to investigate "Omniscio is using 2 GB of RAM, where is it going?" without the user having to open DevTools.

**Why this exists.** Omniscio strips the Windows menu accelerators (`mainWindow.removeMenu()`), so `Ctrl+Shift+I` does not open DevTools. The standard developer workaround — launching with `--remote-debugging-port` — is forbidden by the project's security postmortem (it would expose an authenticated debugger socket on every Omniscio start). This endpoint lets agents drive a memory diagnosis programmatically while keeping Omniscio's debugger surface closed.

## Where to find it

### How to use it

### Calling the endpoint

```bash
TOKEN=$(cat ~/.amc/cli-token)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:19519/diagnostics/heap-snapshot
```

A successful response looks like:

```json
{
  "ok": true,
  "snapshotBytes": 312654321,
  "summary": {
    "totals": {
      "nodeCount": 8421337,
      "edgeCount": 31845121,
      "totalSelfSize": 2147483648
    },
    "byNodeType": [
      { "type": "string", "count": 5132904, "totalSelfSize": 1612345678 },
      { "type": "object", "count": 1842031, "totalSelfSize": 412345122 },
      { "type": "array", "count": 421873, "totalSelfSize": 98123455 }
    ],
    "topClassNames": [
      { "name": "system / Module", "count": 8123, "totalSelfSize": 142312345 },
      { "name": "HTMLDivElement", "count": 142003, "totalSelfSize": 84123887 }
    ],
    "largeStrings": {
      "count": 1247,
      "totalSize": 783451123,
      "categories": { "dataImage": 612345128, "dataOther": 41205555, "generic": 129900440 },
      "samples": [{ "size": 4823472, "preview": "data:image/png;base64,iVBORw0KGgoAAAANSU…" }]
    }
  }
}
```

### Reading the summary

- **`totals.totalSelfSize`** is what to compare against the OS reported renderer RSS. It's the sum of every node's `self_size` in bytes — close to JS-reachable memory, not counting V8 internals or native buffers.
- **`byNodeType`** is your first triage cut. If `string` dominates, you're looking at retained text/base64/buffers; if `object` dominates, you're looking at retained DOM or app objects; if `code` dominates, you have many compiled functions resident (rare).
- **`topClassNames`** (capped at 25 by `totalSelfSize`) names the worst object retainers. Names are pulled from the V8 `strings` table for non-string-type nodes — for plain objects this is the constructor name (`HTMLDivElement`, `Map`, custom classes, etc.).
- **`largeStrings`** isolates the >1 KB string nodes and splits them into `dataImage` (base64-encoded `data:image/...`), `dataOther` (other `data:` URLs — Office docs, blobs), and `generic` (everything else — long JSON blobs, pasted text, agent message buffers). The 10 largest are returned as `samples` with truncated previews so you can sanity-check what's pinned.

### Optional behavior

- `?keep=true` — the captured `.heapsnapshot` file is normally deleted after parsing to save disk. With `keep=true`, the response includes `snapshotPath` so you (or the user) can later open the file in DevTools' Memory panel for a deeper retention-path walk.
- A second POST while the first is still in-flight returns **429** with `error: "snapshot capture already in flight"`. Capture is single-flight by design — running two concurrent `takeHeapSnapshot` calls would double the transient memory pressure.

## How it behaves

### Cost and limits

- **Time**: ~2–6 seconds end-to-end on a 2 GB renderer. The capture itself is fast; reading and parsing the resulting JSON file is the slow part.
- **Disk**: the snapshot file is 200–500 MB for a typical renderer. It is written under `<userData>/heap-snapshots/<ISO-timestamp>.heapsnapshot` and deleted unless `keep=true`.
- **Main-process memory**: parsing the JSON briefly inflates the main process by ~2–3× the snapshot size. Omniscio's main is normally ~200–400 MB resident; expect a transient spike to ~1.5 GB while parsing a 500 MB snapshot. This is acceptable for a user-triggered diagnostic but is the reason capture is single-flight and rate-limited.
- **Rate limit**: shares the 19519 server's global mutation cap (10/min). Combined with single-flight, the effective cap is one capture every few seconds.
- **Auth**: bearer token required (same `~/.amc/cli-token` that gates `/projects`, `/sessions`, the search routes). No special permission beyond that — anyone who can call `/sessions` can call this.

### When to use it

- "Omniscio's RSS is climbing past 2 GB and I want to know what's pinned." Run the endpoint, look at `byNodeType` first, then drill into `topClassNames` or `largeStrings.samples` based on which dominates.
- "I think feature X retains image data after the user navigates away." Capture a snapshot before opening X, capture another after closing it, compare `largeStrings.categories.dataImage` and `topClassNames` deltas.
- "I want to verify a memory fix actually freed memory." Capture before and after — `totals.totalSelfSize` and the per-class entry for the fixed component should both drop.

### When NOT to use it

- Don't poll it on a timer. Each capture costs disk + transient RAM. Use it when you have a specific question, not as a heartbeat.
- Don't use it to inspect non-renderer processes. The endpoint snapshots `mainWindow.webContents` only; child processes (CLI spawns, `mempalace-server`) are out of scope.
- Don't ship code that depends on the response shape long-term — the `summary` shape is a debug aid and may evolve as the parser learns to extract more useful signals.

## Related

- `src/main/services/cli/cli-server-diagnostics.ts` — endpoint handler, auth gate, single-flight lock, file lifecycle.
- `src/main/services/heap-snapshot-parser.ts` — pure parser, V8 heap-snapshot format → `HeapSummary`. Easy to unit-test with a hand-built fixture.
- `tests/unit/services/heap-snapshot-parser.test.ts` and `tests/unit/services/cli-server-diagnostics.test.ts` cover the parser and the endpoint independently.
