@agenticprimitives/privacy-credentials
v0.0.0-alpha.21
Published
Privacy presentation layer (spec 305 / G-6): selectively-disclosed SD-JWT VC presentations, pairwise relationship pseudonyms, spec-304 issuer-key facet verification. One suite per request, never a fallback ladder.
Maintainers
Readme
@agenticprimitives/privacy-credentials
Part of Agentic Primitives — the open-source trust substrate for agentic applications: identity that can sign, authority checked at act time, evidence the owner carries. Developer kit · All packages
The privacy presentation layer — turns vault-held credentials into selectively-disclosed, pairwise-pseudonymous presentations a verifier can check without learning more than the transaction requires.
Spec: specs/305-privacy-credentials.md · Issuer keys:
specs/304-issuer-key-facets.md · Doctrine: the ADR-0010
amendment ("the Smart Agent address is the accountability root, not the default presentation
identifier") and the alignment audit
§6.2 (eIDAS 2.0 Art 5a makes selective disclosure + unlinkability EU market-access requirements).
What it does
import {
issueSdJwt, presentSdJwt, verifyPresentation,
generateHolderSecret, pairwiseHandle,
createIssuerKeyResolver,
} from '@agenticprimitives/privacy-credentials';
// ISSUER (an org SA with a spec-304 facet key; sign = injected KMS ES256):
const credential = await issueSdJwt({
issuer: orgCaip10, keyId: 'issuer-key:sd-jwt:2026-07', vct: 'ap:procurement-delegate',
sdClaims: { authorizedRole: 'procurement.delegate', counterpartyClass: 'approved.vendor' },
holderPublicKeyJwk, sign: kmsEs256Signer,
});
// → into the holder's vault.
// HOLDER (presents to ONE verifier, revealing ONE claim):
const handle = await pairwiseHandle(holderSecret, 'vendor.example'); // stable HERE, unlinkable elsewhere
const payload = await presentSdJwt({
sdJwt: credential, discloseClaims: ['authorizedRole'],
audience: request.audience, nonce: request.nonce, holderSign,
});
// VERIFIER (no AP runtime needed — chain checks injected):
const resolveIssuerKey = createIssuerKeyResolver(lookupSignedFacet, {
verifySaSignature, // UniversalSignatureValidator read
checkAttestation, // AttestationRegistry liveness read
});
const res = await verifyPresentation(
{ type: 'PrivatePresentationV1', suite: 'sd-jwt', relationshipHandle: handle, payload },
request,
{ resolveIssuerKey },
);
// res.claims === { authorizedRole: 'procurement.delegate' } — and nothing else.The verifier learns: the claim it asked for, that a custody-governed Smart Agent issued it (via the spec-304 facet binding), and that the presenter holds the credential (key binding bound to this audience + nonce). It does not learn the holder's SA, the other claims, or anything that links this presentation to the same holder's presentations elsewhere.
Design rules
- One suite per proof request (ADR-0013):
sd-jwt(selective disclosure of claim values under a pairwise pseudonym) andopenac-sd-jwt-p256(zero-knowledge predicates over the same credential — no value, no credential, no issuer signature and no device key reach the verifier);bbs-bls12381-g2,anoncreds-v1andzk-predicateare reserved enum slots that reject, never a fallback ladder. - Predicates reject on sd-jwt — that suite can't prove
amount <= limit; refusing beats silently revealing the field. A request with predicates namesopenac-sd-jwt-p256, whose proving backend is injected (ProvingBackendPort;@agenticprimitives/privacy-credentials-openacbinds openac-sdk). - One visit per audience: a presentation may carry a nullifier (
deriveNullifier), deterministic per device key and audience, which the verifier's store consumes. Its grade isasserted— OpenAC emits no nullifier in-circuit — and the receipt says so. - Issuer facet first: a revoked or unresolvable spec-304 facet fails the whole presentation.
- Dependency-free (WebCrypto only — Node ≥ 20 and workerd); chain reads and KMS signing are injected ports.
Status
Live (zkID Z1–Z6, spec 444): the sd-jwt suite with pairwise pseudonyms; the openac-sd-jwt-p256 predicate suite
over an injected proving port; spec-304 issuer facets resolved and re-verified through the chain; nullifiers; status
presentations against the issuer's live-credential row (@agenticprimitives/estate-projection). A federation door
admits a nameless visitor on "over 18 and a member of your organization", with a credential the organization's own
agent issued and a device key the key service holds. Reserved: BBS, AnonCreds and an in-circuit nullifier.
