Extended Part9a of 12 (Sub-part A)
Main PartPart 9
Related Parts9b, 9c
Version0.1.0-draft
Last Updated2025-11-30
StatusDraft
TypeNORMATIVE (Extended)

WDP Part 9a: Catalog Format Specification

Defines the catalog file format for WDP. Specifies how diagnostic metadata is structured in JSON format for efficient distribution and client-side expansion.

Abstract#

This document defines the catalog file format for the Waddling Diagnostic Protocol (WDP). It specifies how diagnostic metadata is structured in JSON format for efficient distribution and client-side expansion. Catalogs map compact IDs to diagnostic metadata for all severity types (E, W, C, I, H).

Key Points:

  • Two catalog types: Single-namespace (default) and Aggregated (multi-namespace)
  • Three format variants per type: Full (development), Compact (production), Minimal (specialized)
  • Hash algorithm fixed to xxHash64 with seed "wdp-v1" (no algorithm field needed)
  • Field placeholders use double braces {{field}} syntax
  • Namespace fields enable multi-service aggregation with collision-free IDs
  • Catalogs are versioned, cacheable, and distributable via CDN

Conformance

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

WDP catalogs are JSON files that map compact IDs to diagnostic metadata. They enable:

  • Efficient transmission - Send compact IDs instead of full messages
  • Offline capability - Clients cache catalogs locally
  • Multi-language support - Same compact IDs, different language catalogs
  • Fast lookups - O(1) hash-based retrieval for both single and aggregated catalogs
  • Namespace support - Collision-free aggregation of multiple services/libraries

1.2 Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Build Time                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                             โ”‚
โ”‚  โ”‚ Error Registry โ”‚                             โ”‚
โ”‚  โ”‚ (Source Code)  โ”‚                             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                             โ”‚
โ”‚           โ”‚                                     โ”‚
โ”‚           โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                             โ”‚
โ”‚  โ”‚ Generate       โ”‚                             โ”‚
โ”‚  โ”‚ Catalog JSON   โ”‚                             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
            โ”‚
            โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Distribution                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                             โ”‚
โ”‚  โ”‚ catalog.json   โ”‚                             โ”‚
โ”‚  โ”‚ (50KB)         โ”‚                             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                             โ”‚
โ”‚           โ”‚                                     โ”‚
โ”‚           โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ CDN / Static File Server           โ”‚         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

1.3 Design Goals

  • Minimal overhead: Keep catalog file size small
  • Fast parsing: Simple JSON structure
  • Flexible formats: Multiple formats for different use cases
  • Versioned: Support catalog evolution
  • Language-agnostic: Pure JSON, no language-specific features

2. Catalog Purpose and Use Cases#

2.1 Primary Use Cases

Use Case 1: IoT Devices

Problem: Constrained bandwidth, battery, storage

Solution:

Sensor โ†’ {"xY9Kp":{"f":{"temp":"45.2"}}} โ†’ Gateway

Gateway expands using catalog:
"Temperature 45.2ยฐC exceeds threshold"

Benefits: Efficient transmission, minimal battery usage, local catalog caching on gateway

Use Case 2: Mobile Applications

Problem: Slow/expensive mobile networks, offline scenarios

Solution:

1. App downloads catalog once (cached locally)
2. API returns compact diagnostics
3. App expands diagnostics locally (works offline)

Benefits: Fast API responses, works offline, supports multiple languages

Use Case 3: Microservices Architecture

Problem: High-volume logging across distributed services

Solution:

Service A logs: [xY9Kp] Auth failure
Service B logs: [xY9Kp] Auth failure
Service C logs: [xY9Kp] Auth failure

Centralized logging searches by compact ID
Aggregates all instances of same error

Benefits: Compact log entries, easy aggregation, centralized analysis

Use Case 4: Multi-Language Applications

Problem: Supporting multiple languages efficiently

Solution:

Backend โ†’ {"xY9Kp":{"f":{"timestamp":"2024-01-15"}}}

English client: "Token expired at 2024-01-15"
Spanish client: "Token expirรณ el 2024-01-15"
Japanese client: "ใƒˆใƒผใ‚ฏใƒณใฎๆœ‰ๅŠนๆœŸ้™ใŒๅˆ‡ใ‚Œใพใ—ใŸ 2024-01-15"

Benefits: Same compact ID for all languages, clients download appropriate catalog

2.2 Non-Goals

