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

@ajna-inc/vaults

v0.6.3

Published

Post-quantum encrypted vaults with DIDComm protocol for Credo. Client-side encryption using ML-KEM-768, AES-256-GCM, and Shamir secret sharing with P2P sharing via DIDComm messages.

Downloads

549

Readme

Post-Quantum Encrypted Vaults

Post-quantum encrypted vaults with DIDComm protocol for Credo. Client-side encryption using ML-KEM-768, AES-256-GCM, and Shamir secret sharing with P2P sharing via DIDComm messages.

Overview

The Vaults module provides secure, client-side encrypted storage with peer-to-peer sharing capabilities. All cryptographic operations happen locally on the agent - no server ever sees plaintext data. It supports multiple encryption suites including post-quantum ML-KEM-768, flexible access policies (passphrase, any-of, all-of, threshold), and optional external storage (S3) for large files.

Table of Contents

Features

Encryption Suites

  • S3 Suite: Passphrase-based encryption (Argon2id KDF + AES-256-GCM)
  • P1 Suite: Post-quantum encryption (ML-KEM-768 + AES-256-GCM)
  • H1 Suite: Hybrid mode (planned)

Access Policies

  • Passphrase: Single-user encryption with passphrase-derived key
  • Any-of: Multiple recipients, any one can decrypt independently
  • All-of: Multiple recipients, all must cooperate to decrypt
  • Threshold: Shamir secret sharing with configurable t-of-n threshold

Vault Operations

  • Create, open, update, delete encrypted vaults
  • List vaults and get metadata without decrypting
  • KEM keypair generation and management
  • Document signing vault workflows

DIDComm Protocol

  • 15 message types for P2P vault sharing
  • Request, grant, and deny access to vaults
  • Threshold share request and distribution
  • External storage operator support
  • Problem reporting for error handling

Storage

  • Inline storage in agent wallet
  • External S3 storage for large files
  • Configurable inline/external threshold
  • Storage operator mode

How It Works

Architecture

                     Application Code
                          |
                      VaultsApi
          create / open / update / delete / list
          KEM key mgmt / signing vaults
                          |
         +----------------+----------------+
         |                |                |
    VaultService    KEM Service    SigningVaultSvc
         |                |                |
         +--------+-------+-------+--------+
                  |               |
           EncryptionSvc    HPKEService
                  |               |
              WASM Crypto (30+ functions)
                  |
       +----------+----------+
       |          |          |
     AEAD       KEM      Shamir
   AES-256   ML-KEM-768  Secret
    GCM                  Sharing
       |          |          |
  VaultRepository    StorageService
       |                   |
  Agent Wallet       S3 / External

Encryption Flow

  1. Passphrase vault: Passphrase -> Argon2id KDF -> 256-bit CEK -> AES-256-GCM encrypt
  2. Post-quantum vault: ML-KEM-768 encapsulate -> shared secret -> HKDF -> CEK -> AES-256-GCM encrypt
  3. Threshold vault: CEK -> Shamir split into n shares -> wrap each share with recipient's KEM public key

Key Commitment

Every vault includes a key commitment (kcmp) in the header to verify the correct key was used during decryption, preventing key confusion attacks.

Encryption Suites

S3 Suite (Passphrase)

KDF: Argon2id (configurable memory and iterations) AEAD: AES-256-GCM Use Case: Personal vaults protected by a passphrase

  • Passphrase is stretched via Argon2id with random salt
  • Derived key encrypts data with AES-256-GCM
  • Key commitment ensures correct passphrase verification

P1 Suite (Post-Quantum)

KEM: ML-KEM-768 (NIST PQC standard) AEAD: AES-256-GCM Use Case: Sharing vaults between agents with quantum-resistant security

  • Recipient's ML-KEM public key encapsulates a shared secret
  • Shared secret is expanded via HKDF to derive CEK
  • CEK encrypts data with AES-256-GCM

Access Policies

Passphrase Policy

Single user, passphrase-derived encryption key.

Any-of Policy

Multiple recipients each get an independently-encrypted copy of the CEK wrapped with their KEM public key. Any single recipient can decrypt.

All-of Policy

All participants must cooperate. The CEK is split such that every participant's contribution is required for reconstruction.

Threshold Policy

