We currently don’t think that simply plopping encrypted content in to public atproto repositories is a good idea. Having encrypted content broadly available and even archived really raises the stakes for key loss and content leaking. While there is nothing technically preventing it, and folks can build whatever lexicons and stuff whatever bytes in their own repos, we would discourage folks from building atproto services that store encrypted content in repos.
Sending encrypted data over the firehose has the same problem as just putting encrypted data in your PDS.
This was also the perspective of the people working on encrypted p2p content replication at Ink & Switch: if you wanted to have secure, private data, you had to not only encrypt the data, but also protect the encrypted data from unauthorized access.
Adding services to your DID doc is actually pretty difficult right now unfortunately.
It requires not only providing your actual PDS password, i.e. app passwords and OAuth logins both don’t work, but it also requires sending you an email confirmation code, before the PDs will allow you to make a change to your DID document.
It’s a pretty steep process, and it’d be nice if there was an easier way to do it.
Having an easy, standardized way to update your DID doc was kind of where I ended up as my desired solution for improving support for “universal login” and off-protocol services.
Ah that’s fair. My bad! I’m not very well-versed in cryptography, so I didn’t have this context.
This can be circumvented, though, right? Perhaps something like a community relay. Credentialed users could directly submit the encrypted record to the relay, perhaps with metadata on which service it’s intended for. The relay would then serve those records to the intended services, while not knowing of the contents and keeping them off the public network.
The community relay would probably be run by the credential issuer. It has the advantage that 1.) There’s redundancy — if the service fails, the private records persist and are transferrable to a new service. 2.) The service could be factored out and run independently of credential issuance, should the operator be deemed untrustworthy
This would retain the benefits of credentials, service ecosystems for communities rather than central stewardship, and migration between services without cooperation, all while keeping it off the public network.
But it just hit me, if we are already expecting everybody to have a PDS, then what if we just put a record in the user’s public PDS that points to the service endpoints, instead of putting them in the DID doc?
That way it’s possible for any ATProto app to add service endpoints with a normal OAuth scope.
That wouldn’t have helped with my “universal login” idea, since the service entries being in the DID doc was essential to the goal of not requiring a PDS in the first place. But in this case, everyone has a PDS anyway.
Yeah, possibly, I’m still trying to understand the whole of what you’re describing and what different options there are.
The encrypted data in public was the only thing I could speak to confidently so far. I’m gonna try and catch up a bit more on some of this discussion since I’m considering a more ATProto-integrated privacy solution for Roomy now.
Actually, let me reframe this. I don’t think “relay” is really the right way to describe this. If encrypted data shouldn’t be in the public network, then members should submit that content directly to the services they’re posting to, without a middleman.
If the question is “How can community/service-scoped data persist after service failure without changes to PDS architecture?”, though, I think the per-member service key from my initial post does yield something useful.
Since the encrypted records can only be read by the service and the user, a community data store could hold a copy of every encrypted record without being able to read any of them. It could accept encrypted records from members and serve them back on authenticated request.
This essentially allows for persistence without public exposure or exposure to the store. If a service goes down, members can use the store to copy/forward the data to a successor service without cooperation from the old service. If the store goes down, services still have their copies.
The natural default operator for a data store would be the credential issuer (e.g. Northsky, Blacksky, etc), especially since I’d argue that encryption makes it a smaller trust decision. The issuer can recommend a store endpoint at credential issuance, but this default could be overridable and factored out independently.
This would require little to no configuration on the user side, and no changes to existing core infrastructure. It would keep encrypted records off the network and entrusted to a steward that is already entrusted with core community infrastructure by users and services.
So things have changed a bit with Stratos that might help some people who are looking for a solution.
Stratos at the moment is an independent service, it functions by “enrollment” which is done via Oauth to establish a long lived token so that it can write a stub record referencing a record created in stratos. The Stub is public but private is not, public record holds no metadata only a reference. The private record contains the data on which boundary domain it belongs to and can only be accessed by authenticated request which checks the boundaries of the requester before serving it.
As for the enrollment process, since the DID document has such a high barrier to being updated and the only update method is effectively full access, Stratos instead does a signed attestation by writing a record on the users PDS containing data like service endpoint and the attestation contains the boundaries which is signed by the service key so it can be verified. In this attestation is also a key generated for the user so they can verify the records creates in Stratos are “theirs”.
This effectively serves as the credentials for the user but it’s a shared model as Stratos still owns the private key in the current iteration.
Now since this is following the approach of boundaries to act as the permission to the data, it’s not working off a model any smaller than a group and a user has to actively choose to enroll in it. An operator would of course have the ability to remove a user but this hasn’t been implemented yet.
If anyone is keen to look closer at what’s being worked on, drop me a message. There’s one large piece do be done but then it will be close to feature complete.