Wire Protocol & JSON Bindings
Relevant source files
The following files were used as context for generating this wiki page:
- crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js
- crates/crucible-codegen-go/src/lib.rs
- crates/crucible-codegen-php/src/models.rs
- crates/crucible-codegen-dart/src/models.rs
- crates/crucible-codegen-ts/src/names.rs
- crates/crucible-codegen-docs/src/markdown.rs
- crates/crucible-codegen-c/tests/wire.c
The Crucible project utilizes a JSON-over-HTTP protocol named crucibleJson. This protocol defines how modeled data structures, operations, and errors translate into HTTP requests and responses. It supports features like strict type mapping, idempotency tokens, and structured error reporting through custom HTTP headers.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:4, crates/crucible-codegen-docs/src/markdown.rs:408
Protocol Overview
crucibleJson maps Crucible IR shapes to specific JSON representations. All operations transmit data as JSON unless a specific member is designated as a binary payload.
Type Mapping to Wire Format
Crucible maps internal shapes to JSON types to ensure consistency across different language implementations (Go, PHP, Dart, TypeScript).
| Crucible Shape | Wire/JSON Type | Notes |
|---|---|---|
string, enum | string | Enums are sent as their string wire values. |
boolean | boolean | true or false. |
integer, long | number / string | Integers beyond the safe range (2^53-1) may require string transport in some environments. |
float, double | number | Floating point values. |
blob | string (Base64) | Binary data is Base64 encoded for JSON transmission. |
timestamp | string / number | Supports epoch-seconds, http-date, or ISO8601 strings. |
list | Array | Standard JSON array. |
map | Object | Standard JSON object with string keys. |
structure | Object | Modeled members use their defined wireName. |
union | Object | A single-key object where the key is the variant name. |
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:34-58, crates/crucible-codegen-ts/src/names.rs:18-35, crates/crucible-codegen-go/src/lib.rs:72-88
Timestamp Formats
The protocol supports three primary timestamp formats on the wire:
- epoch-seconds: A numeric value representing seconds since the Unix epoch.
- http-date: A RFC 7231 formatted UTC string (e.g.,
Tue, 15 Nov 1994 08:12:31 GMT). - ISO8601: The default format used if no specific format is specified.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:25-32
Request Construction
Requests are constructed by binding modeled input members to specific parts of the HTTP request: Path Labels, Query Parameters, Headers, and the Body/Payload.
Data Flow for Request Building
This diagram illustrates how the buildRequest logic transforms an input object into an HTTP request.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:84-135
Request Bindings
- Path Labels: Members mapped to
{name}placeholders in the URI. Greedy labels{name+}allow split segments. - Query Parameters: Members converted to URL-encoded query strings.
- Headers: Members mapped to HTTP headers. Values may be Base64 encoded if
headerEncodingis specified. - Payload: A single member designated to represent the entire request body. If the member is a
blob, it is sent as raw bytes. - Body: Multiple members combined into a single JSON object for the request body.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:103-125
Error Handling
The protocol uses the X-Crucible-Error HTTP header to transmit machine-readable error codes. This allows clients to identify modeled exceptions even when the JSON body might be ambiguous.
Error Identification Logic
When an HTTP status code is >= 400, the client determines the error name using the following priority:
- The value of the
X-Crucible-Errorheader. - The
__typeorcodefield within the JSON response body. - A fallback to the modeled error corresponding to the HTTP status code if exactly one match exists.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:145-155, crates/crucible-codegen-c/tests/wire.c:23-26
This sequence shows how the X-Crucible-Error header facilitates error modeling.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:145-155, crates/crucible-codegen-docs/src/markdown.rs:595-610
Pagination and Idempotency
The wire protocol natively supports state management for complex operations.
Pagination
Pagination relies on inputToken and outputToken members. Clients must follow the outputToken until it is absent or empty. The protocol implementation in the Explorer and SDKs ensures that repeated tokens are rejected to prevent infinite loops.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:164-185, crates/crucible-codegen-go/src/lib.rs:320-335
Idempotency
Operations can define an idempotencyToken. If the client does not provide one, the runtime generates a unique UUID (e.g., crypto.randomUUID()). This token allows the server to safely retry requests without performing the same side effect multiple times.
Sources: crates/crucible-server-runtime-rust/src/explorer/runtime/try-it.js:91-93, crates/crucible-codegen-rust/src/operations.rs:242-250
Implementation Details
The protocol is implemented across multiple language runtimes using a shared wireSchema (often embedded as JSON).
- Go: Uses
mustSchemato load a JSON-encodedwireSchemaand performs encoding/decoding viaencodeModelanddecodeModel. - PHP: Uses
WireCodecandWireContextto handle thefromWireandtoWireconversions for each class. - Dart: Implements
toWireandfromWirefactories that interact with aWireContextandWireModelinterface. - C: Uses a lower-level
wire_requestandwire_responsestructure with manual header management.
Sources: crates/crucible-codegen-go/src/lib.rs:275-285, crates/crucible-codegen-php/src/models.rs:142-155, crates/crucible-codegen-dart/src/models.rs:135-150, crates/crucible-codegen-c/tests/wire.c:11-20
Summary
Crucible's wire protocol provides a robust foundation for JSON-based communication. It ensures that complex types like binary blobs and high-precision decimals are handled safely across languages. By combining custom headers for errors and built-in support for pagination and idempotency, the protocol minimizes ambiguity in distributed system interactions.