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

@hauska-sdk/vda

v0.1.0

Published

CNS Protocol VDA SDK - Verified Digital Assets management

Downloads

165

Readme

@hauska-sdk/vda

CNS Protocol VDA SDK - Verified Digital Assets management with cross-spoke search and access control.

Installation

npm install @hauska-sdk/vda

Quick Start

import { VDASDK } from "@hauska-sdk/vda";
import { PostgreSQLStorageAdapter } from "@hauska-sdk/adapters-storage-postgres";
import { WalletManager } from "@hauska-sdk/wallet";
import { Pool } from "pg";

// Initialize SDK
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

const sdk = new VDASDK({
  storageAdapter: new PostgreSQLStorageAdapter({
    pool,
    autoMigrate: true,
  }),
  walletManager: new WalletManager(),
  logHook: (level, message, data) => {
    console.log(`[${level}] ${message}`, data);
  },
});

// Mint a VDA
const vda = await sdk.mint({
  assetType: "deed",
  address: "123 Main St, Schertz, TX 78154",
  ownerWallet: "0x...",
  spoke: "real-estate",
});

// Create an access pass
const accessPass = await sdk.createAccessPass({
  grantorWallet: "0x...",
  recipientWallet: "0x...",
  accessibleVDAs: [vda.id],
  permissions: ["view", "download"],
  expiry: Date.now() + 3600000, // 1 hour
  spoke: "real-estate",
  address: "123 Main St",
});

// Verify ownership
const verification = await sdk.verifyOwnership(vda.id, "0x...");
if (verification.hasAccess) {
  // Grant access to resource
}

Features

  • VDA Minting - Create verified digital assets with universal metadata
  • Access Pass Management - Create, revoke, and verify time-bound access passes
  • Ownership Verification - Check direct ownership and access pass permissions
  • Ownership Transfer - Transfer VDA ownership between wallets
  • Cross-Spoke Search - Search VDAs across all spokes by universal metadata keys
  • Automatic Wallet Creation - Seamless wallet management
  • Logging Hooks - Optional monitoring and debugging support

Universal Metadata Schema

VDAs use a universal metadata schema that enables cross-spoke search. At least one universal metadata key is required:

Universal Metadata Keys

  • address - Physical address (911 format: "123 Main St, Schertz, TX 78154")

    • Used by: real-estate, architect, municipal spokes
    • Enables cross-spoke property search
  • legalDesc - Legal description (e.g., "Lot 5, Block 3, Schertz Heights")

    • Used by: real-estate, architect, municipal spokes
    • Alternative to address for property identification
  • patientId - Patient identifier

    • Used by: healthcare spoke
    • Enables patient record aggregation across systems
  • api14 - API well number (format: "42-123-12345-00-00")

    • Used by: oil-gas spoke
    • Standard identifier for oil & gas wells

Spoke Types

  • real-estate - Real estate properties, deeds, titles
  • healthcare - Patient records, medical documents
  • oil-gas - Well logs, permits, leases
  • architect - Blueprints, building plans
  • municipal - Permits, licenses, city records

Asset Types

  • deed - Property deed
  • blueprint - Architectural blueprint
  • permit - Building or operational permit
  • contract - Legal contract
  • health-record - Medical record
  • well-log - Oil & gas well log
  • access-pass - Time-bound access pass
  • invoice - Invoice document

API Reference

VDASDK

Main SDK class that provides all VDA functionality.

Constructor

new VDASDK(config: VDASDKConfig)

Configuration:

interface VDASDKConfig {
  storageAdapter: VDAStorageAdapter;  // Required
  walletManager?: WalletManager;      // Optional, auto-created if not provided
  logHook?: LogHook;                  // Optional logging hook
}

Methods

mint(params: MintVDAParams): Promise<VDA>

Mint a new VDA.

const vda = await sdk.mint({
  assetType: "deed",
  address: "123 Main St, Schertz, TX 78154",
  ownerWallet: "0x...", // Optional if userId/password provided
  spoke: "real-estate",
  // Optional: userId, password for auto-wallet creation
});
getVDA(vdaId: string): Promise<VDA | null>

Get a VDA by ID.

