INTEROP HTTPS first
INTEROP is no longer "one path: string, three transports". The grammar was already HTTPS on paper
(04 §3) and path:-only in the reader. That contradiction is closed.
What changed
Canonical printed form is HTTPS. https://<host>/p/<kind>/<ref>?<params>. parsePayload
dispatches https: then path:. parseUri stays strict (path: only).
path: is an app-to-app alias. op= is allowed there only. Host is the routing index on
HTTPS. Removing the routing hint does not make the object unresolvable for a party that already
knows the holder.
Reference class lost :. [A-Za-z0-9_.-]+. path:pay/path:4a91c2f7e8d3 is malformed. The
URI carries the opaque id (4a91c2f7e8d3), not the ADDRESS spelling path:4a91c2f7e8d3.
One-segment /p/<code> is not PATH. not_path. Two segments with an unknown kind is
unknown_kind, never a payment-link code.
signUri / verifyUri. Ed25519, sig in base64url (not the hex used on JSON envelopes).
Canonicalisation covers kind, reference, and every parameter except sig and op. Host is not
covered. Budget (~300) is measured on the HTTPS form.
issuerOrigin replaces the fictional resolveIssuerBase. The host is the index. Discovery
lists hosts[] for aliases of one issuer.
Conformance profiles are PATH-INTEROP.Read / .Issue / .Pay. Pay is testable: declared
accepts, a resolving rulebook, a declared certificate origin.
What it breaks
Readers and printers that emitted or accepted path:pay/path:… on a sticker. Re-issue those codes
as https://<host>/p/pay/<opaque-id>.
Callers that treated https://host/p/<code> (one segment) as a PATH INTEROP URI. That remains a
legacy payment link.
What to do
Use parsePayload at the scanner. Use buildUri({ host }) or signUri({ host }) for anything
that will be printed or tapped. Keep parseUri for tests and for the alias only.