Gateway - structuring AP objects around a cooperative storywriting service


#1

Hey hi! I am working on free software for collaborative storywriting, Gateway, with its sources available at https://octo.sh/Gateway/ I hope to make it federated one day, once I get it up and running,

My question is related to fitting the objects I create into AP vocabulary.

Our current design implies that individual players make posts, which are blobs of text with some metadata. These are the smallest atoms and may be mutated at any time (for instance, when someone corrects a typo).

A strictly ordered collection of posts is a chapter. A chapter holds some metadata and may be mutated at any time, as well, for example, when someone modifies a post, adds/deletes one, or changes the order of posts in a chapter.

A graph of chapters is a timeline. The timeline holds associations between chapters - which one happens before/after which, along with some metadata as well. These associations are a graph, as they do not need to be linear - a single chapter can fork into several chapters happening at the same time that merge back into one chapter in the end. These links may be mutated by the players.

Additionally, each player has personas, and each post is assigned to exactly one persona. I assume that both players and personas would be AP Actors, but I do not know how to reflect the association between them in an AP manner.

My main question, though is: how do I reflect this structure to make it somewhat compatible with already existing AP-compatible software? A single post, out of context, could theoretically be an Article, but I do not know how to represent a series of Articles that come one after another or how to represent the non-linear

Then there comes the fact that the data in my model is highly mutable and may change at any time. How does AP reflect that? If a single post is changed, does it mean that I must act in a functional manner, which is, to create new “revisions” of that post, its parent chapter, and the chapter’s parent timeline?


#2

So it sounds like you could use the following semantics:

  • Posts = Article if they have formatting or contain line breaks; Note is usually for short content no more than a paragraph. Also, you may want to reserve Note for metadata about each Article, if you wish.

  • Chapter = OrderedCollection of posts.

  • Timeline = inReplyTo maybe? Assuming your graph is directional, the to-node chapter would declare that it is inReplyTo the from-node chapter. A chapter can store every one of its connections in the replies Collection.

  • Player’s persona = an Actor of type Person. You can use your own internal account system, but each persona would have its own Actor and players can manage multiple personas and therefore multiple Actors.

    • If there is a need to reflect which player owns which persona, then you can declare each player as an Actor of type Group or type Organization.
    • If you want personas to be strictly subsets of the player, then you can have the player also be a Person, then declare each persona as an attachment.
    • If the player is the Actor and no persona is performing any actions, then you can declare each persona as a Profile instead of as an Actor.
  • Changing data = send an Update Activity. If you wish to store revisions, you may keep history of all Updates. You can Update any Object's fields just fine. You can also Move a post from one chapter to another, for example.

I don’t think you need to worry too much about compatibility with existing AP software, because each implementation will accept whatever it understands and drop whatever it does not understand. Microblogging environments like Mastodon or Pleroma will probably transform Article posts into incoming statuses, but they might not be aware of the chapters.


#3

Hey! Thanks for the elaborate response.

  • Posts may contain line breaks. Again, it’s hard to measure - some people write posts that are two or three lines long, and others’ posts go multiparagraph. I think it’ll be better to take the upper limit and use Articles.
  • Chapter - perfect.
  • The graph is directional, sure. inReplyTo sounds like a way to express graphs, but, if I understand correctly, that’s stored only in the children. Is there a way in AP for the parents to also store links to their children? Something like replies or anything similar? Also, if I wanted to get all chapters belonging to a timeline, would a good way to do that be to create a global timelines collection, and each timeline object could have a collection of chapters?
  • I assume that personas are separate from their players, so I guess that they will need to be individual actors. As in, a persona might be sometimes “played” by multiple players if e.g. a group of world owners are all allowed to roleplay some important canon character for plot purposes.
  • I guess that I do not want to store revisions - I only want the newest version of all data. In that case, if someone accesses revision 4 of a particular post, for example, can I use a HTTP redirect to route them to revision 8? Or should I use a different status code?

