Maturity: Limited preview. This flow works end to end today. Preview
environments verify against development-ceremony keys, which carry no
cryptographic soundness guarantee.
Diagram: the whole sign-in, end to end
Diagram: the whole sign-in, end to end
receiveFragmentResponse exists.
1. Backend: issue a nonce and sign the request
The request is a compact JSON Web Signature (JWS) signed with your registered presenter key. The browser never holds that key, and the nonce must be issued where its single-use burn happens.2. Browser: render it
Both delivery paths carry the same thing, a deep link holding the signed request. Cross-device renders that link as a single static QR code. Same-device navigates to it.vibecheck for production,
vibecheck-testnet, or vibecheck-dev. It is a required argument, since any
default would be the wrong app for someone. The route is array-siop for both
sign-in and claim requests, and the app dispatches on the request’s typ.
The scheme still reads vibecheck even though the app is called Alive. It is a
registered URL scheme rather than a product name, so it is what the installed
build actually answers to. Do not substitute alive.
On a same-device sign-in the app returns the user to your page with the token
in the URL fragment. Handle that on whichever page you registered as the
return target:
3. Backend: verify
Both delivery paths arrive at the same endpoint, one as JSON and one as the form posthandOffToBackend sends.
Where roots come from
A membership proof says the user is in the identity tree. On its own that proves nothing, because anyone can build a tree and prove membership of it. So the proof is checked against that tree’s Merkle root, and the root has to be one you accept. Our Indexer signs each root with an operator key and publishes it on a feed. You do not trust the feed. The provider polls it in the background and re-verifies every entry against the operator key you pinned, so a tampering relay can stall you but cannot forge for you. Start it once at boot, not per request.roots.warm is true once at least one entry has been fetched and verified.
Until then there is no root to judge a proof against. A fetch that succeeds but
returns nothing leaves the provider cold, which is the ordinary state on a
fresh feed deploy rather than an edge case.
If you only want authentication, drop idFacetProof from the request in step 1
and from the verify call below. You then need no feed, no pins and no provider,
and your ceiling is self_certified.
The constructor above is the minimum. Polling clamps, how freshness is decided,
and what the provider does with the keys the feed serves are in the
RP server reference.
Verify the token
Verdict tiers
membership_proven is the only tier carrying a sybil guarantee. If your gate
cares that one human gets one of something, check for it.
Subject modes
Set by thescope field in step 1.
A pairwise subject derives from your
client_id, so the same person signing
into two applications presents two unrelated subjects. Choose the mode on the
server, not in the page, and pass the same mode to verifySelfIssuedToken as
expectedMode. A token of the other mode is refused, which stops a pairwise
token from passing as a disclosed one.