unsandbox.com

Anonymous remote code, compile, & execution API for humans & machine learning agents.

Docs 📚 View Pricing →
unsandbox.
Protocol Buffers Playground - Compile and Test Protobufs in the Browser

December 02, 2025

Protocol Buffers Playground: Compile and Test Protobufs in the Browser

Working with Protocol Buffers usually means setting up protoc, language-specific plugins, and build pipelines. What if you could just upload a .proto file and immediately start serializing and deserializing messages?

Today we’ll show you how to use unsandbox as a protobuf playground - perfect for learning, debugging, and rapid prototyping.

The Setup: Zero Installation Required

unsandbox containers come with:

  • protoc 3.21.12 (protobuf compiler)
  • Python protobuf library
  • Ruby google-protobuf gem
  • Go protobuf support
  • Full input file support

This means you can upload a .proto schema and immediately work with it - no local installation needed.

Example 1: Your First Protobuf in Python

Let’s define a simple Person message and serialize it:

person.proto:

syntax = "proto3";

message Person {
  string name = 1;
  int32 age = 2;
}

API Request:

{
  "language": "python",
  "code": "import subprocess, sys, os\nos.chdir('/tmp/input')\nsubprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)\nsys.path.insert(0, '.')\nimport person_pb2\n\n# Create a Person message\nperson = person_pb2.Person(name='Alice', age=30)\nprint(f'Created: {person}')\n\n# Serialize to binary\ndata = person.SerializeToString()\nprint(f'Serialized ({len(data)} bytes): {data.hex()}')\n\n# Deserialize back\nperson2 = person_pb2.Person()\nperson2.ParseFromString(data)\nprint(f'Deserialized: name={person2.name}, age={person2.age}')",
  "input_files": [
    {"filename": "person.proto", "content": "c3ludGF4ID0gInByb3RvMyI7CgptZXNzYWdlIFBlcnNvbiB7CiAgc3RyaW5nIG5hbWUgPSAxOwogIGludDMyIGFnZSA9IDI7Cn0K"}
  ]
}

Output:

Created: name: "Alice"
age: 30

Serialized (9 bytes): 0a05416c696365101e
Deserialized: name=Alice, age=30

What just happened:

  1. Uploaded person.proto as base64
  2. protoc compiled it to person_pb2.py
  3. Created a Person message
  4. Serialized to 9 bytes: 0a05416c696365101e
  5. Deserialized it back

Example 2: Decode Unknown Protobuf Binary

Have a mystery .bin file and its schema? Upload both and decode:

{
  "language": "python",
  "code": "import subprocess, sys, os\nos.chdir('/tmp/input')\nsubprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)\nsys.path.insert(0, '.')\nimport person_pb2\n\n# Read the binary protobuf data\nwith open('data.bin', 'rb') as f:\n    data = f.read()\nprint(f'Binary data: {data.hex()}')\n\n# Decode it\nperson = person_pb2.Person()\nperson.ParseFromString(data)\nprint(f'Decoded: name={person.name}, age={person.age}')",
  "input_files": [
    {"filename": "person.proto", "content": "c3ludGF4ID0gInByb3RvMyI7CgptZXNzYWdlIFBlcnNvbiB7CiAgc3RyaW5nIG5hbWUgPSAxOwogIGludDMyIGFnZSA9IDI7Cn0K"},
    {"filename": "data.bin", "content": "CgVBbGljZRAe"}
  ]
}

Output:

Binary data: 0a05416c696365101e
Decoded: name=Alice, age=30

This is perfect for debugging - paste your hex dump, convert to base64, and see what’s inside!

Example 3: Nested Messages and Repeated Fields

Let’s try something more complex:

addressbook.proto:

syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

Python code to populate and serialize:

import subprocess, sys, os
os.chdir('/tmp/input')
subprocess.run(['protoc', '--python_out=.', 'addressbook.proto'], check=True)
sys.path.insert(0, '.')
import addressbook_pb2

# Create an address book
book = addressbook_pb2.AddressBook()

# Add first person
alice = book.people.add()
alice.name = "Alice"
alice.id = 1234
alice.email = "alice@example.com"
phone = alice.phones.add()
phone.number = "555-1234"
phone.type = addressbook_pb2.Person.MOBILE

# Add second person
bob = book.people.add()
bob.name = "Bob"
bob.id = 5678
bob.email = "bob@example.com"
phone = bob.phones.add()
phone.number = "555-5678"
phone.type = addressbook_pb2.Person.WORK

# Serialize
data = book.SerializeToString()
print(f"Address book ({len(data)} bytes):")
print(f"Hex: {data.hex()}")