const vda = await sdk.getVDA("vda-id");
listVDAsByOwner(wallet: string): Promise<VDA[]>

List all VDAs owned by a wallet.

const vdas = await sdk.listVDAsByOwner("0x...");
createAccessPass(params: CreateAccessPassParams): Promise<VDA>

Create an access pass VDA.

const accessPass = await sdk.createAccessPass({
  grantorWallet: "0x...", // Must own all accessible VDAs
  recipientWallet: "0x...", // Optional if userId/password provided
  accessibleVDAs: ["vda-1", "vda-2"],
  permissions: ["view", "download"],
  expiry: Date.now() + 3600000, // Must be in the future
  spoke: "real-estate",
  address: "123 Main St", // Universal metadata
});
revokeAccessPass(accessPassId: string, grantorWallet: string): Promise<VDA>

Revoke an access pass.

const revoked = await sdk.revokeAccessPass(
  accessPassId,
  grantorWallet
);
verifyOwnership(vdaId: string, wallet: string, requiredPermissions?: string[]): Promise<OwnershipVerificationResult>

Verify ownership of a VDA.

const result = await sdk.verifyOwnership(
  vdaId,
  wallet,
  ["view", "download"] // Optional permissions to check
);

if (result.hasAccess) {
  console.log(`Access type: ${result.accessType}`); // "direct" or "access-pass"
  if (result.accessPass) {
    console.log(`Access via pass: ${result.accessPass.id}`);
  }
}
transferOwnership(params: TransferOwnershipParams): Promise<VDA>

Transfer ownership of a VDA.

const updatedVDA = await sdk.transferOwnership({
  vdaId: "vda-id",
  currentOwner: "0x...", // Must match current owner
  newOwner: "0x...",
});
searchByAddress(address: string, options?: SearchOptions): Promise<SearchResult>

Search VDAs by address (supports partial matching).

const result = await sdk.searchByAddress("Main St", {
  limit: 10,
  offset: 0,
});

console.log(`Found ${result.total} VDAs`);
console.log(`Showing ${result.results.length} results`);
searchByPatientId(patientId: string, options?: SearchOptions): Promise<SearchResult>

Search VDAs by patient ID (exact match).

const result = await sdk.searchByPatientId("patient-123", {
  limit: 20,
  offset: 0,
});
searchByAPI14(api14: string, options?: SearchOptions): Promise<SearchResult>

Search VDAs by API14 (exact match).

const result = await sdk.searchByAPI14("42-123-12345-00-00");
searchUniversal(query: string, options?: SearchOptions): Promise<SearchResult>

Search VDAs across all spokes by any universal metadata key. Automatically detects query type.

// Automatically detects API14 format
const result1 = await sdk.searchUniversal("42-123-12345-00-00");

// Tries patient ID first, falls back to address
const result2 = await sdk.searchUniversal("patient-123");

// Searches by address
const result3 = await sdk.searchUniversal("123 Main St");

Access Pass Usage Guide

Access passes are time-bound, revocable permissions that grant access to VDAs. They are themselves VDAs with assetType: "access-pass".

Creating Access Passes

// Create an access pass for a data room
const dataRoomVDA = await sdk.mint({
  assetType: "deed",
  address: "123 Main St",
  ownerWallet: propertyOwnerWallet,
  spoke: "real-estate",
});

const accessPass = await sdk.createAccessPass({
  grantorWallet: propertyOwnerWallet, // Must own dataRoomVDA
  recipientWallet: buyerWallet,
  accessibleVDAs: [dataRoomVDA.id],
  permissions: ["view", "download"], // What the recipient can do
  expiry: Date.now() + 7 * 24 * 3600000, // 7 days
  spoke: "real-estate",
  address: "123 Main St",
});

Verifying Access

// Check if wallet has access to a VDA
const verification = await sdk.verifyOwnership(
  dataRoomVDA.id,
  buyerWallet,
  ["view"] // Required permission
);

if (verification.hasAccess) {
  if (verification.accessType === "direct") {
    console.log("User owns this VDA");
  } else if (verification.accessType === "access-pass") {
    console.log("User has access via access pass");
    console.log(`Access pass expires: ${verification.accessPass?.metadata.expiry}`);
  }
} else {
  console.log(`Access denied: ${verification.reason}`);
}

