Contribution Data in atproto

Tangled found PDS doesn’t fit well for contributions even with upcoming permissioned space spec. So, we are planning to store record-like data outside of PDS, in Knot. Post below explains the reasons & upstream spec change we need.

My previous posts about COBs and cross-service spec might worth reading too.

tl;dr

  • In contribution model, colocated bucket fits better over partitioned space because it’s “you to them” and not “we”.
  • Because we are already doing same thing in Knot, we are going to make Knot more similar to PDS hosting record-like data.
  • Now records can live outside of PDS and we want to use at-uri to reference them.
4 Likes

I agree that colocated bucket matches the use case here. It’s symmetric to how git works, so makes sense that you’d want records to be stored similarly.

Is there a reason this can’t just be an atproto repo stored on the knot and addressed similarly? There’s no reason that the thing storing a repo has to be a “full pds” as long as it fulfills the repo hosting/syncing contract (basically the com.atproto.sync.* namespace).

The only thing that changes when resolving one of these proposed URIs is the service it goes to right? I’d encourage finding a way to still just use the #atproto_pds service entry and preserve normal at-uris.

At-uris are being specified at the IETF so it will be awhile until a change like this can be upstreamed. I don’t personally like any of the options listed.

at://{authority}/{collection}/{record-key}?service=tngl_knot
This implies a user can have arbitrarily many repos at different services. Having 1 repo per account is a very nice constraint IMO. Will confuse things like account status, key material, etc. Putting something like this in query params also strikes me as brittle. Especially if you have multiple params then you have to worry about ordering for string equality.

at://tngl_knot@{authority}/{collection}/{record-key}
We’re already in trouble for our uri authorities :sweat_smile: I think this is too much.

at://{authority}/tngl_knot/{collection}/{record-key}
I probably like this the best, but my expectation is that that segment (if introduced) is reserved for protocol things (ie blobs, labels, spaces, etc). Opening this up as a userland segment is a totally different beast & then we have to worry about future naming conflicts without some sort of namespacing.

3 Likes

I’m not sure why the mechanisms being proposed differ whether you store data them in the PDS (atproto) or the Knot (git). Both have the same access parameter behaviors, and if you’re looking to accept contributions to a project via an API - as this proposal seems to suggest - then you should be able to store them in either the atrepo or the gitrepo

1 Like

Thanks for the response!

Is there a reason this can’t just be an atproto repo stored on the knot and addressed similarly? There’s no reason that the thing storing a repo has to be a “full pds” as long as it fulfills the repo hosting/syncing contract (basically the com.atproto.sync.* namespace).

There are more things we would like to do with Knot-stored COBs like full record version history or native git object referencing (similar to how we reference/garbage-collect blobs in PDS). While we can try hacking with existing atproto spec, it’s much easier and robust when we just run a specialized sidecar protocol.

Also, I think using sidecar data store is kinda inevitable. Git repository itself should still live under #tngl_knot service namespace anyway, and unfortunately there is no way to enforce a service in DID doc to fulfill both AtprotoPersonalDataServer and TangledKnotServer contract. We should pray users to not mess up with those two service entries.

Having 1 repo per account is a very nice constraint IMO

Yes I do agree with that! And ironically that’s why I’m proposing non-PDS repository spec. If a Tangled git repository has both atproto repository and a git repository, we should sync both separately. Instead, we can focus on syncing one repository and that will be git for our case.

For uri schema, I don’t like all of them either, they are few examples from previous post.

At-uris are being specified at the IETF so it will be awhile until a change like this can be upstreamed.

For short-term solution, we are considering custom uri scheme like at+git:// or tngl://, so upstream spec change is not that necessary for us. Though I think it will be great if we can share same URI syntax to “reference a data” regardless of the host server type. Then, it will be great if we can loosen up the AT-URI spec and allow using it to reference completely different data so that’s why I’m sharing this for IETF discussion. It’s one aspect to consider when designing a future-proof spec and I hope this to be helpful!

1 Like

Thanks for the response!