WDP catalogs are NOT intended for:

  • Authentication/Authorization - Compact IDs are not cryptographic tokens
  • Real-time catalog updates - Catalogs are versioned artifacts, not live config
  • Custom business logic - Catalogs store metadata, not executable code
  • Large binary data - Catalogs should be small (< 500KB recommended)

3. Catalog Types#

WDP defines two catalog types based on scope. Each type can use any of the three format variants (Full, Compact, Minimal).

3.1 Single-Namespace Catalog

Purpose: Default catalog type for most clients

Characteristics:

  • Keys in diags MUST be CompactID format (5 alphanumeric characters)
  • Represents errors from a single service, library, or application
  • MAY include optional namespace and namespace_hash fields
  • MUST NOT include namespaces index field
  • MUST NOT use CombinedID format in keys

When to use:

  • Mobile applications (single backend)
  • Web applications (single API)
  • Individual microservices
  • IoT devices (most cases)
  • Single library/SDK

Basic Structure:

Json
{
  "version": "1.0.0",
  "diags": {
    "81E9g": { ... },
    "xY9Kp": { ... }
  }
}

With Optional Namespace Metadata:

Json
{
  "version": "1.0.0",
  "namespace": "auth_service",
  "namespace_hash": "h4tYw2",
  "diags": {
    "81E9g": { ... },
    "xY9Kp": { ... }
  }
}

Key Format: ^[A-Za-z0-9]{5}$

3.2 Aggregated Catalog

Purpose: Catalog combining multiple namespaces

Characteristics:

  • Keys in diags MUST be CombinedID format (<NamespaceHash>-<CompactID>)
  • Represents errors from multiple services, libraries, or sources
  • MAY include namespaces index mapping names to hashes
  • MUST NOT include top-level namespace or namespace_hash fields

When to use:

  • Monitoring dashboards (multiple services)
  • Edge gateways (multiple devices)
  • Log aggregators (multiple sources)
  • SDK bundles (library + dependencies)
  • API gateways/BFFs

Basic Structure (Privacy-Preserving):

Json
{
  "version": "1.0.0",
  "diags": {
    "h4tYw2-81E9g": { ... },
    "k9Px3a-xY9Kp": { ... }
  }
}

With Namespaces Index (Human-Readable):

Json
{
  "version": "1.0.0",
  "namespaces": {
    "auth_service": "h4tYw2",
    "payment_service": "k9Px3a"
  },
  "diags": {
    "h4tYw2-81E9g": { ... },
    "k9Px3a-xY9Kp": { ... }
  }
}

Key Format: ^[A-Za-z0-9]{5}-[A-Za-z0-9]{5}$

3.3 Choosing Catalog Type

Decision Tree:

Does client receive errors from multiple services/libraries?
  โ”œโ”€ NO  โ†’ Use Single-Namespace Catalog
  โ””โ”€ YES โ†’ Use Aggregated Catalog
           โ”œโ”€ Need human-readable names? โ†’ Include namespaces index
           โ””โ”€ Privacy-sensitive? โ†’ Omit namespaces index

3.4 Design Rationale: Flat Structure

Why use CombinedID keys instead of nested namespace objects?

The flat structure with CombinedID keys was chosen over nested structures for these critical reasons:

Performance

  • O(1) lookup: Direct key access without parsing or navigation
  • No nested iteration: Single hash map lookup regardless of namespace count
  • Minimal overhead: Only 6 additional characters per key

Implementation Simplicity

  • Uniform lookup pattern: Both catalog types use identical access: catalog.diags[id]
  • Consistent client code: Same lookup logic regardless of catalog type
  • Easy validation: Single key format check across all entries

Aggregation Benefits

  • Trivial merging: Concatenate diags objects directly
  • No restructuring: Source catalogs preserve structure
  • Build-time efficiency: Simple key transformation

4. Catalog Format Variants#

Each catalog type (Single-Namespace or Aggregated) can use any of three format variants.

4.1 Full Format

Purpose: Development, debugging, documentation

Characteristics:

  • All metadata fields included
  • Human-readable field names
  • Complete documentation

When to use: Development environments, API documentation, error reference guides