Revoking Access Passes

// Revoke an access pass before it expires
const revoked = await sdk.revokeAccessPass(
  accessPass.id,
  propertyOwnerWallet // Must be the grantor
);

// Access is immediately revoked
const verification = await sdk.verifyOwnership(
  dataRoomVDA.id,
  buyerWallet
);
// verification.hasAccess === false

Access Pass Permissions

  • view - Can view the VDA and associated documents
  • download - Can download documents
  • write - Can modify the VDA
  • annotate - Can add annotations

Code Examples

Real Estate: Property Listing

// Mint a property deed
const property = await sdk.mint({
  assetType: "deed",
  address: "123 Main St, Schertz, TX 78154",
  legalDesc: "Lot 5, Block 3, Schertz Heights",
  ownerWallet: ownerWallet,
  spoke: "real-estate",
  ipfsCid: "Qm...", // Link to property documents on IPFS
});

// Create a data room access pass for potential buyers
const dataRoomPass = await sdk.createAccessPass({
  grantorWallet: ownerWallet,
  recipientWallet: buyerWallet,
  accessibleVDAs: [property.id],
  permissions: ["view", "download"],
  expiry: Date.now() + 30 * 24 * 3600000, // 30 days
  spoke: "real-estate",
  address: "123 Main St, Schertz, TX 78154",
});

Healthcare: Patient Record Management

// Mint a patient record
const patientRecord = await sdk.mint({
  assetType: "health-record",
  patientId: "PATIENT-12345",
  ownerWallet: patientWallet,
  spoke: "healthcare",
  ipfsCid: "Qm...", // Encrypted medical records
});

// Grant doctor access
const doctorAccess = await sdk.createAccessPass({
  grantorWallet: patientWallet,
  recipientWallet: doctorWallet,
  accessibleVDAs: [patientRecord.id],
  permissions: ["view", "annotate"],
  expiry: Date.now() + 24 * 3600000, // 24 hours
  spoke: "healthcare",
  patientId: "PATIENT-12345",
});

// Search all records for a patient
const allRecords = await sdk.searchByPatientId("PATIENT-12345");

Oil & Gas: Well Log Management

// Mint a well log
const wellLog = await sdk.mint({
  assetType: "well-log",
  api14: "42-123-12345-00-00",
  ownerWallet: operatorWallet,
  spoke: "oil-gas",
  ipfsCid: "Qm...", // Well log data
});

// Grant regulator access
const regulatorAccess = await sdk.createAccessPass({
  grantorWallet: operatorWallet,
  recipientWallet: regulatorWallet,
  accessibleVDAs: [wellLog.id],
  permissions: ["view"],
  expiry: Date.now() + 365 * 24 * 3600000, // 1 year
  spoke: "oil-gas",
  api14: "42-123-12345-00-00",
});

// Search by API14
const wellData = await sdk.searchByAPI14("42-123-12345-00-00");

Cross-Spoke Search

// Search for all assets at a specific address across all spokes
const allAssets = await sdk.searchByAddress("123 Main St, Schertz, TX 78154");

// Results may include:
// - Real estate deeds
// - Architectural blueprints
// - Municipal permits
// - All from different spokes but same address

Express.js Integration

import express from "express";
import { VDASDK } from "@hauska-sdk/vda";
import { PostgreSQLStorageAdapter } from "@hauska-sdk/adapters-storage-postgres";

const app = express();
const sdk = new VDASDK({
  storageAdapter: new PostgreSQLStorageAdapter({
    pool: new Pool({ connectionString: process.env.DATABASE_URL }),
    autoMigrate: true,
  }),
});

