DocumentWDP-7
Version0.1.0-draft
Last Updated2025-12-25
StatusDraft
CategoryStandards Track (Extended)

WDP Part 7: Namespaces and Error Boundaries

Disambiguation and collision prevention for multi-service architectures

Abstract#

This document specifies Namespaces and the Error Boundary concept for the Waddling Diagnostic Protocol (WDP). Namespaces provide a mechanism for organizing error codes across multiple libraries, services, and applications, enabling disambiguation while permitting independent development. An error boundary defines the scope within which error codes are guaranteed to be unique, addressing collision challenges in multi-library applications and microservice architectures.

Specification Navigation: Refer to STRUCTURE for an overview of all WDP specification documents.

Status of This Memo#

This document specifies an optional extension to the WDP core specification. Implementation of this specification is NOT REQUIRED for basic WDP conformance. Systems that do not require namespace functionality MAY omit this specification entirely.

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 core WDP specification (Parts 1-5) defines error code structure within a single context. However, real-world systems face disambiguation challenges:

Problem 1: Multi-Library Applications

my_app/
  β”œβ”€ uses auth_lib β†’ E.Auth.Token.001
  β”œβ”€ uses payment_lib β†’ E.Auth.Token.001  (identical code)
  └─ logs show: [E.Auth.Token.001] β†’ source indeterminate

Problem 2: Microservice Architectures

UI β†’ calls user_service β†’ E.Database.Connection.021
UI β†’ calls order_service β†’ E.Database.Connection.021 (identical code)
UI receives: {"h":"Xy8Qz"} β†’ source indeterminate

Namespaces solve these problems by defining error boundaries that provide disambiguation while enabling independent development.

1.2 Design Goals#

  • Disambiguation: Identify the source of an error code
  • Independence: Allow libraries/services to define codes without global coordination
  • Automation: Enable error routing, catalog loading, and monitoring
  • Backward Compatibility: Core 5-part codes remain valid (namespaces are optional)
  • Simplicity: Flat namespace structure, no hierarchy

2. Error Boundary Concept#

2.1 Definition#

An error boundary is a named scope that guarantees the uniqueness of WDP error codes. It represents the ownership and context of diagnostic codes.

Formal Definition:

Error Boundary := A named scope B where all error codes within B are unique

Within boundary B:
  - E.Auth.Token.001 β‰  E.Auth.Token.002 (distinct codes)
  - E.Auth.Token.001 β‰  E.Database.Query.017 (distinct codes)
  
Across boundaries B1 and B2:
  - B1:E.Auth.Token.001 is distinct from B2:E.Auth.Token.001
  - Identical structured code, distinct context

2.2 Properties#

Uniqueness Scope:

  • All error codes within a boundary MUST be unique
  • Same code in different boundaries is permitted (different contexts)

Ownership:

  • Each boundary MUST have exactly one owner (team, library, service)
  • Owner is responsible for ensuring uniqueness within the boundary

Independence:

  • Boundaries can define codes without coordination
  • Changes within a boundary don't affect other boundaries

Global Identity:

  • Boundary name + error code = globally unique identifier
  • Enables disambiguation across the entire system

2.3 Relationship to Namespaces#

In WDP, namespaces are the implementation mechanism for error boundaries.

Error Boundary (abstract concept)
        ↓
Namespace (concrete implementation)

Every namespace defines an error boundary. The terms are used interchangeably in this specification.

3. Namespace Specification#

3.1 Definition#

A namespace is a string identifier that names an error boundary. It appears as a prefix to WDP error codes.

Format:

namespace:SEVERITY.COMPONENT.PRIMARY.SEQUENCE -> namespace_hash-code_hash

Example:

auth_lib:E.Auth.Token.001 -> 05o5h-V6a0B

3.2 Namespace Types#

Namespaces can represent different organizational units:

TypeExampleOwnerUse Case
Library/Crateauth_lib, tokio, serdeLibrary maintainersExternal dependencies
Serviceuser_service, order_serviceService teamMicroservices
Applicationmy_app, admin_panelApplication developersTop-level applications
Modulebackend, frontend, mobileModule teamMonorepo components

