Check Revocation Status
Every PDTF credential must carry a credentialStatus entry pointing to a W3C Bitstring Status List. Revocation checking is how a verifier learns that a previously valid credential is now stale, withdrawn, or superseded.
In @pdtf/core, the low-level tools are checkStatus, createStatusList, encodeStatusList, decodeStatusList, and getBit.
1. Check a credential’s status directly
Section titled “1. Check a credential’s status directly”If you already have the credential, the quickest path is checkStatus.
import { checkStatus, type VerifiableCredential } from '@pdtf/core';
const vc: VerifiableCredential = await loadCredentialSomehow();
const isRevoked = await checkStatus( vc.credentialStatus!.statusListCredential, parseInt(vc.credentialStatus!.statusListIndex, 10),);
console.log({ isRevoked });use pdtf_core::status::bitstring::{decode_status_list, get_bit};
let vc = load_credential_somehow().await?;let status = vc.credential_status.as_ref().unwrap();
// Fetch the status list credentiallet resp = reqwest::get(&status.status_list_credential).await?.json().await?;let encoded = resp["credentialSubject"]["encodedList"].as_str().unwrap();let bitstring = decode_status_list(encoded)?;
let index: usize = status.status_list_index.parse()?;let is_revoked = get_bit(&bitstring, index);
println!("Revoked: {is_revoked}");from pdtf_core import check_statusimport json
vc = json.loads(load_credential_somehow())status = vc["credentialStatus"]
is_revoked = check_status( status_list_url=status["statusListCredential"], index=int(status["statusListIndex"]),)
print(f"Revoked: {is_revoked}")using Pdtf.Core;
var vc = LoadCredentialSomehow();var status = vc.CredentialStatus;
var isRevoked = await PdtfCore.CheckStatus( status.StatusListCredential, int.Parse(status.StatusListIndex));
Console.WriteLine($"Revoked: {isRevoked}");checkStatus fetches the status list VC, reads credentialSubject.encodedList, decodes it, and checks the bit at the requested index. A bit value of 1 means revoked or suspended, depending on statusPurpose.
2. Understand the status list shape
Section titled “2. Understand the status list shape”A PDTF status list credential is itself a signed VC. The important part is the subject:
{ "id": "https://adapters.propdata.org.uk/status/epc/list-042", "type": "BitstringStatusList", "statusPurpose": "revocation", "encodedList": "H4sIAAAAAAAAA..."}The encodedList value is a gzip-compressed, base64 string representing the raw bitstring.
3. Decode and inspect the list manually
Section titled “3. Decode and inspect the list manually”If you want more control, decode the list yourself.
import { decodeStatusList, getBit } from '@pdtf/core';
const response = await fetch('https://adapters.propdata.org.uk/status/epc/list-042');const statusListVc = await response.json();
const bitstring = decodeStatusList(statusListVc.credentialSubject.encodedList);
console.log(getBit(bitstring, 18293));use pdtf_core::status::bitstring::{decode_status_list, get_bit};
let resp: serde_json::Value = reqwest::get( "https://adapters.propdata.org.uk/status/epc/list-042").await?.json().await?;
let encoded = resp["credentialSubject"]["encodedList"].as_str().unwrap();let bitstring = decode_status_list(encoded)?;
println!("Bit 18293: {}", get_bit(&bitstring, 18293));from pdtf_core import decode_status_list, get_bitimport requests
resp = requests.get("https://adapters.propdata.org.uk/status/epc/list-042").json()bitstring = decode_status_list(resp["credentialSubject"]["encodedList"])
print(f"Bit 18293: {get_bit(bitstring, 18293)}")using Pdtf.Core;
var resp = await httpClient.GetFromJsonAsync<JsonDocument>( "https://adapters.propdata.org.uk/status/epc/list-042");
var encoded = resp.RootElement .GetProperty("credentialSubject") .GetProperty("encodedList").GetString();
var bitstring = PdtfCore.DecodeStatusList(encoded);Console.WriteLine($"Bit 18293: {PdtfCore.GetBit(bitstring, 18293)}");This is useful for debugging, bulk inspection, or writing your own higher-level verifier.
4. Create a new empty list
Section titled “4. Create a new empty list”Issuers need to create lists before they can allocate indices.
import { createStatusList, encodeStatusList } from '@pdtf/core';
const bitstring = createStatusList();const encodedList = encodeStatusList(bitstring);
console.log(bitstring.length); // 16KB raw by defaultconsole.log(encodedList);use pdtf_core::status::bitstring::{create_status_list, encode_status_list};
let bitstring = create_status_list();let encoded = encode_status_list(&bitstring)?;
println!("Raw length: {} bytes", bitstring.len());println!("Encoded: {encoded}");from pdtf_core import create_status_list, encode_status_list
bitstring = create_status_list()encoded = encode_status_list(bitstring)
print(f"Raw length: {len(bitstring)} bytes")print(f"Encoded: {encoded}")using Pdtf.Core;
var bitstring = PdtfCore.CreateStatusList();var encoded = PdtfCore.EncodeStatusList(bitstring);
Console.WriteLine($"Raw length: {bitstring.Length} bytes");Console.WriteLine($"Encoded: {encoded}");createStatusList() enforces the PDTF minimum size of 131,072 bits, which gives herd privacy and enough capacity for normal issuer volumes.
5. Revoke a credential by setting its bit
Section titled “5. Revoke a credential by setting its bit”If you are the issuer, revocation is just a bit flip in the underlying list.
import { createStatusList, revokeCredential, getBit } from '@pdtf/core';
const bitstring = createStatusList();
revokeCredential(bitstring, 18293);
console.log(getBit(bitstring, 18293)); // trueuse pdtf_core::status::bitstring::{create_status_list, set_bit, get_bit};
let mut bitstring = create_status_list();set_bit(&mut bitstring, 18293, true);
assert!(get_bit(&bitstring, 18293));println!("Bit 18293 is now set");from pdtf_core import create_status_list, revoke_credential, get_bit
bitstring = create_status_list()revoke_credential(bitstring, 18293)
assert get_bit(bitstring, 18293) is Trueprint("Bit 18293 is now set")using Pdtf.Core;
var bitstring = PdtfCore.CreateStatusList();PdtfCore.RevokeCredential(bitstring, 18293);
Debug.Assert(PdtfCore.GetBit(bitstring, 18293));Console.WriteLine("Bit 18293 is now set");In a real system you would then:
- re-encode the list
- rebuild the status list VC
- sign it with the issuer key
- publish it back to the stable HTTPS URL
6. Full verifier behaviour
Section titled “6. Full verifier behaviour”A robust verifier should do more than just read the bit:
- verify the credential proof
- fetch the status list VC
- verify the status list VC proof
- confirm the status list issuer matches the credential issuer
- decode the bitstring and inspect the bit
If you use VcValidator, step 5 is already integrated into the validation pipeline. The standalone status helpers are best when you need targeted revocation logic or issuer-side tooling.
7. Operational advice
Section titled “7. Operational advice”A few rules matter in production:
- never reuse a
statusListIndex - keep the
statusListCredentialURL stable for the lifetime of the VC - serve status lists over HTTPS with short cache lifetimes
- revoke old credentials when source data changes, not just when fraud occurs
- treat missing or unreachable status information as a verification failure for high-trust decisions
In PDTF, revocation is not edge-case plumbing. It is part of the trust contract. SellerCapacity, representation, consent, and adapter data all need a way to become invalid when the world changes.