RATProto DID#
A small library for handling atproto DIDs. Optimized for memory use and speed, especially for did:plc.
Features#
Compact#
- The
Didtype is guaranteed to be exactly 16 bytes (even less than aString!) did:plcuses no heapdid:weballocates up to 255 bytes on the heap- Other DIDs supported (even if not compatible with atproto) - see below.
Performant#
did:plcencoding and decoding uses intrinsics and SIMD for ridiculous performance- If you ever wanted to parse 100M+
did:plcidentifiers per second, look no further
Minimal dependencies#
thiserrorand nothing else
Example#
use ratproto::{Did, DidKind};
fn main() {
let idents = vec![
"did:plc:c6te24qg5hx54qgegqylpqkx",
// ...
];
// Parse from &str
// A `did:plc` identifier is stored as 15 bytes + a discriminant
let mut dids: Vec<Did> = idents
.into_iter()
.filter_map(|s| Did::from_str(s).ok())
.collect();
// Match on the DID method
for did in &dids {
match did.kind() {
DidKind::Plc => { /* ... */ }
DidKind::Web => { /* ... */ }
DidKind::Other => { /* ... */ }
}
}
// Ord & PartialOrd
dids.sort();
// Eq & PartialEq
if dids.contains(&Did::from_str("did:plc:c6te24qg5hx54qgegqylpqkx").unwrap()) {
// ...
}
// Display & Debug
for did in &dids {
println!("{did}, {did:?}")
}
}
Details#
The parser follows specifications outlined in DID syntax and
on the atproto website. Atproto has two officially supported ("blessed") methods, plc
and web, which go through further validation:
General syntax#
- Max. length of 2048 (atproto spec)
Diddoes not parse DID URLs, so no?query, no#fragmentsections, and no/paths- The whole DID must follow DID syntax rules
- Percent-encoding is currently not validated, but the identifier must not end in a
% - Atproto spec currently states that methods must be
[a-z], but DID syntax allows digits as well. See also GitHub issue
- Percent-encoding is currently not validated, but the identifier must not end in a
- DIDs that are not
did:plcordid:weballocate heap space for the method and identifier as needed.
PLC#
did:plc identifiers must be exactly 24 base32 characters
- The PLC spec notes, "It is conceivable that longer DID PLCs, with more of the SHA-256 characters, will be supported in the future." Due to the optimizations this library performs, only 24-character identifiers are supported.
Because allowing longer did:plc identifiers currently feels very unlikely, and it would break the assumptions based on
which this library implements optimizations for PLC identifiers, longer identifiers are currently rejected. Adding
fallback support could make the API unnecessarily messy and lead to a DID variant that basically never exists anywhere,
but this could be implemented in the future.
Web#
did:web identifiers must be syntactically-valid web
domains (DID Web spec)
- Max. 255 characters, domain segment syntax restrictions ( see RFC1035)
- TLDs disallowed by atproto are currently NOT currently checked
- Only hostname-level DIDs are supported (as per atproto spec) - i.e. no
:path localhostis the only hostname that allows a path, but it should only be used in development, and the:must be percent-encoded (e.g.did:web:localhost%3A1234)
Benchmarks#
This crate implements benchmarks using Criterion.rs.
To get started with running benchmarks locally (assuming you have Rust installed, of course), run the following:
cargo bench
# Optionally for longer measurement time:
cargo bench -- --measurement-time 15
Benchmark results can then be found in ./target/criterion/report/index.html.
Miri#
This crate uses a bunch of unsafe, and is Miri-tested for soundness.
To test with Miri, make sure you have it installed first:
rustup +nightly component add miri
Then, run unit tests with:
cargo +nightly miri test
Note that this skips unsupported tests, such as those with SIMD, but those also include much more extensive documentation. See plc_codec.rs.
While this library is still in versions 0.0.x, expect a few potential API breaking changes as the interface is
finalized and a few remaining optimizations are implemented.
For versions 0.x.x the plan is to introduce more items from the feature wishlist before stabilizing at 1.x.x.
Version 1.x.x should provide a feature-complete API, but patches (or further major releases) may introduce more
additional features.
See TODOs for a wishlist of "nice-to-have" features and optimizations.