Json
{
  "version": "1.0.0",
  "generated": "2024-01-15T10:30:00Z",
  "diags": {
    "xY9Kp": {
      "code": "E.AUTH.TOKEN.EXPIRED",
      "severity": "E",
      "message": "Token expired at {{timestamp}}",
      "description": "The JWT token has exceeded its time-to-live (TTL).",
      "hints": [
        "Use /auth/refresh endpoint with refresh token",
        "Check token expiration time (exp claim)"
      ],
      "tags": ["auth", "security", "jwt"],
      "fields": ["timestamp"]
    }
  }
}

4.2 Compact Format

Purpose: Production, network transmission

Characteristics:

  • Abbreviated field names (1-2 chars)
  • Essential fields only
  • Optimized for network transmission

When to use: Production web applications, mobile apps, API responses

Field Mapping:

  • v โ†’ version
  • g โ†’ generated
  • wd โ†’ diags
  • c โ†’ code
  • s โ†’ severity
  • m โ†’ message
  • d โ†’ description
  • h โ†’ hints
  • t โ†’ tags
  • f โ†’ fields
Json
{
  "v": "1.0.0",
  "g": "2024-01-15T10:30:00Z",
  "wd": {
    "xY9Kp": {
      "c": "E.AUTH.TOKEN.EXPIRED",
      "s": "E",
      "m": "Token expired at {{timestamp}}",
      "d": "The JWT token has exceeded its TTL.",
      "h": ["Use /auth/refresh endpoint"],
      "t": ["auth", "jwt"],
      "f": ["timestamp"]
    }
  }
}

4.3 Minimal Format

Purpose: Extremely constrained environments (IoT, embedded)

Characteristics:

  • Array-based, no field names
  • Only code and message
  • Minimalist structure

When to use: IoT devices with storage constraints, embedded systems

Json
{
  "jGKFp": ["E.AUTH.TOKEN.001", "Token missing from Authorization header"],
  "xY9Kp": ["E.AUTH.TOKEN.EXPIRED", "Token expired at {{timestamp}}"],
  "mN3Yr": ["W.DATABASE.CONNECTION.027", "Database pool near capacity ({{current}}/{{max}})"]
}

Array Order: [0] = code (REQUIRED), [1] = message (REQUIRED)

4.4 Format Selection Guide

Recommendation: Start with Compact format for most production applications. Use Full format for documentation and development. Reserve Minimal for truly constrained environments.

5. Field Specifications#

5.1 Top-Level Fields

Note: The hash algorithm for compact IDs is fixed to xxHash64 with seed "wdp-v1" as specified in Part 5. This is immutable for WDP v1 and does not need to be included in catalog files.

5.2 Namespace Fields

These fields enable single-namespace documentation and multi-namespace aggregation.

namespace (OPTIONAL)

  • Type: String
  • Pattern: ^[a-z][a-z0-9_]{0,31}$
  • Example: "auth_service", "payment_lib"
  • Purpose: Human-readable namespace name for single-namespace catalogs

Requirements:

  • MAY be included in single-namespace catalogs for documentation
  • MUST NOT be included in aggregated catalogs
  • MUST start with lowercase letter
  • MUST be 1-32 characters long

namespace_hash (OPTIONAL)

  • Type: String
  • Pattern: ^[A-Za-z0-9]{5}$
  • Example: "h4tYw2", "k9Px3a"
  • Purpose: Stable hash for verification and cross-reference

Requirements:

  • MAY be included alongside namespace
  • MUST NOT be included in aggregated catalogs
  • Hash MUST be generated using xxHash64 with seed "wdp-ns-v1"

namespaces (OPTIONAL)

  • Type: Object (hash map)
  • Keys: Namespace strings
  • Values: 5-character Base62 namespace hashes
  • Example: {"auth_service": "h4tYw2", "payment_lib": "k9Px3a"}
  • Purpose: Human-readable index for aggregated catalogs

5.3 Core Catalog Fields

version (REQUIRED)

  • Type: String
  • Format: Semantic version (MAJOR.MINOR.PATCH)
  • Example: "1.0.0", "2.3.1"
  • Purpose: Catalog version for change tracking and cache invalidation

generated (OPTIONAL)

  • Type: String
  • Format: ISO 8601 timestamp
  • Example: "2024-01-15T10:30:00Z"
  • Purpose: When catalog was generated

diags (REQUIRED)

  • Type: Object (hash map)
  • Keys: CompactID or CombinedID (depends on catalog type)
  • Values: Diagnostic metadata objects
  • Purpose: Main catalog data structure containing all diagnostics

