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

@openagentid/oas-crypto

v1.0.2

Published

Cryptographic primitives for the Open Agent Specification — Ed25519, HKDF-SHA256, BLAKE3, JCS, encoding

Readme

@openagentid/oas-crypto

Cryptographic primitives for the Open Agent Specification (OAS).

This package provides Ed25519 key management, HKDF-SHA256 key derivation, BLAKE3 content hashing, JCS canonicalization (RFC 8785), multibase/base64url encoding, and AgentLineageProof2025 generation and verification.

Installation

npm i @openagentid/oas-crypto

Requirements: Node.js >= 20, ESM-only.

Usage

Generate a keypair and sign a message

import { OasKeyPair } from '@openagentid/oas-crypto';

const kp = await OasKeyPair.generate();
const message = new TextEncoder().encode('hello OAS');
const signature = await kp.sign(message);

const valid = await OasKeyPair.verifyWithKey(kp.verifyingKeyBytes, message, signature);
console.log(valid); // true

Reconstruct a keypair from bytes

import { OasKeyPair } from '@openagentid/oas-crypto';

const kp1 = await OasKeyPair.generate();
const bytes = kp1.signingKeyBytes;
const kp2 = await OasKeyPair.fromSigningKeyBytes(bytes);
// kp1 and kp2 produce identical signatures

Derive a child keypair

import { OasKeyPair, deriveChildKeypair } from '@openagentid/oas-crypto';

const parent = await OasKeyPair.generate();
const child = await deriveChildKeypair(parent, '/agent-my-bot');
// child has a deterministic keypair derived from parent + path

Generate and verify a lineage proof

import { OasKeyPair, AgentLineageProof } from '@openagentid/oas-crypto';

const parent = await OasKeyPair.generate();
const proof = await AgentLineageProof.generate(
  parent,
  'did:oas:l1fe:hmr:alice',
  'did:oas:l1fe:agent:my-bot',
  '/agent-my-bot',
);

const valid = await AgentLineageProof.verify(proof);
console.log(valid); // true

// Verify against a known public key (safer)
const validWithKey = await AgentLineageProof.verifyWithKey(
  proof,
  parent.publicKeyMultibase(),
);
console.log(validWithKey); // true

Hash content with BLAKE3

import { blake3Hash, blake3HashHex } from '@openagentid/oas-crypto';

const data = new TextEncoder().encode('hello');
const hash = blake3Hash(data);         // Uint8Array (32 bytes)
const hex = blake3HashHex(data);       // 64-char hex string

Canonicalize JSON with JCS

import { jcsCanonicalizeToString, jcsCanonicalize } from '@openagentid/oas-crypto';

const canonical = jcsCanonicalizeToString({ b: 2, a: 1 });
console.log(canonical); // '{"a":1,"b":2}'

const bytes = jcsCanonicalize({ childDid: 'did:oas:l1fe:agent:bot' });
// UTF-8 bytes of the canonical JSON

Encode and decode

import {
  multibaseEncode,
  multibaseDecode,
  base64urlEncode,
  base64urlDecode,
} from '@openagentid/oas-crypto';

// Multibase (base58btc with 'z' prefix)
const encoded = multibaseEncode(new Uint8Array([1, 2, 3]));
console.log(encoded.startsWith('z')); // true
const decoded = multibaseDecode(encoded);

// Base64url (RFC 4648, no padding)
const b64 = base64urlEncode(new Uint8Array([72, 101, 108, 108, 111]));
console.log(b64); // 'SGVsbG8'
const original = base64urlDecode(b64);

API Reference

OasKeyPair (class)

Ed25519 keypair for an OAS entity. Private keys are never exposed via toString() or toJSON().

class OasKeyPair {
  /**
   * Generates a new random Ed25519 keypair.
   */
  static async generate(): Promise<OasKeyPair>;

  /**
   * Constructs a keypair from raw 32-byte signing key material.
   * The verifying key is automatically derived.
   * @throws CryptoError if not 32 bytes.
   */
  static async fromSigningKeyBytes(bytes: Uint8Array): Promise<OasKeyPair>;

  /**
   * Validates and returns a copy of 32-byte verifying key bytes.
   * @throws CryptoError if not 32 bytes.
   */
  static verifyingKeyFromBytes(bytes: Uint8Array): Uint8Array;

  /**
   * Verifies an Ed25519 signature against a public key.
   * Uses constant-time comparison internally.
   * @throws CryptoError if key or signature bytes have wrong length.
   */
  static async verifyWithKey(
    publicKeyBytes: Uint8Array,
    message: Uint8Array,
    signatureBytes: Uint8Array,
  ): Promise<boolean>;

  /** Returns a copy of the raw 32-byte signing (private) key bytes. */
  get signingKeyBytes(): Uint8Array;

  /** Returns a copy of the raw 32-byte verifying (public) key bytes. */
  get verifyingKeyBytes(): Uint8Array;

  /** Signs a message. Returns a 64-byte Ed25519 signature. */
  async sign(message: Uint8Array): Promise<Uint8Array>;

  /** Returns the public key as multibase base58btc (with 'z' prefix). */
  publicKeyMultibase(): string;

  /** Debug-safe string representation. Private key is always redacted. */
  toString(): string;

  /** JSON-safe representation. Private key is excluded. */
  toJSON(): { verifyingKey: string };
}

AgentLineageProof

Lineage proof generation and verification (OAS Spec Sections 9.4, 9.5).

