Docs

Plug Kanon into a Credo agent in five minutes.

The plugin registers the Kanon AnonCreds VDR, wraps the verifier service for on-chain status checks, and subscribes to issuance events. You write zero AnonCreds plumbing.

1 · Install

Add the plugin to your backend.

@credo-ts/kanon is a first-party Credo 0.6+ module. The package works with any EVM RPC — Besu, Ethereum mainnet, L2s, private rollups. You need ethers v6.

package.json
"@credo-ts/anoncreds": "0.6.1",
"@credo-ts/core": "0.6.1",
"@credo-ts/kanon": "^0.1.0",
"ethers": "^6.13.0",
2 · Configure the module

Register KanonModule with your AnonCredsModule.

Pass the kanonv2 contract address for AnonCredsStatusRegistry. The plugin replaces the default AnonCredsVerifierService with one that ANDs the on-chain status check.

agent.ts
import { Agent } from '@credo-ts/core'
import { AnonCredsModule } from '@credo-ts/anoncreds'
import { KanonModule, KanonAnonCredsRegistry } from '@credo-ts/kanon'
const agent = new Agent({
modules: {
anoncreds: new AnonCredsModule({
registries: [new KanonAnonCredsRegistry()],
}),
kanon: new KanonModule({
networks: [{ network: 'besu', rpcUrl, privateKey }],
anonCredsStatusRegistryAddress: '0x…',
}),
},
})
3 · Register a schema

Write your attrNames. The plugin handles the rest.

Pass your normal AnonCreds schema to buildKanonSchema. The plugin transparently adds the bookkeeping attribute needed for on-chain status lookup — you never type it.

schema.ts
import { buildKanonSchema } from '@credo-ts/kanon'
await agent.modules.anoncreds.registerSchema({
schema: buildKanonSchema({
name: 'DriverLicense',
version: '1.0',
attrNames: ['fullName', 'dateOfBirth', 'licenseNumber'],
issuerId: did,
}),
})
4 · Issue a credential

Pass your attributes. The plugin adds the credId.

buildKanonCredentialAttributes prepends a fresh UUID for the bookkeeping field. The event listener writes issueCredential(credDefId, credIdHash) when the credential reaches state 'done' — you don't touch the chain.

issue.ts
import { buildKanonCredentialAttributes } from '@credo-ts/kanon'
const { attributes } = buildKanonCredentialAttributes([
{ name: 'fullName', value: 'Jane Doe' },
{ name: 'dateOfBirth', value: '1990-04-12' },
{ name: 'licenseNumber', value: 'NY-441-AAA' },
])
await agent.modules.anoncreds.acceptCredentialOffer({
credentialRecordId,
credentialFormats: { anoncreds: { attributes } },
})
5 · Verify a presentation

Wrap your proof request. Get on-chain status for free.

buildKanonProofRequest adds the bookkeeping attribute to your proof request for each Kanon credDef. The wrapped AnonCredsVerifierService then runs anoncreds-rs and the on-chain isRevoked lookup — both must pass for isValid to be true.

verify.ts
import { buildKanonProofRequest } from '@credo-ts/kanon'
const proofRequest = buildKanonProofRequest({
name: 'age check',
version: '1.0',
requested_attributes: {
fullName: { name: 'fullName', restrictions: [{ cred_def_id }] },
},
kanonCredDefIds: [cred_def_id],
})
// verifyProof returns true only if both the AnonCreds CL proof
// and the on-chain status lookup pass.
6 · Revoke a credential

One transaction. No tails file.

Call revokeCredentialOnChain on the registry. The issuer's signer must be a member of the credDef's issuing org. The next verifier check returns 'revoked' on the very next call.

revoke.ts
import { KanonAnonCredsRegistry } from '@credo-ts/kanon'
const registry = agent.dependencyManager.resolve(KanonAnonCredsRegistry)
await registry.revokeCredentialOnChain(
agentContext,
credDefId,
credId,
)
Reference

kanonCredIdHash — internal.

Internal hash used as the on-chain registry key. The helpers above call it for you; you'll only need this if you're building tooling that talks to the contract directly.

hash.ts
import { kanonCredIdHash } from '@credo-ts/kanon'
// keccak256 of the utf-8 encoding
kanonCredIdHash('a3f4-…-9d2b')
// '0x4a…b2'

Need the optional ZK mode?

Mode B replaces the AnonCreds presentation with a Groth16 SNARK for unlinkable verification. See the architecture page for the circuit and verifier registry.