# 3pm-backend route `nodeProcessTrees`

Paths are relative to the backend's origin (https://fleet.edgecr4ft.net, or your own 3pm-backend), not to this site. Index: https://3pm.edgecr4ft.net/docs/backend/api.md

---

tokens:
  claude: 1887
  o200k: 1140
# GET /api/buckets/{clusterId}/nodes/{nodeId}/trees (nodeProcessTrees)

The process trees of every instance of the node, as last received — every process and the folded picture of `3pm ls --tree` — with the count of trees refused; what the dashboard's panel polls.

- auth: an account — Authorization: Bearer 3pmu_<token>, or the dashboard's session cookie
- node: also open to a node's own bearer (3pma_…) when its row is granted fleet:read; that node's bucket only, and for the calls it placed itself
- answer: ProcessTreesView (JSON)

## Types

```ts
/** `GET /api/buckets/{id}/nodes/{node}/trees`: the process tree of every instance of the node, as the hub last received it. */
interface ProcessTreesView {
  node_id: NodeId;
  /** One per instance the hub holds a tree for; an instance running alone has none. */
  trees: ProcessTreeView[];
  /** Trees refused since the backend first held the node (the count survives its reconnects): an id the process table does not list, or over the caps (100 trees a node, 256 lines a tree, 64 bytes a name). */
  refused: number;
}

/** `3pmn_…` — a node. */
type NodeId = string;

/** `GET /api/buckets/{id}/nodes/{node}/procs/{proc}/tree`: one instance's tree — every process, and the folded picture `3pm ls --tree` draws. 404 while the backend holds none: an instance running alone, before its first tree, or of a past incarnation. */
interface ProcessTreeView {
  node_id: NodeId;
  /** The daemon's process id. */
  id: number;
  /** Incarnation: a restart starts a new one. */
  gen: number;
  /** The tree's revision: moves when the membership changes, never on cpu or memory. */
  rev: number;
  name: string;
  instance: number;
  ns?: string;
  /** The agent's clock on the frame, epoch ms. */
  ts: number;
  /** The totals of the same frame as the lines, omitted members included: percent of one core, RSS summed (an upper bound when members share pages). */
  cpu_percent: number;
  mem_bytes: number;
  /** Members the daemon cut by its caps (256 lines a tree); their figures are in the totals. */
  omitted: number;
  /** Every line: `procs[0]` the spawned pid, a parent before its children, siblings by pid. */
  procs: TreeProc[];
  /** The folded picture: chains of single children on one row, twins grouped. */
  compact: CompactRow[];
}

/** One live process of an instance's tree. */
interface TreeProc {
  pid: number;
  /** As the kernel reports it — not necessarily a member. */
  ppid: number;
  /** The executable's name as the OS shows it (at most 64 bytes, text the app controls), never the arguments. */
  name: string;
  /** Percent of one core; 0 on a member's first tick. */
  cpu_percent: number;
  mem_bytes: number;
  /** Process start, epoch ms (second resolution). */
  started_at_epoch_ms: number;
  /** Left the app's process group (`setsid`): `3pm stop` will not reach it. Absent when false. */
  detached?: boolean;
  /** Always absent on this API: ports do not travel on the link (the record's `listen_ports` is their union). */
  listen_ports?: number[];
  /** The process's network traffic; absent where the node has no source. */
  net?: NetUsage;
}

/** One display row of the folded tree, in drawing order. */
interface CompactRow {
  /** 0 for the spawned pid's row, +1 per level below it. */
  depth: number;
  /** Last among its siblings. */
  last: boolean;
  /** A process name, or a folded chain (`npm › bash › node`). */
  label: string;
  /** The process (the last link of a chain); absent on a `×N` group. */
  pid?: number;
  /** Twins folded into this row; 1 for a single process or chain. */
  count: number;
  /** Processes this row stands for. Sums to `procs.length` over the rows. */
  procs: number;
  cpu_percent: number;
  mem_bytes: number;
  /** The last link's start (a chain), the oldest twin's (a group). */
  started_at_epoch_ms: number;
  /** The row is one detached process. */
  detached: boolean;
  /** Always empty on this API: ports do not travel on the link. */
  listen_ports: number[];
}

/** Network traffic of a process, long keys. */
interface NetUsage {
  rx_bytes_per_s: number;
  tx_bytes_per_s: number;
  /** Since the daemon started measuring this process. */
  rx_bytes: number;
  tx_bytes: number;
  /** A lower bound: the window lost some notifications. Absent when false. */
  partial?: boolean;
}
```
