Skip to main content
Version: 0.7

DIDComm

DIDComm is a messaging protocol for agents that keep a long-lived, encrypted connection with each other. Once two agents are connected, either side can start a protocol over that connection: issue a credential, request a proof, or send a message. Credo implements DIDComm v1 and the Aries RFCs built on it in @credo-ts/didcomm. This page explains when to use DIDComm and how to set up the module.

When to use DIDComm​

Use DIDComm when the parties have an ongoing relationship, because the connection stays usable after the first exchange. It is also the only protocol Credo supports for AnonCreds credentials, and the one other Aries-based agents use.

For one-off issuance or verification, and for wallets built for the EU Digital Identity Wallet ecosystem, use OpenID4VC instead. An agent can register both modules.

DIDCommOpenID4VC
RelationshipA connection that either side can use at any timeOne exchange per offer or request
Credential formatsAnonCreds, W3C VCsSD-JWT VC, mdoc, W3C VCs
Mobile walletNeeds a mediator to receive messagesCalls the issuer and verifier over HTTPS

DIDs and DIDComm explains the concepts.

Supported protocols​

ProtocolAries RFCAPI
Out-of-band 1.10434agent.didcomm.oob
DID Exchange 1.10023agent.didcomm.connections
Connections 1.0 (deprecated)0160agent.didcomm.connections
DID Rotate 1.00794agent.didcomm.connections
Trust Ping 1.00048agent.didcomm.connections
Issue Credential 2.00453agent.didcomm.credentials
Issue Credential 1.0 (deprecated)0036agent.didcomm.credentials, with DidCommCredentialV1Protocol from @credo-ts/anoncreds/didcomm
Revocation Notification 1.0 and 2.00183, 0721agent.didcomm.credentials
Present Proof 2.00454agent.didcomm.proofs
Present Proof 1.0 (deprecated)0037agent.didcomm.proofs, with DidCommProofV1Protocol from @credo-ts/anoncreds/didcomm
Coordinate Mediation 1.00211agent.didcomm.mediator, agent.didcomm.mediationRecipient
Pickup 1.0 and 2.00212, 0685agent.didcomm.messagePickup
Discover Features 1.0 and 2.00031, 0557agent.didcomm.discovery
Basic Message 1.00095agent.didcomm.basicMessages
Action Menu 1.00509@credo-ts/action-menu
Question Answer 1.00113@credo-ts/question-answer
DIDComm RPC 1.00804@credo-ts/drpc

Use DID Exchange and the 2.0 protocols. The deprecated protocols are removed in Credo 0.8, so only use them for agents that support nothing else. Credo doesn't implement DIDComm v2.

Install​

npm install @credo-ts/didcomm@^0.7.2

On Node.js the inbound transports come from @credo-ts/node, which you install when you set up the agent.

Configure the module​

Register a DidCommModule under the key didcomm. Credo only exposes the API as agent.didcomm under that key. Under any other key it is available as agent.modules.<key>.

A server agent listens for incoming messages and advertises the URLs where other agents reach it.

agent.ts
import { AskarModule } from '@credo-ts/askar'
import { Agent } from '@credo-ts/core'
import {
DidCommConnectionEventTypes,
type DidCommConnectionStateChangedEvent,
DidCommDidExchangeState,
DidCommHttpOutboundTransport,
DidCommModule,
DidCommWsOutboundTransport,
} from '@credo-ts/didcomm'
import { DrizzleStorageModule } from '@credo-ts/drizzle-storage'
import { didcommBundle } from '@credo-ts/drizzle-storage/didcomm'
import { agentDependencies, DidCommHttpInboundTransport, DidCommWsInboundTransport } from '@credo-ts/node'
import { NativeAskar } from '@openwallet-foundation/askar-nodejs'
import { drizzle } from 'drizzle-orm/libsql'

const agent = new Agent({
dependencies: agentDependencies,
modules: {
// Drizzle stores the records. Run its migrations before you start the agent.
storage: new DrizzleStorageModule({ database: drizzle('file:agent.db'), bundles: [didcommBundle] }),
// Askar stores the keys: DIDComm needs its XSalsa20-Poly1305 support
askar: new AskarModule({
askar: NativeAskar,
enableStorage: false,
store: { id: 'acme-agent-keys', key: 'acme-agent-store-key' },
}),
didcomm: new DidCommModule({
// The public URLs other agents use to reach this agent, in order of preference
endpoints: ['http://localhost:4040', 'ws://localhost:4041'],
transports: {
inbound: [new DidCommHttpInboundTransport({ port: 4040 }), new DidCommWsInboundTransport({ port: 4041 })],
outbound: [new DidCommHttpOutboundTransport(), new DidCommWsOutboundTransport()],
},
connections: { autoAcceptConnections: true },
}),
},
})

