This is a personal project, a set of libraries and a simple egui tool for loading, modifying, and signing PLC operations (spec). I worked on this in order to learn more about cryptographic signing keys (namely, secp256k1 and P-256), did:key, and especially did:plc.
DISCLAIMER:
This code was not originally meant to be made public in this state. A lot of documentation is missing, some features might be implemented only partially or not at all... you get the point.All code is provided as-is, and I make no guarantees about its stability or how it runs on your machine. I tried to make things clean, but they're only as clean as you'd expect for a personal project intended to explore a whole new topic.
If you just want to test out the GUI tool, you only need to clone the repo locally, make sure you have Rust installed (
rustup
), build the plc-interface
binary, and launch it. I'm on Rust 1.86.0-nightly
and use some experimental
features, but it should work well enough with the provided config files.
Note that this has only been tested on my desktop, which runs Windows 11.
Be aware that the gui application saves some state data, it uses
the default eframe settings under the ID PLC Interface
(
e.g. AppData/Roaming/PLC Interface/*
for Windows). This location isn't cleared when you stop using the app, so if you
want to save 16kB of disk space, you know where to look.
The application uses stdout for errors and other info, including resulting signed PLC operations. The latter are
printed directly, and you should always see error messages, but you can enable extra details via the RUST_LOG=info
environment variable.
The app is also completely offline at runtime - everything is local, but you'll need to handle a few simple HTTP requests on your own (see practical examples below).
I wrote some guides detailing how I'd go about using my PLC tool, you can follow these steps once you get the
visual app running. You'll also need an HTTP client (such as Insomnia) to send some GET and POST requests - the
app is completely offline and makes no HTTP calls of its own! Besides that, I assume basic technical knowledge about
Bluesky (I mean, since you clicked on this repo, I'd assume you already know that did:plc is some sort of identifier),
but not much more besides that. If you managed to clone the repo, run rustup
, and build the plc-interface
binary,
you should be good.
The guides pretty much became blog posts, but I suppose it makes for a nicer read too:
The interface consists of two main sections: The key store, and the PLC operation editor interface itself.
The key store simply loads and keeps track of PEM-encoded signing keys located in a directory (kinda like ssh keys).
This tries to default to .key_store
relative to the working dir.
- Click 🔁 to reload all keys
- Expand the dropdown and generate a new random key (which then gets saved to the same location with its did:key representation as its name)
The PLC operation editor interface mirrors the JSON structure of a PLC operation object:
- Also known as: a new-line-separated list of
at://
aliases - Rotation keys: an ordered array of did:keys.
- Red-colored keys have an invalid format, green-colored keys are keys you own (i.e. keys that were found in your key store).
- Verification methods: key-value map of services and did:keys (you'll most likely care only about
atproto
) - Services: key-value map of services and endpoints (again, you'll most likely only care about
atproto_pds
).- Does not support adding/removing entries in the GUI, but you can still edit your endpoints.
- Previous CID: the
prev
field of the operation. This can be calculated from another signed operation, or cleared (for a genesis operation).
You may load a signed PLC operation with a button at the top, which also generates a new prev
CID referencing that
operation (in other words, this does NOT copy the original CID). The Previous CID section allows you to generate and
replace only the CID from a signed operation.
Finally, once you're done modifying the PLC operation, you may either print the whole unsigned operation (no sig
field) as JSON, or generate a signature using your selected (and owned!) rotation key (selected using a radio button to
the left of each rotation key).
Besides the main binary, the codebase also contains several libraries. Importantly, there's a custom implementation of did:key and did:plc, as well as some helper structs and traits e.g. for the current "blessed" signing methods.
I decided to implement both did:key and did:plc on my own, because the existing libraries I found didn't work well for my purposes, and I ran into issues with dependencies. Besides, it was a good opportunity to properly learn more about everything myself.
As stated before, the code is missing a lot of documentation, though you'll at least find some unit tests and some
example uses in did-plc/src/main.rs
. The rest of the examples is effectively the whole plc-interface
crate.
Since this was and still is a personal project, I don't plan on maintaining the repository, and I'm unlikely to respond to issues or accept PRs. My plan is to develop a "proper version 2" of this library and GUI tool, so this repo will eventually become outdated anyway (and I'll add a link to the new one). All code is provided as-is, and forking is welcome too.
For programming, I use full-line code completion (which comes with JetBrains IDEs and runs entirely locally), but I refrain from using generative language models of any greater scope. Computational expenses aside, there are ethical issues with the way certain models were/are trained, and generating code this way requires spending more time on review instead (and is likely to introduce bugs). In addition, using text prompts would pull me out of the coding flow, and I much prefer figuring things out on my own, because that's what makes programming fun for me. Due to the sensitive nature of these topics, I feel like it's responsible to add this disclaimer - I'm comfortable with the way this machine learning model was trained, and it doesn't take away any part of what makes programming enjoyable for me.
All code in this codebase is effectively written by me, or adapted from internet sources. I just saved a few more keystrokes than I would have with only common IDE code completion/generation utilities.