I’m not sure why the mechanisms being proposed differ whether you store data them in the PDS (atproto) or the Knot (git). Both have the same access parameter behaviors

Sorry for the lack of context. It started from finding that we need our own event stream to sync git repository similar to atproto firehose. And if we are building our own sync anyway, it’s easier to store records directly in git repository instead of maintaining atproto repository in parallel. We get free VCS by doing this so that’s also a huge win for contribution-driven data.

Also in colocated bucket model, ACL happens in data store layer rather than application layer. Regardless of whether we split git repo’s PDS and Knot as separate services or not, we have to expect some kind of custom behavior from gitrepo’s atproto PDS and I think that’s not how PDS should be treated like.

1 Like

Given the need for custom sync I don’t know that what I’m about to describe is relevant to your use-case, but I would like to address the idea of contribution-driven data more generally.

This is exactly the kind of data that Roomy is concerned with because, beyond chat, it aims to provide communities “gardening” tools for long term cultivation of knowledge, including forum-like threads and wiki-like pages.

While different communities will have different needs and this will need to be configurable, threads and pages should usually not be able to be deleted without notice by community members after “contribution” to the community space.


But we actually think that the permissioned data proposal is perfectly fine for this!

In this case the Roomy “guild”[1] is represented by a DID acting as the space host for permisioned spaces.

Each channel, thread, and wiki page is it’s own permissioned space, and instead of generally expecting everybody to write to their own perm repos in the space, we’re actually expecting records to be written to the guild’s perm repo under the guild DID. This makes sure the community always has the data, and also allows the admins to moderate the space.

There is actually no reason to stop a user from writing copies of the records to their own perm repo too, so that they have a copy of their data if the guild chooses to moderate or delete their content.


The obvious challenge in this case is that we need to allow multiple writers to a single repo which is not an existing pattern in ATProto.

That’s why we’re working on an “arbiter” that will sit in front of an XRPC API, such as the PDS’s permissioned data API, and enforce a very configurable policy written in Rego, that can be customized per-guild.

So the arbiter sits as a middleman in front of the space host to allow anybody to contribute to the chats, wikis, etc. while enforcing rules like “you can only set your own DID as the author of the chat message”, “only team members can use @everyone mentions”, “only admins can delete chats by other users”, etc.

The nice thing about this is that it’s just a normal ATProto repo to any other app. The only thing that changes is that writers to the repo are required to send their request through the arbiter, instead of directly to the PDS, because they are not writing to their own PDS.


Any AppView could actually do the same thing the arbiter is doing if it wanted to. The PDS just become’s the AppView’s canonical datastore, living on protocol instead of in Postgres or something like that.

The hope with the arbiter, though, is to make it easier to use this “community / organization owned” data thing, on protocol, so that we don’t need everybody to re-implement access control for the PDS / permissioned repos and so clients can have a relatively standard way of issuing writes to repo data through an arbiter.


Again, maybe this doesn’t help you if you already determined that you need custom sync, but as far as contributed data in general, I don’t see that it necessitates a break from ATProto once permissioned data lands.


  1. unfortunately Roomy “guilds” are called “spaces” but that makes it so much harder to talk about now that perm data also uses the term. :sweat_smile: ↩︎

4 Likes

Yeah my inclination is to handle it like this as well. I actually think permissioned spaces work pretty well for this design. They give you addressing, sync, access control, etc. The problem of data being deleted when an individual user deletes it can be solved by storing the canonical copy in the space authority’s permissioned repo as @zicklag.dev suggests.

Storing version history is something we’re interested in introducing as well. But there’s ways to do that in the current protocol still (sidecar records, etc).

Storing Lexicon data in git is no longer atproto. That’s not to say there might not be a reason to do so (though I’d pretty strongly argue atproto is the better fit). But if you do do so, then you’ll want to reference it through a new protocol scheme rather than smuggling into some variation of at://

4 Likes

Thank you all for the feedbacks!

But we actually think that the permissioned data proposal is perfectly fine for this!