# Show the text format
print("\nText format:")
print(book)

Output:

Address book (62 bytes):
Hex: 0a1e0a05416c696365...

Text format:
people {
  name: "Alice"
  id: 1234
  email: "alice@example.com"
  phones {
    number: "555-1234"
  }
}
people {
  name: "Bob"
  id: 5678
  email: "bob@example.com"
  phones {
    number: "555-5678"
    type: WORK
  }
}

Example 4: Raw Wire Format Inspection

Want to understand the protobuf wire format? Let’s decode it byte by byte:

import subprocess, sys, os
os.chdir('/tmp/input')
subprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)
sys.path.insert(0, '.')
import person_pb2

person = person_pb2.Person(name="Alice", age=30)
data = person.SerializeToString()

print("Wire format breakdown:")
print(f"Raw bytes: {data.hex()}")
print()

# Decode wire format manually
i = 0
while i < len(data):
    tag_byte = data[i]
    field_number = tag_byte >> 3
    wire_type = tag_byte & 0x07

    wire_type_names = {0: 'varint', 1: '64-bit', 2: 'length-delimited', 5: '32-bit'}
    print(f"Field {field_number}, Wire type {wire_type} ({wire_type_names.get(wire_type, 'unknown')})")

    i += 1
    if wire_type == 0:  # Varint
        value = 0
        shift = 0
        while data[i] & 0x80:
            value |= (data[i] & 0x7f) << shift
            shift += 7
            i += 1
        value |= data[i] << shift
        i += 1
        print(f"  Value: {value}")
    elif wire_type == 2:  # Length-delimited
        length = data[i]
        i += 1
        content = data[i:i+length]
        i += length
        print(f"  Length: {length}, Content: {content} ({content.hex()})")

Output:

Wire format breakdown:
Raw bytes: 0a05416c696365101e

Field 1, Wire type 2 (length-delimited)
  Length: 5, Content: b'Alice' (416c696365)
Field 2, Wire type 0 (varint)
  Value: 30

Now you can see exactly how protobuf encodes data!

Example 5: Schema Evolution Testing

Protobuf is famous for backward/forward compatibility. Test it:

v1.proto (original):

syntax = "proto3";
package v1;
message User {
  string name = 1;
  int32 age = 2;
}

v2.proto (new field added):

syntax = "proto3";
package v2;
message User {
  string name = 1;
  int32 age = 2;
  string email = 3;  // New field!
}

Test forward compatibility:

import subprocess, sys, os
os.chdir('/tmp/input')

# Compile both versions
subprocess.run(['protoc', '--python_out=.', 'v1.proto'], check=True)
subprocess.run(['protoc', '--python_out=.', 'v2.proto'], check=True)

sys.path.insert(0, '.')
import v1_pb2
import v2_pb2

# Create v2 message with new field
user_v2 = v2_pb2.User(name="Alice", age=30, email="alice@example.com")
data = user_v2.SerializeToString()
print(f"v2 serialized: {data.hex()}")

# Read with v1 (old code reading new data)
user_v1 = v1_pb2.User()
user_v1.ParseFromString(data)
print(f"v1 reads: name={user_v1.name}, age={user_v1.age}")
print("Email field? v1 ignores unknown fields - forward compatible!")

Quick Reference: Base64 Encoding Your Proto Files

To upload files, you need base64. Here’s how:

Linux/Mac:

base64 -w 0 person.proto

Python:

import base64
with open('person.proto', 'rb') as f:
    print(base64.b64encode(f.read()).decode())

JavaScript:

const content = "syntax = \"proto3\";\n\nmessage Person {\n  string name = 1;\n  int32 age = 2;\n}\n";
console.log(btoa(content));

Use Cases

1. Learning Protobufs

Skip the installation hassle. Just write a .proto, upload it, and experiment.

2. Debugging Wire Format

Got a hex dump from a network capture? Paste it, decode it, understand it.

3. Schema Validation

Test if your schema compiles before committing. Verify field numbers don’t conflict.

4. Cross-Language Testing

Serialize in Python, deserialize in Go. Verify your messages work across languages.

5. API Documentation

Show working examples of your protobuf messages with actual serialized output.

Limits

  • File size: Max 5MB per file (plenty for schemas)
  • Execution time: 15 minute timeout
  • protoc version: 3.21.12 (proto3 fully supported)

Try It Now

  1. Go to unsandbox.com
  2. Select Python
  3. Paste your protobuf code
  4. Upload your .proto file (or use the base64 input_files parameter)
  5. Run!

No installation. No build pipeline. Just protobufs.

Happy serializing!