3.3 Namespace Selection Guidelines#

Implementations SHOULD:

  • Use the library/crate/package name as namespace
  • Keep namespace concise (8-16 characters ideal)
  • Reflect ownership (who maintains the code)

Implementations SHOULD NOT:

  • Include version numbers (e.g., auth_lib_v2)
  • Use generic names (e.g., lib, service)
  • Create hierarchical namespaces (e.g., org/service/module)

Examples:

Rust crate:     tokio, serde, auth_lib
NPM package:    express β†’ express (or my_org_express if namespaced)
Python package: django, flask, user_auth
Maven artifact: com.acme.auth β†’ acme_auth
Service:        user_service, payment_gateway
Application:    my_app, waddling_lang_tool

4. Namespace Hash#

4.1 Purpose#

The namespace hash provides a compact representation of the namespace for efficient transmission and global uniqueness.

4.2 Algorithm#

PropertyValue
InputNamespace string (UTF-8 encoded)
Hash FunctionxxHash3 (xxh3_64)
Seed0x31762D736E706477 ("wdpns-v1" as little-endian u64)
EncodingBase62 (40-bit, same as code hash)
Output LengthExactly 5 characters

4.3 Computation#

Step 1: Encode namespace as UTF-8 bytes
  "auth_lib" β†’ [0x61, 0x75, 0x74, 0x68, 0x5F, 0x6C, 0x69, 0x62]

Step 2: Compute hash using xxHash3
  hash_value = xxh3_64(bytes, seed=0x31762D736E706477)

Step 3: Extract 40 bits (bytes 3-7) and encode as Base62 (5 characters)
  Base62(hash_value[3:8]) β†’ "05o5h"

4.4 Seed Rationale#

Rationale for "wdpns-v1" instead of "wdp-v1":

Using a distinct seed for namespace hashes prevents collisions between namespace hashes and error code hashes:

Error code seed:  "wdp-v1"   (0x000031762D706477)
Namespace seed:   "wdpns-v1" (0x31762D736E706477)

This separation ensures:
  hash("auth_lib", "wdpns-v1") β‰  hash("E.AUTH.TOKEN.001", "wdp-v1")

This design eliminates any possibility of a namespace hash colliding with an error code hash.

4.5 Collision Probability#

With 5-character Base62 encoding (916,132,832 combinations), collision probability follows the birthday paradox formula:

NamespacesCollision ProbabilityAssessment
1,0000.05%Negligible
10,0005.5%Low
100,00068%High (unlikely scale)

For realistic deployment scales (< 10,000 namespaces across an entire ecosystem), collisions are statistically rare and acceptable.

5. Combined Error Identity#

5.1 Full Error Identity

A complete WDP error identity with namespace consists of 6 parts:

namespace:SEVERITY.COMPONENT.PRIMARY.SEQUENCE -> namespace_hash-code_hash
β”‚          β”‚        β”‚         β”‚        β”‚          β”‚             β”‚
β”‚          β”‚        β”‚         β”‚        β”‚          β”‚             └─ Part 5: Code hash
β”‚          β”‚        β”‚         β”‚        β”‚          └─────────────── Namespace hash
β”‚          β”‚        β”‚         β”‚        └─────────────────────────  Part 4: Sequence
β”‚          β”‚        β”‚         └──────────────────────────────────  Part 3: Primary
β”‚          β”‚        └────────────────────────────────────────────  Part 2: Component
β”‚          └─────────────────────────────────────────────────────  Part 1: Severity
└────────────────────────────────────────────────────────────────  Part 0: Namespace

5.2 Dual Hash Format

Format: namespace_hash-code_hash
Example: 05o5h-V6a0B

Components:
  - namespace_hash: 5 Base62 characters
  - separator: single hyphen '-'
  - code_hash: 5 Base62 characters
  
Total length: 11 characters

5.3 Parsing

Input: "05o5h-V6a0B"

