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

@aerocert/sdk

v0.8.1

Published

AeroCert privacy-first SDK for server-to-server certificate issuance.

Readme

@aerocert/sdk

Privacy-first AeroCert SDK for server-to-server issuance.

The SDK mirrors the /tokenize happy path for client software:

  1. Canonicalize the JSON payload locally.
  2. Encrypt it locally.
  3. Compute the SHA-256 hash locally.
  4. Send only ciphertext, IV, and hash material to POST /api/v1/privacy/anchor with X-API-Key.
  5. Return the transaction hash plus verifier artifacts for QR/PDF generation and long-term archive in the client system.
npm install @aerocert/sdk
import { AeroCert, buildPrivacyCertificatePayload } from '@aerocert/sdk';

const aerocert = new AeroCert({
  apiKey: process.env.AEROCERT_API_KEY!,
  apiBaseUrl: 'https://api.aerocert.co',
  privacyKey: process.env.AEROCERT_PRIVACY_KEY!,
});

const payload = await buildPrivacyCertificatePayload({
  schema: 'training_certificate@1',
  documentType: 'Training Certificate',
  chainId: 43113,
  chainName: 'Avalanche Fuji Testnet',
  contractAddress: process.env.AEROCERT_CERTIFICATE_REGISTRY_ADDRESS!,
  issuerOrganizationName: 'Aero Training Org',
  issuerOrganizationId: 'org-1',
  issuerWallet: '0x1234567890abcdef1234567890abcdef12345678',
  recipientName: 'Alice Moreau',
  courseName: 'Data Science',
  issueDate: '2026-05-19',
  certificateNumber: 'MBA-2026-0001',
  metadata: {
    holder_name: 'Alice Moreau',
    training_type: 'Data Science',
    training_date: '2026-05-19',
    certificate_number: 'MBA-2026-0001',
  },
});

const result = await aerocert.issuePrivacyCertificate({
  payload,
  includeQrDataUrl: true,
  includePdf: true,
});

console.log(result.transactionHash);
console.log(result.publicQrUrl);
console.log(result.verificationUrl);
console.log(result.payloadEncoded);
console.log(result.verificationArchive);
console.log(result.qrCode?.dataUrl);
console.log(result.pdfBase64);

CommonJS services can use the same package:

const { AeroCert, buildPrivacyCertificatePayload } = require('@aerocert/sdk');

Existing API services can also send ordinary certificate JSON through certify(...). The SDK loads non-secret issuer configuration from AeroCert using the API key, then builds the verifier-ready payload locally:

const result = await aerocert.certify({
  schema: 'generic:training_certificate',
  clientReference: 'request-123',
  dataToAnchor: {
    certificate_number: 'MBA-2026-0001',
    holder_first_name: 'Alice',
    holder_last_name: 'Moreau',
    training_name: 'Aviation Safety',
    issued_date: '2026-05-19',
  },
}, {
  includePdf: true,
  includeQrDataUrl: true,
});

Timeout, abort, and idempotent retry

Every SDK networked method accepts request controls:

  • timeoutMs: aborts one AeroCert HTTP request after the given number of milliseconds and rejects with AeroCertTimeoutError.
  • signal: composes a caller-owned AbortSignal with the SDK timeout and rejects aborted requests with AbortError.

Timeouts do not prove whether the API completed the operation. For retryable issuance flows, replay the same logical request with the same idempotencyKey.

import { AeroCert, AeroCertTimeoutError } from '@aerocert/sdk';

const idempotencyKey = `cert-${certificate.id}`;

try {
  await aerocert.certify({
    schema: 'generic:training_certificate',
    clientReference: certificate.id,
    dataToAnchor: certificate,
  }, {
    idempotencyKey,
    timeoutMs: 5_000,
  });
} catch (error) {
  if (error instanceof AeroCertTimeoutError) {
    await aerocert.certify({
      schema: 'generic:training_certificate',
      clientReference: certificate.id,
      dataToAnchor: certificate,
    }, {
      idempotencyKey,
      timeoutMs: 15_000,
    });
  } else {
    throw error;
  }
}

apiKey authenticates the organization. privacyKey encrypts the payload before it leaves the client system; do not treat the API key as the decryption secret or derive the privacy key from it. buildPrivacyCertificatePayload(...) produces the v=1 plaintext shape accepted by AeroCert /verify; arbitrary JSON can still be anchored with the lower-level anchorPrivacyPayload(...) method, but it will not produce a certificate verification URL accepted by /verify.

Successful SDK issuance returns both the chain proof and client artifacts:

  • transactionHash, dataHash, anchorRecordId
  • payloadEncoded, verificationUrl, publicQrUrl, qrPayload
  • verificationArchive
  • qrCode.dataUrl when includeQrDataUrl is true
  • pdfBase64 when includePdf is true

