NodeGraf IntegrationLaravel Reverb consumer
Resource events

Consume live NodeGraf metrics.

Use a Laravel Reverb-compatible client to subscribe to resource events and render CPU, memory, disk, and status data in your application.

There are two contracts. Select one based on the resource type.

Portainer resource

Usage for one resource

Use the resource name as the channel identity.

Eventnodegraf.snapshot
Channelprivate-stack-{stack_name}
Identitystack_name
Proxmox resource

Usage for one server

Use the service ID as the channel identity.

Eventnodegraf.server_snapshot
Channelprivate-instant-app-server.{service_id}
Identityservice_id

1. Configure the Reverb client

Keep the signing secret on the server. The browser receives only public connection settings and a proof for the selected channel.

01
Resolve resource metadata

Load the resource identity and its allocation limits from your application.

02
Create the channel proof

Sign {service_id}|{client_id}|{channel_name} with your server-side Reverb secret.

03
Authorize the private channel

Your auth endpoint checks the session and returns the standard Pusher channel signature.

Client bootstrap

const pusher = new Pusher(config.appKey, {
  cluster: 'mt1',
  wsHost: config.host,
  wsPort: config.port,
  wssPort: config.port,
  wsPath: config.path,
  forceTLS: config.scheme === 'https',
  enabledTransports: ['ws', 'wss'],
  disableStats: true,
  channelAuthorization: {
    endpoint: config.authEndpoint,
    transport: 'ajax',
    params: {
      csrfToken: config.csrfToken,
      channelProof: config.channelProof
    }
  }
});

2. Portainer resource

Subscribe to one resource channel and accept only events carrying the same resource name.

Portainer usage event

CPU usage uses the resource allocation supplied by your application. Memory and disk values are bytes.

nodegraf.snapshot

Payload fields

  • Identity: stack_name
  • CPU: cpu_cores_used
  • Memory: memory_used_bytes, memory_limit_bytes
  • Disk: optional disk_used_bytes
  • Time: sampled_at

Example payload

{
  "node_name": "node-01",
  "sampled_at": "2026-08-24T05:00:00Z",
  "stack_name": "service-18106",
  "cpu_cores_used": 0.3,
  "memory_used_bytes": 415236096,
  "memory_limit_bytes": 2147483648,
  "disk_used_bytes": 125829120,
  "disk_collected_at": "2026-08-24T04:59:30Z"
}

Consumer

const channelName = `private-stack-${stackName}`;
const channel = pusher.subscribe(channelName);

channel.bind('nodegraf.snapshot', (payload) => {
  if (payload.stack_name !== stackName) return;

  renderUsage({
    cpuPercent: round(payload.cpu_cores_used / cpuLimit * 100),
    memoryUsedMb: round(payload.memory_used_bytes / 1048576),
    diskUsedMb: payload.disk_used_bytes == null
      ? null
      : round(payload.disk_used_bytes / 1048576),
    sampledAt: payload.sampled_at
  });
});

3. Proxmox resource

Subscribe to one server channel and accept only events carrying the same service ID.

Server usage event

CPU allocation is included in the event. Memory, disk, and status are reported for the selected server.

nodegraf.server_snapshot

Payload fields

  • Identity: service_id
  • Server: vmid, vm_name, status
  • CPU: cpu_cores_used, cpu_cores_allocated
  • Memory: memory_used_bytes, memory_limit_bytes
  • Disk: optional used and limit values

Example payload

{
  "node_name": "pve-01",
  "sampled_at": "2026-08-24T05:00:00Z",
  "service_id": 1001,
  "vmid": 1201,
  "vm_name": "server-1001",
  "status": "running",
  "cpu_cores_used": 0.25,
  "cpu_cores_allocated": 2,
  "memory_used_bytes": 536870912,
  "memory_limit_bytes": 4294967296,
  "disk_used_bytes": 21474836480,
  "disk_limit_bytes": 53687091200
}

Consumer

const channelName = `private-instant-app-server.${serviceId}`;
const channel = pusher.subscribe(channelName);

channel.bind('nodegraf.server_snapshot', (payload) => {
  if (Number(payload.service_id) !== Number(serviceId)) return;

  renderUsage({
    cpuPercent: round(payload.cpu_cores_used / payload.cpu_cores_allocated * 100),
    memoryUsedMb: round(payload.memory_used_bytes / 1048576),
    diskUsedMb: payload.disk_used_bytes == null
      ? null
      : round(payload.disk_used_bytes / 1048576),
    status: payload.status,
    sampledAt: payload.sampled_at
  });
});

4. Normalize and handle failures

Live metrics are best-effort. Keep allocation data visible when a live sample is unavailable.

ConditionClient behavior
Wrong resource identityIgnore the event. Never render it in the current view.
Missing optional metricRender Unavailable; do not convert it to zero.
No event for 30 secondsMark live usage unavailable and keep the last known allocation.
Authorization or connection failureShow a non-blocking error and stop retrying in a tight loop.
Resource changesUnsubscribe from the old channel before subscribing to the new one.
Keep the contracts separate.

A Portainer consumer handles nodegraf.snapshot. A Proxmox consumer handles nodegraf.server_snapshot. Do not share their channel or identity validation.