I definitely see that we can hack on PDS, build version logs on top of it, and manage it alongside with custom data like git repo. But I’m still leaning towards the custom data server for contribution data.

PDS is not designed for multi-user writes. We can hack on it by adding .author for all records and gating with services like arbiter, but I think this open-write model deserves its own dedicated service. Also, if we just wrap PDS with custom service, we are basically using PDS only for sync. Then what’s the point of using PDS at first place? I think internal storage/sync implementations are the least essential part of atproto. If we can build specialized service for contributions, we can do many things better. Using git doesn’t isn’t just Tangled-specific, I think wiki services will also want similar thing internally. Multi-author architecture and record history builtin to the protocol are also quite useful.

The users will still verify their writes by writing attestation records on their PDS. But contribution bucket will have to gather writes from public, perform ACL, and log all modifications. I think this justifies the need of non-PDS service since we don’t expect these from non-PDS service. It can use actual PDS internally, but that doesn’t matter much if we hide almost every part of it.

If we expect some common contribution-related features to a service, that service is not an AtprotoPersonalDataServer any more. We are expecting more or completely different spec, so using PDS doesn’t make much sense imo.

1 Like

Storing Lexicon data in git is no longer atproto. That’s not to say there might not be a reason to do so.

I thought “atproto” can be more broader concept where even PDS is not essential and AT-URI can used to reference anything that authority owns, but if you say so, yeah that’s fair point so I guess we don’t really need changes from AT-URI spec and can just run our own sidecar protocol.

Another interesting problem to solve with upstream protocol would be permission scopes. Because as far as I understand, the granted permission list is not shared with space authority from current permissioned space spec. So bucket host can’t know which permissions are granted to that client.

Also in case for tangled, we want to render space identity in consent screen as something other than DID or Handle, specifically owner.com/repo-slug format. Which will be pretty tricky one to solve…

1 Like

To me the value comes from the standard ATProto records format allowing other apps to come into the space and index the records themselves and implement a custom AppView.

This is still possible with non-PDS services, too, but it’s just a new standard. If I use the PDS standard then I can grab an off-the-shelf ATProto compatible service like https://happyview.dev to much more easily make a custom API on top of the ATProto data, aggregated in the way that I need to.

That’s not to say some use-cases aren’t different enough to warrant a custom protocol, I can definitely understand that.

2 Likes

Yeah I understand your points too. I think we are seeing largely two sides for having new standard or not:

Write (with ACL)

We will need new standard for this anyway. It can be Arbiter or Knot with embedded PDS, but the exposed entrypoint to write a record will be non-PDS service. We expect multi-authored writes here, so we can’t really just use an #atproto_pds service here.

Read (sync)

Existing atproto wins, no doubt on that. It’s less of a problem in case for Tangled because we should figure out the git-syncing problem off-protocol anyway, but your doubts on “unnecessary new standard” is pretty valid.

@oyster.cafe recently experimented exposing git-backed records as atproto compatible CAR format, so that might be good insight on how to get advantage of both directions.

2 Likes

I wouldn’t fixate on if something is a “PDS” or not. A “PDS” is certainly a role that is important to specify! But a service can be a “repo host” (providing hosting & sync of repos) without being a “PDS”. This can all be better documented/explained.

It’s not just sync, the important thing is the data model and addressing scheme! Do you want records to be addressed in the “universal data space” of atproto? In other words, are they records “in the atmosphere”? Can they be generically understood and indexed by atproto services like relays/microcosm/future generic atproto databases? Can they be referenced “natively” as at-uris by other atproto records? If there’s a future atproto UI framework, will it understand these as records? Can authorization for these records be discussed using atproto OAuth semantics? Can developers interact with them using existing atproto intuition (without needing to understand a new protocol)?

I wouldn’t focus too much on “the PDS” or “sync”. It’s the whole atproto data model and network!

Of course use the right tool for your job! But my encouragement would be to do a custom data server, but still store/sync these records with atproto so you get all the interoperability benefits of using it as a data network.

4 Likes