Using Keyhive in WASM to model capability groups
A few months ago I wrote about BeeKEM, which is the key agreement part of Ink & Switch’s Keyhive project. In short, it’s a capabilities protocol for eventually consistent data: only authorised readers can decrypt it, and unauthorised writes/deletions to that data can be cryptographically invalidated. Check out the Ink & Switch lab notebooks or Brooklyn Zelenka’s talk about it at Local-First Conf for more details. In the last few days, I wanted to see if I could try out just the group management parts of Keyhive in a web app - that is, just modelling capability groups without worrying about CRDT ops, sync, key agreement or encryption.
Demo: https://keyhive-delegation-demo.meri.garden/
Code: https://github.com/meri-leeworthy/keyhive/tree/wasm-demo
A mental model for delegations
Building this was also a cool way to consolidate my understanding of the delegations concept for modelling transitive capabilities - here’s my summary of how adding works:
Firstly, Keyhive has four types of ‘agents’: individual (e.g. a phone), group (e.g. a person with multiple devices, or an organisation), document (e.g. a text file you want encrypted) and active (the ‘self’ running the code). You can kind of think of an agent as a black box with a secret key inside, which you will never see, but you can ask the agent to do things. When you create a group or a document agent, it:
-
creates an Ed25519 key pair
- delegates “admin” access to you (the active agent)
- signs that delegation using its secret signing key
- returns the signed delegation to you
A signed delegation has:
- a ‘can’ field which is just a string that’s either “pull”, “read”, “write” or “admin”
- a delegate (e.g. my active agent identifier = public key)
- a signature (and signature issuer) so i can verify where it came from
- maybe a proof, which is another signed delegation
The more times a capability was sub-delegated, the bigger the delegation will recursively grow, but it always has the original group or document delegation at the bottom.
Process comments
The bulk of the code starting was written by Claude Code: it cost at least $14 USD, which I think is too much. Apparently it’s cheaper to buy a pro subscription, but I’ve been feeling pretty ambivalent about LLM-assisted coding. That said, if the price was better, I would be happier, so maybe I should.
I needed to add a few extra bindings to the existing keyhive_wasm
code. Rust made this pretty easy - just look at the cargo doc
generated docs, look at the code, take a wild stab and see if there’s a linter error. I think if I want to extend this work further, I might create a new crate that wraps keyhive_core
and keyhive_wasm
so I can expose any bindings as needed.
Why do this?
My thinking was that this could be, in theory, used to model permissions in a centralised database in place of a more typical Access Control List. The high level is that for any request to access a remote resource, instead of sending e.g. an access token, a client could send a signed delegation. An authorisation microservice sitting in front of a DB (for example) could then essentially just check that delegation to make sure:
- The capability (“write”, “admin” etc) and subject ID allows the action the user is trying to perform
- The signatures are valid
- That it hasn’t heard of any updates to the auth graph that would revoke the claimed capability
My interest, and our shared interest at Muni Town working on Roomy, is ultimately in using Keyhive for encryption as well, but I guess 1. there are so many complex concepts wrapped up in this project that it’s been genuinely a major challenge to just wrap my head around how to use it, so breaking it down helps with that, and 2. being able to split it out and think about, for example, a more centralised approach, helps make incrementally adopting Keyhive in Roomy feel more feasible.
What next?
Something that also finally just clicked for me is that one key thing that distinguishes Documents from Groups is that they can have an attached CGKA (the data structure that enables agreement on shared symmetric secret keys for group encryption - specifically here it’s BeeKEM). When I get around to it, I’d like to try out using this: validate my mental model for how the CGKA flow works down to each operation, then encrypt some random data (i.e. not a CRDT).
My Muni Town workmate Zicklag has been looking at the project from the other end, looking at using the consistent chunking algorithm Sedimentree with other CRDT formats. Hopefully we will be able to keep working on this long enough to pull it all together.