Extended Part7 of 12
Version0.1.0-draft
Last Updated2025-11-30
StatusDraft
TypeOPTIONAL (Extension)

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 allowing independent development. An error boundary defines the scope within which error codes are guaranteed to be unique, solving collision problems in multi-library applications and microservice architectures.

Specification Navigation: See STRUCTURE for an overview of all WDP specification parts.

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#

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  (same code!)
  └─ logs show: [E.Auth.Token.001] β†’ which library?

Problem 2: Microservice Architectures

UI β†’ calls user_service β†’ E.Database.Connection.021
UI β†’ calls order_service β†’ E.Database.Connection.021 (same code!)
UI sees: {"h":"Xy8Qz"} β†’ which service?

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 (different codes)
  - E.Auth.Token.001 β‰  E.Database.Query.017 (different codes)
  
Across boundaries B1 and B2:
  - B1:E.Auth.Token.001 is distinct from B2:E.Auth.Token.001
  - Same structured code, different 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 -> h4tYw2-81E9g

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#

SHOULD:

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

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 is a compact representation of the namespace for efficient transmission and global uniqueness.

4.2 Algorithm#

PropertyValue
InputNamespace string (UTF-8 encoded)
Hash FunctionxxHash64
Seed"wdp-ns-v1" (UTF-8 bytes: [0x77, 0x64, 0x70, 0x2D, 0x6E, 0x73, 0x2D, 0x76, 0x31])
EncodingBase62
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: Hash with xxHash64
  hash_value = xxHash64(bytes, seed="wdp-ns-v1")

Step 3: Encode as Base62 (5 characters, left-padded with '0' if needed)
  Base62(hash_value) β†’ "h4tYw2"

4.4 Seed Rationale#

Why "wdp-ns-v1" instead of "wdp-v1"?

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

Error code seed:  "wdp-v1"
Namespace seed:   "wdp-ns-v1"

Prevents:
  hash("auth_lib", "wdp-ns-v1") β‰  hash("E.AUTH.TOKEN.001", "wdp-v1")

This 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):

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

For realistic scales (< 10,000 namespaces across an entire ecosystem), collisions are 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: h4tYw2-81E9g

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

5.3 Parsing

Input: "h4tYw2-81E9g"

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

5.4 Backward Compatibility

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

Without namespace (5-part):
  [E.Auth.Token.001] -> 81E9g
  Valid, no namespace required

With namespace (6-part):
  auth_lib:[E.Auth.Token.001] -> h4tYw2-81E9g
  Adds global context, core code 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 (different from 001)
E.Auth.Session.001    ← Unique (different component.primary)
W.Auth.Token.001      ← Unique (different severity)

Implementation Requirement: Namespace owners MUST ensure codes don't collide within their boundary.

6.2 Across Namespaces (PERMITTED)

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

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

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

This is healthy collision - different contexts, different meanings, disambiguated by namespace.

6.3 Global Uniqueness (ACHIEVED)

Global uniqueness is achieved via the combined identifier:

auth_lib:E.Auth.Token.001 β†’ h4tYw2-81E9g
payment_lib:E.Auth.Token.001 β†’ k9Xmq-81E9g
                                 ^^^^^^^^^^ Different!

The namespace hash ensures these resolve to different identifiers despite having the same structured code.

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 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 my library has multiple crates/packages, should each have its own namespace?

Answer: Depends on error space:

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)

Use Option A unless crates are truly 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: _

Rules:

  • MUST start with a lowercase letter
  • MUST contain only lowercase letters, digits, and underscores
  • MUST be 1-32 characters in length
  • 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 char minimum
auth_lib             // valid: typical library name
user_service         // valid: typical service name
waddling_lang_tool   // valid: 18 chars
my_app_v2            // valid: with version, if truly needed
tokio                // valid: short, well-known name
acme_payment_gateway // valid: 31 chars - near limit

8.4 Invalid Examples

Auth_lib            // invalid: uppercase not allowed
auth-lib            // invalid: hyphen not allowed
auth.lib            // invalid: period not allowed
auth/lib            // invalid: slash not allowed
_auth_lib           // invalid: cannot start with underscore
2auth_lib           // invalid: cannot start with digit
auth lib            // invalid: space not allowed
this_is_a_very_long_namespace_name // invalid: 33 chars - too long

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}$

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] -> 81E9g

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

Verbose Mode (Developer):

❌ [E.Parser.Argument.001] -> h4tYw2-81E9g
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": "81E9g",
  "namespace_hash": "h4tYw2",
  "full_id": "h4tYw2-81E9g",
  "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] -> 81E9g

With namespace (Level 4):
  auth_lib:[E.Auth.Token.001] -> h4tYw2-81E9g

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": "h4tYw2",
  "errors": { ... }
}

13.5 To Presentation (Part 10)

Presentation layer uses namespace for context-aware display (see Section 12).

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: h4tYw2-81E9g (hyphen separates hashes)
  • Ambiguity: Is auth-lib-81E9g 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 h4tYw2-81E9g

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 -> h4tYw2-81E9g

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(), "h4tYw2-81E9g");

// 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-81E9g");  // 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: xxHash64
Seed:      "wdp-ns-v1"
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: h4tYw2-81E9g

Appendix B: Normative References#

End of Specification