What is StatusList2021?

StatusList2021 (now formalized in the W3C Bitstring Status List Recommendation, with the original community draft at w3.org/TR/vc-status-list) is the W3C-standard mechanism for revoking Verifiable Credentials at scale. It boils down to one idea: instead of querying the issuer once per verification, the issuer publishes a single compressed bitstring containing one bit per credential. Verifiers fetch the whole list and check the bit at the credential's assigned index.

Bit value 0 = credential is valid. Bit value 1 = credential is revoked (or suspended; the statusPurpose field disambiguates).

How a verifier checks status

  1. Holder presents a signed VC. Inside is a credentialStatus block with a statusListCredential URL and a statusListIndex (e.g. "4287").
  2. Verifier GETs the URL. The response is itself a signed StatusList2021Credential wrapping an encodedList field.
  3. Verifier base64-decodes encodedList, then GZIP-decompresses it, getting a raw bitstring (typically 16,384 bits = 2,048 bytes).
  4. Verifier reads the bit at index 4287. 1 = revoked, 0 = valid.

Honest comparison: revocation approaches for VCs

ApproachHow it worksHonest tradeoff
StatusList2021 One signed bitstring per status purpose; verifier downloads whole list. Privacy-preserving (issuer can't tell which credential is being checked); scales to ~131K credentials per 16KB list. List grows with issuer scale; freshness depends on republish cadence.
Per-credential CRL query Verifier asks issuer "is credential X still valid?" each time. Issuer learns who is being verified, when, by whom — classic OCSP-style privacy leak. Server load scales with verification volume, not credential count.
OCSP-like online check Lightweight protocol returning status of a specific cert. Same privacy concern as CRL; adds availability dependency on the issuer being online for every verification. OCSP stapling exists but is weakly adopted in the VC ecosystem.
Short-lived credentials Don't revoke at all; expire credentials in minutes/hours. Eliminates the revocation problem for low-stakes claims; useless for long-lived credentials (diplomas, employment, age proofs); shifts burden to a busy reissue endpoint.
Cryptographic accumulators (RSA/ZK) Issuer publishes a single accumulator value; non-revocation proofs accompany the VC. Strongest privacy and constant-size status; complex to implement, witness updates are an open UX problem, performance is improving but still niche.

Why a bitstring instead of a database

  • Privacy by uniformity: the verifier downloads the entire list. The issuer can observe the GET, but not which credential is being checked. Edge caching makes even that observation noisy.
  • Compactness: 131,072 credentials × 1 bit = 16 KB raw. Mostly-zero lists compress trivially under GZIP — an empty 16K list shrinks to ~50 bytes.
  • Simplicity: it's a static file. Cloudflare/CloudFront/any CDN can serve it. No backend, no rate limiting, no per-verification cost.
  • Auditability: anyone can fetch the same artifact and see the same bits. Tampering with a single credential's status is impossible without producing a new signed list and burning the issuer key trust.

The trade-offs nobody mentions in the marketing

  • List size grows with issuer scale. One issuer with 10 million credentials needs ~76 lists of 131K bits each, plus a routing scheme to map credential to list.
  • Freshness is a knob, not a guarantee. If the issuer republishes hourly, a revocation can take up to an hour to propagate. Sub-minute freshness defeats the whole "static file on a CDN" benefit.
  • Revoked vs suspended is fuzzy. The spec uses statusPurpose (revocation or suspension) to distinguish, but most implementations only model permanent revocation. Re-clearing a "revoked" bit is cryptographically trivial and semantically loaded — auditors will ask why a revocation was undone.
  • Index assignment leaks issuance order. If indices are sequential, anyone with two credentials from the same issuer can estimate when the other was minted. Random-index assignment helps but complicates list compaction.
  • List rotation is unsolved at the spec level. When a list fills, you start a new one — but mapping "credential X is on list 7, index 4287" must be discoverable, and the spec leaves that to issuer policy.
  • Verifiers must validate the list signature, including key-rotation history. A fresh list signed by a now-compromised key is worse than no list.

What this demo does and does NOT do

Demo doesDemo does NOT do
  • Build a syntactically valid StatusList2021Credential with the W3C envelope (@context, type, credentialSubject.encodedList)
  • GZIP-compress + base64-encode the bitstring using the browser's native CompressionStream('gzip')
  • Mint VCs that reference a status list URL + index via credentialStatus
  • Flip individual bits and re-emit the encoded list
  • Visualize the first 256 bits as a grid so you can see which positions are flipped
  • Persist the list and issued credentials in localStorage across reloads
  • Fetch the status list over HTTPS — everything is local
  • Sign the StatusListCredential (production needs an issuer key + JWS or Data Integrity proof)
  • Handle list rotation, list-of-lists routing, or index allocation policy
  • Distinguish “revoked” (permanent) from “suspended” (temporary) cleanly — both flip the same bit here
  • Validate the issuer's key against a key-rotation history
  • Cache the list, validate validUntil on the list itself, or enforce republish freshness

How this fits with the other demos in this section

  • did:web Verifiable Credentials Demo — build the issuer DID and sign credentials
  • StatusList2021 Demo (this page) — the revocation primitive that signed credentials reference
  • Together they cover the W3C VC lifecycle except for trust-framework membership and HTTPS hosting.

Issue a Status List Credential

Generate the bitstring (initialized to all zeros), GZIP-compress it, base64-encode the result, and wrap it in a W3C StatusList2021Credential envelope. The list is persisted in localStorage so revocations survive page reloads.

Spec recommends ≥ 131,072 for production. 16,384 used as the default here so the bit grid stays browsable. Must be a multiple of 8.

List metrics

--
List size (bits)
--
Revoked
--
Encoded bytes
--
Compression ratio

StatusList2021Credential JSON

In production this credential would be signed by the issuer (Data Integrity proof or VC-JOSE), hosted at the URL above, and refreshed on a documented cadence. This demo emits the unsigned envelope so you can see the structure without distracting JWS tokens. The signing pattern is the same one demonstrated in the did:web tab.

Issue a Verifiable Credential with Status

Mint a regular W3C VC 2.0 credential, but add a credentialStatus block that points back to the status list and assigns this credential a bit index. Indices auto-increment per session; you can override.

Auto-incremented; bumped after each successful issuance. Must be < list size.

Credential JSON

This demo does NOT sign the credential. The did:web demo on the sibling tool covers signing — here we focus on the credentialStatus structure. A real verifier would reject an unsigned VC; the JSON shape, however, is exactly what the W3C spec requires.

Revoke or Restore Issued Credentials

Issued credentials are tracked in localStorage under nexus_statuslist_issued. Revoking flips the bit at that credential's index in the stored list. Restoring clears it. The list's encodedList is recomputed each time (decode → flip → recompress → re-encode).

--
Issued credentials
--
Revoked bits
--
List size
--
Encoded bytes

First 256 bits of the list

bit = 0 (valid) bit = 1 (revoked) Showing the start of the bitstring; revoke a credential beyond index 255 to see metrics update without a visual change here.

Issued credentials

Verify a Credential's Revocation Status

Paste a credential that contains a credentialStatus block. The verifier extracts the status list URL and index, looks up the bit in the locally-stored list, and reports VALID or REVOKED.

Lookup details

A real verifier would fetch() the status list URL each time (with HTTP caching) and validate the list's signature before trusting any bit. This demo skips the network round trip and uses the list from your local storage. If the credential's statusListCredential URL doesn't match the locally-stored list URL, the verifier flags it and refuses to look up the bit.