Key Format Rules:

  • Single-Namespace: Keys MUST be CompactID format (^[A-Za-z0-9]{5}$)
  • Aggregated: Keys MUST be CombinedID format (^[A-Za-z0-9]{5}-[A-Za-z0-9]{5}$)
  • MUST NOT mix formats within same catalog

5.4 Diagnostic Metadata Fields

severity (REQUIRED)

5.5 Field Requirements by Format

6. Validation#

Catalogs MUST be validated to ensure they conform to either single-namespace or aggregated patterns.

6.1 Rule 1: Key Format Consistency

MUST NOT mix CompactID and CombinedID formats:

Python
def validate_key_consistency(diags):
    has_hyphen = any('-' in key for key in diags.keys())
    no_hyphen = all('-' not in key for key in diags.keys())
    
    if not (has_hyphen or no_hyphen):
        raise ValidationError("Cannot mix CompactID and CombinedID formats")
    
    if has_hyphen:
        # All keys must be CombinedID (11 chars with hyphen)
        for key in diags.keys():
            if not re.match(r'^[A-Za-z0-9]{5}-[A-Za-z0-9]{5}$', key):
                raise ValidationError(f"Invalid CombinedID format: {key}")
    else:
        # All keys must be CompactID (5 chars)
        for key in diags.keys():
            if not re.match(r'^[A-Za-z0-9]{5}$', key):
                raise ValidationError(f"Invalid CompactID format: {key}")

Examples:

Json
// โœ… Valid: All CompactID
{
  "diags": {
    "81E9g": {...},
    "xY9Kp": {...}
  }
}

// โœ… Valid: All CombinedID
{
  "diags": {
    "h4tYw2-81E9g": {...},
    "k9Px3a-xY9Kp": {...}
  }
}

// โŒ Invalid: Mixed formats
{
  "diags": {
    "81E9g": {...},           // CompactID
    "h4tYw2-xY9Kp": {...}     // CombinedID - ERROR!
  }
}

6.2 Rule 2: Namespace Field Constraints

Single-Namespace Catalogs (CompactID keys):

  • MAY include namespace field
  • MAY include namespace_hash field
  • MUST NOT include namespaces field

Aggregated Catalogs (CombinedID keys):

  • MUST NOT include namespace field
  • MUST NOT include namespace_hash field
  • MAY include namespaces field

6.3 Structural Validation

Valid JSON
Top-level is object
Required fields present (version, diags)
Compact IDs match expected format (5 or 11 chars)
Diagnostic entries have required fields
Namespace fields follow constraints

6.4 Semantic Validation

Version follows semver
CompactIDs are Base62 (5 chars)
CombinedIDs are Base62-Base62 (11 chars with hyphen)
Codes match WDP format
Severity matches code prefix
Field placeholders match fields list
Namespace fields consistent with key format

6.5 Compatibility Checking

Javascript
function isCompatible(catalogVersion, clientVersion) {
  const catMajor = parseInt(catalogVersion.split('.')[0]);
  const catMinor = parseInt(catalogVersion.split('.')[1]);
  const cliMajor = parseInt(clientVersion.split('.')[0]);
  const cliMinor = parseInt(clientVersion.split('.')[1]);
  
  // Major versions must match
  if (catMajor !== cliMajor) return false;
  
  // Client minor must be >= catalog minor
  return cliMinor >= catMinor;
}

// Examples:
isCompatible("1.2.0", "1.3.0") // true
isCompatible("1.3.0", "1.2.0") // false: client too old
isCompatible("2.0.0", "1.9.0") // false: major mismatch

7. Catalog Distribution#

7.1 Distribution Strategies

Strategy 1: Static File Hosting (RECOMMENDED)

Host catalog as static file on CDN:

https://cdn.example.com/catalogs/v1.0.0/catalog.json
https://cdn.example.com/catalogs/v1.0.0/catalog-compact.json
https://cdn.example.com/catalogs/v1.0.0/catalog-minimal.json

Benefits: Fast global distribution, built-in caching, high availability, low cost

Best Practices:

  • Use versioned URLs
  • Set long cache headers (365 days)
  • Enable gzip/brotli compression
  • Use HTTPS
  • Enable CORS for browser clients

HTTP Headers:

Http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=31536000, immutable
Content-Encoding: gzip
Access-Control-Allow-Origin: *
ETag: "abc123"

Strategy 2: API Endpoint

Serve catalog via versioned API:

