package idna
Install
dune-project
Dependency
Authors
Maintainers
Sources
md5=d5160b5ecb3d2fb81bd466920579f783
sha512=05e7c370be7ba6be2e4c3d9d4c76e6853e3c4ac08034c3d21efba819139cf309bf4faaab89e4e4a80653d7144576fdb38fbcfa037dfbba620294cb035b3b8732
doc/README.html
ocaml-idna
Internationalized Domain Names for OCaml (IDNA 2008 and UTS #46).
Pure OCaml implementation of IDNA2008 hostname validation, UTS #46 compatibility processing, Punycode encoding/decoding, and Unicode NFC normalization. No C dependencies. Unicode 16.0.0.
Installation
opam install idnaSemantic layers
The library exposes three separate semantic layers:
Idna.Registration: strict IDNA2008 registration validationIdna.Lookup: strict IDNA2008 lookup preparation/conversionIdna.Uts46: UTS #46 Nontransitional compatibility processing
This separation is intentional. Registration, Lookup, and Uts46 do not share exactly the same acceptance and conversion rules.
If you are migrating from the earlier top-level API, choose the layer that matches the operation you need:
Idna.check_label->Idna.Registration.check_labelIdna.is_valid_hostname->Idna.Registration.is_valid_hostnameIdna.to_ascii->Idna.Uts46.to_asciifor compatibility processingIdna.to_unicode->Idna.Uts46.to_unicodefor compatibility processing
The old top-level names are not part of the current public API. Registration, Lookup, and Uts46 intentionally have different result shapes and policy flags where their governing semantics differ.
For the normative baseline of these layers, see SPEC_CONFORMANCE.md. For governing-source precedence, see NORMATIVE_HIERARCHY.md. For runtime/test evidence status, see TEST_EVIDENCE_AUDIT.md. For open and confirmed deviations, see DEVIATION_REGISTER.md. For requirement-to-test coverage, see NORMATIVE_TEST_MATRIX.md. For diagnostics policy, ordering, and diagnostics-specific test coverage, see DIAGNOSTICS_POLICY.md, DIAGNOSTICS_ORDERING.md, and DIAGNOSTICS_TEST_MATRIX.md.
Usage
(* Strict registration validation *)
Idna.Registration.is_valid_hostname "example.com" (* true *)
Idna.Registration.is_valid_hostname "-invalid.com" (* false *)
Idna.Registration.check_label "xn--maana-pta" (* Ok () *)
Idna.Registration.check_label "XN--MAANA-PTA"
(* Error "... lowercase canonical ..." *)
(* Strict lookup conversion *)
Idna.Lookup.to_ascii "XN--MAANA-PTA.com" (* Ok "xn--maana-pta.com" *)
Idna.Lookup.to_unicode "XN--MAANA-PTA.com" (* Ok "mañana.com" *)
(* UTS #46 compatibility processing *)
Idna.Uts46.to_ascii "Königsgäßchen.example"
(* Ok "xn--knigsgchen-b4a3dun.example" *)
Idna.Uts46.to_unicode "xn--maana-pta.com"
(* { value = "mañana.com"; errored = false } *)
(* Uts46.to_unicode always returns { value; errored }.
Uts46.to_ascii returns Ok value | Error msg instead.
Those plain Uts46.to_ascii error strings are intentionally coarser than
Diagnostics.Uts46, which is the structured explanatory surface. *)
(* Structured diagnostics *)
let report = Idna.Diagnostics.Registration.check_label "/" in
report.accepted (* false *)
List.map Idna.Diagnostics.string_of_code
(List.map (fun e -> e.Idna.Diagnostics.code) report.events)
(* ["label_ascii_nr_ldh"; "idna2008_nv8"; "codepoint_disallowed"] *)
(* Punycode *)
Idna.Punycode.decode "maana-pta" (* Ok [0x6D; 0x61; 0xF1; ...] *)
Idna.Punycode.encode [0x6D;0x61;0xF1;0x61;0x6E;0x61] (* Ok "maana-pta" *)
(* NFC normalization *)
Idna.nfc [0x0065; 0x0301] (* [0x00E9] — e + acute → é *)Features
- UTS #46 Nontransitional processing (
Idna.Uts46.to_ascii,Idna.Uts46.to_unicode) - IDNA2008 hostname and label validation (RFC 5890, 5891, 5892)
- Bidirectional text rules 1-6 (RFC 5893), with layer-specific label/domain enforcement
- Punycode encoding and decoding (RFC 3492)
- Unicode NFC normalization (canonical decomposition, ordering, composition; Hangul)
- Codepoint classification (PVALID, CONTEXTJ, CONTEXTO)
- CONTEXTJ/CONTEXTO contextual rules
- STD3 ASCII rules
Diagnostics
Idna.Diagnostics mirrors the public API and provides structured explainability and provenance for Registration, Lookup, and Uts46.
Public contract:
report.acceptedmatches the corresponding public API outcomereport.eventsare emitted in deterministic pipeline order, but the public ordering contract is intentionally only the partial-order subset documented in DIAGNOSTICS_ORDERING.mdErrorevents contribute to rejectionWarningevents are semantically notable or provenance-only and do not by themselves reject the inputInfoevents are neutral trace/classification factsevent.detailis explanatory text only and is not a stability contract
For Uts46, diagnostics is also the precise explanatory surface: the plain Uts46.to_ascii error strings are intentionally coarser than the corresponding Diagnostics.Uts46 reports.
Examples of provenance exposed by diagnostics:
Idna2008_nv8Idna2008_xv8Uts46_deviation
Serialization_failed is part of the public diagnostics surface too, but it is not a normative IDNA/UTS #46 condition. It is a defensive runtime failure and is intentionally left outside default-suite coverage.
Regenerating Unicode tables
Tables are generated from Unicode 16.0.0 UCD.
Requires Python with unicodedata.unidata_version == "16.0.0" (Python 3.14+). The generator refuses to run on older Python because unicodedata.normalize is used for the RFC 5892 Unstable check and would mix Unicode versions.
./tools/download_ucd.sh 16.0.0
uv run --python 3.14 python tools/gen_tables.py --format 64 -o lib/idna-tables-64/idna_tables.ml
uv run --python 3.14 python tools/gen_tables.py --format 32 -o lib/idna-tables-32/idna_tables.mlLicense
ISC