Rust and WASM did-method-plc tools and structures
README.md

plc-audit WASM Implementation#

This is a JavaScript implementation of the plc-audit binary that uses WebAssembly for cryptographic operations. It fetches and validates DID audit logs from plc.directory.

Prerequisites#

  • Rust (stable toolchain)
  • wasm-pack - Install with: cargo install wasm-pack
  • Node.js (v18 or later) - For running the JavaScript tool

Building#

1. Build the WASM Module#

From the project root directory:

# Build WASM module with wasm feature
wasm-pack build --target nodejs --out-dir wasm/pkg --features wasm

# Or use the provided build script
./wasm/build.sh

This will:

  • Compile the Rust library to WebAssembly
  • Generate JavaScript bindings
  • Create a wasm/pkg/ directory with the WASM module and TypeScript definitions

2. Install Node.js Dependencies#

cd wasm
npm install

Usage#

The JavaScript version supports the same command-line interface as the Rust binary:

Basic Validation#

Validate a DID and show detailed output:

node plc-audit.js did:plc:z72i7hdynmk6r22z27h6tvur

Verbose Mode#

Show all operations and detailed cryptographic validation steps:

node plc-audit.js did:plc:z72i7hdynmk6r22z27h6tvur --verbose
# or
node plc-audit.js did:plc:z72i7hdynmk6r22z27h6tvur -v

Output includes:

  • Operation index and CID
  • Creation timestamp
  • Operation type (Genesis/Update)
  • Previous operation reference
  • Step-by-step chain linkage validation
  • Detailed signature verification with rotation key tracking

Quiet Mode#

Only show validation result (useful for scripts):

node plc-audit.js did:plc:z72i7hdynmk6r22z27h6tvur --quiet
# or
node plc-audit.js did:plc:z72i7hdynmk6r22z27h6tvur -q

Output: ✅ VALID or error message

Custom PLC Directory#

Use a custom PLC directory server:

node plc-audit.js did:plc:example --plc-url https://custom.plc.directory

What is Validated?#

The WASM version performs the same comprehensive validation as the Rust binary:

  1. DID Format Validation

    • Checks prefix is did:plc:
    • Verifies identifier is exactly 24 characters
    • Ensures only valid base32 characters (a-z, 2-7)
  2. Chain Linkage Verification

    • First operation must be genesis (prev = null)
    • Each subsequent operation's prev field must match previous operation's CID
    • No breaks in the chain
  3. Cryptographic Signature Verification

    • Each operation's signature is verified using rotation keys
    • Genesis operation establishes initial rotation keys
    • Later operations can rotate keys
    • Identifies which specific rotation key verified each signature

Example Output#

Standard Mode#

🔍 Fetching audit log for: did:plc:z72i7hdynmk6r22z27h6tvur
   Source: https://plc.directory

📊 Audit Log Summary:
   Total operations: 4
   Genesis operation: bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm
   Latest operation: bafyreifn4pkect7nymne3sxkdg7tn7534msyxcjkshmzqtijmn3enyxm3q

🔐 Validating operation chain...
✅ Validation successful!

📄 Final DID State:
   Rotation keys: 2
     [0] did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg
     [1] did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK

Verbose Mode#

Shows detailed step-by-step validation:

Step 1: Chain Linkage Validation
================================
  [1] Checking prev reference...
      Expected: bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm
      Actual:   bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm
      ✅ Match - chain link valid
  [2] Checking prev reference...
      Expected: bafyreihmuvr3frdvd6vmdhucih277prdcfcezf67lasg5oekxoimnunjoq
      Actual:   bafyreihmuvr3frdvd6vmdhucih277prdcfcezf67lasg5oekxoimnunjoq
      ✅ Match - chain link valid
  ...

✅ Chain linkage validation complete

Step 2: Cryptographic Signature Validation
==========================================
  [0] Genesis operation - extracting rotation keys
      Rotation keys: 2
        [0] did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg
        [1] did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK
      ⚠️  Genesis signature cannot be verified (bootstrapping trust)
  [1] Validating signature...
      CID: bafyreihmuvr3frdvd6vmdhucih277prdcfcezf67lasg5oekxoimnunjoq
      Signature: 1mEWzRtFOgeRXH-YCSPTxb990JOXxa__n8Qw6BOKl7Ndm6OFFmwYKiiMqMCpAbxpnGjF5abfIsKc7u3a77Cbnw
      Available rotation keys: 2
        [0] did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg
        [1] did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK
      Parsed verifying keys: 2/2
      ✅ Signature verified with rotation key [1]
         did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK
  ...

✅ Cryptographic signature validation complete

Architecture#

The WASM implementation divides responsibilities:

  • JavaScript (plc-audit.js):

    • Command-line argument parsing
    • HTTP requests to plc.directory
    • Console output formatting
    • Control flow and orchestration
  • WASM (Rust compiled to WebAssembly):

    • DID parsing and validation
    • Operation parsing from JSON
    • Cryptographic signature verification (P-256 and secp256k1)
    • Rotation key management
    • All security-critical operations

Performance#

The WASM version has comparable performance to the native Rust binary:

  • WASM module size: ~200KB (optimized)
  • Validation time: ~200-500ms for typical DIDs (4-10 operations)
  • Startup overhead: ~50-100ms for WASM initialization

Troubleshooting#

Build Errors#

If you encounter build errors:

# Ensure wasm32-unknown-unknown target is installed
rustup target add wasm32-unknown-unknown

# Clean and rebuild
rm -rf wasm/pkg
wasm-pack build --target nodejs --out-dir wasm/pkg --features wasm

Runtime Errors#

If you get "Module not found" errors:

# Ensure the WASM module is built
ls wasm/pkg/atproto_plc_bg.wasm

# If missing, rebuild
wasm-pack build --target nodejs --out-dir wasm/pkg --features wasm

Node.js Version#

Ensure you're using Node.js v18 or later:

node --version  # Should be v18.0.0 or higher

Comparison with Rust Binary#

Feature Rust Binary WASM/JavaScript
Performance Native speed ~2-3x slower
Binary Size 1.5MB ~200KB WASM
Dependencies None (static) Node.js runtime
Platform Per-platform compilation Universal (runs anywhere with Node.js)
Use Case Production servers, CLI Cross-platform, web integration

Integration Examples#

Use in a Node.js Application#

import { WasmDid, WasmOperation, WasmVerifyingKey } from './pkg/atproto_plc.js';

async function validateDid(didString) {
  // Parse DID
  const did = WasmDid.parse(didString);

  // Fetch audit log
  const response = await fetch(`https://plc.directory/${did.did}/log/audit`);
  const auditLog = await response.json();

  // Parse operations
  const operations = auditLog.map(entry => ({
    operation: WasmOperation.fromJson(JSON.stringify(entry.operation)),
    cid: entry.cid,
  }));

  // Validate chain
  for (let i = 1; i < operations.length; i++) {
    const prev = operations[i].operation.prev();
    if (prev !== operations[i - 1].cid) {
      throw new Error('Chain linkage broken');
    }
  }

  return true;
}

Use in Browser (with bundler)#

// Build with web target instead
// wasm-pack build --target web --out-dir wasm/pkg --features wasm

import init, { WasmDid } from './pkg/atproto_plc.js';

await init(); // Initialize WASM

const did = WasmDid.parse('did:plc:ewvi7nxzyoun6zhxrhs64oiz');
console.log('Valid DID:', did.did);

License#

Dual-licensed under MIT or Apache-2.0, same as the parent library.