if contains single '-':
    parts = split('-')
    namespace_hash = parts[0]  # "05o5h"
    code_hash = parts[1]       # "V6a0B"
else:
    # Legacy format (no namespace)
    namespace_hash = None
    code_hash = input          # "V6a0B"

5.4 Backward Compatibility

Systems using WDP without namespaces (Level 0-1 conformance) continue to operate correctly:

Without namespace (5-part):
  [E.Auth.Token.001] -> V6a0B
  Valid; no namespace required

With namespace (6-part):
  auth_lib:[E.Auth.Token.001] -> 05o5h-V6a0B
  Provides global context; core code remains unchanged

6. Uniqueness Guarantees#

6.1 Within a Namespace (GUARANTEED)

All error codes within a single namespace MUST be unique.

Namespace: auth_lib

E.Auth.Token.001      ← Unique
E.Auth.Token.002      ← Unique (distinct from 001)
E.Auth.Session.001    ← Unique (distinct component.primary)
W.Auth.Token.001      ← Unique (distinct severity)

Implementation Requirement: Namespace owners MUST ensure that codes do not collide within their boundary.

6.2 Across Namespaces (PERMITTED)

The same error code structure MAY appear in different namespaces with distinct meanings.

Namespace: auth_lib
  E.Auth.Token.001 β†’ "Authentication token missing"

Namespace: payment_lib  
  E.Auth.Token.001 β†’ "Payment authorization token missing"

This represents an acceptable collisionβ€”distinct contexts, distinct meanings, disambiguated by namespace.

6.3 Global Uniqueness (ACHIEVED)

Global uniqueness is achieved via the combined identifier:

auth_lib:E.Auth.Token.001 β†’ 05o5h-V6a0B
payment_lib:E.Auth.Token.001 β†’ k9Xmq-V6a0B
                                 ^^^^^^^^^^ Distinct identifiers

The namespace hash ensures these resolve to different identifiers despite having identical structured codes.

7. Scope and Ownership#

7.1 Ownership Model

Each namespace MUST have exactly one owner:

  • Library namespace: Library maintainers
  • Service namespace: Service team
  • Application namespace: Application developers

Owner Responsibilities:

  1. Ensure all codes within the namespace are unique
  2. Document error codes in a catalog (RECOMMENDED)
  3. Maintain backward compatibility within the namespace
  4. Assign namespace to the appropriate software artifact

7.2 Boundary Selection

Library Authors:

Rust
// Rust crate: auth-lib
pub const NAMESPACE: &str = "auth_lib";

Service Developers:

Yaml
# Kubernetes service
metadata:
  name: user-service
wdp:
  namespace: user_service

Application Developers:

Json
// package.json
{
  "name": "my-app",
  "wdp": {
    "namespace": "my_app"
  }
}

7.3 Multi-Artifact Considerations

Question: If a library comprises multiple crates or packages, should each have its own namespace?

Guidance: The decision depends on error space boundaries:

Option A: Shared namespace (RECOMMENDED)
  auth_lib_core β†’ namespace: auth_lib
  auth_lib_jwt β†’ namespace: auth_lib
  (All errors under one boundary)

Option B: Separate namespaces
  auth_lib_core β†’ namespace: auth_lib_core
  auth_lib_jwt β†’ namespace: auth_lib_jwt
  (Independent error spaces)

Option A is RECOMMENDED unless crates are genuinely independent with no shared error semantics.

8. Format Specification#

8.1 Namespace Format

Pattern: [a-z][a-z0-9_]{0,31}

Character Set:

  • Lowercase ASCII letters: a-z
  • ASCII digits: 0-9
  • Underscore: _

Format Requirements:

  • Namespace MUST start with a lowercase letter
  • Namespace MUST contain only lowercase letters, digits, and underscores
  • Namespace MUST be 1-32 characters in length
  • Namespace MUST NOT contain hyphens, periods, slashes, or other special characters

8.2 Regex Pattern

^[a-z][a-z0-9_]{0,31}$

Explanation:

  • ^ - Start of string
  • [a-z] - Must start with lowercase letter
  • [a-z0-9_]{0,31} - Followed by 0-31 lowercase letters, digits, or underscores
  • $ - End of string