// Mint a VDA
app.post("/api/vdas", async (req, res) => {
  try {
    const vda = await sdk.mint({
      assetType: req.body.assetType,
      address: req.body.address,
      ownerWallet: req.body.ownerWallet,
      spoke: req.body.spoke,
    });
    res.json(vda);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// Verify access before serving document
app.get("/api/documents/:vdaId", async (req, res) => {
  const wallet = req.headers["x-wallet-address"] as string;
  
  const verification = await sdk.verifyOwnership(
    req.params.vdaId,
    wallet,
    ["view"]
  );

  if (!verification.hasAccess) {
    return res.status(403).json({ error: verification.reason });
  }

  // Fetch and serve document from IPFS
  // ...
});

// Create access pass
app.post("/api/access-passes", async (req, res) => {
  try {
    const accessPass = await sdk.createAccessPass({
      grantorWallet: req.body.grantorWallet,
      recipientWallet: req.body.recipientWallet,
      accessibleVDAs: req.body.accessibleVDAs,
      permissions: req.body.permissions,
      expiry: req.body.expiry,
      spoke: req.body.spoke,
      address: req.body.address,
    });
    res.json(accessPass);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Troubleshooting

Common Issues

"Invalid VDA metadata: At least one universal metadata key is required"

Problem: VDA is missing required universal metadata (address, legalDesc, patientId, or api14).

Solution: Provide at least one universal metadata key:

// ❌ Missing universal metadata
await sdk.mint({
  assetType: "deed",
  ownerWallet: "0x...",
  spoke: "real-estate",
});

// ✅ Include address or legalDesc
await sdk.mint({
  assetType: "deed",
  address: "123 Main St",
  ownerWallet: "0x...",
  spoke: "real-estate",
});

"Grantor does not own the following VDAs"

Problem: Trying to create an access pass for VDAs the grantor doesn't own.

Solution: Verify grantor owns all accessible VDAs:

// Check ownership first
const verification = await sdk.verifyOwnership(
  vdaId,
  grantorWallet
);

if (verification.hasAccess && verification.accessType === "direct") {
  // Grantor owns it, can create access pass
}

"Expiry must be in the future"

Problem: Access pass expiry is set to a past timestamp.

Solution: Ensure expiry is in the future:

// ❌ Past expiry
expiry: Date.now() - 1000

// ✅ Future expiry
expiry: Date.now() + 3600000 // 1 hour from now

"Access pass is already revoked"

Problem: Trying to revoke an already-revoked access pass.

Solution: Check revocation status before revoking:

const accessPass = await sdk.getVDA(accessPassId);
if (accessPass?.metadata.revoked) {
  // Already revoked
  return;
}
await sdk.revokeAccessPass(accessPassId, grantorWallet);

Search returns no results

Problem: Search query doesn't match stored addresses.

Solution:

  • For address search, use partial matching (e.g., "Main St" instead of full address)
  • Ensure addresses are normalized (case-insensitive)
  • Check that VDAs were indexed correctly
// ✅ Partial matching works
const results = await sdk.searchByAddress("Main St");

// ✅ Exact match also works
const results = await sdk.searchByAddress("123 Main St, Schertz, TX 78154");

"VDA not found" after transfer

Problem: VDA ID doesn't exist or was deleted.

Solution: Verify VDA exists before transfer:

const vda = await sdk.getVDA(vdaId);
if (!vda) {
  throw new Error("VDA not found");
}
await sdk.transferOwnership({ vdaId, currentOwner, newOwner });

Performance Tips

  1. Use pagination for large result sets:

    const result = await sdk.searchByAddress("Main St", {
      limit: 50,
      offset: 0,
    });
  2. Index frequently searched fields: The PostgreSQL adapter automatically creates indexes on address, patient_id, api14, and owner_wallet.

  3. Batch operations: When creating multiple VDAs, use Promise.all() for parallel execution:

    const vdas = await Promise.all(
      addresses.map(addr => sdk.mint({ address: addr, ... }))
    );

Storage Adapters

PostgreSQL (Production)

import { PostgreSQLStorageAdapter } from "@hauska-sdk/adapters-storage-postgres";
import { Pool } from "pg";

const adapter = new PostgreSQLStorageAdapter({
  pool: new Pool({
    connectionString: process.env.DATABASE_URL,
  }),
  autoMigrate: true, // Automatically creates tables
});

Mock (Testing)

import { MockStorageAdapter } from "@hauska-sdk/adapters-storage-mock";

const adapter = new MockStorageAdapter();

License

MIT