Rust and WASM did-method-plc tools and structures
Rust 83.0%
JavaScript 11.1%
Shell 0.5%
Other 5.4%
3 2 0

Clone this repository

https://tangled.org/zicklag.dev/atproto-plc
git@tangled.org:zicklag.dev/atproto-plc

For self-hosted knots, clone URLs may differ based on your setup.

README.md

atproto-plc#

Crates.io Documentation License

did-method-plc implementation for ATProtocol with WASM support.

Features#

  • ✅ Validate did:plc identifiers
  • ✅ Parse and validate DID documents
  • ✅ Create new did:plc identities
  • ✅ Validate operation chains
  • ✅ Native Rust and WASM support
  • ✅ Recovery mechanism with 72-hour window
  • ✅ Support for both P-256 and secp256k1 keys
  • ✅ DAG-CBOR encoding for operations
  • ✅ Comprehensive test suite

Quick Start#

Rust#

Add this to your Cargo.toml:

[dependencies]
atproto-plc = "0.1"

Validate a DID#

use atproto_plc::Did;

let did = Did::parse("did:plc:ewvi7nxzyoun6zhxrhs64oiz")?;
println!("Valid DID: {}", did);

Create a new DID#

use atproto_plc::{DidBuilder, SigningKey, ServiceEndpoint};

// Generate keys
let rotation_key = SigningKey::generate_p256();
let signing_key = SigningKey::generate_k256();

// Build the DID
let (did, operation, keys) = DidBuilder::new()
    .add_rotation_key(rotation_key)
    .add_verification_method("atproto".into(), signing_key)
    .add_also_known_as("at://alice.example.com".into())
    .add_service(
        "atproto_pds".into(),
        ServiceEndpoint::new(
            "AtprotoPersonalDataServer".into(),
            "https://pds.example.com".into(),
        ),
    )
    .build()?;

println!("Created DID: {}", did);

JavaScript/WASM#

Installation#

npm install atproto-plc

Usage#

import { parseDid, createDidBuilder, generateP256Key, generateK256Key } from 'atproto-plc';

// Validate a DID
const did = await parseDid('did:plc:ewvi7nxzyoun6zhxrhs64oiz');
console.log('Valid DID:', did.identifier);

// Create a new DID
const rotationKey = await generateP256Key();
const signingKey = await generateK256Key();

const builder = await createDidBuilder();
const result = builder
  .addRotationKey(rotationKey)
  .addVerificationMethod('atproto', signingKey)
  .build();

console.log('Created DID:', result.did);

DID Format#

A did:plc identifier consists of:

  • Prefix: did:plc:
  • Identifier: 24 lowercase base32 characters (alphabet: abcdefghijklmnopqrstuvwxyz234567)

Example: did:plc:ewvi7nxzyoun6zhxrhs64oiz

Valid Characters#

The identifier uses a restricted base32 alphabet that excludes confusing characters:

  • ✅ Allowed: a-z, 2-7
  • ❌ Excluded: 0, 1, 8, 9 (avoid confusion with letters)
  • ❌ No uppercase letters

Architecture#

Core Components#

  • DID Validation (src/did.rs): Parse and validate did:plc identifiers
  • Cryptography (src/crypto.rs): Signing and verification with P-256 and secp256k1
  • Documents (src/document.rs): DID document structures (PLC state and W3C format)
  • Operations (src/operations.rs): Genesis, update, and tombstone operations
  • Validation (src/validation.rs): Operation chain validation and recovery
  • Builder (src/builder.rs): Convenient API for creating DIDs
  • Encoding (src/encoding.rs): Base32, base64url, and DAG-CBOR utilities
  • WASM (src/wasm.rs): WebAssembly bindings for JavaScript

Key Concepts#

Rotation Keys (1-5 required)#

Rotation keys are used to:

  • Sign operations that modify the DID
  • Recover control within a 72-hour window
  • Establish a priority order for fork resolution

Verification Methods (up to 10)#

Verification methods are used for:

  • Authentication
  • Signing application data (e.g., ATProto records)
  • General cryptographic operations

Recovery Mechanism#

If a higher-priority rotation key (lower array index) signs a conflicting operation within 72 hours, it can invalidate operations signed by lower-priority keys.

Command-Line Tools#

plc-audit: DID Audit Log Validator#

The plc-audit binary fetches and validates DID audit logs from plc.directory:

# Build the tool
cargo build --release --features cli --bin plc-audit

# Validate a DID
./target/release/plc-audit did:plc:z72i7hdynmk6r22z27h6tvur

# Verbose mode (shows all operations)
./target/release/plc-audit did:plc:z72i7hdynmk6r22z27h6tvur --verbose

# Quiet mode (only shows VALID/INVALID)
./target/release/plc-audit did:plc:z72i7hdynmk6r22z27h6tvur --quiet

# Custom PLC directory
./target/release/plc-audit did:plc:example --plc-url https://custom.plc.directory

The tool performs comprehensive validation:

  • ✅ Chain linkage verification (prev references)
  • ✅ Cryptographic signature verification
  • ✅ Operation ordering and consistency

Example output:

🔍 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
   Verification methods: 1
   Also known as: 1
   Services: 1

Examples#

Validate DIDs#

cargo run --example validate_did

Create a New DID#

cargo run --example create_did

Parse DID Documents#

cargo run --example parse_document

Building#

Native#

# Build
cargo build --release

# Run tests
cargo test --all-features

# Run benchmarks
cargo bench

# Generate documentation
cargo doc --open --no-deps --all-features

WASM#

# Install wasm-pack
cargo install wasm-pack

# Build for web
wasm-pack build --target web --out-dir wasm/pkg

# Run WASM tests
wasm-pack test --headless --chrome

Testing#

The library includes comprehensive tests:

# Run all tests
cargo test --all-features

# Run specific test suites
cargo test --test did_validation
cargo test --test crypto_operations
cargo test --test document_parsing
cargo test --test operation_chain

Specification#

This library implements the did:plc specification:

Performance#

Run benchmarks:

cargo bench

Contributing#

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: cargo test --all-features
  5. Run formatter: cargo fmt
  6. Run clippy: cargo clippy --all-targets --all-features -- -D warnings
  7. Submit a pull request

License#

Licensed under either of:

at your option.

Contribution#

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.