Total length: 1-32 characters

8.3 Valid Examples

a                    // valid: 1 character minimum
auth_lib             // valid: typical library name
user_service         // valid: typical service name
waddling_lang_tool   // valid: 18 characters
my_app_v2            // valid: with version suffix, if necessary
tokio                // valid: concise, well-known name
acme_payment_gateway // valid: 20 characters

8.4 Invalid Examples

Auth_lib            // invalid: uppercase characters not permitted
auth-lib            // invalid: hyphen not permitted
auth.lib            // invalid: period not permitted
auth/lib            // invalid: slash not permitted
_auth_lib           // invalid: MUST NOT start with underscore
2auth_lib           // invalid: MUST NOT start with digit
auth lib            // invalid: space not permitted
this_is_a_very_long_namespace_name // invalid: 33 characters exceeds maximum

9. Validation Rules#

9.1 Format Validation

Implementations MUST validate that namespaces:

  1. Start with a lowercase ASCII letter (a-z)
  2. Contain only lowercase letters, digits, and underscores
  3. Are between 1 and 32 characters in length
  4. Match the regex pattern ^[a-z][a-z0-9_]{0,31}$

Invalid namespaces MUST be rejected prior to hash computation.

9.2 Validation Examples

Rust
fn is_valid_namespace(ns: &str) -> bool {
    if ns.is_empty() || ns.len() > 32 {
        return false;
    }
    
    let mut chars = ns.chars();
    
    // First character must be lowercase letter
    if !matches!(chars.next(), Some('a'..='z')) {
        return false;
    }
    
    // Remaining characters must be lowercase letter, digit, or underscore
    chars.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_'))
}

assert!(is_valid_namespace("auth_lib"));
assert!(!is_valid_namespace("Auth_lib"));
assert!(!is_valid_namespace("auth-lib"));

10. Usage Patterns#

10.1 Library Pattern

Rust
// Define namespace constant
pub const NAMESPACE: &str = "auth_lib";

// Use in error definitions
#[derive(WdpError)]
#[wdp(namespace = "auth_lib")]
pub enum AuthError {
    #[wdp(code = "E.Auth.Token.001")]
    TokenMissing,
    
    #[wdp(code = "E.Auth.Token.018")]
    TokenExpired,
}

// Generated methods
impl AuthError {
    pub fn namespace(&self) -> &str {
        NAMESPACE
    }
    
    pub fn full_code(&self) -> String {
        format!("{}:{}", NAMESPACE, self.code())
    }
    
    pub fn full_id(&self) -> String {
        format!("{}-{}", 
            namespace_hash(NAMESPACE), 
            self.compact_id()
        )
    }
}

10.2 Service Pattern

Yaml
# service-config.yml
service:
  name: user-service
  wdp:
    namespace: user_service
    catalog: /etc/wdp/user_service_catalog.json

10.3 Application Pattern

Typescript
// Initialize with multiple namespaces
import { registerNamespace } from '@wdp/core';

// Register dependencies
registerNamespace('auth_lib');
registerNamespace('payment_lib');
registerNamespace('my_app');

// Errors carry namespace
const error = new WdpError({
  namespace: 'my_app',
  code: 'E.Application.Startup.001',
});

console.log(error.fullCode());  // "my_app:E.Application.Startup.001"
console.log(error.fullId());     // "pQ7Zw-mN3Yr"

11. Automation and Routing#

11.1 Error Routing

Namespaces enable automatic routing based on error source:

Yaml
# routing-config.yml
namespaces:
  user_service:
    team: "User Team"
    slack: "#user-service-alerts"
    pagerduty: "user-service-oncall"
    
  payment_lib:
    team: "Payment Team"
    slack: "#payment-alerts"
    pagerduty: "payment-oncall"
Rust
fn route_error(error: &WdpError) {
    let config = ROUTING_CONFIG.get(error.namespace())?;
    
    match error.severity() {
        Severity::Error | Severity::Critical => {
            page_oncall(config.pagerduty);
            post_to_slack(config.slack, error);
        }
        _ => log_only(error),
    }
}