Shamir secret sharing splits the CEK into n shares with a threshold of t. Any t shares can reconstruct the CEK. Each share is wrapped with the respective participant's KEM public key.

Protocol Flows

1. Create and Open a Vault

Agent
  |-- create(passphrase, data) --> Argon2id + AES-256-GCM --> VaultRecord stored
  |-- open(passphrase) --> derive key --> AES-256-GCM decrypt --> plaintext

2. Share Vault via DIDComm

Owner                                  Recipient
  |                                        |
  |  1. GrantAccessMessage (header + CT)   |
  |--------------------------------------->|
  |                                        |
  |  2. VaultStoredAckMessage              |
  |<---------------------------------------|

3. Request Access

Requester                                Owner
  |                                        |
  |  1. RequestAccessMessage               |
  |--------------------------------------->|
  |                                        |
  |  2. GrantAccessMessage / DenyAccess    |
  |<---------------------------------------|

4. Threshold Reconstruction

Requester                   ShareHolder1    ShareHolder2
  |                              |               |
  |  RequestShareMessage         |               |
  |----------------------------->|               |
  |  RequestShareMessage                         |
  |--------------------------------------------->|
  |                              |               |
  |  ProvideShareMessage         |               |
  |<-----------------------------|               |
  |  ProvideShareMessage                         |
  |<---------------------------------------------|
  |                                              |
  |  [Reconstruct CEK from t shares]             |

5. Document Signing Vault

Owner                                    Signer
  |                                        |
  |  createSigningVault(doc, recipientDid) |
  |  shareSigningVault(connectionId)       |
  |--------------------------------------->|
  |                                        |
  |                      openSigningVault()|
  |                      sign document     |
  |  returnSignedDocument()                |
  |<---------------------------------------|

Usage Examples

Basic Vault Operations

import { Agent } from '@credo-ts/core'
import { VaultsModule } from '@ajna-inc/vaults'

const agent = new Agent({
  config: { /* ... */ },
  modules: {
    vaults: new VaultsModule()
  }
})

await agent.initialize()

// Create a vault
const vault = await agent.modules.vaults.create({
  passphrase: 'my-secure-passphrase',
  data: Buffer.from('sensitive document content'),
  metadata: {
    description: 'Contract draft',
    tags: ['legal', 'draft']
  }
})

// Open (decrypt) a vault
const plaintext = await agent.modules.vaults.open(vault.vaultId, {
  passphrase: 'my-secure-passphrase'
})

// List all vaults
const vaults = await agent.modules.vaults.list()

// Get vault info without decrypting
const info = await agent.modules.vaults.getInfo(vault.vaultId)

// Update vault data
await agent.modules.vaults.update(vault.vaultId, {
  passphrase: 'my-secure-passphrase',
  data: Buffer.from('updated content')
})

// Delete a vault
await agent.modules.vaults.delete(vault.vaultId)

KEM Key Management

// Generate an ML-KEM-768 keypair
const keypair = await agent.modules.vaults.generateKemKeypair()

// Store a peer's KEM public key
await agent.modules.vaults.storePeerKemKey(connectionId, peerPublicKey)

// Retrieve peer's key for encryption
const peerKey = await agent.modules.vaults.getPeerKemKey(connectionId)

Signing Vault Workflow

// Owner creates a signing vault for a specific recipient
const signingVault = await agent.modules.vaults.createSigningVault({
  data: documentBytes,
  recipientDid: signerDid
})

// Owner shares the vault with the signer
await agent.modules.vaults.shareSigningVault(connectionId, signingVault.vaultId)

// Signer opens the vault
const doc = await agent.modules.vaults.openSigningVault(vaultId)

// Signer signs and returns the document
await agent.modules.vaults.returnSignedDocument(connectionId, {
  vaultId,
  signedData: signedDocBytes
})

S3 Storage Configuration

const agent = new Agent({
  modules: {
    vaults: new VaultsModule({
      storage: {
        bucket: 'my-vault-bucket',
        region: 'us-east-1',
        accessKeyId: 'AKIA...',
        secretAccessKey: '...'
      },
      inlineThreshold: 1024 * 1024, // 1MB - larger vaults go to S3
      operatorMode: false
    })
  }
})

Installation

pnpm add @ajna-inc/vaults

