npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

peercert

v0.2.1

Published

Peer-to-peer certificate issuance and management.

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 peercert

Quick Start

Auto-Send via MessageBox (Simplest)

import { PeerCert } from 'peercert'

const peercert = new PeerCert()

// Issue and automatically send via MessageBox
await peercert.issue({
  certificateType: 'employment', // Human-readable names are normalized automatically
  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: 'employment',
  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 compact = PeerCert.encodeCertificate(masterCert) // Compact base64, 50-70% smaller than JSON
// Display as QR (`peercert:${compact}`), 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() accepts every format a certificate can arrive in:

// JSON string
const result = await peercert.receive(serializedCertString)

// Compact base64 string (from a QR code or URL)
const result = await peercert.receive(qrData.replace('peercert:', ''))

// Raw binary (from an NFC tag or file)
const result = await peercert.receive(uint8ArrayFromNfc)

// 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 peercert.listCertificates({
  certifiers: [issuerPublicKey],
  types: ['employment'] // Same value you issued with
})

// Publicly reveal only selected fields
const broadcastResult = await peercert.reveal({
  certificate: certs[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 certifier

Verifiable Certificates (Selective Disclosure)

Create certificates that reveal only specific fields to a verifier:

// You have a certificate in your wallet
const certs = await peercert.listCertificates({ limit: 1 })

// Create a verifiable certificate revealing only some fields
const verifiableCert = await peercert.createVerifiableCertificate({
  certificate: certs[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 }
  )
  
  // Revoked certificates fail closed: result.verified is false
  if (result.verified && result.revocationStatus?.status === 'valid') {
    console.log('✅ Certificate is valid')
    console.log('Revealed fields:', result.fields)
  } else {
    console.log('⚠️  Rejected:', result.error ?? result.revocationStatus?.message)
  }
}

Certificate Revocation

Revoking Certificates You Issued:

// Get a certificate you issued
const issuedCerts = await peercert.listCertificates({ limit: 1 })

// Revoke it
const result = await peercert.revoke(issuedCerts[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.status === 'valid') {
  console.log('✅ Certificate is still valid')
} else if (status.status === 'revoked') {
  console.log('⚠️  Certificate has been revoked')
} else {
  // 'unknown': the lookup failed — do NOT assume the certificate is valid
  console.log('❓ Could not determine revocation status:', status.message)
}

Use Cases

Trust Networks

Build decentralized reputation systems where peers vouch for each other:

await peercert.issue({
  certificateType: 'reputation',
  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: 'identity-verification',
  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: 'skill-endorsement',
  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: '...') → Server

The 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 Overlay

Under the hood:

  1. Issuer creates a MasterCertificate with encrypted fields
  2. Issuer signs the certificate with their identity key
  3. Issuer creates a DID revocation outpoint on-chain (tagged with serial number)
  4. Recipient receives and verifies the signature
  5. Recipient stores the certificate in their wallet using acquireCertificate()
  6. 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
  7. 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 to new WalletClient())
  • options?: PeerCertOptions - Optional configuration
    • originator?: string - Originator domain for wallet operations
    • networkPreset?: 'mainnet' | 'testnet' | 'local' - Network configuration for DID operations
    • messageBoxHost?: 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: a base64 type ID (used as-is), or any human-readable name (e.g. 'employment'), which is deterministically normalized via PeerCert.certificateTypeFromName(). Only strings that cannot be base64 are normalized, so existing type IDs are never rewritten.
  • subjectIdentityKey: string - The peer's identity public key
  • fields: Record<string, string> - Certificate fields to attest
  • autoSend?: 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 | Uint8Array | MasterCertificate | DecodedCertificate - Any supported format:
    • JSON string (from MessageBox or JSON.stringify(cert))
    • Compact base64 string (from PeerCert.encodeCertificate(), e.g. QR codes/URLs)
    • Raw binary Uint8Array (e.g. from NFC tags or files)
    • MasterCertificate or decoded certificate object

Returns: Promise<ReceiveResult>

  • success: boolean - Whether the operation succeeded
  • walletCertificate?: WalletCertificate - The stored certificate
  • error?: string - Error message if failed

peercert.listCertificates(options?)

List certificates held in your wallet.

Parameters: ListCertificatesOptions (all optional)

  • certifiers?: string[] - Only certificates issued by these certifier public keys
  • types?: string[] - Only certificates of these types (human-readable names are normalized the same way as in issue())
  • limit?: number - Maximum number of certificates to return

Returns: Promise<WalletCertificate[]> - The matching certificates

peercert.reveal(options)

Publicly reveal selected certificate fields to the BSV overlay network.

Parameters: RevealOptions

  • certificate: WalletCertificate - Certificate from your wallet
  • fieldsToReveal: 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 key
  • serializedCertificate: string - Serialized certificate JSON
  • issuance?: 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<IncomingCertificate[]> - Array of incoming certificates

IncomingCertificate Type:

  • serializedCertificate: string - Serialized certificate JSON
  • messageId: string - MessageBox message ID for acknowledgment
  • sender: string - Sender's identity public key
  • issuance: boolean - Whether this was issued TO you (use receive()) or shared for inspection (use verifyVerifiableCertificate())

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 JSON
    • messageId - MessageBox message ID
    • sender - Sender's public key
    • issuance - 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 - use certifiersRequired: ['all'] when listing)
  • verifierPublicKey: string - Public key of who will verify the certificate
  • fieldsToReveal: string[] - Which fields to reveal (other fields remain encrypted)

Returns: Promise<VerifiableCertificate> - Verifiable certificate with selective field revelation

Example:

const certs = await peercert.listCertificates({
  certifiers: ['certifierIdentityKey'],
  limit: 1
})

const verifiable = await peercert.createVerifiableCertificate({
  certificate: certs[0],
  verifierPublicKey: '03verifier...',
  fieldsToReveal: ['role', 'company']
})

peercert.verifyVerifiableCertificate(certificate, options?)

Verify and decrypt a verifiable certificate shared with you.

Parameters:

  • certificate: string | VerifiableCertificate | object - The verifiable certificate as a JSON string, VerifiableCertificate instance, or plain object
  • options?: VerifyVerifiableCertificateOptions
    • checkRevocation?: boolean - Automatically check if certificate has been revoked (default: false)

Returns: Promise<VerifyVerifiableCertificateResult>

  • verified: boolean - Whether verification succeeded
  • fields?: Record<string, string> - Decrypted revealed fields
  • revocationStatus?: RevocationStatus - Revocation status if checkRevocation was enabled
  • error?: string - Error message if verification failed

With checkRevocation: true, a revoked certificate fails closed: verified is false and error explains why. If the revocation lookup itself fails, verified reflects the signature check but revocationStatus.status is 'unknown' — treat that as unconfirmed in trust-sensitive flows.

Example:

const result = await peercert.verifyVerifiableCertificate(certString, {
  checkRevocation: true
})

if (result.verified && result.revocationStatus?.status === 'valid') {
  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>

  • status: 'valid' | 'revoked' | 'unknown' - The revocation state; 'unknown' means the lookup failed and the certificate could NOT be confirmed valid
  • isRevoked: boolean - True only when the certificate is definitively revoked
  • revocationOutpoint: string - The revocation outpoint that was checked
  • message?: string - Additional details about revocation status

Example:

const status = await peercert.checkRevocation(myCertificate)

if (status.status !== 'valid') {
  console.log('Do not trust this certificate:', status.message)
}

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 succeeded
  • txid?: string - Transaction ID of the revocation
  • revocationOutpoint?: string - The revocation outpoint that was spent
  • error?: string - Error message if revocation failed

Example:

const result = await peercert.revoke(issuedCertificate)

if (result.success) {
  console.log('Certificate revoked! TXID:', result.txid)
}

PeerCert.encodeCertificate(certificate, outputFormat?)

Encode a MasterCertificate to a compact binary format — typically 50-70% smaller than JSON. Ideal for QR codes, NFC tags, URLs, and files.

Parameters:

  • certificate: MasterCertificate - The certificate to encode
  • outputFormat?: 'base64' | 'binary' - 'base64' (default) returns a string; 'binary' returns a Uint8Array

Returns: string | Uint8Array

PeerCert.decodeCertificate(encoded)

Decode a compact certificate back to certificate data. The format is detected from the input type (string = base64, Uint8Array = binary). Decoding is hardened against untrusted input: malformed or malicious payloads throw a clear error.

Parameters:

  • encoded: string | Uint8Array - The compact certificate

Returns: DecodedCertificate - Certificate data, which receive() accepts directly

Example:

// QR code contents: 'peercert:AQd...'
const compact = qrData.replace('peercert:', '')
const result = await peercert.receive(compact) // receive() decodes automatically

PeerCert.certificateTypeFromName(name)

Derive a 32-byte base64 certificate type from a human-readable name (SHA-256, deterministic). issue() and listCertificates() apply this automatically to any type that isn't already valid base64, so you rarely need to call it yourself — it's exposed so independent verifiers can compute the same type identifier. Note: a short name that happens to be base64-shaped (base64 alphabet, multiple of 4 characters, e.g. 'work') passes through unchanged — call this method explicitly if you want such a name hashed.

Parameters:

  • name: string - Human-readable type name, e.g. 'employment'

Returns: string - Base64-encoded 32-byte certificate type

Complete Example

Here's a complete workflow showing certificate issuance, receipt, and public reveal:

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: 'professional-endorsement',
  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 certs = await bobPeercert.listCertificates({
  certifiers: [alicePublicKey],
  types: ['professional-endorsement'] // Same value Alice issued with
})

const broadcastResult = await bobPeercert.reveal({
  certificate: certs[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 private

Security Considerations

  1. Signature Verification: The receive() and verifyVerifiableCertificate() methods automatically verify certificate signatures
  2. Trust Model: Certificates represent the issuer's attestation - trust is based on knowing and trusting the issuer
  3. 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
  4. Revocation:
    • All certificates include DID-based revocation outpoints on the BSV blockchain
    • Use checkRevocation() to manually verify revocation status
    • Use verifyVerifiableCertificate() with checkRevocation: true for automatic checking — revoked certificates fail closed (verified: false)
    • In trust-sensitive flows, only accept a certificate when revocationStatus.status === 'valid'; a status of 'unknown' means the lookup failed, not that the certificate is valid
    • Issuers can revoke certificates they issued using revoke()
  5. Private Keys: Never share your wallet's private keys - certificates use identity-based encryption
  6. 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 declarations

License

Open BSV License

Support

For issues, questions, or contributions, please visit the GitHub repository