Skip to the content.

PCA Reference Implementation Guide

This guide walks through implementing a Policy Commitment Attestation (PCA) issuer and verifier using off-the-shelf W3C VC libraries, following the decisions recorded in Dictiva’s ADR-042.

Stack

An alternative TypeScript-native stack is DIDKit / Spruce libraries; the interfaces are broadly compatible.

Minimum viable issuer

import * as vc from "@digitalbazaar/vc";
import { Ed25519VerificationKey2020 } from "@digitalbazaar/ed25519-verification-key-2020";
import { Ed25519Signature2020 } from "@digitalbazaar/ed25519-signature-2020";
import { securityLoader } from "@digitalbazaar/security-document-loader";

// 1. Generate or load the tenant's Ed25519 issuer key.
const issuerDid = "did:web:example.com";
const issuerKey = await Ed25519VerificationKey2020.generate({
  id: `${issuerDid}#key-1`,
  controller: issuerDid,
});

// 2. Build a document loader that resolves issuer + subject DIDs.
//    For did:web, this typically fetches /.well-known/did.json.
const baseLoader = securityLoader().build();
const documentLoader = async (url) => {
  if (url.startsWith("did:web:")) {
    // Fetch + cache DID document
    return { contextUrl: null, documentUrl: url, document: await resolveDidWeb(url) };
  }
  return baseLoader(url);
};

// 3. Build the credential payload (per SPEC §3).
const credential = {
  "@context": [
    "https://www.w3.org/ns/credentials/v2",
    "https://policycommitment.dictiva.com/contexts/policy-commitment/v1"
  ],
  type: ["VerifiableCredential", "PolicyCommitmentCredential"],
  issuer: issuerDid,
  validFrom: new Date().toISOString(),
  validUntil: new Date(Date.now() + 90 * 86400 * 1000).toISOString(),
  credentialSubject: {
    id: "did:web:example.com:agents:code-reviewer",
    commitment: { /* ... see SPEC §3.2 ... */ },
  },
};

// 4. Issue + sign.
const suite = new Ed25519Signature2020({ key: issuerKey });
const signed = await vc.issue({ credential, suite, documentLoader });

Minimum viable verifier

const result = await vc.verifyCredential({
  credential: signed,
  suite: new Ed25519Signature2020(),
  documentLoader,
});

if (!result.verified) {
  throw new Error(`PCA verification failed: ${result.error?.message}`);
}

// Then enforce tier floors (SPEC §4):
const tier = signed.credentialSubject.commitment.commitmentTier;
const evidence = signed.credentialSubject.commitment.evidence;
enforceTierFloor(tier, evidence); // implementation-specific

DID method guidance

Canonical statement hash

The statementRef.hash field is SHA-256 over the statement body after JCS canonicalization (RFC 8785). Reference implementation:

import { canonicalize } from "json-canonicalize";
import { createHash } from "node:crypto";

function canonicalStatementHash(statement) {
  const canonical = canonicalize(statement);
  const digest = createHash("sha256").update(canonical).digest("hex");
  return `sha256:${digest}`;
}

Revocation via Status List 2021

Tenants maintain a published status list credential per VC Status List 2021. Each issued PCA credential carries a credentialStatus referencing an index into that list. Revocation flips the bit at the index.

import * as StatusList from "@digitalbazaar/vc-status-list";
const statusListCredential = await StatusList.createList({
  length: 131072, // 128K revocation slots
  statusPurpose: "revocation",
});

Evidence resolution

Verifiers MUST recompute digests against referenced artifacts. For memory_file / agents_md / skill / adr evidence, this means fetching the file at its reference URI + recomputing SHA-256. Implementations SHOULD support:

OSCAL export

For Level 2 conformance (see SPEC §6.2), credentials map to OSCAL assessment-results per SPEC §7.1. Reference tooling:

Development checklist

Common pitfalls

Reference implementation

Dictiva’s smoke test at scripts/attestix-spike/smoke-test.mjs is a working, reproducible end-to-end example. Clone the Dictiva repo, cd scripts/attestix-spike, npm install, npm run smoke.