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

@smartledger/keys

v2.0.0

Published

Key management SDK for Lumen cryptographic operations - Now with proper ML-DSA-44/65/87 implementations

Readme

@smartledger/keys SDK Documentation

Single source of truth for cryptographic key operations.

Version: 1.5.1
Status: ✅ Production Ready
Tests: 52/52 passing

New in 1.5.1: All three ML-DSA security levels now available (44/65/87)


Why This SDK?

Keys are the root of everything in your crypto architecture:

  • Identity
  • Signatures
  • PQ migration
  • Regulatory/audit story

Without a clean SDK layer, key handling becomes ad hoc and brittle:

  • ❌ Random generateKeypair() implementations scattered across repos
  • ❌ Slightly different formats (hex here, base64 there)
  • ❌ Surprise refactors when adding PQ suites
  • ❌ Private keys exposed to calling code

With @smartledger/keys, all your agents/modules/bridge code simply ask:

"Give me a signing key of suite X"
"Sign this payload with keyId Y"

And don't care how keys are stored, rotated, or implemented.


Design Principles

  1. Private keys hidden - Never exposed to most callers
  2. Crypto agility - Suite is config, not code
  3. Single entry point - Easy to audit
  4. Idempotent operations - Safe for retries
  5. Minimal API surface - Small, focused, easy to learn

Installation

npm install @smartledger/keys

Supported ML-DSA Variants

Choose the right security level for your use case:

| Variant | NIST Level | Equivalent | Use Case | |---------|------------|------------|----------| | ml-dsa-44 | Level 2 | AES-128 | IoT devices, high-throughput systems | | ml-dsa-65 | Level 3 | AES-192 | General purpose (recommended) | | ml-dsa-87 | Level 5 | AES-256 | High-security, long-term confidential data |


Quick Start

import { createKeySDK } from '@smartledger/keys';

// One line to get a ready-to-use SDK with ML-DSA + ECDSA suites registered
const sdk = createKeySDK();

// Create both PQ + ECDSA keys for an agent
// Using ml-dsa-65 (Level 3) - recommended for most use cases
const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
  'agent-resonance',
  {
    primarySignatureSuite: 'ml-dsa-65',  // or 'ml-dsa-44', 'ml-dsa-87'
    secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
  }
);

// Sign with both suites (private keys stay hidden)
const message = new TextEncoder().encode('agent output');
const { signatures } = await sdk.signWithSuites('agent-resonance', message);

// Verify both signatures
const verify = await sdk.verifyWithSuites('agent-resonance', message, signatures);
console.log(verify.allValid); // true

API Reference

Factory Helper

createKeySDK(config?) returns a ready-to-use SDK wired with in-memory storage and the default ML-DSA + ECDSA suites. Pass your own keyRegistry or suiteRegistry to override defaults.

KeySDK Interface

createKey(agentId, profile, options?)

Create a new key for an agent/module.

Parameters:

  • agentId (string) - Agent identifier (e.g. 'agent-resonance')
  • profile (CryptoProfile) - Which signature suites to use
  • options (CreateKeyOptions) - Optional expiration, usage, etc.

Returns: Promise<KeyRecord> - KeyRecord with metadata and public key

Example:

const key = await sdk.createKey(
  'agent-schema',
  { primarySignatureSuite: 'ml-dsa-65' },  // Recommended: Level 3
  {
    expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(), // 1 year
    usage: ['signing'],
  }
);

getKey(keyId)

Get an existing key by ID.

Parameters:

  • keyId (string) - Key identifier

Returns: Promise<KeyRecord | null> - KeyRecord or null if not found

Example:

const key = await sdk.getKey('agent-resonance:pk-ml-1');
if (key) {
  console.log(key.meta.suiteId); // 'ml-dsa-65' or 'ml-dsa-44', 'ml-dsa-87'
  console.log(key.publicKey); // Uint8Array
}

signWithKey(keyId, message)

Sign a message with a key. Private key never leaves the SDK.

Parameters:

  • keyId (string) - Which key to sign with
  • message (Uint8Array) - Message to sign (will be hashed internally)

Returns: Promise<Uint8Array> - Signature bytes

Throws: Error if key not found or not active

Example:

const message = new TextEncoder().encode('agent output');
const signature = await sdk.signWithKey('agent-resonance:pk-ml-1', message);

verifySignature(keyId, message, signature)

Verify a signature against a public key.

