Blog · architecture

Implementing clients: where the rubber meets the road

MintID’s verifier SDKs share one verification core, embedded in Python and never rewritten per language. TypeScript and later languages are thin clients with no cryptography and no policy, calling a verification service you host yourself, so no binding can weaken a check. An implementation audit of the core is a scheduled milestone whose findings are published.

Updated 2026-10-04: delivery and wording.

When we announced the MintID SDKs — now delivered to integrators from a tagged release — we described what ships inside every binding: the same acceptance checks, the same ten-second presentation lifetime, zero policy knobs. What we hadn't yet published is how several languages can carry one guarantee. That decision is now made and locked into the spec, and it is worth explaining, because the obvious way to build multi-language SDKs is a security mistake.

The obvious way is wrong

The obvious way is to rewrite the verification pipeline in each language: a TypeScript implementation for Node shops, another for the commercial web, a Python implementation for the data stacks. Every rewrite is a fresh chance to get a check subtly wrong — a different cryptographic library with different edge cases, a different clock behaviour, a different idea of what "expired" means a millisecond either side of the boundary. Three implementations means three attack surfaces, three audits that can disagree, and divergence that compounds with every bugfix that lands in one language first.

The specification now fixes it: the verification pipeline exists in exactly one implementation — the core, now in production — and the specification rejects rewriting it per language.

Python embeds; every other language delegates

Python is the language the core is packaged for directly: the Python SDK embeds the pipeline in your backend, the way the verifier pages have always described embedding.

Every other SDK — TypeScript first, further languages as demand appears — is deliberately a thin client. It issues challenges and delegates every decision to a verification service the verifier runs itself, next to its own backend — the same core, running as a service inside the agency's own infrastructure. The clients contain no cryptography and no policy. With nothing to verify, they have nothing that could weaken a check. A thin client cannot skip an acceptance condition, stretch a lifetime or mis-verify a proof, because it never verifies anything — its self-hosted service does.

MintID does not host verification for you. The embedded, not hosted principle stands unchanged — there is no central endpoint and no API of ours in the path. The service mode is your deployment, on your machines, holding your keys. Keys and challenge issuance stay server-side in every configuration, and none of the SDKs is a browser artefact.

No SDK ships without passing the shared conformance suite

Thin clients only give the same guarantee as embedding if you can prove the whole chain behaves identically — so every SDK, in every language, must pass one shared conformance suite before release: the same challenges, the same responses, the same expected decisions and reason codes. A verifier gets the same answer whether its backend embeds the core or calls it through a client. Under the spec, no SDK ships without passing it.

Python first, then the frozen contract, then TypeScript

Architecture decisions are cheap until something has to be built in order. Ours is fixed: Python first, because it packages the core itself; then the service contract the clients are built against, frozen and versioned as a first-class artefact; then the TypeScript client; then further languages, as demand appears. Building the clients against a frozen contract — rather than against a moving core — is what makes "thin" a property that holds, instead of a diagram that erodes.

Everything here is delivered from a tagged release, not yet from a package index; integration is offered under commercial terms while the index release is still ahead. What has changed is that the shape is no longer open: one verified implementation, embedded from Python or reached through a thin client, never rewritten per language. The acceptance rules stay where the protocol put them — fixed by the spec, not by the integrator, in any language you call them from.

One pipeline, whatever you run

Embed the core in Python, or point a thin client at your own verification service. The SDKs are delivered from a tagged release — integration starts as a conversation today.