11.2 Catalog Loading

Javascript
// Smart catalog loading
async function expandError(compactId) {
    const [nsHash, codeHash] = compactId.split('-');
    
    // Resolve namespace from hash
    const namespace = await resolveNamespace(nsHash);
    
    // Load only the required catalog
    const catalog = await loadCatalog(namespace);
    
    // Expand error
    return catalog.lookup(codeHash);
}

11.3 Monitoring and Alerting

Sql
-- Group errors by namespace
SELECT namespace, COUNT(*) as error_count
FROM error_logs
WHERE timestamp > NOW() - INTERVAL '1 hour'
GROUP BY namespace
ORDER BY error_count DESC;

-- Namespace-specific SLA monitoring
SELECT 
  namespace,
  COUNT(*) FILTER (WHERE severity = 'E') as error_count,
  COUNT(*) as total_count,
  (COUNT(*) FILTER (WHERE severity = 'E')::float / COUNT(*)) as error_rate
FROM logs
WHERE timestamp > NOW() - INTERVAL '1 day'
GROUP BY namespace
HAVING error_rate > 0.01;  -- Alert if > 1% error rate

12. Presentation Guidelines#

12.1 Context-Aware Display

Namespace visibility SHOULD depend on context:

ContextShow Namespace?Format
User-facing UINo[E.Auth.Token.001]
Developer consoleMetadataSeparate line or field
Structured logsInlineauth_lib:E.Auth.Token.001
JSON APIField{"namespace":"auth_lib","code":"..."}
MonitoringDimensionGroup/filter by namespace

12.2 Console Display Examples

Normal Mode (User-Facing):

[E.Parser.Argument.001] -> V6a0B

error: Expected 'domain' keyword
   --> state.travel:10:5

Verbose Mode (Developer):

[E.Parser.Argument.001] -> 05o5h-V6a0B
namespace: waddling_lang_tool

error: Expected 'domain' keyword
  --> state.travel:10:5

Structured Logs:

Json
{
  "level": "error",
  "namespace": "waddling_lang_tool",
  "code": "E.Parser.Argument.001",
  "compact_id": "V6a0B",
  "namespace_hash": "05o5h",
  "full_id": "05o5h-V6a0B",
  "message": "Expected 'domain' keyword"
}

13. Relationship to Other Parts#

13.1 To Core Specification (Parts 1-5)

Namespaces are an optional extension to the core 5-part error structure.

Without namespace (Level 0-1):
  [E.Auth.Token.001] -> V6a0B

With namespace (Level 4):
  auth_lib:[E.Auth.Token.001] -> 05o5h-V6a0B

The core 5-part code remains unchanged; namespace is a prefix.

13.2 To Sequence Conventions (Part 6)

Sequence conventions apply within namespace boundaries:

auth_lib:E.Auth.Token.001  (001 = MISSING, per convention)
auth_lib:E.Auth.Token.018  (018 = STALE, per convention)

payment_lib:E.Auth.Token.001  (also MISSING, same convention)

Conventions are namespace-independent.

13.3 To I18N (Part 8)

Namespaces and localization are orthogonal:

Namespace: auth_lib (fixed)
Language: en, es, ja (varies)

English:  auth_lib:E.Auth.Token.001 -> "Token missing"
Spanish:  auth_lib:E.Auth.Token.001 -> "Token faltante"
Japanese: auth_lib:E.Auth.Token.001 -> "γƒˆγƒΌγ‚―γƒ³γŒγ‚γ‚ŠγΎγ›γ‚“"

Same namespace, different languages.

13.4 To Catalogs (Part 9)

Catalogs MAY be organized by namespace:

catalogs/
  β”œβ”€ auth_lib_catalog.json
  β”œβ”€ payment_lib_catalog.json
  └─ user_service_catalog.json

Each catalog includes its namespace:

Json
{
  "namespace": "auth_lib",
  "namespace_hash": "05o5h",
  "errors": { ... }
}