Parameters:

  • keyId (string) - Which key to verify with
  • message (Uint8Array) - Original message
  • signature (Uint8Array) - Signature to verify

Returns: Promise<boolean> - true if valid

Example:

const valid = await sdk.verifySignature(
  'agent-resonance:pk-ml-1',
  message,
  signature
);

listKeysForAgent(agentId, activeOnly?)

List all keys for an agent.

Parameters:

  • agentId (string) - Agent identifier
  • activeOnly (boolean) - If true, only return active keys (default: true)

Returns: Promise<KeyRecord[]> - Array of KeyRecords

Example:

const keys = await sdk.listKeysForAgent('agent-schema');
for (const key of keys) {
  console.log(`${key.meta.keyId} (${key.meta.suiteId})`);
}

getActiveCryptoProfile(agentId)

Get the active crypto profile for an agent.

Looks up the most recent active keys and infers the profile.

Parameters:

  • agentId (string) - Agent identifier

Returns: Promise<CryptoProfile | null> - CryptoProfile or null if no keys found

Example:

const profile = await sdk.getActiveCryptoProfile('agent-schema');
console.log(profile.primarySignatureSuite); // 'ml-dsa-65'
console.log(profile.secondarySignatureSuite); // 'bsv-ecdsa-secp256k1'

rotateAgentKeys(agentId)

Rotate an agent's keys.

Generates new keypairs for all active keys, marks old ones as rotated.

Parameters:

  • agentId (string) - Agent identifier

Returns: Promise<KeyRecord[]> - Array of new KeyRecords

Example:

const newKeys = await sdk.rotateAgentKeys('agent-schema');
for (const key of newKeys) {
  console.log(`New: ${key.meta.keyId}`);
  console.log(`Rotated from: ${key.meta.rotatedFrom}`);
}

getOrCreateKey(agentId, profile, options?)

Create key if it doesn't exist, otherwise return existing.

Idempotent key creation for agent setup.

Parameters:

  • agentId (string) - Agent identifier
  • profile (CryptoProfile) - Crypto configuration
  • options (CreateKeyOptions) - Creation options

Returns: Promise<KeyRecord> - KeyRecord (new or existing)

Example:

// Safe to call multiple times
const key1 = await sdk.getOrCreateKey('agent-validator', {
  primarySignatureSuite: 'ml-dsa-65',
});

const key2 = await sdk.getOrCreateKey('agent-validator', {
  primarySignatureSuite: 'ml-dsa-65',
});

console.log(key1.meta.keyId === key2.meta.keyId); // true

createDualSignatureKeys(agentId, profile, options?)

Create both primary and secondary keys in one call. Requires profile.secondarySignatureSuite.

Returns: { primaryKey, secondaryKey }

Example:

const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
  'agent-bridge',
  {
    primarySignatureSuite: 'ml-dsa-65',  // Recommended
    secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
  }
);

getOrCreateDualSignatureKeys(agentId, profile, options?)

Idempotent version of the above; reuses active keys when present.

Returns: { primaryKey, secondaryKey }


signWithSuites(agentId, message, suites?)

Sign once per suite. When suites is omitted, active primary + secondary are used.

Returns: { signatures: Array<{ suiteId, keyId, signature }> }

Example:

const message = new TextEncoder().encode('hybrid payload');
const { signatures } = await sdk.signWithSuites('agent-bridge', message);

verifyWithSuites(agentId, message, signatures)

Verify multiple signatures and get per-suite results.

Returns: { results: Array<{ suiteId, keyId, valid }>, allValid: boolean }

Example:

const verify = await sdk.verifyWithSuites('agent-bridge', message, signatures);
console.log(verify.allValid); // true when every suite verifies


Types

KeyRecord

Public key record (safe to expose, no private key).

interface KeyRecord {
  meta: KeyMeta;          // Key metadata
  publicKey: Uint8Array;  // Public key bytes
}

CreateKeyOptions

Options for key creation.

interface CreateKeyOptions {
  expiresAt?: string;                              // ISO 8601
  usage?: Array<'signing' | 'encryption' | 'both'>; // Default: ['signing']
  cryptoProfileVersion?: string;                    // Default: '1.0.0'
}

CryptoProfile

Per-agent/module crypto configuration.

interface CryptoProfile {
  primarySignatureSuite: string;     // e.g. 'ml-dsa-65' (recommended), 'ml-dsa-44', 'ml-dsa-87'
  secondarySignatureSuite?: string;  // e.g. 'bsv-ecdsa-secp256k1' (hybrid)
  keyEncapsulationSuite?: string;    // e.g. 'ml-kem-768' (future)
}

