OpenResidency Docs

Offline verification

How a credential is checked with no network — a signature check against the cached issuer public key, plus a revocation check against the cached status list — and why offline is permissive while online is strict.

In plain terms. What is actually checked when there is no network, and why the offline answer is deliberately more forgiving than the online one — a verifier at a rural clinic should not have to turn someone away because a server is unreachable.

"Offline-verifiable" means a verifier can confirm a credential is genuine and valid with no network connection at verification time — it never phones home to the issuer. This page is the mechanics: exactly what gets checked, and how the online and offline paths differ.

Why not just call the issuer?

A traditional state ID is checked by querying a central database ("is #12345 valid?"). That needs live connectivity, the database to be up, and it leaks every check to the centre. A Verifiable Credential removes all three: the credential is self-contained and signed, and the verifier checks it against data it cached earlier — the two trust artifacts.

The two local checks

Offline verification is two computations, both local — no request leaves the device:

1. Signature — against the cached issuer public key

The credential carries its claims and the issuer's signature. To verify the signature you need the issuer's public key, which the verifier fetched once while online (from the DID document at /.well-known/did.json) and cached. Verifying a signature against a key you already hold is pure local math.

  • The trust list holds a list of keys (current first, retired after), so a credential signed before a key rotation still verifies. The credential's header names a kid, so the verifier tries the right key first.
  • No matching key for the issuer → UNTRUSTED_ISSUER. Bad signature → BAD_SIGNATURE. Expired → EXPIRED. All decided locally.

2. Revocation — against the cached status list

The credential's credentialStatus names a status list URL and a statusListIndex (a position). Instead of asking the issuer "is this revoked?", the verifier caches the whole status list — a compact Bitstring Status List, synced when last online — and checks the bit at that index locally. Bit set → REVOKED. Bit clear → still valid.

The sync-then-verify lifecycle

While online, the verifier fetches two things once: the DID document (keys) and the status list (revocation bitstring). After that it verifies any number of credentials offline. The credential comes to the field (a QR, or a wallet); the trust artifacts are already cached.

WHILE ONLINE (once):   fetch /.well-known/did.json      → cache issuer keys
                       fetch /.well-known/status/ng.json → cache status bitstring

OFFLINE (all day):     scan credential (QR / wallet)
                         1. signature valid against a cached key?   (local)
                         2. not expired?                            (local)
                         3. bit at statusListIndex clear?           (local, vs cached list)
                       → accept / reject, no network

Example: a rural clinic syncs the keys + status list over the morning's signal, then verifies residents' QR credentials all day with zero internet — never contacting OpenResidency again.

Offline is permissive; online presentation is strict

The revocation posture deliberately changes with context — this is a security invariant, not an accident:

Offline credential check (VcVerifier)Online presentation (VpVerifier)
If revocation can be checkedenforced (revoked → reject)enforced (revoked → reject)
If revocation can't be checkedpermissive: valid: true, checkedRevocation: falsefails closed: refuse (REVOCATION_UNCHECKABLE)
Whya disconnected field officer must still get a useful answer against the last synca connected server has no excuse to skip revocation; "accept but flag" means a revoked credential slips past every RP that ignores the flag

The verifier always returns checkedRevocation, so a relying party knows whether revocation was actually confirmed or merely couldn't be reached. See "What not to break".

What offline verification can and can't establish

PropertyOffline?How
Genuine (issuer signed it)✅signature vs cached key
Unexpired✅local time vs validUntil
Not revoked✅ as of last syncbit vs cached status list
Held by this person (holder binding)❌needs a presentation — interactive, see below

A credential verified offline is proven genuine, but not that the person presenting it is its holder — that's a presentation check (holder binding + nonce + audience), which is inherently interactive and online. Offline you're trusting possession; the presentation path adds proof of the holder.

In the code

On this page