Inspect traffic
Edit Protobuf and gRPC
Since 0.4.0 Requires Cloak Proxy 0.4.0 or later.
Change gRPC, gRPC-Web, Connect and Protobuf messages as JSON in breakpoints, Compose, Map Local and scripts; Cloak Proxy encodes them back to Protobuf and shows each call's gRPC status.
Protobuf bodies are binary, so you can’t edit them like JSON. Cloak Proxy shows them as JSON wherever you can change a body: a paused breakpoint, Compose, a Map Local rule and a script. When you send it, the JSON is encoded back to Protobuf and framed the way the call expects (gRPC, gRPC-Web or Connect). You can mock an edge case of a gRPC API without writing code.
With your .proto files imported, messages use field names. Without them, fields are keyed by number, in a form that encodes back to exactly the same bytes.
A paused gRPC-Web response, edited as JSON. Execute encodes it again.
- How the body is framed and decoded
- A field typed in its key
What Cloak Proxy reads
Section titled “What Cloak Proxy reads”| Content-Type | Framing | Status |
|---|---|---|
application/grpc, application/grpc+proto |
gRPC: length-prefixed messages | In the grpc-status header or HTTP/2 trailers |
application/grpc-web, application/grpc-web+proto |
gRPC-Web: the same frames, then a trailer frame | In the trailer frame, inside the body |
application/grpc-web-text, …+proto |
gRPC-Web as base64 text | In the trailer frame |
application/connect+proto |
Connect streaming: frames, then an end-of-stream message | In the end-of-stream JSON |
application/proto with a Connect-Protocol-Version header |
Connect unary: one bare message | The HTTP status, with a JSON {code, message} body for errors |
application/x-protobuf, application/protobuf, application/vnd.google.protobuf |
One bare message | None |
Messages compressed with gzip or deflate are decompressed to read and edit them, and sent on uncompressed, which every gRPC peer accepts.
The gRPC status
Section titled “The gRPC status”gRPC answers HTTP 200 whatever happened; the call’s own result is its gRPC status. Cloak Proxy finds it in the headers, in a gRPC-Web trailer frame or in a Connect end-of-stream message.
| Where | What you see |
|---|---|
| Traffic list | A failed call’s Status shows 200 ✕ in red and its dot turns red. Hover for the status name and message. |
| Inspector header | A gRPC NOT_FOUND pill (green for gRPC OK) next to the HTTP status. Hover for the code and message. |
| Body | The decode note starts with the status, for example Status NOT_FOUND · gRPC-Web without a schema: fields by number. A body with only a status shows it as {status, message}. |
The HTTP status says 200; the gRPC status says the order wasn't found.
Edit messages
Section titled “Edit messages”- In a breakpoint, a paused gRPC, gRPC-Web, Connect or Protobuf body opens as JSON under a purple bar. The bar says how it’s framed, the message type (or field numbers (no schema)) and how many messages it holds.
- Edit the JSON. The editor’s Tree view works too.
- Click Execute. Cloak Proxy encodes the messages, frames them as before and keeps the trailers. If the JSON doesn’t fit (an unknown field, an enum value that doesn’t exist, a decimal in a whole-number field), the bar shows why and nothing is sent.
A body you don’t change is sent byte for byte. A stream with several messages is a JSON array, one item per message; add or remove items to change how many are sent.
Field numbers without a schema
Section titled “Field numbers without a schema”Without a schema, each key is a field number, with the wire type after a colon when the value alone doesn’t say it:
| Key and value | Encodes as |
|---|---|
"1": 150 |
A varint. Negative numbers are written as 64-bit two’s complement, as int32 and int64 are. |
"2": "Ada Park" |
Length-delimited text |
"3": {"1": 7} |
A nested message |
"4:bytes": "3q2+7w==" |
Length-delimited bytes, in base64 |
"5:float": 1.5, "5:fixed32": 7 |
32-bit |
"6:double": 2.5, "6:fixed64": 7 |
64-bit |
"7:sint64": -2 |
A zigzag varint (sint32, sint64) |
"8": [1, 2, 3] |
A repeated field: one value per occurrence |
Packed repeated fields arrive as one length-delimited value, so they show as bytes. Import the .proto file to edit them as lists.
With a schema
Section titled “With a schema”With an imported schema, messages use the Protocol Buffers JSON mapping: field names, enums by name, 64-bit numbers as strings. The type is the one picked for the URL under Decode As, or the gRPC method’s input or output type. Unknown fields and enum values are refused, so a typo can’t silently drop a field.
Write Protobuf from JSON
Section titled “Write Protobuf from JSON”Compose
Section titled “Compose”Set the request’s Content-Type to a Protobuf type (for example application/grpc-web+proto), then write the body as JSON. A purple bar above the body says it will be encoded. When you send, the JSON is encoded with the URL’s message type, or by field number, and framed. Edit and Repeat on a captured Protobuf request opens its body as JSON.
Map Local
Section titled “Map Local”A Map Local rule can answer a gRPC call with a message written as JSON.
A gRPC-Web answer written as JSON, with its status.
- In the rule’s Respond with › Inline, set the
Content-Typeheader to the type the app expects. - Turn on Write the body as JSON and send it as Protobuf.
- Enter a Message type from your imported schemas, or leave it empty to write fields by number (or to use the type the gRPC path gives).
- For gRPC-Web, Connect and gRPC, pick the gRPC status and a message, for example 5 NOT_FOUND to test the app’s error handling. gRPC-Web puts it in a trailer frame, Connect in the end-of-stream message, and gRPC in the
grpc-statusheader. - Write the body: an object for one message, an array for several (a stream), or
[]for a reply that is only a status.
The editor checks the body as you type. If a request matches while the body doesn’t encode, the app gets a 500 that says why.
Scripts
Section titled “Scripts”A script’s request and response get a protobuf property for Protobuf bodies: the message as JSON (an array for several), with field names or field numbers as above. Change it, and the body is encoded again:
function onResponse(context, url, request, response) { response.protobuf.status = "CANCELLED"; // with a schema // response.protobuf["3"] = 4; // without one return response;}body still holds the raw body as text; leave it alone when you change protobuf.
With an AI assistant
Section titled “With an AI assistant”The get_protobuf MCP tool returns a captured body as JSON with its framing, message type, gRPC method, every message of a stream, and the gRPC status and trailers. To change a reply, an assistant can create a Map Local rule with upsert_rule: headers with the Content-Type, body as the JSON, and protobuf: {messageType, grpcStatus, grpcMessage}.
Make the app’s GetOrder call fail with NOT_FOUND, using the response of request #42 as the starting point.
Limits
Section titled “Limits”- gRPC over HTTP/2 carries its status in trailers. A Map Local answer sends it in the headers instead, which gRPC clients accept for a reply with no messages (trailers-only); with messages, most clients read it too, but some expect a trailer.
- WebSocket messages that carry Protobuf are shown, not edited, as Protobuf.
- Groups (a deprecated wire type) can’t be edited without a schema.