WDP Part 9b: Wire Protocol Specification
Specification for the WDP wire protocol, defining how diagnostics are transmitted over HTTP in a compact, efficient format.
Abstract#
This document specifies the wire protocol for transmitting WDP diagnostics over HTTP. It defines how servers encode diagnostics in responses and how clients parse and expand them using catalogs. Part 9b focuses on runtime communication, while Part 9a 9a-CATALOG-FORMAT.md specifies catalog file format and Part 9c 9c-IMPLEMENTATION.md provides implementation guidance.
Key Points:
- Compact identifiers as JSON keys for efficient lookup (CompactID or CombinedID format)
"wd"wrapper when mixing diagnostics with application data- Field placeholders use
{{field}}syntax - Supports all severity types (E, B, C, W, H, S, K, I, T)
- Wire protocol MUST NOT include namespace strings (namespace-free)
- Efficient wire format with minimal transmission overhead
Specification Navigation: Refer to STRUCTURE.md for an overview of all WDP specification documents.
Status of This Memo#
This document specifies a standards track protocol for the WDP community and requests discussion and suggestions for improvements. Distribution of this memo is unlimited.
Requirements Language#
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
1. Introduction#
1.1 Purpose#
The WDP wire protocol defines how diagnostics are transmitted over HTTP in a compact, efficient format. Servers transmit compact identifiers and field values; clients expand them using pre-downloaded catalogs.
Benefits:
- Efficient transmission with compact wire format
- Offline expansion capability (clients expand diagnostics locally)
- Language-agnostic (identical compact identifiers work for all languages)
- O(1) lookup (compact identifiers as keys enable direct catalog access)
1.2 Design Principles#
- Compact Identifiers as Keys: Direct JSON key-based access without separate hash field
- Minimal Wrappers: Use
"wd"only when necessary to avoid conflicts - Severity Agnostic: Identical format for errors, warnings, info, help, and critical diagnostics
- Natural Multi-Diagnostic: Single format works for one or multiple diagnostics
- Flexible: Supports both standalone and wrapped formats
- Namespace-Free: Wire protocol MUST NOT include namespace strings or metadata
1.3 Conformance Levels#
An implementation is considered conformant at Level 2 (Extended) if it correctly implements all requirements marked with MUST, REQUIRED, or SHALL in this specification, in addition to achieving Level 0 (Error Codes) and Level 1 (Compact IDs) conformance.
2. Compact Diagnostic Response Format#
2.1 Standalone Diagnostics#
When diagnostics are the only content in the response, use compact identifiers as top-level keys:
Structure:
{
"<compact_id>": {
"f": {
"<field_name>": "<field_value>"
}
}
}Fields:
<compact_id>(key) — CompactID (5 characters) or CombinedID (11 characters with hyphen)f(object, OPTIONAL) — Field values for message interpolation
Key Format:
- CompactID:
^[A-Za-z0-9]{5} $— Single-namespace contexts (e.g., "V6a0B") - CombinedID:
^[A-Za-z0-9]{5}-[A-Za-z0-9]{5} $— Multi-namespace contexts (e.g., "05o5h-V6a0B")
IMPORTANT: Wire protocol MUST NOT include namespace as a separate field. Namespace information is encoded in the identifier format itself (via the namespace hash in CombinedID).
Example (with fields):
{
"sR5Kg": {
"f": {
"timestamp": "2024-01-15T10:30:00Z"
}
}
}Example (no fields):
{
"V6a0B": {}
}Note: When a diagnostic has no fields, implementations MUST use an empty object {} as the value. Implementations MUST NOT use null.
2.2 Mixed with Application Data#
When response contains both application data and diagnostics, implementations MUST use "wd" wrapper:
Structure:
{
"data": { ... },
"wd": {
"<compact_id>": {
"f": { ... }
}
}
}Rationale:
"wd"represents "Waddling Diagnostic" or "WDP"- 2 characters (versus 11 for "diagnostics")
- Unambiguous prefix prevents key conflicts
- Consistent with WDP's compact philosophy
2.3 Multiple Diagnostics#
Multiple diagnostics use the same format (multiple keys):
Standalone:
{
"KF52S": {
"f": {
"field": "password",
"min_length": 8
}
},
"aG8eT": {
"f": {
"field": "email"
}
},
"pL2Xk": {}
}2.4 Compact Identifier Format Semantics#
The wire protocol supports two identifier formats to accommodate single-namespace and multi-namespace contexts.
2.4.1 CompactID Format (Single-Namespace)
Pattern: ^[A-Za-z0-9]{5} $
Length: 5 characters
Example: "V6a0B", "sR5Kg"
Applicability:
- Single-namespace services
- Mobile and web applications with single backend
- Individual microservices
- IoT devices (typical case)
2.4.2 CombinedID Format (Multi-Namespace)
Pattern: ^[A-Za-z0-9]{5}-[A-Za-z0-9]{5} $
Length: 11 characters
Format: <NamespaceHash>-<CompactID>
Example: "05o5h-V6a0B", "k9Px3a-sR5Kg"
Applicability:
- Edge gateways aggregating multiple devices
- Monitoring dashboards displaying multiple services
- Log aggregators collecting from multiple sources
- API gateways routing to multiple backends
2.4.3 Server Decision Logic
Producers MUST follow this logic:
def generate_diagnostic_key(namespace_context, compact_id):
if namespace_context is None:
# Single-namespace service
return compact_id # "V6a0B"
else:
# Multi-namespace aggregator
namespace_hash = compute_namespace_hash(namespace_context)
return f"{namespace_hash}-{compact_id}" # "05o5h-V6a0B"2.4.4 Consumer Behavior
Consumers MUST treat keys as opaque identifiers:
function processDiagnostic(key: string, data: object, catalog: Catalog) {
// Lookup by key (works for both formats)
const entry = catalog.diags[key];
if (!entry) {
console.warn(`Unknown diagnostic key: ${key}`);
return;
}
// Optional: Extract namespace for display
if (key.includes('-')) {
const nsHash = key.split('-')[0];
const nsName = findNamespaceByHash(catalog, nsHash);
console.log(`Diagnostic from ${nsName || nsHash}`);
}
// Expand and display
const fields = data.f || {};
const message = interpolate(entry.message, fields);
displayDiagnostic(entry.severity, message);
}Consumers MUST NOT:
- Infer namespace names from keys without consulting catalog
- Assume all keys are 5 characters
- Parse or decompose keys except for namespace attribution
- Require namespace strings in wire protocol
2.4.5 Privacy Benefits
CombinedIDs hide internal service names. Benefits include security through obscurity, stable public API, and reduced information leakage.
3. HTTP Response Patterns#
3.1 Pattern 1: Error (Standalone)#
Use Case: Operation failed, diagnostics are the only content
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"sR5Kg": {
"f": {
"timestamp": "2024-01-15T10:30:00Z"
}
}
}3.2 Pattern 2: Multiple Diagnostics (Validation)#
Use Case: Multiple validation failures
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"KF52S": {
"f": {
"field": "password",
"min_length": 8
}
},
"aG8eT": {
"f": {
"field": "email"
}
}
}3.3 Pattern 3: Warning with Success Data#
Use Case: Operation succeeded with warnings
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": "12345",
"uploaded": true
},
"wd": {
"wN4Qm": {
"f": {
"quota_used": "850",
"quota_limit": "1000",
"quota_percent": "85"
}
}
}
}3.4 Pattern 4: Critical System Alert#
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "operational",
"wd": {
"9wWb9": {
"f": {
"usage": "95",
"mount_point": "/var/log"
}
}
}
}3.5 Pattern 5: Help and Informational Messages#
HTTP/1.1 200 OK
Content-Type: application/json
{
"requests": 990,
"limit": 1000,
"wd": {
"iW8uz": {
"f": {
"remaining": "10"
}
}
}
}3.6 Pattern 6: Optional Header#
Use Case: Fast filtering in logs and monitoring without parsing body
Implementations MAY include an X-WDP-Diagnostic header:
HTTP/1.1 401 Unauthorized
X-WDP-Diagnostic: sR5Kg
Content-Type: application/json
{...}3.7 Severity to HTTP Status Code Mapping#
| Severity | HTTP Status | Example |
|---|---|---|
| E (Error) | 4xx, 5xx | 400, 401, 403, 404, 422, 500 |
| B (Blocked) | 4xx, 5xx | 423, 429, 503 |
| C (Critical) | 200, 500, 503 | May succeed but critical alert |
| W (Warning) | 200, 201, 202 | Success with warnings |
| H (Help) | 200, 429 | Guidance, rate limits |
| S (Success) | 200, 201, 202 | Success with positive confirmation |
| K (Completed) | 200, 201, 202 | Task completion |
| I (Info) | 200, 201, 202 | Success with informational message |
| T (Trace) | 200 | Trace information |
4. Field Interpolation#
4.1 Placeholder Syntax#
Field placeholders in catalog messages use double curly brace syntax:
"Token expired at {{timestamp}}"
"User {{user_id}} exceeded quota {{quota_limit}}"Syntax Rules:
- Placeholders MUST be enclosed in
{{and}} - Field names MUST match pattern:
[a-zA-Z_][a-zA-Z0-9_]* - Single braces
{field}are NOT valid - Placeholders MUST NOT be nested:
{{{field}}}is invalid
4.2 Template Expression Limitations#
WDP v1.0 DOES NOT support template expressions or calculations. Only simple field replacement is permitted.
4.3 Interpolation Process#
// Step 1: Catalog Message
"message": "User {{user_id}} exceeded quota {{quota_limit}}"
// Step 2: Wire Protocol
"f": { "user_id": "12345", "quota_limit": "1000" }
// Step 3: Client Interpolation
"User 12345 exceeded quota 1000"4.4 Missing Fields#
If a field value is not provided in the wire protocol, implementations SHOULD document which strategy they use:
- Option 1 (RECOMMENDED): Keep placeholder (e.g., "Token expired at {{timestamp}}")
- Option 2: Use default marker (e.g., "Token expired at <missing>")
- Option 3: Use empty string
4.5 Field Value Types#
Field values SHOULD be transmitted as strings or numbers. Clients MUST convert all field values to strings during interpolation.
4.6 Cause Fields (Wrapped Errors)#
When wrapping external or unclassified errors (see 6-SEQUENCE-CONVENTIONS.md Section 8.4), implementations SHOULD use standardized cause_* field names to preserve original error details:
| Field | Type | Description |
|---|---|---|
| cause_code | string | Original error code from external system |
| cause_message | string | Original error message |
| cause_type | string | Original error type/class name |
Example (flat structure):
{
"sR5Kg": {
"f": {
"cause_code": "ORA-12154",
"cause_message": "TNS:could not resolve the connect identifier specified",
"cause_type": "DatabaseError"
}
}
}Example (nested structure, OPTIONAL):
{
"sR5Kg": {
"f": {
"cause": {
"code": "ORA-12154",
"message": "TNS:could not resolve the connect identifier specified",
"type": "DatabaseError"
}
}
}
}4.7 PII Placeholders#
Personally Identifiable Information (PII) MUST be separated from regular fields using the pii/ prefix syntax and transmitted in the pii.data field.
Placeholder Examples:
"Login failed for {{pii/email}} at {{timestamp}}"
"Card ending in {{pii/last4}} declined"
"User {{pii/email:masked}} from {{pii/ip}} exceeded quota"Wire Format with PII:
{
"V6a0B": {
"f": {
"timestamp": "2024-01-15T10:30:00Z"
},
"pii": {
"v": 1,
"data": {
"email": "john@example.com",
"ip": "192.168.1.100"
}
}
}
}4.7.1 PII Redaction Suffixes
PII placeholders support optional format suffixes to indicate redaction responsibility:
{{pii/field}}→ Defaults to :masked (safe default){{pii/field:raw}}→ Raw PII (receiver handles security){{pii/field:masked}}→ Pre-redacted PII (explicit)
4.8 PII Field Guidelines#
When to use different modes:
- Use
:rawfor internal logging systems, secure admin interfaces, and when actual PII is needed for processing. - Use
:maskedfor external aggregators, public displays, and untrusted recipients. - Use regular fields for opaque identifiers (user_id), timestamps, and metrics.
PII Categories (MUST use pii/ prefix): Email, names, phone numbers, IP addresses, payment info, health info, government IDs.
5. Client Expansion Process#
5.1 Overview#
1. Client receives compact diagnostic(s)
2. Extract compact identifiers from response
3. Look up each identifier in local catalog
4. Interpolate fields into message
5. Display with appropriate severity stylingDetailed step-by-step examples are available in the full specification.
5.3 Handling Unknown Compact Identifiers#
When a compact identifier cannot be resolved in the catalog, implementations SHOULD generate a synthetic diagnostic that conforms to WDP code format (e.g., E.MyApp.Diagnostic.UNRESOLVED).
5.4 Handling Mixed Responses#
When response contains both application data and diagnostics, check for the "wd" wrapper first, then fall back to checking for top-level compact identifiers.
6. Namespace Context and Wire Protocol#
6.1 Core Principle#
Namespace information MUST NOT be transmitted as a separate field in the wire protocol.
Rationale: Efficiency, information hiding, stability, and simplicity.
6.2 How Clients Determine Namespace#
- Single-namespace clients: Already know the service; no ambiguity.
- Multi-namespace clients: Use combined identifiers (encoding hash) and look up the name in the catalog's
namespacesindex.
6.4 Privacy-Preserving Mode#
When a catalog omits the namespaces index, clients can still look up and display diagnostics using the combined identifier but cannot display the human-readable service name. This is intentional for security-sensitive environments.
7. Catalog-Wire Format Compatibility#
7.1 Over-Qualified Identifiers#
Scenario: Wire uses CombinedID, Catalog is single-namespace.
Behavior: Graceful fallback. Client should try direct lookup, then if not found, strip namespace hash and try compact ID lookup.
7.2 Under-Qualified Identifiers#
Scenario: Wire uses CompactID, Catalog is aggregated.
Behavior: Strict rejection (Error). Ambiguity prevents correct resolution.
8. Client Implementation#
8.1 Client Parsing Helper#
Clients should implement a helper function to extract diagnostics from responses, checking for both the "wd" wrapper and top-level identifiers.
8.2 Version Detection#
Clients can use the catalog version or X-WDP-Version header (1.0) to detect support.
Appendix A: Complete Wire Protocol Examples#
Refer to the markdown specification for comprehensive examples of Error, Warning, Critical, and Help patterns.
Appendix B: Wire Format Benefits#
Bandwidth Reduction: WDP wire format typically achieves 50-60% size reduction compared to verbose JSON error usage.
Appendix C: References#
Normative references include RFC 2119, RFC 8174, RFC 8259, and RFC 7231.