GET /api/v1/catalog
GET /api/v1/catalog?format=compact
GET /api/v1/catalog?version=1.2.0

Strategy 3: Bundled with Application

Bundle catalog directly in application:

/assets/catalog.json
/public/catalog.json

When to use: Small catalogs (< 50KB), infrequent changes, offline-first apps

7.2 Versioning Strategy

Use semantic versioning for catalogs:

MAJOR.MINOR.PATCH

1.0.0 โ†’ Initial release
1.0.1 โ†’ Fix typo in message
1.1.0 โ†’ Add new errors
2.0.0 โ†’ Breaking change (remove errors, change format)

Rules:

  • PATCH: Bug fixes, typo corrections, no new errors
  • MINOR: New errors added, backward compatible
  • MAJOR: Breaking changes (removed errors, format changes)

7.3 Caching Recommendations

Client-Side Caching

Javascript
// Service worker caching
cache.put('catalog.json', response);

// LocalStorage for metadata
localStorage.setItem('catalog_version', '1.0.0');
localStorage.setItem('catalog_updated', Date.now());

Update Strategy:

1. Check current version
2. Fetch latest version metadata
3. If newer:
   a. Download in background
   b. Validate catalog
   c. Swap atomically
4. If same:
   a. Continue using cached

7.4 Compression

Recommended Compression:

Gzip:    50-60% size reduction
Brotli:  55-65% size reduction (better)

Example Sizes:

catalog.json          150 KB
catalog.json.gz        60 KB (60% reduction)
catalog.json.br        50 KB (67% reduction)

7.5 Aggregation Strategies

Strategy 1: Build-Time Aggregation

Python
def aggregate_catalogs(catalogs_list):
    aggregated = {
        "version": "1.0.0",
        "namespaces": {},
        "diags": {}
    }
    
    for catalog in catalogs_list:
        namespace = catalog.get('namespace', 'unknown')
        namespace_hash = catalog.get('namespace_hash')
        
        if not namespace_hash:
            namespace_hash = generate_namespace_hash(namespace)
        
        # Add to namespaces index
        aggregated['namespaces'][namespace] = namespace_hash
        
        # Transform keys from CompactID to CombinedID
        for compact_id, diag in catalog['diags'].items():
            combined_id = f"{namespace_hash}-{compact_id}"
            aggregated['diags'][combined_id] = diag
    
    return aggregated

Strategy 2: Runtime Aggregation

Typescript
class CatalogAggregator {
  private catalogs: Map<string, Catalog> = new Map();
  
  async loadCatalog(namespace: string, url: string) {
    const catalog = await fetch(url).then(r => r.json());
    this.catalogs.set(namespace, catalog);
  }
  
  lookup(combinedId: string): DiagEntry | null {
    const [nsHash, compactId] = combinedId.split('-');
    
    for (const [ns, catalog] of this.catalogs) {
      if (catalog.namespace_hash === nsHash) {
        return catalog.diags[compactId];
      }
    }
    
    return null;
  }
}

Strategy 3: Manifest-Based

Json
{
  "version": "1.0.0",
  "catalogs": [
    {
      "namespace": "auth_service",
      "namespace_hash": "h4tYw2",
      "url": "/catalogs/auth_service-v1.0.0.json"
    },
    {
      "namespace": "payment_service",
      "namespace_hash": "k9Px3a",
      "url": "/catalogs/payment_service-v1.0.0.json"
    }
  ]
}

Appendix A: Complete Examples#

A.1 Full Format Example