Usage Patterns

Single Signature (PQ-only)

// Agent uses only ML-DSA (recommended: ml-dsa-65)
const key = await sdk.createKey('agent-research', {
  primarySignatureSuite: 'ml-dsa-65',  // or 'ml-dsa-44' for IoT, 'ml-dsa-87' for max security
});

const message = new TextEncoder().encode('research results');
const signature = await sdk.signWithKey(key.meta.keyId, message);

Hybrid Signature (ECDSA + PQ)

// One call to create both keys
await sdk.getOrCreateDualSignatureKeys('agent-bridge', {
  primarySignatureSuite: 'ml-dsa-65',  // Balanced security (recommended)
  secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
});

// Sign with both suites
const message = new TextEncoder().encode('bridge output');
const { signatures } = await sdk.signWithSuites('agent-bridge', message);

// Verify both signatures
const verified = await sdk.verifyWithSuites('agent-bridge', message, signatures);
console.log(verified.allValid); // true

Agent Setup (Idempotent)

// Safe to call on every startup
async function setupAgent(agentId: string) {
  const key = await sdk.getOrCreateKey(agentId, {
    primarySignatureSuite: 'ml-dsa-65',  // Recommended default
  }, {
    expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
  });
  
  return key;
}

Monthly Key Rotation

// Automated rotation job
async function monthlyRotation() {
  const agents = ['agent-resonance', 'agent-schema', 'agent-validator'];
  
  for (const agentId of agents) {
    const newKeys = await sdk.rotateAgentKeys(agentId);
    console.log(`✓ Rotated ${agentId}: ${newKeys.length} keys`);
  }
}

Benefits

🔒 Security

  • Private keys hidden - Never exposed to calling code
  • Single audit point - All key operations go through SDK
  • No key leakage - Keys stay in KeyRegistry storage
  • Consistent hashing - Suite-appropriate algorithms

🔄 Crypto Agility

  • Suite is config - Change ML-DSA → Falcon without code changes
  • Profile-driven - CryptoProfile defines agent's crypto setup
  • Easy migration - Hybrid mode for ECDSA → PQ transition

🛠️ Developer Experience

  • Clean API - 8 methods, easy to learn
  • Idempotent - getOrCreateKey safe for retries
  • TypeScript - Full type safety
  • Well-tested - 23 tests covering all scenarios

📊 Operations

  • Centralized - One SDK for all agents
  • Auditable - Easy to track key operations
  • Scalable - Storage backends (in-memory, file, KMS)
  • Rotation-friendly - Built-in rotation support

Best Practices

✅ Do

  • Use SDK for all key operations
  • Call getOrCreateKey for idempotent setup
  • Set expiration dates on keys
  • Rotate keys regularly (monthly/quarterly)
  • Use signWithKey (not direct registry access)

❌ Don't

  • Expose private keys to calling code
  • Create keys manually outside SDK
  • Hard-code suite IDs in business logic
  • Skip key rotation

Integration with Existing Code

Before (Direct Registry)

// Scattered key operations
const suite = globalRegistry.getSuite('ml-dsa-87');
const keypair = await suite.generateKeypair();
await keyRegistry.registerKey('agent-1:pk-1', keypair, 'ml-dsa-87');

const key = await keyRegistry.getKey('agent-1:pk-1');
const sig = await suite.sign(key.keypair.privateKey, message);

After (SDK)

// Clean, centralized
const key = await sdk.createKey('agent-1', {
  primarySignatureSuite: 'ml-dsa-65',  // Choose: 'ml-dsa-44', '65', or '87'
});

const sig = await sdk.signWithKey(key.meta.keyId, message);

What's Next?

  • File Storage - Persistent key storage (not just in-memory)
  • KMS Integration - AWS KMS, Azure Key Vault, HSM
  • Key Backup - Encrypted backup/recovery procedures
  • Multi-tenant - Isolate keys per tenant
  • Hardware Keys - Support for non-exportable hardware keys

Summary

@smartledger/keys is the single source of truth for cryptographic keys:

✅ 23 tests passing
✅ Private keys hidden from callers
✅ Crypto-agnostic API (suite is config)
✅ Idempotent operations
✅ Easy to audit
✅ Production-ready

Use it for all key operations. Make crypto agility a reality.


See Also:


Version: 1.0.0
Last Updated: November 28, 2025