const PROOF_TYPE = 'AgentLineageProof2025';
const PROOF_ALGORITHM = 'HKDF-SHA256-Ed25519';

interface AgentLineageProofData {
  readonly proofType: typeof PROOF_TYPE;
  readonly parentDid: string;
  readonly childDid: string;
  readonly derivationPath: string;
  readonly algorithm: typeof PROOF_ALGORITHM;
  readonly publicKeyMultibase: string;
  readonly signature: string;
}

const AgentLineageProof = {
  /**
   * Generates a new lineage proof signed by the parent keypair.
   * @throws CryptoError on signing or canonicalization failure.
   */
  async generate(
    parentKeypair: OasKeyPair,
    parentDid: string,
    childDid: string,
    derivationPath: string,
  ): Promise<AgentLineageProofData>;

  /**
   * Verifies a lineage proof using the embedded public key.
   * @throws CryptoError on decoding or verification errors.
   */
  async verify(proof: AgentLineageProofData): Promise<boolean>;

  /**
   * Verifies a lineage proof against an externally provided public key.
   * @throws CryptoError on decoding or verification errors.
   */
  async verifyWithKey(
    proof: AgentLineageProofData,
    publicKeyMultibase: string,
  ): Promise<boolean>;

  /** Serializes a proof to a plain JSON object. */
  toJSON(proof: AgentLineageProofData): Record<string, string>;

  /**
   * Deserializes a proof from a plain JSON object.
   * @throws CryptoError if required fields are missing.
   */
  fromJSON(obj: Record<string, unknown>): AgentLineageProofData;
};

Key derivation

/**
 * Derives a child Ed25519 keypair from a parent keypair using HKDF-SHA256.
 * IKM: parent signing key. Salt: parent verifying key. Info: derivation path.
 * @throws CryptoError on derivation failure.
 */
async function deriveChildKeypair(
  parent: OasKeyPair,
  derivationPath: string,
): Promise<OasKeyPair>;

/**
 * Low-level HKDF-SHA256 key material derivation. Returns 32 bytes.
 * @throws CryptoError on derivation failure.
 */
function deriveKeyMaterial(
  ikm: Uint8Array,
  salt: Uint8Array,
  info: Uint8Array,
): Uint8Array;

Hashing

/** Computes a 32-byte BLAKE3 hash. */
function blake3Hash(data: Uint8Array): Uint8Array;

/** Computes a BLAKE3 hash as a 64-character lowercase hex string. */
function blake3HashHex(data: Uint8Array): string;

Canonicalization

/**
 * Canonicalizes a JSON value to UTF-8 bytes per RFC 8785 (JCS).
 * @throws CryptoError on unsupported values (undefined, functions, circular refs).
 */
function jcsCanonicalize(value: unknown): Uint8Array;

/**
 * Canonicalizes a JSON value to a string per RFC 8785 (JCS).
 * @throws CryptoError on unsupported values.
 */
function jcsCanonicalizeToString(value: unknown): string;

Encoding

/** Encodes bytes as multibase base58btc (with 'z' prefix). */
function multibaseEncode(bytes: Uint8Array): string;

/**
 * Decodes a multibase base58btc string (strips 'z' prefix).
 * @throws CryptoError if prefix is not 'z' or decoding fails.
 */
function multibaseDecode(encoded: string): Uint8Array;

/** Encodes bytes as base64url (RFC 4648, no padding). */
function base64urlEncode(bytes: Uint8Array): string;

/**
 * Decodes a base64url string to bytes.
 * @throws CryptoError on decoding failure.
 */
function base64urlDecode(encoded: string): Uint8Array;

Error classes

class CryptoError extends Error {
  readonly code: CryptoErrorCode;
  readonly details: Readonly<Record<string, unknown>>;
  constructor(code: CryptoErrorCode, message: string, details?: Record<string, unknown>);
}

const CryptoErrorCode = {
  InvalidSignature: 'INVALID_SIGNATURE',
  InvalidKeyLength: 'INVALID_KEY_LENGTH',
  DerivationFailed: 'DERIVATION_FAILED',
  MultibaseDecodeFailed: 'MULTIBASE_DECODE_FAILED',
  Base64DecodeFailed: 'BASE64_DECODE_FAILED',
  CanonicalizationFailed: 'CANONICALIZATION_FAILED',
  InvalidProofPayload: 'INVALID_PROOF_PAYLOAD',
  KeyConstructionFailed: 'KEY_CONSTRUCTION_FAILED',
} as const;

type CryptoErrorCode = (typeof CryptoErrorCode)[keyof typeof CryptoErrorCode];

Dependencies

| Package | Purpose | |---------|---------| | @noble/ed25519 2.1.0 | Ed25519 signatures (audited by Cure53) | | @noble/hashes 1.6.1 | SHA-256, HKDF, BLAKE3 (audited by Cure53) | | @scure/base 1.2.1 | Base58, Base64url encoding |

All dependencies are audited, pure-JavaScript, and have zero native addons.

Security

  • Private key bytes are stored as Uint8Array, never as strings
  • toString() and toJSON() always redact the signing key
  • Constant-time signature verification via @noble/ed25519
  • Callers should zero signing key bytes when done: keypair.signingKeyBytes.fill(0)

License

Copyright © 2026 L1fe Labs, Inc.

Licensed under either of Apache License 2.0 or MIT license, at your option.