14. Design Rationale#

14.1 Why Flat Namespace (No Hierarchy)?

Considered: org/service/module (hierarchical)

Rejected:

  • Complex parsing and validation
  • Verbose (acme_corp/user_service/auth_module)
  • Abuse potential (unlimited depth)
  • Unnecessary (single-level sufficient)

Chosen: Flat snake_case

  • Simple to parse and validate
  • Concise (single token)
  • Sufficient for disambiguation

14.2 Why snake_case Instead of kebab-case?

Considered: auth-lib (kebab-case)

Rejected:

  • Collision with hash separator: 05o5h-V6a0B (hyphen separates hashes)
  • Ambiguity: Is auth-lib-V6a0B namespace-code or namespace-hash-code?

Chosen: auth_lib (snake_case)

  • No collision (underscore β‰  hyphen)
  • Familiar (Python/Rust package naming)
  • Clear parsing: auth_lib:E.Auth.Token.001 or 05o5h-V6a0B

14.3 Why Maximum 32 Characters?

Prevents abuse:

acme_corp_v2_payment_gateway_production_service_auth_module_v3
  (Too long, unclear, poor naming)

acme_payment_gateway
  (Concise, clear, descriptive)

32 characters is sufficient for descriptive names while discouraging overly complex identifiers.

14.4 Why Not Make Namespace Core?

Namespaces are optional because:

  1. Simple systems don't need them (single app, no dependencies)
  2. Core should be minimal (only essential parts)
  3. Opt-in adoption (use when needed)
  4. Backward compatible (existing codes remain valid)

15. Examples#

15.1 Complete Error Identity Examples

Library error:
  auth_lib:E.Auth.Token.001 -> 05o5h-V6a0B

Service error:
  user_service:E.Database.Query.017 -> pQ7Zw-kN8Qz

Application error:
  waddling_lang_tool:E.Parser.Syntax.003 -> mN3Yr-xY9Kp

15.2 Multi-Library Application

Rust
// Application with multiple dependencies

// Dependency 1: auth_lib
let auth_error = auth_lib::TokenMissing;
assert_eq!(auth_error.full_code(), "auth_lib:E.Auth.Token.001");
assert_eq!(auth_error.full_id(), "05o5h-V6a0B");

// Dependency 2: payment_lib (same code structure!)
let payment_error = payment_lib::TokenMissing;
assert_eq!(payment_error.full_code(), "payment_lib:E.Auth.Token.001");
assert_eq!(payment_error.full_id(), "k9Xmq-V6a0B");  // Different hash!

// No collision - namespace disambiguates
assert_ne!(auth_error.full_id(), payment_error.full_id());

15.3 Microservice Error Propagation

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    E.Database.Query.017    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ user_service β”‚ ─────────────────────────> β”‚  API Gateway β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                     β”‚
                                                     β”œβ”€ Parse namespace
                                                     β”œβ”€ user_service β†’ User Team
                                                     └─ Route to #user-service-alerts

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   E.Database.Query.017     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ order_service  β”‚ ─────────────────────────> β”‚  API Gateway β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                     β”‚
                                                     β”œβ”€ Parse namespace
                                                     β”œβ”€ order_service β†’ Order Team
                                                     └─ Route to #order-service-alerts

Same code structure, different namespaces, different routing!

Appendix A: Quick Reference#

Namespace Format

Pattern:   [a-z][a-z0-9_]{0,31}
Regex:     ^[a-z][a-z0-9_]{0,31}$
Length:    1-32 characters
Required:  NO (optional extension)

Namespace Hash

Algorithm: xxHash3 (xxh3_64)
Seed:      "wdpns-v1" (0x31762D736E706477)
Encoding:  Base62
Length:    5 characters

Combined Format

Full code:    namespace:SEVERITY.COMPONENT.PRIMARY.SEQUENCE
Full hash:    namespace_hash-code_hash
Example code: auth_lib:E.Auth.Token.001
Example hash: 05o5h-V6a0B

Appendix B: Normative References#

End of Specification