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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@relaycorp/veraid

v3.2.11

Published

DNS without the Internet

Readme

VeraId library for Node.js

npm version

This is the Node.js implementation of VeraId, an offline authentication protocol powered by DNSSEC. This library implements all the building blocks that signature producers and consumers need.

The latest version can be installed from NPM:

npm install @relaycorp/veraid

Usage

Signature production

To produce a signature for a given plaintext, you need a Member Id Bundle (produced by a VeraId organisation; e.g., via VeraId Authority) and the Member's private key.

For example, if you wanted to produce signatures valid for up to 30 days for a service identified by the OID 1.2.3.4.5, you could implement the following function and call it in your code:

import { MemberIdBundle, SignatureBundle } from '@relaycorp/veraid';
import { addDays } from 'date-fns';

const TTL_DAYS = 30;
const SERVICE_OID = '1.2.3.4.5';

async function produceSignature(
  plaintext: ArrayBuffer,
  memberIdBundleSerialised: ArrayBuffer,
  memberSigningKey: CryptoKey,
): Promise<ArrayBuffer> {
  const memberIdBundle = MemberIdBundle.deserialise(memberIdBundleSerialised);
  const expiryDate = addDays(new Date(), TTL_DAYS);
  const signatureBundle = await SignatureBundle.sign(
    plaintext,
    SERVICE_OID,
    memberIdBundle,
    memberSigningKey,
    expiryDate,
  );
  return signatureBundle.serialise();
}

The output is the VeraId Signature Bundle, which contains the Member Id Bundle and the actual signature. It does not include the plaintext.

To produce an organisation signature, use the class OrganisationSigner instead of a MemberIdBundle.

Note that for signatures to actually be valid for up to 30 days, the TTL override in the VeraId TXT record should allow 30 days or more.

Signature verification

To verify a VeraId signature, you simply need the Signature Bundle and the plaintext to be verified. For extra security, this library also requires you to confirm the service where you intend to use the plaintext.

If VeraId's maximum TTL of 90 days or the TTL specified by the signature producer may be too large for your application, you may also want to restrict the validity period of signatures.

For example, if you only want to accept signatures valid for the past 30 days in a service identified by 1.2.3.4.5, you could use the following function:

import { type IDatePeriod, SignatureBundle } from '@relaycorp/veraid';
import { subDays } from 'date-fns';

const TTL_DAYS = 30;
const SERVICE_OID = '1.2.3.4.5';

async function verifySignature(
  plaintext: ArrayBuffer,
  signatureBundleSerialised: ArrayBuffer,
): Promise<string> {
  const now = new Date();
  const datePeriod: IDatePeriod = { start: subDays(now, TTL_DAYS), end: now };
  const signatureBundle = SignatureBundle.deserialise(signatureBundleSerialised);
  const {
    member: { user, organisation },
  } = await signatureBundle.verify(plaintext, SERVICE_OID, datePeriod);
  return user === undefined ? organisation : `${user}@${organisation}`;
}

signatureBundle.verify() will throw an error if the signature is invalid for whatever reason. See SignatureBundleVerification for more details on the result.

verifySignature() will return the id of the VeraId member that signed the plaintext, which looks like [email protected] if the member is a user or simply example.com if the member is a bot (acting on behalf of the organisation example.com).

Testing with mock trust chains

You can use MockTrustChain to test your integration with VeraId by generating valid signature bundles without the real DNSSEC infrastructure. This makes it easy to test signature creation and verification, but it won't work in production because it relies on mock DNSSEC trust anchors.

For example, to test the produceSignature() function illustrated above, you could use MockTrustChain as follows:

import { MockTrustChain } from '@relaycorp/veraid';
import { addMinutes } from 'date-fns';
import { describe, expect, test } from 'vitest';

const mockTrustChain = await MockTrustChain.generate(
  'example.com', 
  'alice', // Use `undefined` for bot signatures
  addMinutes(new Date(), 10), // Expiry date
);

describe('produceSignature', () => {
  test('should produce valid signatures', async () => {
    const plaintext = new TextEncoder().encode('Hello world');
    const memberIdBundleSerialised = mockTrustChain.chain.serialise();

    const signatureBundleSerialised = await produceSignature(
      plaintext,
      memberIdBundleSerialised,
      mockTrustChain.signerPrivateKey,
    );

    const signatureBundle = SignatureBundle.deserialise(signatureBundleSerialised);
    const { member } = await signatureBundle.verify(
      undefined, // The plaintext is already encapsulated
      SERVICE_OID,
      new Date(),
      mockTrustChain.dnssecTrustAnchors,
    );
    expect(member.organisation).toBe('example.com');
    expect(member.user).toBe('alice');
  });
});

To test the verifySignature() function illustrated above, you'd have to add an optional parameter for the DNSSEC trust anchors and use MockTrustChain as follows:

import { type IDatePeriod, type TrustAnchor, MockTrustChain, SignatureBundle } from '@relaycorp/veraid';
import { addMinutes, subDays } from 'date-fns';
import { describe, expect, test } from 'vitest';

// Modify the verifySignature function to accept custom trust anchors
async function verifySignature(
  plaintext: ArrayBuffer,
  signatureBundleSerialised: ArrayBuffer,
  trustAnchors?: readonly TrustAnchor[], // New optional parameter
): Promise<string> {
  const now = new Date();
  const datePeriod: IDatePeriod = { start: subDays(now, TTL_DAYS), end: now };
  const signatureBundle = SignatureBundle.deserialise(signatureBundleSerialised);
  const {
    member: { user, organisation },
  } = await signatureBundle.verify(plaintext, SERVICE_OID, datePeriod, trustAnchors);
  return user === undefined ? organisation : `${user}@${organisation}`;
}

const mockTrustChain = await MockTrustChain.generate(
  'example.com', 
  'alice', 
  addMinutes(new Date(), 10), // Expiry date
);

describe('verifySignature', () => {
  test('should verify valid signatures', async () => {
    const plaintext = new TextEncoder().encode('Hello world');
    const signatureBundle = await mockTrustChain.sign(plaintext, SERVICE_OID);

    const memberId = await verifySignature(
      plaintext,
      signatureBundle.serialise(),
      mockTrustChain.dnssecTrustAnchors,
    );

    expect(memberId).toBe('[email protected]');
  });
});

Custom trust anchors

There are only two legitimate reasons to override the DNSSEC trust anchors during verification:

  • To test a service implementation locally (e.g., in a CI pipeline, during development).
  • To reflect an official change to the root zone trust anchors, if you're not able to use a version of this library that uses the new trust anchors.

API docs

The API documentation can be found on docs.relaycorp.tech.

WebCrypto notes

Private keys passed to this library may optionally define a provider property, which would be used as the SubtleCrypto instance when producing digital signatures (e.g., when issuing certificates). If not provided, the default SubtleCrypto instance will be used.

As of this writing, only @relaycorp/webcrypto-kms supports this functionality.

Contributions

We love contributions! If you haven't contributed to a Relaycorp project before, please take a minute to read our guidelines first.