As for compatibility, I think it’ll need to be ironed out when the software is already built and the federation starts becoming available - you are right.


#4

Is there […] something like replies or anything similar?

Yes, there is exactly this in the replies Collection as stated above :slight_smile: one additional point that just occurred to me though is that I am unsure if you can declare multiple inReplyTo, or if that would be needed. I think most software currently expects only one inReplyTo, while replies can contain an arbitrary number of members.

If I wanted to get all chapters belonging to a timeline,

Sounds like you could use the existing streams and have it contain one Collection for each timeline, but if you use reply-chains to establish the timeline graph, it may be enough to simply store the top-level chapter, as all replies can be fetched via recursive descent. It may still be a good idea to use streams to prevent clients and other servers from having to repeat this expensive fetching process, though!

I assume that personas are separate from their players […] a persona might be sometimes “played” by multiple players

The only important semantics here are for you keeping internal track of what is going on. It sounds like personas might need their own Actor, but I’m still not sure players strictly need an Actor unless you’d like your internal system to be fully ActivityStreams-based as well; this would allow you to easily describe “Player updated persona” type activities in your log. If this is desired, perhaps a simpler approach would be to use a Profile for each persona, and to have player Actors take ownership of all activities. There definitely needs to be a bit more thought about whether players, personas, or both will need their own Actor representation.

I guess that I do not want to store revisions

In that case, your Update activities should maintain the same URL, so that re-fetching a resource via HTTP will automatically give you the latest version. No need to worry about status codes if you’re not storing different revisions at different URLs.


#5

Is it legal to specify multiple replies at all? If yes, then, what is the worst thing that can happen in existing software?

I’m not aware of the concept - I’ll need to re-read the relevant part of the AP specification.

Yes, this sounds good. Though, I expect that personas might change hands at some point in time, that is, be assigned to a different player - I therefore think that it would be better to have personas as separate Actors.


#6

It is legal and normal to specify multiple replies, as all replies should be added to the replies collection. If a post “a” receives replies “b”, “c”, and “d”, then you should see a JSON representation akin to

"a": {
    [...]
    "replies": ["b", "c", "d"],
    [...] 
}

and if it then receives reply “e”, you should update the replies Collection. Here’s a more real-life example of how replies should be structured: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies

It is also actually valid to specify one or more inReplyTo, it seems: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto

It’s rather loosely specified as such, the first item under MAY: https://www.w3.org/TR/activitypub/#actor-objects

Implementations MAY, in addition, provide the following properties:

streams
A list of supplementary Collections which may be of interest.

Also of interest is the discussion about implementing them in this Github issue: https://github.com/w3c/activitypub/issues/299

The question is not really whether players and personas need separate representations – the answer to that is clearly yes. But rather, should a persona be an Actor or a Profile? That depends on whether a persona will be performing actions, or if it will simply be metadata about the post/chapter/timeline, i.e. this post belongs to the persona but was written by this player. If a persona were a Profile, then you could still have one persona per post, but the persona couldn’t perform actions. See more about Profile: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile


#7

Welp - I actually meant inReplyTo. Thank you for guessing what I meant and answering that question, too. :slight_smile:

That is a good question. Theoretically, personas are just “data structures”, pieces of data that are tied to individual posts. On the other hand, I still can imagine the possibility of some action that may be performed by a persona and not a player - for example, if someone decides to play a persona anonymously (which I can imagine), then there will be no player Actor to perform an action like making a new post in a chapter. This would imply that personas need to be Actors who create these new posts.

If they are implemented as Profiles, is it possible to later modify what Actor they are associated with?


#8

Not necessarily! In cases like those, you could still assign an anonymous instance-wide Actor to show as the public owner of the Profile. There’s a lot of flexibility in how you choose to describe that data structure. If you choose to make both personas and players into Actors, then you probably need to figure out how to allow certain activities to be performed “on behalf of” the persona Actor that owns the post. That would be handled by your own internal server logic.


#9

Could you give me an example of such an activity?