Context
ADCP #3690 security.mdx line 1132 mandates IDNA-2008 A-label form for host canonicalization on origin comparisons. The package currently uses stdlib host.encode(\"idna\") (IDNA 2003) in four places:
#775's commit body acknowledged this divergence and made the deliberate call to match the existing convention rather than fragment the canonicalization story across modules. This issue tracks the package-wide migration.
Practical impact (today)
IDNA 2003 vs IDNA 2008 differ on a small set of code points:
ß (German sharp s) — IDNA 2003 maps to ss; IDNA 2008 preserves as xn--strae-oqa.
- Final sigma
ς — IDNA 2003 maps to σ; IDNA 2008 preserves.
- ZWJ / ZWNJ — IDNA 2003 strips; IDNA 2008 may preserve in valid contexts.
For ad-tech operator domains this is near-zero risk in practice. The real exposure is cross-SDK conformance: a peer SDK using the idna package would canonicalize the same wire bytes differently, and the two would disagree on whether straße.de and xn--strae-oqa.de are the same origin.
What to change
- Add
idna to pyproject.toml [project.dependencies].
- Replace
host.encode(\"idna\") with idna.encode(host, uts46=True) in all four callsites in a single commit.
- Update the canonicalization rationale comments in each module (the four currently cross-reference each other; the migration should update all of them).
- Add a regression test:
straße.de canonicalizes to xn--strae-oqa.de (IDNA 2008 behavior), not strasse.de (IDNA 2003 behavior). Verifies the migration actually changed behavior.
- Run the storyboard + conformance suites to confirm no regressions in the existing IDNA paths.
Why one commit
Splitting the migration across PRs would leave the codebase in a state where two SDK modules canonicalize the same host differently. Keep all four callsites coherent — one commit, one review.
References
🤖 Generated with Claude Code
Context
ADCP #3690 security.mdx line 1132 mandates IDNA-2008 A-label form for host canonicalization on origin comparisons. The package currently uses stdlib
host.encode(\"idna\")(IDNA 2003) in four places:src/adcp/signing/jwks.py:201src/adcp/signing/ip_pinned_transport.py:110src/adcp/signing/revocation_fetcher.py:380src/adcp/signing/key_origins.py(added in feat(signing): key_origins check + brand.json chain error codes (#350 stage 4) #775)#775's commit body acknowledged this divergence and made the deliberate call to match the existing convention rather than fragment the canonicalization story across modules. This issue tracks the package-wide migration.
Practical impact (today)
IDNA 2003 vs IDNA 2008 differ on a small set of code points:
ß(German sharp s) — IDNA 2003 maps toss; IDNA 2008 preserves asxn--strae-oqa.ς— IDNA 2003 maps toσ; IDNA 2008 preserves.For ad-tech operator domains this is near-zero risk in practice. The real exposure is cross-SDK conformance: a peer SDK using the
idnapackage would canonicalize the same wire bytes differently, and the two would disagree on whetherstraße.deandxn--strae-oqa.deare the same origin.What to change
idnatopyproject.toml[project.dependencies].host.encode(\"idna\")withidna.encode(host, uts46=True)in all four callsites in a single commit.straße.decanonicalizes toxn--strae-oqa.de(IDNA 2008 behavior), notstrasse.de(IDNA 2003 behavior). Verifies the migration actually changed behavior.Why one commit
Splitting the migration across PRs would leave the codebase in a state where two SDK modules canonicalize the same host differently. Keep all four callsites coherent — one commit, one review.
References
docs/building/by-layer/L1/security.mdxline 1132idnaPyPI package implementation reference)tldtswhich is IDNA-2008-compliant; matching its behavior is the cross-SDK target🤖 Generated with Claude Code