Message Types

| Message | Purpose | |---------|---------| | CreateVaultMessage | Notify of vault creation | | UpdateVaultMessage | Update vault data | | DeleteVaultMessage | Delete vault | | RequestAccessMessage | Request access to a vault | | GrantAccessMessage | Grant access with encrypted data | | DenyAccessMessage | Deny access request | | RequestShareMessage | Request a threshold share | | ProvideShareMessage | Provide a threshold share | | DenyShareMessage | Deny share request | | StoreVaultMessage | Store vault at storage service | | VaultStoredAckMessage | Confirm storage | | RetrieveVaultMessage | Retrieve vault from storage | | VaultDataMessage | Vault data transmission | | VaultReferenceMessage | Storage reference | | VaultProblemReportMessage | Error reporting |

Event System

Subscribe to vault lifecycle events:

import { VaultEventTypes } from '@ajna-inc/vaults'

agent.events.on(VaultEventTypes.VaultCreated, (event) => {
  console.log('Vault created:', event.payload.vaultId)
})

agent.events.on(VaultEventTypes.AccessGranted, (event) => {
  console.log('Access granted to vault:', event.payload.vaultId)
})

agent.events.on(VaultEventTypes.ThresholdMet, (event) => {
  console.log('Threshold met, vault can be decrypted')
})

Available Events

  • VaultCreated, VaultOpened, VaultUpdated, VaultDeleted
  • VaultShared, VaultError
  • AccessRequested, AccessGranted, AccessDenied
  • ThresholdMet, ShareProvided, ShareRequested, ShareDenied
  • StorageAllocated, StorageUploaded, StorageDownloaded

Security Considerations

Cryptographic Primitives

  • ML-KEM-768: NIST post-quantum standard for key encapsulation
  • AES-256-GCM: Authenticated encryption with 256-bit keys
  • Argon2id: Memory-hard KDF resistant to GPU/ASIC attacks
  • Shamir Secret Sharing: Information-theoretically secure threshold scheme
  • Key Commitment: Prevents key confusion and related attacks

Best Practices

  1. Use strong passphrases for passphrase-based vaults
  2. Configure adequate Argon2id memory (default is reasonable for most use cases)
  3. Store KEM private keys securely in the agent wallet
  4. Verify key commitments are checked on every decryption
  5. Use threshold policies for high-value data requiring multi-party authorization
  6. Consider external storage for large files to keep wallet lean

Client-Side Only

All encryption and decryption happens locally on the agent. No server, mediator, or storage operator ever has access to plaintext data or encryption keys.

Vault Header Format

{
  v: 1,                          // Protocol version
  suite: 'S3' | 'P1' | 'H1',   // Encryption suite
  aead: 'AES-256-GCM',          // AEAD algorithm
  docId: string,                 // Document identifier
  vaultId: string,               // Vault identifier
  epoch: number,                 // Version counter
  nonce: string,                 // base64url nonce
  kcmp: string,                  // Key commitment
  salt?: string,                 // Argon2id salt (S3 suite)
  argon2?: {                     // KDF parameters (S3 suite)
    memory: number,
    iterations: number
  },
  policy?: {                     // Access policy
    mode: 'passphrase' | 'any-of' | 'all-of' | 'threshold'
  },
  recipients?: RecipientWrap[],  // Per-recipient wrapped keys
  shares?: ThresholdShare[],     // Threshold shares
  metadata?: {                   // Optional metadata
    description?: string,
    tags?: string[]
  }
}

Error Handling

import { VaultError, BadSuiteError, DecryptKemError, DecryptAeadError, PolicyError } from '@ajna-inc/vaults'

try {
  await agent.modules.vaults.open(vaultId, { passphrase: 'wrong' })
} catch (error) {
  if (error instanceof DecryptAeadError) {
    console.log('Wrong passphrase or corrupted data')
  } else if (error instanceof DecryptKemError) {
    console.log('KEM decapsulation failed - wrong key')
  } else if (error instanceof PolicyError) {
    console.log('Access policy violation')
  } else if (error instanceof BadSuiteError) {
    console.log('Unknown encryption suite')
  }
}

Testing

# Run vault tests
pnpm test

# Watch mode
pnpm test:watch

License

Apache-2.0