ProtoDecodeX is a Protobuf wire-format decoder that runs in the browser.
It is built for the annoying cases: you have raw bytes, maybe no .proto, maybe only a log dump or a copied payload from a proxy, and you still need to figure out what is inside.
This is not a big all-in-one Protobuf tool. It is closer to a workbench for the moments when you need to inspect Protobuf data quickly, without spinning up a backend service or dragging in a full code-generated setup.
It is useful when you want to answer questions like:
- What fields are in this payload?
- Is this hex, Base64, a Java byte array, or a Java escaped string?
- Does this stream contain length-delimited messages?
- Can I paste a small
.protosnippet and map field numbers to names? - Is that bytes field XOR-encoded, and can I keep digging?
Everything runs locally in the browser. You can paste data, upload a file, inspect the wire structure in a table, and add a lightweight schema when it helps.
- Reverse engineering unknown Protobuf messages
- Inspecting binary payloads pulled from logs, packet captures, or proxies
- Looking at gRPC message bodies
- Working with Java byte arrays or escaped strings from code and logs
- Peeling through nested bytes fields one layer at a time
Most Protobuf tools assume you already know the schema. Real debugging is often messier than that.
Sometimes you only have:
- a chunk of hex from a log
- a Base64 blob from an API trace
- a Java byte dump
- a gRPC payload with framing still attached
ProtoDecodeX starts from that kind of raw input. First get the wire format in view, then add just enough context to make the data readable.
- Supports hex, Base64, Java decimal byte arrays, Java escaped strings, and local file upload
- Detects the input format and normalizes it into readable hex
- Decodes Protobuf wire data into a table with expandable content
- Parses varint length-delimited message streams
- Tries to skip gRPC framing headers when present
- Lets you paste message definitions for schema-aware display
- Includes Smart Decode for conditional XOR and manual XOR workflows
- Lets you reuse decrypted bytes as the current decode input
- Persists useful state in
localStorage - The repo also includes tests, CI, Docker, and deployment config
| Format | Example |
|---|---|
| Hex bytes | 08 96 01 |
| Base64 | CJYB |
| Java decimal byte array | 8, 150, 1 |
| Java escaped string | \010\226\001 |
| Local file upload | Any local binary file read by the browser |
- Node.js 20 is recommended to match CI
- npm
npm install
npm startThen open http://localhost:3000.
npm run buildThe build output goes to build/.
npm test- Paste raw data or upload a file.
- ProtoDecodeX detects the input format and normalizes it into pretty hex.
- Click
Decodeto parse the payload into Protobuf wire parts. - Inspect the result table, byte ranges, and nested byte fields.
- Optionally enable advanced tools such as schema-based display or Smart Decode.
The decoder shows the pieces that matter when you are staring at unknown payloads:
- Field number
- Wire type
- Byte range
- Raw value bytes
- Nested message-like structures when applicable
That is usually enough to sketch the shape of the message even when the original .proto file is nowhere to be found.
Every supported input format ends up in the same normalized, read-only hex view. It is a simple idea, but it helps a lot when the original source came from logs, Java arrays, or escaped strings and you want one clean byte representation before you go further.
If your input contains multiple messages with varint length prefixes, enable parse varint length delimited input and the app will split the stream into message boundaries. This comes up more often than you would think in batched payload dumps and stream-oriented transports.
If the payload looks like it starts with a gRPC frame header, the decoder tries to skip it before parsing. Small thing, but it saves an annoying manual step.
You can paste lightweight message definitions in the advanced panel to map field numbers to names and types.
Example:
message User {
string name = 1;
uint32 age = 2;
repeated string tags = 3;
}With a schema in place, ProtoDecodeX can show:
- Field names instead of only field numbers
- Basic type-aware rendering such as
string,bool,float, anddouble - Repeated and optional field hints
The parser is intentionally lightweight. Good enough for quick inspection, not meant to replace full .proto compilation.
Some payloads hide the useful part inside a bytes field and then add a simple XOR step on top. The Smart Decode panel supports two workflows:
- Conditional XOR: decode one field with the value from another field, optionally gated by a varint field condition
- Manual XOR: paste arbitrary input and apply a numeric XOR value
If the result looks like valid Protobuf, you can push those bytes back into the main decoder and keep going.
ProtoDecodeX is local-first:
- Decoding happens in the browser
- The app does not require a backend decode API
- Local files are read by the browser and turned into bytes locally
So you do not have to paste everything again every time, the app stores some state in localStorage, including:
- Input text
- Detected input format
- The
parseDelimitedtoggle - Pasted schema text
- Selected message name
- Whether a decode result existed
If you work with sensitive payloads on a shared machine, clear the app state or use a private browser profile.
ProtoDecodeX is a tool for debugging and inspection, not a full protoc replacement.
- It focuses on wire-format decoding, not full schema compilation or validation
- Schema parsing is intentionally lightweight and supports a subset of
.protosyntax - Packed repeated detection is heuristic
- Some malformed payloads may decode partially and leave
leftOverbytes - There is no built-in export-to-file or share-by-URL workflow today
- There is no backend storage or collaborative session model
| Command | Description |
|---|---|
npm start |
Start the Vite development server on port 3000 |
npm run build |
Create a production build in build/ |
npm test |
Run the test suite with Vitest |
npm run test:watch |
Run tests in watch/UI mode |
The repository also includes convenience scripts:
scripts/local-build.shscripts/docker-build.sh
The repository already includes a multi-stage Dockerfile, docker-compose.yml, and nginx.conf, so containerized development and deployment are straightforward.
docker compose --profile dev up --build protobuf-decoder-devdocker compose --profile prod up --build protobuf-decoder-prod- React 18
- Vite 5
- Semantic UI React
bufferfor byte handlingjsbifor large integer support- Vitest + Testing Library + jsdom
Issues and pull requests are welcome. The most useful contributions here are usually pretty concrete:
- Decoder correctness improvements
- Better schema parsing coverage
- More type-aware rendering
- UX polish for large payloads
- Better documentation and examples
Before opening a PR, it is a good idea to run:
npm test
npm run buildMIT
