Earlier I posted a piece about adversarial space migration
Adversarial Spaces
Thinking about how records move through space
And then Daniel Holmgren responded
A Trenchcoat of Adversities
Firstly, let me be clear that Daniel's point about my post conflating 3 things is 100% correct. Part of the reason I've been thinking of these things together is because I'm imagining a solution that solves all 3, but in reality I've been framing a problem that is really 3 smaller problems in a trenchcoat.
So with that let's think about our 3 problems posing as one problem.
- 1.
Migration Away from an Adversarial Space Host β Solved, in effect, by DID:PLC assuming that someone outside the space host holds the primary rotation key (same story as DID:PLC and PDS migration)
- 2.
Space Governance Between Disagreeing admins β Solvable by Shamir Secret Sharing, maybe something that can be prototyped without changes to PLC or PDS changes, but likely requiring something in those systems long term.
- 3.
Migration of Community Members Away from an Adversarial Community β Impossible?
Herding Cats
Part of the appeal of Atproto is that when decides to prioritize things like sports and art instead of humorless liberal politics I can switch platforms without losing followers, posts, likes, reposts, etc.
In a way it would be amazing if we could bring the same behavior to communities, let's think about what that could look like.
GlΓΆgg
Here's the recipe my father shared with me for mulled wine:
Pour the following into a large pot or kettle:
20 bottles of Red Dry Table Wine
Insert a cheesecloth bag or Fine strainer containing the following:
25 oz Dried Orange Peel
25 oz Cinnamon Sticks
90 Cardamon Seeds
90 Cloves
Boil slowly for 15 minutes, stirring occasionally.
Add 1 lb each of blanched almonds and seedless raisins and continue to boil for additional 15 minutes
Remove pot/kettle from stove and place wire grill containing 2 lb of sugar cubes over the opening.
Pour 5 litres of 5 Star Brandy over the sugar until it is saturated.
Light a match to the sugar and let it flame. After the sugar has melted replace cover to extinguish the flame.
Now add 5 litres of Absolute Vodka and 5 litres of Scotch and stir.
Let sit and simmer for about 2-3 hours, never boiling the brew.
Stir again and remove spice bag.
Serve hot in punch cups or tea mugs with a few almonds and raisins.
I didn't want to make 30 litres of mulled wine so I simplified the recipe:
3 750ml bottles of Dry Red Table Wine
1 750ml bottle of 5 Star Brandy
1 750ml bottle of Vodka
1 750ml bottle of Scotch
That's better, 4.5 litres, I can bring that to a holiday party in a cockpot and we can all stumble home after. I actually made this a few times, it was considered a pretty dangerous addition to any party I was invited to.
I was talking to my dad and when I mentioned the "equal parts wine and liquor" he was shocked, it turns out the right ratio was:
1 750ml bottle of Dry Red Table Wine
25ml 5 Star Brandy
25ml Vodka
25ml Scotch
Let's turn these into records in an atproto space keeping it simple:
- 1.
My dad creates a recipe record with the incorrect porportions
- 2.
After talking to my dad I create a variant of the recipe with the correct proportions
For example purpose let's assume that my variant just notes the scaled down booze, not the other steps. It references he original recipe with an at uri.
Which is another way of saying that if you only have one record you only have half the picture.
We've Gotta Get Out of this Space
The space that these records are in is no longer amenable to drink recipes, time to relocate. There are four possibilities:
- 1.
We both decide we're going to leave these records in the space β Nothing happens and this doesn't matter
- 2.
We both decide we're going to move our records to a new space β During the move we could repoint urls
- 3.
I want to move my record, my dad does not
- 4.
My dad want's to move his record, I do not
We're going to consider 3 and 4 only for these purposes since it's the most interesting part of the problem.
My Suggestion - Space is Not the Root Authority
I'd proposed that the triple of did/rtype/rkey could (continue) to be treated as a unique key constraint in the PDS and the addition of spaceDid/stype/skey would be used as a way to authorize reads only. Each record could have 0 or more spaceDid/stype/skey associations with it.
I'm going to move my record β I add a new spaceDid/stype/skey to my record and remove the old one. I don't update my record so it points at the old aturi for my dad's record which includes the old spaceDid/stype/skey. The result:
Anyone in the new space can see my record with the right proportions but no steps
Anyone in the old space can see my dad's record with the right steps but incorrect proportions
Anyone with access to both spaces can probably see the whole picture
Now let's move my dad's record instead, keeping mine in place. Generally the same logic applies except now my record is pointing at a 6 part aturi that references an invalid spaceDid/stype/skey for the root record β I can't update my record in this flow since if I was actually going to do that I would probably just move it.
Now when resolving my record you find a dead aturi, how do you resolve it? Because the did/rtype/rkey triple is globally unique you know which PDS to talk to, but you don't necessarily know which space to authorize with, lacking that we just start guessing.
Transition
There is one case where my specific proposal works well. If we both agree to move records but can't coordinate that move. If I move my record first it remains valid, then when my dad eventually moves his my record would still be valid if you were to attempt to authorize both the record with both the original space and the space the reference is coming from. Additionally because records can co-exist in multiple spaces we could more easily add a mechanism where all the records that reference a specific ATURI add a new space authorizer to them, this could be similar to the request to delete spaces.
Just Copy the Records
In our 4 cases above, just copying the records works the same as my fancy re-authorizing flows. The one exception is the transitory case, in that case we need to do 3 writes.
- 1.
I write my record to a new space, pointing at my dad's record in the previous space
- 2.
My dad moves his record to the new space β My record is now broken
- 3.
I update my record to point to my dads' record in the new space
Because we're literally copying records we should also be a lot more suspect about any "please move this record" requests, in my framing of spaces solely as authorizers it would be acceptable to add authorizers based on the request through a space host, it's less acceptable to allow those requests to write records to your PDS.
So how is Daniel right β I think there's clearly a case to be made for my approach of treating the space purely as authorizer and not part of the storage structure β well...
Security
There are a few gaping security holes in my scheme that I hid behind the following line
http status code juggling
Here's the real issue:
I want to write a record did:space/recipes/1234/did:me/recipes/5678
I get a 409 error code back β conflict! A record is already there.
I read did:space/recipes/1234/did:me/recipes/5678
I get a 404 error code back β Not found! No record is there.
Repeat until the heat death of the universe
Because my PDS has to deny that records exist in spaces that you cannot read it must respond with a 404 β this is a requirement of the deniable records and one that is generally a security best practice.
However because my PDS has to stop you from writing records with the same did/rtype/rkey it needs to give you an error if you would write that record in a different space.
The only effective solution to this problem would be if a PDS could just randomly reserve certain rtype/rkeys and reject them arbitrarily, then you couldn't prove a record existed, but let's be honest that's a terrible plan.
So are we just stuck not being able to move part of our community?
Yo' Dawg! I Heard You Liked DIDs
What if we just did a lot of DIDs? We know DIDs are the "solution" to adversarial PDS migration and adversarial space host migration, maybe they're a solution here.
Let's go back to our Discuss It example from my previous blog post.
DID-a-palooza
After the PE firm exited new management stepped in and wanted to bring it back to it's atproto roots. They have a new design, every thread has it's own DID:PLC - the poster of the thread holds the primary rotation key for the DID. Each community is represented by a space host, a thread is part of a community if 2 things are true:
- 1.
It's DID points to the community space host as it's space host
- 2.
The community includes it in manifest records that are recorded under a "community DID"
This design will create hundreds of millions of DIDs but it has one primary advantage. By updating the space host on a thread DID, and writing appropriate manifest records a user can move a thread between communities. Because all the associated replies live under the same did they also "move" all without aturis, or storage changing.
Why do we need a different DID:PLC for each post?
- 1.
We can't use DID:WEB since that's controlled by the holder of the URL and we want to decouple from them
- 2.
We can't use the original posters DID:PLC because each DID can only point to a single space host
- 3.
A space host is specific to a community since it includes community membership, permissions etc.
Sure it's going to be hard on the PLC to support all those dids, maybe we should look into it.
DID:AT?
There's something kinda cool about this idea of having tons of DID:PLCs except for the having tons of DID:PLCs bit, what if instead we added a new DID method: DID:AT?
did:at:<aturi with some escaping rules>That's the syntax, and the aturi points to a record in a PDS that is a did document. That's all, it means that I can effectively generate as many did docs as my pds will hold. These don't need rotation keys since they're effectively delegated to the pds.
I built a little POC for did:at resolution over here - https://tangled.org/chris.pardy.family/did-method-at
So was right?
Yes and No
I think ultimately we're probably venturing beyond the realm of "protocol problem" and into the realm of "people problem". In that sense I think Daniel is right. I don't love the notion that the space is the primary part of the path of a record but I haven't found a good solution that meets the security constraints, and the actual resolution constraints.
That being said, I think unlike with the "locked open" nature of public atproto it's really really important that we get data models right. To that end go read 's excellent pieces on permissioned data shapes.