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:
-
DID Format Validation
- Checks prefix is
did:plc: - Verifies identifier is exactly 24 characters
- Ensures only valid base32 characters (a-z, 2-7)
- Checks prefix is
-
Chain Linkage Verification
- First operation must be genesis (prev = null)
- Each subsequent operation's
prevfield must match previous operation's CID - No breaks in the chain
-
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.