atproto has an authorization issue. Not in the traditional sense, we have OAuth and service auth and they work great! but for the case of app level actions taken by making records.

The crux of the issue

Take the example of Bluesky posts and thread gating: Any user can, at any time, write an app.bsky.feed.post record to their PDS that points to any other Bluesky post positioning itself as a reply. This is how Bluesky threads work and is at the core of atprotos ideology. I own my posts and you own yours. However sometimes as a post author I want to control who can reply to my post. Essentially I want to signal to any potential AppViews/post indexers/etc that post records that reply to my most should only be considered valid if it's made by some specific other people. That's what app.bsky.feed.threadgate is for. It's a record you make in your repo to specify who is "allowed" to reply to you ie. what reply post records should be considered valid. This is good! It solves the issue by using the protocol capabilities and fits the protocols ideology around "speech" (anyone can write a reply record) vs "reach" (but it'll only get shown to users if it's deemed "allowed" or "valid"). But it's not without it's issues ...

A core design of atproto is that the data on PDSs is authoritative. Not the data on AppViews. To fully live up to that promise I should be able to spin up a second Bluesky AppView, backfill the network data from PDSs, validate the data there, and get an API server with a (practically) identical state of the network to the one api.bsky.app has. But these kinds of "reach controlling records" are hard to deal with in this case because of one fundamental property of atproto: History does not exist. Specifically history that can be trusted and is independently verifiable and backfill-able does not exist. This is an issue because the wanted restrictions can change over time.

Say I make a post that ends up reaching a bunch of transphobes. I (as a trans person that would like to not be bombarded with transphobia) might want to temporarily lock down replies for a few days until the transphobes move on and then open them up again after the dust has settled. However with the current approach that would mean making a thread gate record and then, once the dust has settled, remove it again (or set it's options to be permissive). For the case of the current status quo this isn't an issue for api.bsky.app since it was consuming the firehose during that whole period and knows what records where created in that gated period and should thus be rejected. But what happens if a new AppView is spun up (or api.bsky.app has do to a backfill from scratch for whatever reason) after all that has happened? How would they know that any replies made in the time frame when the thread gate was active shouldn't be considered valid? (the thread gate record is gone or in a permissive state now after all) How would they even find out which records were made in that time frame? (the createdAt field on post records isn't trustworthy, especially not for something like this)

This is the core issue: If the validity of a record depends on when it was created then we have effectively broken the core promise of atproto: The data on PDSs is not enough to reconstruct the network state from the ground up. PDSs are no longer authoritative. This lack of history is a core property of atproto. We have many different actors that make and distribute data, if you were not personally observing the firehose when some record was created then you effectively haven't got a clue when it was created because each independent actor could claim to have created their records at any arbitrary time!

Solutions?

I have two main ideas for how this could be addressed. I'll describe the former here as it's the most atproto-esque of the two and my other idea is based on talks I have had with another community member, uses transparency logs and thus will require more context to explain but is (I feel) overall a stronger solution and I feel is more likely to be the direction the ecosystem should take and thus I think it deserves it's own post. (though spoiler alert: I'm not 100% happy with either solution, hence this post to try and get community discussion rolling, but both my methods are still valuable tools for app developers I think).

The first idea is to add in a little more centralisation, just like how this issue is handled currently, but to do it in a backfill friendly way: Instead of just requiring an AppView to have been consuming from the firehose while all the records in question were being created we can instead require that the records themselves ask an authorised service to prove their validity with a signature! If a record hasn't been signed by the relevant service then AppViews should consider it invalid. This does solve a big part of the issue: Backfill from PDSs and validating the data coming from them is enough to reconstruct the network state. However it has a few big disadvantages. The main one being now we need this other service to exist and be authorised to sign records. This can work for cases like Tangled where a knot could sensibly be the service that signs valid records that interact with the repos it hosts. but it's a really big issue for the Bluesky reply case. There isn't really an immediately sensible option in that case.

This option also potentially has issues with key rotations of the signing service. This can be fixed by having the service publish its signatures not by having users add the signatures to their records but my the signing service instead publishing validating records in its own atproto repo that point to the validated records. I think you can imagine other issues that might cause though.

This is a valid and functional solution I think and it leans into some existing paradigms in the ecosystem. But it's also a very unattractive solution for many many use cases. I think the second idea is much more generic and has fewer potential issues but it does still have it's issues. Stay tuned for a part 2 whenever I can get myself to write it up. I'm not sure how we will end up solving this to be honest. atproto is not a blockchain or similar. "objective" timestamps (or at least event order) are not something the protocol offers on it's own but that is essentially what we want in this case. I'd be interested in what other peoples thoughts are! maybe I missed something really valuable.