xml-pipeline/xml_pipeline/message_bus/steps/c14n.py
dullfig e653d63bc1 Rename agentserver to xml_pipeline, add console example
OSS restructuring for open-core model:
- Rename package from agentserver/ to xml_pipeline/
- Update all imports (44 Python files, 31 docs/configs)
- Update pyproject.toml for OSS distribution (v0.3.0)
- Move prompt_toolkit from core to optional [console] extra
- Remove auth/server/lsp from core optional deps (-> Nextra)

New console example in examples/console/:
- Self-contained demo with handlers and config
- Uses prompt_toolkit (optional, falls back to input())
- No password auth, no TUI, no LSP — just the basics
- Shows how to use xml-pipeline as a library

Import changes:
- from agentserver.* -> from xml_pipeline.*
- CLI entry points updated: xml_pipeline.cli:main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 21:41:19 -08:00

53 lines
No EOL
1.9 KiB
Python

"""
c14n.py — Canonicalization step for the full <message> envelope.
After repair, the envelope_tree may have different but semantically equivalent
representations (attribute order, namespace prefixes, whitespace, etc.).
This step produces Exclusive XML Canonicalization (C14N 1.1) bytes that are
identical for equivalent documents — essential for validation and signing.
Part of AgentServer v2.1 message pump.
"""
from lxml import etree
from xml_pipeline.message_bus.message_state import MessageState
async def c14n_step(state: MessageState) -> MessageState:
"""
Canonicalize the envelope_tree to Exclusive C14N form.
If repair_step succeeded, this step normalizes the tree so that:
- Validation against envelope.xsd is deterministic
- Future signing/federation comparisons are reliable
On failure, sets state.error and continues (downstream steps will short-circuit).
"""
if state.envelope_tree is None:
state.error = "c14n_step: no envelope_tree (previous repair failed)"
return state
try:
# lxml's tostring with method="c14n" implements Exclusive XML Canonicalization
# (the same form we require on egress)
c14n_bytes = etree.tostring(
state.envelope_tree,
method="c14n", # Exclusive C14N 1.0 (lxml default)
exclusive=True,
with_comments=False, # Comments not part of canonical form
strip_text=False,
)
# Re-parse the canonical bytes to get a clean tree (prefixes normalized, etc.)
# This ensures downstream steps see a consistent document
clean_tree = etree.fromstring(c14n_bytes)
state.envelope_tree = clean_tree
# raw_bytes already cleared by repair_step
except Exception as exc: # pylint: disable=broad-except
state.error = f"c14n_step failed: {exc}"
state.envelope_tree = None
return state