peercert
v0.1.4
Published
Peer-to-peer certificate issuance and management.
Maintainers
Readme
peercert
Peer-to-peer certificate issuance and management.
PeerCert provides a clean API for issuing, receiving, and publicly revealing peer-to-peer certificates on the BSV blockchain. Built on top of @bsv/sdk, it simplifies complex certificate workflows into simple method calls.
Features
- ✅ Direct Certificate Issuance - Issue certificates directly from one peer to another
- ✅ Integrated MessageBox Delivery - Send certificates automatically via MessageBox
- ✅ Multiple Delivery Methods - Supports MessageBox, QR codes, NFC, files, and custom channels
- ✅ Selective Disclosure - Create verifiable certificates revealing only selected fields
- ✅ Automatic Verification - Verify shared certificates with optional automatic revocation checking
- ✅ Certificate Revocation - Revoke certificates you issued via DID tokens on BSV overlay
- ✅ Revocation Checking - Check if certificates have been revoked
- ✅ Public Reveal - Broadcast certificates to BSV overlay for public verification
- ✅ No Server Required - Eliminates the need for a central certifier server
- ✅ Cryptographic Verification - Full signature verification using BSV identity keys
- ✅ TypeScript Support - Full type safety and IntelliSense support
Installation
npm install peercertQuick Start
Auto-Send via MessageBox (Simplest)
import { Utils } from '@bsv/sdk'
import { PeerCert } from 'peercert'
const peercert = new PeerCert()
// Issue and automatically send via MessageBox
await peercert.issue({
certificateType: Utils.toBase64(Utils.toArray('employment', 'utf8')),
subjectIdentityKey: '03abc123...', // Peer's identity key
fields: {
role: 'Engineer',
company: 'ACME Corp',
start_date: '2024-01-15'
},
autoSend: true // Automatically sends via MessageBoxClient!
})Manual Sending (More Control)
// Issue the certificate
const masterCert = await peercert.issue({
certificateType: Utils.toBase64(Utils.toArray('employment', 'utf8')),
subjectIdentityKey: '03abc123...',
fields: { role: 'Engineer', company: 'ACME Corp' }
})
// Send via MessageBox manually
await peercert.send({
recipient: '03abc123...',
serializedCertificate: JSON.stringify(masterCert)
})
// Or send via other methods (QR code, NFC, file, etc.)
const serialized = JSON.stringify(masterCert)
// Display as QR, write to NFC, save to file, etc.Custom Wallet (Optional)
If you need to use a custom wallet instance:
import { WalletClient } from '@bsv/sdk'
import { PeerCert } from 'peercert'
const customWallet = new WalletClient()
const peercert = new PeerCert(customWallet)Receiving Certificates
From MessageBox (Recommended):
// Get incoming certificates from your MessageBox
const incoming = await peercert.listIncomingCertificates()
for (const cert of incoming) {
console.log(`Certificate from ${cert.sender}`)
// Receive and store the certificate
const result = await peercert.receive(cert.serializedCertificate)
if (result.success) {
console.log('Certificate accepted!')
// Acknowledge to remove from MessageBox
await peercert.acknowledgeCertificate(cert.messageId)
}
}Live Listening:
// Listen for certificates in real-time
await peercert.listenForCertificates(async (serializedCertificate, messageId, sender) => {
console.log(`New certificate from ${sender}!`)
const result = await peercert.receive(serializedCertificate)
if (result.success) {
await peercert.acknowledgeCertificate(messageId)
}
})From Other Sources (QR, NFC, File, etc.):
// Receive from string
const result = await peercert.receive(serializedCertString)
// Or receive from MasterCertificate object
const result = await peercert.receive(masterCertObject)
if (result.success) {
console.log('Certificate accepted!')
console.log('Certificate:', result.walletCertificate)
} else {
console.error('Failed to receive certificate:', result.error)
}Publicly Revealing Certificate Attributes
After receiving a certificate, you can publicly reveal selected attributes to the BSV overlay network:
// Get your wallet certificate from storage
const certs = await wallet.listCertificates({
certifiers: [issuerPublicKey],
types: [certificateType]
})
// Publicly reveal only selected fields
const broadcastResult = await peercert.reveal({
certificate: certs.certificates[0],
fieldsToReveal: ['role', 'company'] // Only these fields go public
})
console.log('Certificate revealed on overlay:', broadcastResult.txid)
// Anyone can now verify these fields without contacting the certifierVerifiable Certificates (Selective Disclosure)
Create certificates that reveal only specific fields to a verifier:
// You have a certificate in your wallet
const certs = await wallet.listCertificates({
certifiersRequired: ['all'], // Get certificates with keyring
limit: 1
})
// Create a verifiable certificate revealing only some fields
const verifiableCert = await peercert.createVerifiableCertificate({
certificate: certs.certificates[0],
verifierPublicKey: '03verifier...',
fieldsToReveal: ['role', 'company'] // Only these fields revealed
})
// Send to verifier via MessageBox
await peercert.send({
recipient: '03verifier...',
serializedCertificate: JSON.stringify(verifiableCert),
issuance: false // This is for inspection, not storage
})Verifying Shared Certificates:
// Receive and verify shared certificates
const incoming = await peercert.listIncomingCertificates()
for (const cert of incoming) {
// Verify with automatic revocation check
const result = await peercert.verifyVerifiableCertificate(
cert.serializedCertificate,
{ checkRevocation: true }
)
if (result.verified) {
if (result.revocationStatus?.isRevoked) {
console.log('⚠️ Certificate has been revoked!')
} else {
console.log('✅ Certificate is valid')
console.log('Revealed fields:', result.fields)
}
}
}Certificate Revocation
Revoking Certificates You Issued:
// Get a certificate you issued
const issuedCerts = await wallet.listCertificates({
limit: 1
})
// Revoke it
const result = await peercert.revoke(issuedCerts.certificates[0])
if (result.success) {
console.log('Certificate revoked! TXID:', result.txid)
} else {
console.error('Revocation failed:', result.error)
}Checking Revocation Status:
// Check if any certificate has been revoked
const status = await peercert.checkRevocation(certificate)
if (status.isRevoked) {
console.log('⚠️ Certificate has been revoked')
console.log(status.message)
} else {
console.log('✅ Certificate is still valid')
}Use Cases
Trust Networks
Build decentralized reputation systems where peers vouch for each other:
await peercert.issue({
certificateType: Utils.toBase64(Utils.toArray('reputation', 'utf8')),
subjectIdentityKey: peerKey,
fields: {
rating: '5',
completed_transactions: '47',
endorsed_skills: 'JavaScript,TypeScript,React'
}
})Identity Verification
Peers can verify each other's identity attributes:
await peercert.issue({
certificateType: Utils.toBase64(Utils.toArray('identity-verification', 'utf8')),
subjectIdentityKey: peerKey,
fields: {
verified_name: 'true',
verified_email: 'true',
verified_date: '2024-01-15'
}
})Skill Endorsements
Create professional endorsements without centralized platforms:
await peercert.issue({
certificateType: Utils.toBase64(Utils.toArray('skill-endorsement', 'utf8')),
subjectIdentityKey: peerKey,
fields: {
skill: 'Smart Contract Development',
level: 'expert',
years_known: '3'
}
})How It Works
Traditional Certificate Model
In the traditional model, you need a server:
Client → acquireCertificate(acquisitionProtocol: 'issuance', certifierUrl: '...') → ServerThe server verifies attributes and signs the certificate.
PeerCert Model
With PeerCert, peers directly issue certificates:
Peer A (Issuer) → peercert.issue() → Master Certificate → Send to Peer B
Peer B (Recipient) → peercert.receive() → Verifies & Stores Certificate
Peer B (Optional) → peercert.reveal() → Broadcasts to BSV OverlayUnder the hood:
- Issuer creates a
MasterCertificatewith encrypted fields - Issuer signs the certificate with their identity key
- Issuer creates a DID revocation outpoint on-chain (tagged with serial number)
- Recipient receives and verifies the signature
- Recipient stores the certificate in their wallet using
acquireCertificate() - Recipient can:
- Publicly reveal selected fields to the overlay network
- Create verifiable certificates revealing only selected fields to specific verifiers
- Check revocation status via DID overlay queries
- Issuer can revoke issued certificates by spending the DID token
API Reference
new PeerCert(wallet?, options?)
Create a new PeerCert instance.
Parameters:
wallet?: WalletInterface- Optional wallet to use for operations (defaults tonew WalletClient())options?: PeerCertOptions- Optional configurationoriginator?: string- Originator domain for wallet operationsnetworkPreset?: 'mainnet' | 'testnet' | 'local'- Network configuration for DID operationsmessageBoxHost?: string- MessageBox server URL (defaults to 'https://messagebox.babbage.systems')enableMessageBoxLogging?: boolean- Enable logging for MessageBox operations
Returns: PeerCert instance
Example:
// Simple usage with default wallet
const peercert = new PeerCert()
// With custom wallet
const peercert = new PeerCert(myWallet)
// With options
const peercert = new PeerCert(myWallet, {
originator: 'myapp.com',
networkPreset: 'mainnet'
})peercert.issue(options)
Issue a certificate to a peer.
Parameters: IssueOptions
certificateType: string- Certificate type identifier (base64 encoded)subjectIdentityKey: string- The peer's identity public keyfields: Record<string, string>- Certificate fields to attestautoSend?: boolean- Automatically send via MessageBox (defaults to false)
Returns: Promise<MasterCertificate> - The created certificate
peercert.receive(certificate)
Receive and verify a certificate from a peer.
Parameters:
certificate: string | MasterCertificate- Serialized certificate JSON or MasterCertificate object
Returns: Promise<ReceiveResult>
success: boolean- Whether the operation succeededwalletCertificate?: WalletCertificate- The stored certificateerror?: string- Error message if failed
peercert.reveal(options)
Publicly reveal selected certificate fields to the BSV overlay network.
Parameters: RevealOptions
certificate: WalletCertificate- Certificate from your walletfieldsToReveal: CertificateFieldNameUnder50Bytes[]- Fields to reveal publicly
Returns: Promise<RevealResult> - Broadcast response from overlay network (BroadcastResponse | BroadcastFailure)
peercert.send(options)
Send a certificate to a recipient via MessageBox.
Parameters: SendOptions
recipient: string- Recipient's identity public keyserializedCertificate: string- Serialized certificate JSONissuance?: boolean- Whether this is an issuance (true, default) or sharing for inspection (false)
Returns: Promise<void>
peercert.listIncomingCertificates()
List incoming certificates from your MessageBox.
Returns: Promise<PendingCertificate[]> - Array of incoming certificates
PendingCertificate Type:
serializedCertificate: string- Serialized certificate JSONmessageId: string- MessageBox message ID for acknowledgmentsender: string- Sender's identity public key
peercert.acknowledgeCertificate(messageId)
Acknowledge a certificate message in MessageBox (marks it as read/processed).
Parameters:
messageId: string- The message ID to acknowledge
Returns: Promise<void>
peercert.listenForCertificates(callback)
Listen for live certificate messages from MessageBox.
Parameters:
callback: (serializedCertificate: string, messageId: string, sender: string, issuance: boolean) => void | Promise<void>serializedCertificate- The certificate JSONmessageId- MessageBox message IDsender- Sender's public keyissuance- Whether this is an issuance (true) or shared for inspection (false)
Returns: Promise<void> - Starts listening (call is async but keeps connection open)
peercert.createVerifiableCertificate(options)
Create a verifiable certificate that reveals only selected fields to a verifier.
Parameters: CreateVerifiableCertificateOptions
certificate: WalletCertificate- Certificate from your wallet (must have keyring - usecertifiersRequired: ['all']when listing)verifierPublicKey: string- Public key of who will verify the certificatefieldsToReveal: string[]- Which fields to reveal (other fields remain encrypted)
Returns: Promise<VerifiableCertificate> - Verifiable certificate with selective field revelation
Example:
const certs = await wallet.listCertificates({
certifiers: ['certifierIdentityKey'], // Required to get keyring
limit: 1
})
const verifiable = await peercert.createVerifiableCertificate({
certificate: certs.certificates[0],
verifierPublicKey: '03verifier...',
fieldsToReveal: ['role', 'company']
})peercert.verifyVerifiableCertificate(serializedCertificate, options?)
Verify and decrypt a verifiable certificate shared with you.
Parameters:
serializedCertificate: string- Serialized verifiable certificate JSONoptions?: VerifyVerifiableCertificateOptionscheckRevocation?: boolean- Automatically check if certificate has been revoked (default: false)
Returns: Promise<VerifyVerifiableCertificateResult>
verified: boolean- Whether verification succeededfields?: Record<string, string>- Decrypted revealed fieldsrevocationStatus?: RevocationStatus- Revocation status if checkRevocation was enablederror?: string- Error message if verification failed
Example:
const result = await peercert.verifyVerifiableCertificate(certString, {
checkRevocation: true
})
if (result.verified && !result.revocationStatus?.isRevoked) {
console.log('Valid certificate:', result.fields)
}peercert.checkRevocation(certificate)
Check if a certificate has been revoked.
Parameters:
certificate: WalletCertificate- Certificate to check
Returns: Promise<RevocationStatus>
isRevoked: boolean- Whether the certificate has been revokedrevocationOutpoint: string- The revocation outpoint that was checkedmessage?: string- Additional details about revocation status
Example:
const status = await peercert.checkRevocation(myCertificate)
if (status.isRevoked) {
console.log('Certificate has been revoked')
}peercert.revoke(certificate)
Revoke a certificate that you issued.
Parameters:
certificate: WalletCertificate- Certificate you issued (that you want to revoke)
Returns: Promise<RevokeResult>
success: boolean- Whether revocation succeededtxid?: string- Transaction ID of the revocationrevocationOutpoint?: string- The revocation outpoint that was spenterror?: string- Error message if revocation failed
Example:
const result = await peercert.revoke(issuedCertificate)
if (result.success) {
console.log('Certificate revoked! TXID:', result.txid)
}Complete Example
Here's a complete workflow showing certificate issuance, receipt, and public reveal:
import { Utils } from '@bsv/sdk'
import { PeerCert } from 'peercert'
// Step 1: Alice issues a certificate to Bob
const alicePeercert = new PeerCert()
const bobPublicKey = '02bob...'
const masterCert = await alicePeercert.issue({
certificateType: Utils.toBase64(Utils.toArray('professional-endorsement', 'utf8')),
subjectIdentityKey: bobPublicKey,
fields: {
skill: 'TypeScript Development',
level: 'expert',
projects_completed: '15',
years_known: '2'
}
})
// Alice sends the certificate to Bob (JSON.stringify(masterCert))
const serialized = JSON.stringify(masterCert)
// Step 2: Bob receives and verifies Alice's certificate
const bobPeercert = new PeerCert()
const result = await bobPeercert.receive(serialized)
if (!result.success) {
throw new Error('Certificate verification failed: ' + result.error)
}
console.log('Certificate received and stored!')
console.log('Certifier:', result.walletCertificate?.certifier)
// Step 3: Bob publicly reveals some fields on the overlay network
// First get the certificate from Bob's wallet
const bobWallet = bobPeercert['wallet'] // Access internal wallet
const certs = await bobWallet.listCertificates({
certifiers: [alicePublicKey],
types: [Utils.toBase64(Utils.toArray('professional-endorsement', 'utf8'))]
})
const broadcastResult = await bobPeercert.reveal({
certificate: certs.certificates[0],
fieldsToReveal: ['skill', 'level'] // Only these fields go public
})
console.log('Certificate revealed:', broadcastResult.txid)
// Anyone can now verify Bob's skill and level without contacting Alice
// projects_completed and years_known remain privateSecurity Considerations
- Signature Verification: The
receive()andverifyVerifiableCertificate()methods automatically verify certificate signatures - Trust Model: Certificates represent the issuer's attestation - trust is based on knowing and trusting the issuer
- Selective Disclosure:
- Use
reveal()to share only necessary fields publicly on the overlay network - Use
createVerifiableCertificate()to share selected fields privately with a specific verifier
- Use
- Revocation:
- All certificates include DID-based revocation outpoints on the BSV blockchain
- Use
checkRevocation()to manually verify revocation status - Use
verifyVerifiableCertificate()withcheckRevocation: truefor automatic checking - Issuers can revoke certificates they issued using
revoke()
- Private Keys: Never share your wallet's private keys - certificates use identity-based encryption
- Verifiable Certificates: When creating verifiable certificates, only specified fields are decryptable by the verifier
Development
# Install dependencies
npm install
# Run tests
npm test
# Build the package
npm run build
# This creates:
# - dist/cjs/ - CommonJS build
# - dist/esm/ - ES modules build
# - dist/types/ - TypeScript declarationsLicense
PROPRIETARY
Support
For issues, questions, or contributions, please visit the GitHub repository
