DIDComm protocol support for the CREDEBL Mobile SDK — establish connections between wallets, exchange verifiable credentials, respond to proof requests, send messages, and connect to a mediator for offline delivery.
What is DIDComm? A secure, peer-to-peer messaging protocol for SSI wallets. Your wallet connects directly to an issuer or verifier (via a QR code or deep link), and they exchange signed messages — no central server involved.
See the root README for prerequisites and installation.
npm install @credebl/ssi-mobile-didcomm @hyperledger/anoncreds-react-native @hyperledger/indy-vdr-react-native
# or
yarn add @credebl/ssi-mobile-didcomm @hyperledger/anoncreds-react-native @hyperledger/indy-vdr-react-native
iOS:
cd ios && pod install
Android — ensure minSdkVersion >= 24 in android/app/build.gradle.
DidCommSDK instanceimport { DidCommSDK, DidCommMediatorPickupStrategy } from '@credebl/ssi-mobile-didcomm'
const didcommModule = new DidCommSDK({
peerNumAlgoForDidExchangeRequests: 1,
peerNumAlgoForDidRotation: 4,
processDidCommMessagesConcurrently: false,
mediatorPickupStrategy: DidCommMediatorPickupStrategy.PickUpV2LiveMode,
// Optional: configure Indy VDR ledger networks for AnonCreds credentials
indyVdr: {
networks: [
{
id: 'bcovrin:test',
isProduction: false,
indyNamespace: 'bcovrin:test',
genesisTransactions: '...genesis transactions string...',
},
],
},
})
MobileSDKOptionsimport { MobileSDKOptions, ConsoleLogger, LogLevel } from '@credebl/ssi-mobile-core'
import { DidCommSDK } from '@credebl/ssi-mobile-didcomm'
type AppModules = { didcomm: DidCommSDK }
export function createConfig(walletId: string, walletKey: string): MobileSDKOptions<AppModules> {
return {
agentConfig: {
label: 'My Wallet',
logger: new ConsoleLogger(LogLevel.debug),
},
askarConfig: {
id: walletId,
key: walletKey,
},
modules: { didcomm: didcommModule },
}
}
The DidCommProvider wires up all DIDComm React hooks. It must be rendered after the SDK is initialized.
import { MobileSDKProvider, MobileSDK, useMobileSDK } from '@credebl/ssi-mobile-core'
import { DidCommSDK } from '@credebl/ssi-mobile-didcomm'
type AppModules = { didcomm: DidCommSDK }
function SDKProviders({ children }: { children: React.ReactNode }) {
const { sdk } = useMobileSDK<AppModules>()
if (!sdk?.agent) return null
return (
<DidCommSDK.DidCommProvider agent={sdk.agent}>
{children}
</DidCommSDK.DidCommProvider>
)
}
export default function App() {
return (
<MobileSDKProvider>
<MobileSDK.AppProvider>
<SDKProviders>
{/* your app */}
</SDKProviders>
</MobileSDK.AppProvider>
</MobileSDKProvider>
)
}
import { useMobileSDK } from '@credebl/ssi-mobile-core'
import { DidCommSDK } from '@credebl/ssi-mobile-didcomm'
type AppModules = { didcomm: DidCommSDK }
function MyScreen() {
const { sdk } = useMobileSDK<AppModules>()
const didcomm = sdk.modules.didcomm
// Access: didcomm.connections, didcomm.credentials, didcomm.proofs
// didcomm.basicMessages, didcomm.questionAnswer, didcomm.mediatorRecipient
}
A connection is a secure, persistent channel between two wallets (or between a wallet and an issuer/verifier). Connections are usually established by scanning a QR code or following a deep link.
didcomm.connections.acceptInvitationFromUrl(url, config)Accept a DIDComm invitation from a scanned QR code URL or a deep link. This is the most common way to establish a connection.
const oobRecord = await didcomm.connections.acceptInvitationFromUrl(
'https://example.com?oob=eyJ...',
{ label: 'My Wallet' }
)
didcomm.connections.createInvitation(domain, config?)Create an invitation URL that another wallet can scan to connect with yours.
const { invitationUrl, record, invitation } = await didcomm.connections.createInvitation(
'https://myapp.example.com'
)
// Share invitationUrl as a QR code
didcomm.connections.parseInvitationFromUrl(url)Parse an invitation URL without accepting it — useful to inspect the invitation before accepting.
const invitation = await didcomm.connections.parseInvitationFromUrl('https://example.com?oob=...')
didcomm.connections.acceptInvitation(invitation, config)Accept a pre-parsed invitation object.
const oobRecord = await didcomm.connections.acceptInvitation(invitation, { label: 'My Wallet' })
didcomm.connections.waitForConnectionToComplete(connectionId, timeoutMs?)Wait until a connection reaches the Completed state. Call this after accepting an invitation if you need to confirm the handshake finished.
const connection = await didcomm.connections.waitForConnectionToComplete(
oobRecord.connectionRecord?.id,
30000 // 30 second timeout (optional)
)
didcomm.connections.getAll()Get all connection records.
const connections = await didcomm.connections.getAll()
didcomm.connections.getById(connectionId)Get a single connection by ID. Throws if not found.
const connection = await didcomm.connections.getById('connection-id')
didcomm.connections.findById(connectionId)Find a connection by ID — returns undefined instead of throwing if not found.
const connection = await didcomm.connections.findById('connection-id')
if (!connection) {
console.log('Connection not found')
}
didcomm.connections.deleteById(connectionId)Delete a connection record from the wallet.
await didcomm.connections.deleteById('connection-id')
didcomm.connections.hangup(connectionId, deleteAfterHangup?)Terminate an active connection and optionally delete the record.
await didcomm.connections.hangup('connection-id', true)
didcomm.connections.rotate(options)Rotate to a new DID on an existing connection (for key rotation / privacy).
await didcomm.connections.rotate({ connectionId: 'connection-id' })
A credential exchange is the process of an issuer offering a verifiable credential and the wallet holder accepting or declining it.
didcomm.credentials.getAllCredentialExchangeRecords()Get all credential exchange records (pending offers, accepted, declined, etc.).
const records = await didcomm.credentials.getAllCredentialExchangeRecords()
didcomm.credentials.getFormattedCredentialData(credentialRecordId)Get the decoded credential attributes and metadata for a given exchange record. Useful for displaying the credential offer to the user.
const data = await didcomm.credentials.getFormattedCredentialData('exchange-record-id')
console.log(data.offer) // the credential offer data
didcomm.credentials.acceptCredentialOffer({ credentialExchangeRecordId })Accept a pending credential offer. The credential will be stored in the wallet.
await didcomm.credentials.acceptCredentialOffer({
credentialExchangeRecordId: 'exchange-record-id',
})
didcomm.credentials.declineCredentialOffer({ credentialExchangeRecordId })Decline a pending credential offer.
await didcomm.credentials.declineCredentialOffer({
credentialExchangeRecordId: 'exchange-record-id',
})
didcomm.credentials.updateCredentialExchangeRecord(record)Update an existing credential exchange record (e.g. to store custom metadata).
await didcomm.credentials.updateCredentialExchangeRecord(credentialRecord)
didcomm.credentials.deleteCredentialExchangeRecordById(id, options?)Delete a credential exchange record.
await didcomm.credentials.deleteCredentialExchangeRecordById('exchange-record-id', {
deleteAssociatedCredentials: true,
})
didcomm.credentials.sendCredentialProblemReport(options)Report a problem with a credential exchange back to the issuer.
await didcomm.credentials.sendCredentialProblemReport({
credentialRecordId: 'exchange-record-id',
description: 'The credential data is incorrect',
})
didcomm.credentials.proposeCredential(options)Initiate a credential exchange by proposing a credential to a connected issuer.
await didcomm.credentials.proposeCredential({
connectionId: 'connection-id',
credentialFormats: {
anoncreds: {
credentialDefinitionId: 'cred-def-id',
attributes: [{ name: 'name', value: 'Alice' }],
},
},
})
A proof request is when a verifier asks your wallet to prove something (e.g. “prove you are over 18”). Your wallet selects matching credentials and sends a presentation.
didcomm.proofs.getCredentialsForProofRequest(options)Get all credentials in the wallet that can satisfy a given proof request.
const credentials = await didcomm.proofs.getCredentialsForProofRequest({
proofRecordId: 'proof-record-id',
})
didcomm.proofs.selectCredentialsForProofRequest(options)Automatically select the best matching credentials for a proof request.
const selected = await didcomm.proofs.selectCredentialsForProofRequest({
proofRecordId: 'proof-record-id',
})
didcomm.proofs.acceptProofRequest(options)Accept and respond to a proof request using the selected credentials.
await didcomm.proofs.acceptProofRequest({
proofRecordId: 'proof-record-id',
proofFormats: selected,
})
didcomm.proofs.declineProofRequest(options)Decline a proof request.
await didcomm.proofs.declineProofRequest({
proofRecordId: 'proof-record-id',
})
didcomm.proofs.getProofFormatData(proofRecordId)Get the raw proof request data — useful for displaying what is being asked for.
const data = await didcomm.proofs.getProofFormatData('proof-record-id')
console.log(data.request) // the proof request
didcomm.proofs.getProofRequestAgentMessage(proofRecordId)Get the raw DIDComm message for a proof request.
const message = await didcomm.proofs.getProofRequestAgentMessage('proof-record-id')
didcomm.proofs.sendProofProblemReport(options)Report a problem with a proof exchange.
await didcomm.proofs.sendProofProblemReport({
proofRecordId: 'proof-record-id',
description: 'Unable to satisfy the proof request',
})
Send and receive plain text messages over an existing connection.
didcomm.basicMessages.sendBasicMessage(connectionId, message)await didcomm.basicMessages.sendBasicMessage('connection-id', 'Hello!')
Send structured questions with multiple-choice responses over a connection.
didcomm.questionAnswer.sendQuestion(connectionId, config)Send a question to a connected peer.
const record = await didcomm.questionAnswer.sendQuestion('connection-id', {
question: 'Do you consent to share your credentials?',
validResponses: [{ text: 'Yes' }, { text: 'No' }],
detail: 'Your answer will be recorded on the ledger.',
})
didcomm.questionAnswer.sendAnswer(questionRecordId, response)Answer a question received from a connected peer.
await didcomm.questionAnswer.sendAnswer('question-record-id', 'Yes')
didcomm.questionAnswer.getAllQuestionAnswerRecords(query)Retrieve all Q&A records.
const records = await didcomm.questionAnswer.getAllQuestionAnswerRecords({})
didcomm.questionAnswer.getQuestionAnswerRecordById(id)Get a single Q&A record by ID.
const record = await didcomm.questionAnswer.getQuestionAnswerRecordById('qa-record-id')
A mediator is a server that holds messages for your wallet when your app is offline and delivers them when you reconnect. This is required for receiving credential offers and proof requests in the background.
didcomm.mediatorRecipient.startMediation(mediatorInvitationUrl, label)Connect to a mediator using its invitation URL.
const mediationRecord = await didcomm.mediatorRecipient.startMediation(
'https://mediator.example.com?oob=eyJ...',
'My Wallet'
)
didcomm.mediatorRecipient.initiateMessagePickup(mediator?, pickupStrategy?)Start picking up queued messages from the mediator. Call this after the SDK initializes.
await didcomm.mediatorRecipient.initiateMessagePickup(mediationRecord)
didcomm.mediatorRecipient.stopMessagePickup()Stop the message pickup process (e.g. when the app goes to background).
await didcomm.mediatorRecipient.stopMessagePickup()
All hooks below must be used inside DidCommSDK.DidCommProvider. They update in real time as records change.
import {
useConnections,
useConnectionById,
} from '@credebl/ssi-mobile-didcomm'
// All connections
const { records: connections, loading } = useConnections()
// Single connection
const connection = useConnectionById('connection-id')
import {
useCredentials,
useCredentialById,
useCredentialsByConnectionId,
useCredentialByState,
useCredentialNotInState,
DidCommCredentialState,
} from '@credebl/ssi-mobile-didcomm'
// All credential exchange records
const { records: credentials } = useCredentials()
// Single record by ID
const credential = useCredentialById('record-id')
// All records for a specific connection
const connectionCredentials = useCredentialsByConnectionId('connection-id')
// Records in a specific state (e.g. pending offers)
const pendingOffers = useCredentialByState(DidCommCredentialState.OfferReceived)
// Records NOT in a specific state
const nonPending = useCredentialNotInState(DidCommCredentialState.OfferReceived)
import {
useProofs,
useProofById,
useProofsByConnectionId,
useProofByState,
useProofNotInState,
DidCommProofState,
} from '@credebl/ssi-mobile-didcomm'
// All proof exchange records
const { records: proofs } = useProofs()
// Single record by ID
const proof = useProofById('record-id')
// Pending proof requests
const pendingRequests = useProofByState(DidCommProofState.RequestReceived)
import {
useBasicMessages,
useBasicMessagesByConnectionId,
} from '@credebl/ssi-mobile-didcomm'
// All messages
const { records: messages } = useBasicMessages()
// Messages for a specific connection
const connectionMessages = useBasicMessagesByConnectionId('connection-id')
import {
useQuestionAnswer,
useQuestionAnswerByConnectionId,
useQuestionAnswerById,
} from '@credebl/ssi-mobile-didcomm'
// All Q&A records
const { questionAnswerMessages } = useQuestionAnswer()
// Q&A for a specific connection
const byConnection = useQuestionAnswerByConnectionId('connection-id')
// Single record by ID
const record = useQuestionAnswerById('qa-record-id')