← Back to Blog

How to Inspect WebSocket and Protobuf Frames on macOS

· 8 min read

A realtime feature stops updating. The WebSocket is still connected, frames are still flowing, and your browser's network tab shows a long list of messages that all read as Binary Message — a length and nothing else. You can see that a frame arrived. You cannot see what it carried, so you cannot tell which message broke the UI.

That opacity is the whole problem. Many realtime protocols pack their payloads as Protobuf or another binary format, and a generic viewer has no way to turn those bytes back into fields. The outcome of this guide is concrete: capture the WebSocket connection through Rockxy on macOS, read its frames in order, and use the Protobuf preview to decode a binary frame without providing a schema — so you can point at the exact message that carried the bad payload.

Why binary WebSocket frames are hard to read

A WebSocket is one long-lived connection that both sides push messages over. Two things make the binary case painful:

  1. The frames are opaque by default. A text frame is JSON you can skim. A binary frame is raw bytes — Protobuf, MessagePack, a custom packing — and a hex dump does not tell you the field that changed between a working message and a broken one.
  2. The interesting frame is buried. A busy socket sends heartbeats, presence updates, and payload frames on the same channel. The one message that matters is somewhere in a scrolling list, next to hundreds that do not.

Reading the connection in order and decoding individual frames turns that wall of bytes into fields you can compare. Rockxy is a local proxy on your Mac, so the whole workflow runs on your machine — the capture and the decode both stay local.

What you will need

  • Rockxy on macOS 14 (Sonoma) or later. It is a native app; download it from the Rockxy download page.
  • A client that opens a WebSocket — a browser app, a native app, or a script — that you can route through Rockxy's proxy port.
  • For an encrypted wss:// connection: the host inside Rockxy's SSL Proxying scope and the client trusting Rockxy's certificate path. Plain ws:// needs neither.
  • A way to trigger the realtime action you want to inspect, so the frame you care about is actually sent during capture.

Step 1 — Capture the WebSocket connection

Start Rockxy, confirm capture is on, and route your client through its proxy port. The WebSocket upgrade begins as an ordinary HTTP request, so once the client is pointed at Rockxy the connection appears in the traffic list like any other flow. If your client is a local service and nothing shows up, that is the loopback bypass, not a WebSocket problem — the fix is in Why Localhost Traffic Isn't Showing in Your Proxy on macOS.

For wss://, the upgrade is inside TLS, so the host has to be in SSL Proxying scope with the certificate trusted. Without that you will still see a row, but it stays a tunnel — no frames to read. The certificate steps are in How to Debug HTTPS Traffic on macOS and the certificates and trust docs.

What you should see in Rockxy: a row for the WebSocket host, with the 101 Switching Protocols response that marks a successful upgrade. That row is the live connection — the next step reads inside it.

Step 2 — Open the connection and read its frames in order

Select the WebSocket flow. Instead of a single request and response, Rockxy shows the stream of frames sent over that connection, in the order they crossed the wire, with direction (sent versus received) and size. Now trigger the realtime action that misbehaves — send the message, flip the toggle, move the object — so the frame you care about lands in the list while you are watching.

Text frames read directly. Binary frames show up as binary — a size and a payload you cannot yet interpret. That is expected. The point of reading in order is to narrow the search: correlate the moment the UI broke with the frame that arrived at that instant, so you are decoding one specific frame rather than guessing across the whole stream.

Step 3 — Decode a binary frame with the Protobuf preview

Select a binary frame and open Rockxy's Protobuf preview. This is a best-effort decode of the frame's bytes into a structured field view — you do not have to supply a .proto schema. Rockxy reads the Protobuf wire format directly, so field numbers, wire types, and values come back as a readable tree even when you do not have the message definition on hand.

That is usually enough to answer the question that started the debugging session: was the field set, was the number the value you expected, did an enum land on the wrong case, is a repeated field empty when it should have items? Decode the frame from the working moment and the frame from the broken moment, then compare the two field trees. The message that carried the bad payload stops being a length in a list and becomes a specific field with a specific wrong value.

What you should see in Rockxy: the selected binary frame rendered as a field-by-field structure rather than a hex dump — the same shape shown in the previewer below. If a frame does not decode cleanly, that is a real signal too: it is often not valid Protobuf, which points at a different packing format or a corrupted payload.

Rockxy's custom previewer tab on macOS decoding a binary WebSocket frame into a readable Protobuf field tree, showing field numbers and values instead of a raw hex dump.
A binary Protobuf WebSocket frame read in Rockxy's previewer without supplying a schema. The opaque payload becomes a field-by-field structure, so you can see exactly what the message carried.

If it does not work

The WebSocket row stays a tunnel and shows no frames. For wss:// that is TLS, not the socket. Add the host to SSL Proxying scope and trust the certificate; until the handshake is decrypted, Rockxy cannot see the frames inside it.

The connection fails as soon as you route it through the proxy. If the server pins its certificate, Rockxy will not silently defeat that — a pinned or failed handshake is surfaced as a failed transaction in the traffic list so you can see it happened, rather than being decrypted. Pinned hosts cannot be inspected this way.

The binary frame does not decode into fields. The Protobuf preview is a best-effort decode. If the frame is not Protobuf — a different binary packing, a compressed payload, or a fragmented message — it will not resolve into a clean tree. Confirm the payload format on the sending side before reading too much into a failed decode.

No frame appears for the action you triggered. The frame has to be sent while capture is live. Trigger the realtime action after the connection is selected and capturing, not before, so the message lands in the stream you are watching.

What this does and does not do

Two limits are worth stating plainly. The Protobuf preview is a best-effort decode of a frame without a schema — it reads the wire format to show field numbers and values, but it is not a schema-aware editor and it does not map field numbers back to your named message types. Field 3 shows as 3, not as its name in your .proto; cross-referencing to your schema is on you.

Second, this is a per-frame decode of a captured connection, not a lifecycle view. Rockxy shows the frames that crossed the wire while it was capturing; reason about ordering from that list. Rockxy is macOS-only today.

Next steps

The core idea is short: capture the WebSocket, read its frames in order, and decode the binary ones into fields so a broken realtime moment maps to a specific message. From there, if your setup also needs to reach the server through a corporate proxy, the combined path is covered in Debugging WebSocket Protobuf Traffic Behind an Upstream Proxy. For the encrypted case, How to Debug HTTPS Traffic on macOS covers certificate trust end to end, and the Rockxy docs go deeper on capture and previewers. When you need the app, grab it from the download page.

Rockxy — native macOS proxy debugger

Try Rockxy — Free and Open Source

A native macOS proxy debugger for HTTP, HTTPS, WebSocket, and API traffic. No cloud account. No subscription. Download the app or audit the source directly on GitHub.

Open source · AGPL-3.0 · Native macOS · No account required · Source available to audit