Artifact meaning:

  • publicQrUrl and qrPayload are the privacy-preserving QR target. They use https://aerocert.co/q/<public-token>#k=<public-key>, then resolve to the public verifier without putting the clear verifier payload in the printed QR.
  • verificationUrl is the self-contained verifier URL https://aerocert.co/verify#p=<payloadEncoded>. It contains the public verifier payload in the URL fragment and can be archived by the client system as a long-term recovery path.
  • payloadEncoded is the self-contained verifier payload. Store it with the issued certificate record if your privacy policy allows public certificate verification metadata to be archived outside AeroCert.
  • verificationArchive is the structured archive object to persist for official certificates. It carries the chain id, transaction hash, data hash, registry contract, organization registry, issuer wallet, organization profile hash, QR URL, self-contained verifier URL, payload, SDK version, and, from 0.7.10, the registry version, block number, blockchain timestamp when the AeroCert API response provides them, the organization profile hash algorithm, plus the minimal registry interface signatures needed to decode the archived transaction and reconstruct the on-chain verifier later.

For official mainnet certificates, archive verificationArchive plus the final issued PDF bytes or PDF hash. When a partner export includes artifact hashes, the AeroCert archive verifier can also compare local PDFs with --final-pdf <path> and --aerocert-proof-pdf <path>.

Revocation & Replacement

SDK 0.8.0 adds lifecycle helpers for corrected certificates. Existing issuance calls are unchanged: certify(...), issuePrivacyCertificate(...), package exports, ESM imports, and CommonJS imports keep the same contract.

Reason codes are the on-chain CertificateRegistryV4 values:

| Code | Meaning | |---|---| | 1 | Error in document | | 2 | Document expired or replaced | | 3 | Fraud or falsification | | 4 | Other |

Use replace(...) when a corrected certificate supersedes an old anchor. The SDK resolves the replaced anchor dataHash, injects it as metadata.replaces, injects metadata.correction_reason when reasonText is provided, anchors the corrected payload, then asks AeroCert to revoke the old anchor.

import { AeroCert, type ReplaceResponse } from '@aerocert/sdk';

const aerocert = new AeroCert({
  apiKey: process.env.AEROCERT_API_KEY!,
  apiBaseUrl: 'https://api.aerocert.co',
  privacyKey: process.env.AEROCERT_PRIVACY_KEY!,
});

const original = await aerocert.certify({
  schema: 'generic:training_certificate',
  dataToAnchor: {
    certificate_number: 'MBA-2026-0001',
    holder_name: 'Alice Moreau',
    training_name: 'Aviation Safety',
    issued_date: '2026-05-19',
  },
});

const replacement: ReplaceResponse = await aerocert.replace({
  schema: 'generic:training_certificate',
  dataToAnchor: {
    certificate_number: 'MBA-2026-0001-CORRECTED',
    holder_name: 'Alice Moreau',
    training_name: 'Aviation Safety',
    issued_date: '2026-05-20',
  },
  replacesAnchorId: original.anchorId,
  reasonCode: 2,
  reasonText: 'Corrected score after review',
});

if (replacement.status === 'partial') {
  // The corrected certificate exists. Retry revocation of the old anchor only.
  await aerocert.revoke({
    anchorId: original.anchorId,
    reasonCode: 2,
    reasonText: 'Corrected score after review',
  });
} else {
  console.log(replacement.anchorId);
  console.log(replacement.lineage.replacesAnchorId);
  console.log(replacement.revocation.txHash);
}

If your system already loaded the old anchor hash, pass replacesDataHash to avoid the SDK read of GET /api/v1/privacy/anchors/:id:

const correctedVerifierPayload = {
  v: 1,
  schema: 'generic:training_certificate',
  hashAlgorithm: 'SHA-256',
  chainId: 43113,
  contractAddress: '0x1111111111111111111111111111111111111111',
  issuerOrganizationName: 'Aero Training Org',
  issuerWallet: '0x2222222222222222222222222222222222222222',
  organizationProfile: { name: 'Aero Training Org' },
  organizationProfileHash: '0x3333333333333333333333333333333333333333333333333333333333333333',
  recipientName: 'Alice Moreau',
  courseName: 'Aviation Safety',
  issueDate: '2026-05-20',
  certificateNumber: 'MBA-2026-0001-CORRECTED',
  metadata: {},
};

await aerocert.replace({
  payload: correctedVerifierPayload,
  replacesAnchorId: 2475,
  replacesDataHash: '0x2424242424242424242424242424242424242424242424242424242424242424',
  reasonCode: 2,
  reasonText: 'Corrected score after review',
});

Webhook receiver signatures

Webhook delivery bodies must be verified from the raw request body:

import { verifyWebhookSignature } from '@aerocert/sdk';

const valid = verifyWebhookSignature({
  payload: rawBody,
  secret: process.env.AEROCERT_WEBHOOK_SECRET!,
  headers: req.headers,
});

if (!valid) {
  throw new Error('Invalid AeroCert webhook signature');
}

verifyWebhookSignature(...) rejects deliveries whose X-AeroCert-Webhook-Timestamp is more than 5 minutes from the receiver clock by default. Keep that check enabled and store processed X-AeroCert-Webhook-Id values if your handler must be strictly once-only.

The partner contract, endpoint list, and webhook semantics are documented in docs/api/PARTNER_INTEGRATION_CONTRACT.md.