await agent.initialize()

The Node.js example stores records with Drizzle, which also explains the packages and migrations it needs, and keys with Askar. DIDComm needs Askar, or another key management backend with XSalsa20-Poly1305, for its keys. See Storage for the options.

Endpoints and transports​

endpoints lists the public URLs that go into your invitations and DID documents, in order of preference. Behind a reverse proxy, use the public URL (for example https://agent.example.com/didcomm), not the local port. An agent without endpoints can only receive messages through a mediator, or over a WebSocket it opened itself.

TransportPackagePlatforms
DidCommHttpOutboundTransport@credo-ts/didcommNode.js, React Native
DidCommWsOutboundTransport@credo-ts/didcommNode.js, React Native
DidCommHttpInboundTransport@credo-ts/nodeNode.js
DidCommWsInboundTransport@credo-ts/nodeNode.js

DidCommHttpInboundTransport takes a port and an optional path. Pass app instead of port to mount it on an Express app you start yourself. DidCommWsInboundTransport takes a port, or a server (a WebSocketServer from ws). To serve HTTP and WebSocket on one port, give the WebSocket server noServer: true and forward the HTTP server's upgrade events to it, as the sample mediator does.

Protocols​

The connection, out-of-band and discover features protocols are always on. The other protocols are on by default. Pass false to turn one off, which also removes its API from agent.didcomm, or pass an object to configure it.

OptionConfiguresDetails
connectionsConnections and DID ExchangeConnections
credentialsIssue Credential 2.0Issuing credentials
proofsPresent Proof 2.0Requesting proofs
mediatorActing as a mediatorMediation
mediationRecipientUsing a mediatorMediation
messagePickupPickup 1.0 and 2.0Mediation
basicMessagesBasic messagesMessaging
warning

By default, credentials and proofs register the 2.0 protocols without any credential formats, so the agent can't issue or verify anything. Configure the formats you need as described in Issuing credentials and Requesting proofs.

Other options​

OptionDefaultDescription
processDidCommMessagesConcurrentlyfalseProcess incoming messages in parallel instead of one at a time.
queueTransportRepositoryin memoryWhere messages for agents without an endpoint wait until they are picked up. Mediators need a persistent queue.
didCommMimeTypeDidCommMimeType.V1Content type for sent messages. DidCommMimeType.V0 (application/ssi-agent-wire) is for older agents.
useDidKeyInProtocolstrueEncode keys as did:key in protocol messages (RFC 0360).
useDidSovPrefixWhereAllowedfalseUse the legacy did:sov:BzCbsNYhMrjHiqZDTUASHg;spec message type prefix for older agents.

Events​

Credo emits an event for every state change, such as a connection being completed or a message arriving. Listen with agent.events.on(), and remove the listener with agent.events.off(). agent.events.observable() returns the same events as an RxJS observable.

agent.ts
agent.events.on<DidCommConnectionStateChangedEvent>(
DidCommConnectionEventTypes.DidCommConnectionStateChanged,
({ payload }) => {
const { connectionRecord, previousState } = payload
console.log(`Connection ${connectionRecord.id}: ${previousState} -> ${connectionRecord.state}`)

if (connectionRecord.state === DidCommDidExchangeState.Completed) {
console.log(`Connected with ${connectionRecord.theirLabel}`)
}
}
)

The event type enums and payload types are exported from @credo-ts/didcomm.

Event typePayload
DidCommConnectionEventTypes.DidCommConnectionStateChangedconnectionRecord, previousState
DidCommOutOfBandEventTypes.OutOfBandStateChangedoutOfBandRecord, previousState
DidCommCredentialEventTypes.DidCommCredentialStateChangedcredentialExchangeRecord, previousState
DidCommProofEventTypes.ProofStateChangedproofRecord, previousState
DidCommBasicMessageEventTypes.DidCommBasicMessageStateChangedbasicMessageRecord, message
DidCommRoutingEventTypes.MediationStateChangedmediationRecord, previousState

Each event also has metadata.contextCorrelationId, which tells you which tenant it belongs to when you use multi-tenancy.