Source code for taimoe.platform.proxy.headers
"""Build Taimoe + W3C Trace Context headers for L4 proxy mode.
Produces two layers of headers in one call:
- **Taimoe-native** ``X-Taimoe-*`` headers for audit / governance / routing
consumed by the Taimoe Gateway and audit logs.
- **W3C Trace Context** (``traceparent`` / ``tracestate``) so the same trace
flows transparently into any downstream service or APM that speaks OTel.
IDs are W3C-format (32-hex trace, 16-hex span) — no Taimoe-only prefixes,
so OTel collectors accept them without translation.
"""
from __future__ import annotations
from typing import Final
from taimoe.platform._ids import (
new_span_id,
new_trace_id,
normalize_span_id,
normalize_trace_id,
)
# --- Header name constants --------------------------------------------------
# Single source of truth — backend audit middleware and tests should import
# these instead of hard-coding the strings.
HEADER_TRACE_ID: Final = "X-Taimoe-Trace-Id"
HEADER_SPAN_ID: Final = "X-Taimoe-Span-Id"
HEADER_PARENT_SPAN_ID: Final = "X-Taimoe-Parent-Span-Id"
HEADER_RUNTIME_ID: Final = "X-Taimoe-Runtime-Id"
HEADER_AGENT_ID: Final = "X-Taimoe-Agent-Id"
HEADER_PARENT_AGENT_ID: Final = "X-Taimoe-Parent-Agent-Id"
HEADER_STEP: Final = "X-Taimoe-Step"
HEADER_SESSION_ID: Final = "X-Taimoe-Session-Id"
HEADER_USER_ID: Final = "X-Taimoe-User-Id"
HEADER_TRACEPARENT: Final = "traceparent"
HEADER_TRACESTATE: Final = "tracestate"
# W3C traceparent format: "{version}-{trace_id}-{span_id}-{flags}"
_TRACEPARENT_VERSION: Final = "00"
_TRACEPARENT_SAMPLED_FLAG: Final = "01"
_TRACEPARENT_NOT_SAMPLED_FLAG: Final = "00"
_TRACESTATE_VENDOR: Final = "taimoe"
_MAX_ID_FIELD_LEN: Final = 256 # generous cap for runtime_id / agent_id / etc.
def _validate_id_field(name: str, value: str) -> str:
"""Reject empty, CRLF-bearing, or oversized HTTP header values."""
if not isinstance(value, str) or not value or not value.strip():
raise ValueError(f"{name} must be a non-empty string.")
if "\r" in value or "\n" in value:
raise ValueError(f"{name} must not contain CR or LF characters.")
if len(value) > _MAX_ID_FIELD_LEN:
raise ValueError(f"{name} exceeds the {_MAX_ID_FIELD_LEN}-char limit.")
return value
[docs]
def build_traceparent(
trace_id: str, span_id: str, *, sampled: bool = True
) -> str:
"""Build a W3C ``traceparent`` value from a (trace_id, span_id) pair.
Args:
trace_id: 32 lowercase hex chars.
span_id: 16 lowercase hex chars.
sampled: Whether the trace should be exported by downstream APMs.
Raises:
ValueError: if either ID is not in W3C format.
"""
flags = _TRACEPARENT_SAMPLED_FLAG if sampled else _TRACEPARENT_NOT_SAMPLED_FLAG
return f"{_TRACEPARENT_VERSION}-{trace_id}-{span_id}-{flags}"
[docs]
def build_tracestate(*, runtime_id: str | None = None) -> str:
"""Build a minimal ``tracestate`` carrying our vendor segment.
For now we only stamp the runtime; we can add more vendor keys later
without breaking parsers (downstream services preserve unknown vendors).
"""
if not runtime_id:
return f"{_TRACESTATE_VENDOR}="
return f"{_TRACESTATE_VENDOR}=runtime:{runtime_id}"