Json
{
  "version": "1.0.0",
  "generated": "2024-01-15T10:30:00Z",
  "diags": {
    "jGKFp": {
      "code": "E.AUTH.TOKEN.001",
      "severity": "E",
      "message": "Token missing from Authorization header",
      "description": "The JWT token was not provided in the request.",
      "hints": [
        "Include header: Authorization: Bearer <token>",
        "Obtain token from /auth/login endpoint"
      ],
      "tags": ["auth", "security", "http"],
      "fields": []
    },
    "xY9Kp": {
      "code": "E.AUTH.TOKEN.EXPIRED",
      "severity": "E",
      "message": "Token expired at {{timestamp}}",
      "description": "The JWT token has exceeded its TTL.",
      "hints": [
        "Use /auth/refresh endpoint with refresh token",
        "Check token expiration time (exp claim)"
      ],
      "tags": ["auth", "security", "jwt"],
      "fields": ["timestamp"]
    },
    "mN3Yr": {
      "code": "W.DATABASE.CONNECTION.027",
      "severity": "W",
      "message": "Database connection pool near capacity ({{current}}/{{max}})",
      "description": "The connection pool is at 80% capacity.",
      "hints": ["Monitor connection pool metrics", "Consider increasing pool size"],
      "tags": ["database", "performance", "monitoring"],
      "fields": ["current", "max"]
    },
    "cP9Wm": {
      "code": "C.DISK.SPACE.CRITICAL",
      "severity": "C",
      "message": "Critical: Disk usage at {{usage}}% on {{mount_point}}",
      "description": "Disk space is critically low.",
      "hints": ["Free up disk space immediately", "Check for large log files"],
      "tags": ["disk", "critical", "infrastructure"],
      "fields": ["usage", "mount_point"]
    },
    "hK3Qn": {
      "code": "H.API.RATE.LIMIT",
      "severity": "H",
      "message": "Rate limit: {{remaining}} requests remaining",
      "description": "You are approaching your rate limit.",
      "hints": ["Implement exponential backoff", "Cache responses when possible"],
      "tags": ["api", "rate-limit", "help"],
      "fields": ["remaining"]
    }
  }
}

A.2 Compact Format Example

Json
{
  "v": "1.0.0",
  "g": "2024-01-15T10:30:00Z",
  "wd": {
    "jGKFp": {
      "c": "E.AUTH.TOKEN.001",
      "s": "E",
      "m": "Token missing from Authorization header",
      "d": "The JWT token was not provided in the request.",
      "h": ["Include header: Authorization: Bearer <token>"],
      "t": ["auth", "security"],
      "f": []
    },
    "xY9Kp": {
      "c": "E.AUTH.TOKEN.EXPIRED",
      "s": "E",
      "m": "Token expired at {{timestamp}}",
      "d": "The JWT token has exceeded its TTL.",
      "h": ["Use /auth/refresh endpoint"],
      "t": ["auth", "jwt"],
      "f": ["timestamp"]
    }
  }
}

A.3 Minimal Format Example

Json
{
  "jGKFp": ["E.AUTH.TOKEN.001", "Token missing from Authorization header"],
  "xY9Kp": ["E.AUTH.TOKEN.EXPIRED", "Token expired at {{timestamp}}"],
  "mN3Yr": ["W.DATABASE.CONNECTION.027", "Database pool near capacity ({{current}}/{{max}})"],
  "cP9Wm": ["C.DISK.SPACE.CRITICAL", "Critical: Disk at {{usage}}% on {{mount_point}}"],
  "hK3Qn": ["H.API.RATE.LIMIT", "Rate limit: {{remaining}} requests remaining"]
}

Appendix B: JSON Schema#

B.1 Full Format Schema

Json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://waddling.dev/schemas/catalog-full.json",
  "title": "WDP Full Catalog Format",
  "type": "object",
  "required": ["version", "diags"],
  "additionalProperties": false,
  "properties": {
    "version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$"
    },
    "generated": {
      "type": "string",
      "format": "date-time"
    },
    "diags": {
      "type": "object",
      "patternProperties": {
        "^[0-9A-Za-z]{5}$": {
          "$ref": "#/definitions/diagEntry"
        }
      }
    }
  },
  "definitions": {
    "diagEntry": {
      "type": "object",
      "required": ["code", "severity", "message"],
      "properties": {
        "code": {
          "type": "string",
          "pattern": "^[EWCIH]\\.[A-Z]"
        },
        "severity": {
          "type": "string",
          "enum": ["E", "W", "C", "I", "H"]
        },
        "message": { "type": "string" },
        "description": { "type": "string" },
        "hints": { "type": "array", "items": { "type": "string" } },
        "tags": { "type": "array", "items": { "type": "string" } },
        "fields": { "type": "array", "items": { "type": "string" } }
      }
    }
  }
}

References#

  • JSON - JavaScript Object Notation
  • RFC 8259 - JSON specification
  • ISO 8601 - Date/time format
  • Semantic Versioning - Version numbering (semver.org)
  • RFC 2119 - Requirement keywords

Related Specifications:

  • Part 9b: Wire Protocol - Transmission format
  • Part 9c: Implementation - Code examples
  • Part 5: Compact IDs - Hash generation
  • Part 7: Namespaces - Namespace hashing

End of Part 9a: Catalog Format Specification