Accepted adr-006 · 2026-09-30

A Person's Client Is One Code With Two Invokers: the CLI Runs It Natively, a Web Page Loads It as WebAssembly From the Pack Root, and the Assertion Comes From the Caller's Provider

Context

signatio ships a person's own client in its CLI (sow-signatio-person-client: login, authorize, verify-signature, each answer verified against the pinned key) and accepts a WebAuthn assertion as a person's proof on /v1/token (adr-005, sow-signatio-browser-presence). A consumer's web page — SpecDossier's guest page — has to prove a person from the browser. Written by the consumer, that page would be a second client of signatio's protocol in JavaScript, beside the CLI: a second path, and one signatio does not own. On 2026-09-30 the principal chose that the page load signatio's own person client compiled to WebAssembly, the page's code performing only the browser's WebAuthn ceremony; and that the assertion be pluggable by contract, so the same client serves the browser page, a native desk (a future Tauri plugin over the platform's authenticator) and the tests (a software authenticator).

Measured 2026-09-30: wasm32-unknown-unknown and wasm-bindgen 0.2.126 on the host; signatio-attest (answer domain, JCS) compiles to wasm32 as it is; the CLI verifies Ed25519 with aws-lc-rs, which does not compile to wasm; every browser in use and node offer Ed25519 in WebCrypto (Chrome 154, node 24 measured).

Decision

A PERSON'S CLIENT IS ONE CODE. What a person asks the authority — a challenge, a token for their own proof, an Authorize decision for a document and its bytes — and how every answer is taken apart and checked against the pinned keys before anything of it is read, live in one library, signatio-person. The CLI calls it natively; a web page loads it compiled to WebAssembly. What differs between the two is what the platform gives: the transport (the CLI's TLS agent; the runtime's fetch) and the Ed25519 verifier (aws-lc-rs; WebCrypto) — neither a library that can sign.

THE ASSERTION IS THE CALLER'S, BY CONTRACT. The library never produces an assertion. Its login asks the caller's assertion provider — the relying party, the challenge's bytes, the credentials it may answer with — and sends what the provider answers: a page's navigator.credentials.get, a native desk's platform authenticator, a test's software authenticator. A secret or a minisign signature over the nonce is a proof the caller supplies the same way.

THE WEB CLIENT SHIPS IN THE PACK ROOT. The WebAssembly build and its bindings are laid out as web/ in the pack root, beside schemas/, and are what the source builds, byte for byte. A consumer serves them from the provider it declares (its signauthz provider's root), never from a copy in its own tree, as it imports the protocol (adr-001).

THE TOKEN STAYS WITH THE CALLER. Each call returns the authority's signed answer as it came, once verified; the library keeps nothing.

WHAT THIS DOES NOT DECIDE: the origin a native client declares (a desk's provider is not a browser, and nothing outside a browser enforces an origin) — a decision of its own before a native provider is built; how a consumer serves web/ to its page.

Constraints

Alternatives considered