diff --git a/content/app/activitypub.md b/content/app/activitypub.md deleted file mode 100644 index d001204..0000000 --- a/content/app/activitypub.md +++ /dev/null @@ -1,32 +0,0 @@ -+++ -+++ - -# ActivityPub - -## HTTP verbs -GET and POST/PUT/DELETE[^1] json docs? maybe some validation? needs some basic authentication too i guess - -~~uri schema is handled by the client, not the server (client PUT).~~ activitypub section 6 says client should POST either an activity or an object to /outbox and then the server returns the `id` value. Server decides id, client does NOT get to decide what id is. it only gets to decide `url`. - -## authorization - -maybe need a token or capability to obtain read/write privs to given directories or objects? need to look into how to generate and revoke captokens and how to expose that info to clients through a standardized endpoint... OAuth? an admin token could manage and revoke which tokens have access to read/write to certain resources, maybe? but there would have to be a way of linking tokens to people or device names somehow for it to be human-meaningful... - -the real issue is that i need to figure out how to create and manage actors. we can say that the oauth flow can request scopes as paths e.g. `"scope": [{"/": "rw"}]` for full access to all paths (or something like that)... but ugh idk. maybe be lazy and fallback to POSIX filesystem ACL? what that indicates is that the server will have to do its own access control but idk how. i need to figure out how. - -## how to create and manage actors? -xmpp i would do like `prosodyctl adduser JID` and `prosodyctl passwd JID` so you could have a cli tool to generate read-write tokens maybe? idk. that kind of out-of-band token generation seems a bit complicated but it might be necessary if we don't want usernames to be canonical. maybe some config file could work? really lost here... - -## spitballing about c2s - -if the defined actor flow is as follows: - -1. ask for actor -1. discover outbox endpoint -1. POST to outbox -1. server will assign id and return it to client with 201 + Location header - -then idk what that means for my plans with id/url distinction... i think it means that clients have to save the id internally if they want to refer to objects later, for example. if a client POSTs an Object and it gets wrapped with a Create and then both ids are assigned... did the client get to decide the URL? and how does the server know if the URL is allowed? presumably then, we have two different levels of authorization to deal with -- one for the actor namespace (but this can be handled by the server tbh), and then another for where the client can serve? or... maybe this means that the client and server ideally should be running on different sub/domains as i suspected! **if you put the server and client on the same FQDN then there will be URL/namespace conflicts.** the server will create e.g. /actors/1/outbox and generate a corresponding OAuth token maybe. the server probably also needs to know which namespaces it actually has access to? some way to map a more human-friendly identifier to the activitypub id? can we mandate webfinger? - -## footnotes -[^1] POST means the server decides the URI of the resource it receives, PUT means the client decides and the server just dumps it there \ No newline at end of file diff --git a/content/app/communication.md b/content/app/communication.md deleted file mode 100644 index 91e11c2..0000000 --- a/content/app/communication.md +++ /dev/null @@ -1,45 +0,0 @@ -+++ -+++ - -# Communication paradigms - -## qualitative analysis -- deliver / fetch -- side effects (receive or relay?) -- 1-1, 1-n, n-n? - -## types of communication -### Chat (n-n delivery) -- all kinds of messengers -- fixed participants/addressing -- smtp, xmpp, activitypub -### Broadcast (1-n delivery/fetch) -- feeds/social -- make a resource available to audience -- actitypub (when not @ anyone), rss, atom -- xmpp status updates / PEP, "stories" (loosely) -### Wall/Forum (1-n relay/fetch) -- Facebook profile/group page, bulletin board forums -- loosely organized by page -- activitypub only? which vocab to use? what type of actor? how to follow a thread? needs extensions... -### Room (n-n relay) -- IRC and its various clones, mailing lists, etc -- irc channel, smtp mailing list, xmpp muc, activitypub Service actor? - -## What do i actually use tho - -- read (feeds -- rss, atom, activitypub?) -- publish (broadcast -- but also websites/pages/etc?) -- chat (chat -- messaging over smtp/xmpp/activitypub) - - 1:1 chat - - closed group (define participants) - - deliver to all those participants, or have them fetch it from a central Service? (probably the former) - - open group (participants can join/leave at any time without approval) - - better served by room model than chat model - - delivery to all participants becomes impractical and limited, so central Service becomes more necessary to store and forward -- lurking in rooms? - - kind of like read, but with ability to send (so a mix of read + open group?) - - rooms are actually misused imo! people turn irc channels into the equivalent of forums and it's actually the wrong paradigm entirely - - i think rooms should be created and destroyed as needed for when people need to actually have a live chat - - i'm mostly fine with offloading this to jitsi i guess? no pressing reason to implement my own stuff for this, and also jitsi provides voice/video chat and screensharing at the same time so it's probably way better than anything i could ever do on my own - - perhaps by design rooms should ask the user to turn on logging? to maybe communicate that this is not a permanent place for discussion? or an option to destroy the room and convert the chatlog to a forum post? idk... \ No newline at end of file diff --git a/content/app/compatibility.md b/content/app/compatibility.md deleted file mode 100644 index 3570292..0000000 --- a/content/app/compatibility.md +++ /dev/null @@ -1,17 +0,0 @@ -+++ -+++ - -# Things you would need to do for compatibility - -## Mastodon - -### Unique username+domain pair - -- webfinger domain.example for resource `acct:preferredUsername@domain` must link to your activitypub actor - - mastodon will do this even when given a direct activitypub actor id - - if webfinger is forwarded, mastodon will get the linked id and do the above - -## Representing polls - -- `Create Question` instead of just `Question` or `Create Note{attachment: Question}` (being treated like an object instead of an activity) -- Votes are `Create Note{name, inReplyTo: Note{attachment: Question}}` and the replied-to note must be local \ No newline at end of file diff --git a/content/app/ideas.md b/content/app/ideas.md deleted file mode 100644 index 8960884..0000000 --- a/content/app/ideas.md +++ /dev/null @@ -1,141 +0,0 @@ -+++ -+++ - -# Ideas for extensions and differences from current implementations - -## max followers - -https://github.com/mastodon/mastodon/issues/20089 - -max followers? mastodon has a MAX_FOLLOWS and MAX_FOLLOW_RATIO so why not MAX_FOLLOWERS - -## Webfinger Content-Type of activitystreams profile - -i wonder if setting a Content-Type of `"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""` instead of `"application/activity+json"` would break anything - -## Message type - -Message is an IntransitiveActivity? - -```yaml -id: https://alice.social/activities/1 -actor: https://alice.social -type: Message -to: https://bob.social -content: "hello bob" -``` - -could also be a regular Activity, where `object` is the conversation or room? - -```yaml -id: https://alice.social/activities/1 -actor: https://alice.social -type: Message -object: https://alice.social/rooms/1 -to: https://alice.social/rooms/1/audience -content: "hello room" -``` - -instead of making it transitive, we could swap `object` for `context` which is a bit more semantically correct if pointing to a collection? - -```yaml -id: https://alice.social/activities/1 -actor: https://alice.social -type: Message -to: https://alice.social/rooms/1/audience -content: "hello room" -context: https://alice.social/rooms/1 -``` - -or have both `object` and `context`? - -```yaml -id: https://alice.social/activities/1 -actor: https://alice.social -type: Message -object: https://alice.social/rooms/1 -to: https://alice.social/rooms/1/audience -content: "hello room" -context: https://alice.social/rooms/1/context -``` - -## distribution model - -[tldr i should choose if i want my reply to go to my followers, or their followers, or just them, or all of the above, or whatever] - -direct addressing should be better supported - -alice makes a post and sends it to her followers or whatever. optionally it can be made available to the public? - -```yaml -id: alice.example/54078934249073290847321094/activity/1 -actor: alice.example -type: Create -object: - id: alice.example/54078934249073290847321094 - type: Note - content: "hello world" - audience: Public # could be alice.example/followers instead -cc: [alice.example/followers, Public] -``` - -bob replies to alice, they send that to alice only, and *maybe* optionally to their followers. **this should be up to the user (bob).** - -```yaml -id: bob.example/94378256423895476238954/activity/1 -actor: bob.example -type: Create -object: - id: bob.example/94378256423895476238954 - type: Note - inReplyTo: alice.example/54078934249073290847321094 - content: "hi i'm bob" - audience: Public -to: [alice.example] -cc: [alice.example/followers, bob.example/followers] # alice's followers will be inbox-forwarded if alice allows it -``` - -alice can then choose to redistribute that reply. **this should be up to the recipient (alice)** and also **this should be made clear to the replier (bob)** - -```yaml -id: alice.example/7659804769058743906/activity/1 -actor: alice.example -type: Announce -object: bob.example/94378256423895476238954 -cc: [alice.example/followers, bob.example] -``` - -note that alice might allow bob's replies to be automatically redistributed, if alice trusts bob. - -bob may not want his reply to be redistributed though - -```yaml -id: bob.example/15789436589346/activity/1 -actor: bob.example -type: Create -object: - id: bob.example/15789436589346 # may be excluded but could be better to explicitly specify as null. if provided, will return 401 Not Authorized if you do HTTP GET - type: Note - inReplyTo: alice.example/54078934249073290847321094 - content: "i'm gay. don't tell anyone" - audience: alice.example -to: [alice.example] -``` - -alice can reply to bob's private reply, but no one else can see that reply - -bob might also make his reply explicitly anonymous and transient by using id: null or not providing id - -alice's reply SHOULD NOT target her followers, although she is capable of doing so. **this should be an explicit decision.** - -### maybe objects shouldn't be inlined tho - -spritely magenc golem ocappub etc etc etc - -```yaml -id: alice.example/54078934249073290847321094/activity/1 -actor: alice.example -type: Create -object: alice.example/54078934249073290847321094 # how to fetch this? -cc: [alice.example/followers, Public] -``` \ No newline at end of file diff --git a/content/app/implementation-details.md b/content/app/implementation-details.md deleted file mode 100644 index 968343f..0000000 --- a/content/app/implementation-details.md +++ /dev/null @@ -1,7 +0,0 @@ -+++ -+++ - -## api? - -- no counts/numbers unless directly relevant. example: `replies_count` integer -> `has_replies` boolean. - - follower counts can be faked \ No newline at end of file diff --git a/content/app/joke.md b/content/app/joke.md deleted file mode 100644 index f04027e..0000000 --- a/content/app/joke.md +++ /dev/null @@ -1,16 +0,0 @@ -+++ -+++ - -# Joke ideas for federation that are still actually kinda valid but funny - -## Federated mutes - -as2 has an Ignore type so you could theoretically federate out mutes in the same way that blocks are currently federated - -### Block/Mute reminders - -anytime someone muted tries to reply, you respond with an Ignore - -anytime someone blocked tries to interact, you respond with a Block - -what if you do this *every single time* they try to reply/interact \ No newline at end of file diff --git a/content/app/prior-art.md b/content/app/prior-art.md deleted file mode 100644 index 23506cf..0000000 --- a/content/app/prior-art.md +++ /dev/null @@ -1,18 +0,0 @@ -+++ -+++ - -# Prior art - -## email -- plain text content only, with everything else being handled by metadata and headers -- no auto quoting -- quoting should be intentional -- no signatures -- that's just extraneous. at the very least signatures should be dynamic instead of static? -- username / password can be used for actor authentication via oauth? logging into an ap client should be like logging in with google? - -## irc - -- relay model. you send everything to a central server and it relays it to everyone connected - -## xmpp - -## activitypub \ No newline at end of file diff --git a/content/app/routing.md b/content/app/routing.md deleted file mode 100644 index 16fd1f9..0000000 --- a/content/app/routing.md +++ /dev/null @@ -1,29 +0,0 @@ -+++ -+++ - -# Routing table - -## trwnh.com -- /.well-known/webfinger -> ??? - - should map a@trwnh.com to ap.trwnh.com/users/ea9f49f6-d1cc-4b9e-a221-71f6aa617efa -- /notes, /articles, etc etc -> broadcast.trwnh.com [maybe? or do i just want to run the c2s app on the root domain? indieweb? idk] - -## ap.trwnh.com -- /users - - /actors? how do i want to separate profiles from users? users should be able to have multiple profiles. -- /inboxes -- /outboxes -- i have absolutely no idea how to handle authorization beyond vaguely something oauth-ish - -## chat.trwnh.com -smtp / xmpp (jsxc embedded?) / activitypub dms - -## rooms.trwnh.com -irc, xmpp groups, i think it makes sense to add webrtc too. basically jitsi meet or nextcloud talk, but with persistence. - -## reader.trwnh.com -rss / atom / activitypub? - -## accounts.trwnh.com or id.trwnh.com - -identity provider? \ No newline at end of file diff --git a/content/app/webfinger.md b/content/app/webfinger.md deleted file mode 100644 index 5787dfb..0000000 --- a/content/app/webfinger.md +++ /dev/null @@ -1,15 +0,0 @@ -+++ -+++ - -this is really the interface that makes pure AP servers discoverable. - -problem: AP `id` can be literally anything -solution: have webfinger return the `id` based on some other "friendly" uri lookup - -/.well-known/webfinger?resource= - -- username? `acct:trwnh` or for masto compat `?resource=username@domain.com` or `?resource=user%40email.com@domain.com` per rfc for acct uri -- phone number? `tel:+1205578890` -- needs to be standardized/internationalized i guess, maybe also accept `sms:` -- email? `mailto:a@trwnh.com` -- xmpp? `xmpp:a@trwnh.com` -- other -- http? https? urn? \ No newline at end of file diff --git a/content/tech/communication.md b/content/tech/communication.md new file mode 100644 index 0000000..82f0e8f --- /dev/null +++ b/content/tech/communication.md @@ -0,0 +1,36 @@ ++++ ++++ + +## Paradigms + +### 1-1 or 1-n + +### push or pull + +### relaying aka intermediaries + +## Separation of concerns + +### Publishing + +> "I want to share this with the world." + +For a resource to be visible to other people, it must first be published. Typically, resources are published at a location or with a specific identifier; people who wish to view that resource can then go to that location or search for that identifier using a lookup service in order to view the resource. Most commonly, resources are published on the World Wide Web, and they are found via their uniform resource location (URL). When you type, paste, or click a link in your Web browser, your browser will make a request to fetch that resource on your behalf, and the server responsible for that resource will respond with the resource. + +### Subscribing + +> "I want to see the things I am interested in." + +OK, so there are resources that we can access if they are published at a known location or with a known identifier. How do we know when there are new or updated resources? At the most basic level, you can manually check a resource to see if it has been updated; for example, you can check a website to see if there are any new pages. To make this manual checking process easier, publishers can generate a feed or stream of resources that can be subscribed to. When you subscribe to that feed, your feed reader will periodically check the feed for any changes; for example, a feed may be updated with the latest articles from a Web site, which your reader will then pull and show to you as an update. + +### Messaging + +> "I want to communicate directly with people." + +In some ways, messaging can be covered by having a publisher and a subscriber on each end of a conversation, but it usually makes more sense to deliver messages to your conversational partners directly rather than having them check for new messages on their own schedule. + +### Discussing + +> "I want to gather in a community or around a topic." + +Traditionally, this has been handled by forums, bulletin board systems, and groups. \ No newline at end of file diff --git a/content/tech/social.md b/content/tech/social.md index 3ca0ed9..4bcf80d 100644 --- a/content/tech/social.md +++ b/content/tech/social.md @@ -48,10 +48,10 @@ updated = "2021" - assuming public by default. no. let the user determine what they want to *opt in* to publishing, not what they want to *opt out* of having visible. by default, nothing should be known about the user. ->Copying and pasting made people look at what they shared, and think about it, at least for a moment. When the retweet button debuted, that friction diminished. Impulse superseded the at-least-minimal degree of thoughtfulness once baked into sharing. Before the retweet, Twitter was largely a convivial place. After, all hell broke loose — and spread. +> Copying and pasting made people look at what they shared, and think about it, at least for a moment. When the retweet button debuted, that friction diminished. Impulse superseded the at-least-minimal degree of thoughtfulness once baked into sharing. Before the retweet, Twitter was largely a convivial place. After, all hell broke loose — and spread. --[The Man Who Built The Retweet: “We Handed A Loaded Weapon To 4-Year-Olds”](https://www.buzzfeednews.com/article/alexkantrowitz/how-the-retweet-ruined-the-internet) ->[S]ocial media has been overtaken by metrics, which are driven in large part by the vicious cycle of advertisers wanting to know which influencers are worth paying; and by toxic fan battles to make your favorite social media accounts gain followers and likes, and to downrank your favorites' rivals. +> [S]ocial media has been overtaken by metrics, which are driven in large part by the vicious cycle of advertisers wanting to know which influencers are worth paying; and by toxic fan battles to make your favorite social media accounts gain followers and likes, and to downrank your favorites' rivals. --[Demetrification: improving social media by removing public like/follower/repost counts](https://boingboing.net/2019/09/12/flickrs-glory-days.html) \ No newline at end of file diff --git a/content/tech/spec/activitypub/!gotchas.md b/content/tech/spec/activitypub/!gotchas.md deleted file mode 100644 index a4de84e..0000000 --- a/content/tech/spec/activitypub/!gotchas.md +++ /dev/null @@ -1,85 +0,0 @@ -+++ -+++ -# Gotchas - -{{}} - -## DO NOT VALIDATE @CONTEXT IF YOU DO NOT UNDERSTAND JSON-LD - -you CANNOT check for the presence of `https://www.w3.org/ns/activitystreams` AT ALL -- the activitystreams context may be included within another context hosted elsewhere. just ignore this property entirely if you don't understand it - -## DON'T BE STRICT ABOUT VALIDATING ID - -do NOT try to be overly strict about dereferencing IDs. some IDs may not be on your domain, or otherwise they may not be present at all, or they may be explicitly null. null or missing id indicates a transient activity. - -### example: Follow semantics - -Follows are realistically transient. it is therefore enough to: - -- keep track of local state -- mutate state based on activities - -if you receive an Accept/Reject Follow, check ONLY for the following: - -- actor -- type == Accept/Reject -- object.actor -- object.type == Follow -- object.object == actor - -if object is inlined, you don't need to check that object.id is local. the above is enough information, PROVIDED THAT you have a local pending follow request. if you do not have a pending follow, then DO NOT process an incoming Accept Follow. however, you may receive a Reject Follow at any time, indicating that you should destroy that follow relationship. note that you may also receive an Undo Accept Follow by some implementations. this is discouraged but should be handled as well - - - -## DO NOT CHECK TYPES DURING VALIDATION - -an Actor has an `inbox` and `outbox`. that's it. - -an Activity has an `actor`. that's it. - -a Collection has `items` or `orderedItems`. that's it. - -etc - -## DON'T PANIC WHEN YOU SEE A TYPE YOU DON'T UNDERSTAND - -say you understand tags of type Mention and Hashtag and Emoji. someone sends you a `tag` array with a raw Link. DON'T PANIC. the document is still valid. just filter out anything you don't understand, something like - -```python -UNDERSTOOD_TAG_TYPES = set("Mention", "Hashtag", "Emoji") -document = json.loads(...) -tags = document.get('tag', []) -tags = [ - tag - for tag in tags - if tag['type'] in UNDERSTOOD_TAG_TYPES -] -# do whatever you need to now -``` - -```javascript -const UNDERSTOOD_TAG_TYPES = new Set(["Mention", "Hashtag", "Emoji"]) -let document = ... -tags = document.tag.filter(tag => tag.type in UNDERSTOOD_TAG_TYPES) -// do whatever you need to now -``` - -## DO NOT CONFUSE ITEMS AND ORDEREDITEMS - -`items` indicates that this is a Collection - -however, `orderedItems` is valid on both Collection and OrderedCollection! it is defined like this in the as2 context: - -```json -"orderedItems": { - "@id": "as:items", - "@type": "@id", - "@container": "@list" -} -``` - -so it is basically just an alias for `items` where it MUST be an array, and the array's order matters. (if it were `@container: set`, then it MUST be an array, but the array's order does not matter.) - -### tangent: a Collection may be ordered without being an OrderedCollection - -OrderedCollection is defined as strictly reverse chronological by ActivityPub. however, other orderings are valid on regular Collections. the use of the `orderedItems` term allows plain-JSON implementations to do exactly this. \ No newline at end of file diff --git a/content/tech/spec/activitypub/activity.md b/content/tech/spec/activitypub/Activity.md similarity index 100% rename from content/tech/spec/activitypub/activity.md rename to content/tech/spec/activitypub/Activity.md diff --git a/content/tech/spec/activitypub/collection.md b/content/tech/spec/activitypub/Collection.md similarity index 100% rename from content/tech/spec/activitypub/collection.md rename to content/tech/spec/activitypub/Collection.md diff --git a/content/tech/spec/activitypub/follow.md b/content/tech/spec/activitypub/Follow.md similarity index 100% rename from content/tech/spec/activitypub/follow.md rename to content/tech/spec/activitypub/Follow.md diff --git a/content/tech/spec/activitypub/!requirements.md b/content/tech/spec/activitypub/_requirements.md similarity index 100% rename from content/tech/spec/activitypub/!requirements.md rename to content/tech/spec/activitypub/_requirements.md diff --git a/content/app/_index.md b/content/tech/spec/activitypub/confusion/_index.md similarity index 100% rename from content/app/_index.md rename to content/tech/spec/activitypub/confusion/_index.md diff --git a/content/tech/spec/activitypub/!media-type.md b/content/tech/spec/activitypub/confusion/accept-content-type.md similarity index 100% rename from content/tech/spec/activitypub/!media-type.md rename to content/tech/spec/activitypub/confusion/accept-content-type.md diff --git a/content/tech/spec/activitypub/!note-vs-article.md b/content/tech/spec/activitypub/confusion/note-vs-article.md similarity index 100% rename from content/tech/spec/activitypub/!note-vs-article.md rename to content/tech/spec/activitypub/confusion/note-vs-article.md diff --git a/content/tech/spec/activitypub/extensions/Emoji.md b/content/tech/spec/activitypub/extensions/Emoji.md new file mode 100644 index 0000000..825175f --- /dev/null +++ b/content/tech/spec/activitypub/extensions/Emoji.md @@ -0,0 +1,85 @@ +### Emoji {#emoji} + +See for more: + +A sub-type of Link that refers to a `:custom_emoji:`, typically used for replacing a plain-text substring with an inline image (by implementations that do not otherwise support arbitrary inline images). + +#### Implementation details {#emoji-implementation} + +Handling an Emoji is typically done by matching for the Emoji's `name` and replacing with an `` element where `src` is the `url` of the Emoji's `icon`. + +

Note that this extension uses `icon` instead of `href`, but the same "search-and-replace" semantics apply, as with any microsyntax.

+ +Consider the following Note: + +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji" + } + ], + + "id": "https://example.com/@alice/hello-world", + "type": "Note", + "content": "

Hello world :kappa:

", + "tag": [ + { + "id": "https://example.com/emoji/123", + "type": "Emoji", + "name": ":kappa:", + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://example.com/files/kappa.png" + } + } + ] +} +``` + +- We extract the natural-language properties of `name`, `summary`, and `content`. In this case, we have only `content` of `Hello world :kappa:`. +- We extract any elements from `tag` with a type of `Emoji`. In this case, we have only one, for `:kappa:`. +- We search for `Emoji.name` (`:kappa:`) within the identified `content` (`Hello world :kappa:`) and replace it with an inline image sourced from `Emoji.icon.url` (`https://example.com/files/kappa.png`), resulting in the final value for content being `

Hello world

`. + +Pseudocode might look something like this: + +```py +# Extract custom emojis from tag array +tags = Object.tag +Emojis = [tag for tag in tags where tag.type == "Emoji"] + +for Emoji in Emojis: + + # replace :emoji: microsyntax with an inline image (within name) + name = Object.name # may need to extract `name` from `nameMap` instead + name_matches = regex.match(substring = Emoji.name, text = name) + for match in name_matches: + search_and_replace( + text = name, + search = Emoji.name, + replace = f'' + ) + + # replace :emoji: microsyntax with an inline image (within summary) + summary = Object.summary # may need to extract `summary` from `summaryMap` instead + summary_matches = regex.match(substring = Emoji.name, text = summary) + for match in summary_matches: + search_and_replace( + text = summary, + search = Emoji.name, + replace = f'' + ) + + # replace :emoji: microsyntax with an inline image (within content) + content = Object.content # may need to extract `content` from `contentMap` instead + content_matches = regex.match(substring = Emoji.name, text = content) + for match in content_matches: + search_and_replace( + text = content, + search = Emoji.name, + replace = f'' + ) +``` \ No newline at end of file diff --git a/content/tech/spec/activitypub/flag.md b/content/tech/spec/activitypub/extensions/Flag.md similarity index 100% rename from content/tech/spec/activitypub/flag.md rename to content/tech/spec/activitypub/extensions/Flag.md diff --git a/content/tech/spec/activitypub/extensions/Hashtag.md b/content/tech/spec/activitypub/extensions/Hashtag.md new file mode 100644 index 0000000..6ab6e41 --- /dev/null +++ b/content/tech/spec/activitypub/extensions/Hashtag.md @@ -0,0 +1,33 @@ +### Hashtag {#hashtag} + +A sub-type of Link that refers to a `#topic`, typically used for associating the object with a collection of other objects sharing the same topic. + +#### Implementation details {#hashtag-implementation} + +

Not officially part of the ActivityPub context definition, but still assumed to be included in the ActivityStreams `as:` namespace by most implementations (for historical reasons). Implementations should manually define `as:Hashtag` in their JSON-LD `@context`.

+ +The `href` typically links to a collection of all objects tagged with the same Hashtag. + +Consider the following Note: + +```json +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "Hashtag": "as:Hashtag" + } + ], + + "id": "https://example.com/@alice/hello-world", + "type": "Note", + "content": "

I am talking about a #topic

", + "tag": [ + { + "type": "Hashtag", + "name": "#topic", + "href": "https://example.com/tagged/topic" + } + ] +} +``` \ No newline at end of file diff --git a/content/tech/spec/activitypub/extensions/_index.md b/content/tech/spec/activitypub/extensions/_index.md new file mode 100644 index 0000000..f7ba421 --- /dev/null +++ b/content/tech/spec/activitypub/extensions/_index.md @@ -0,0 +1,2 @@ ++++ ++++ \ No newline at end of file diff --git a/content/tech/spec/activitypub/gotchas/_index.md b/content/tech/spec/activitypub/gotchas/_index.md new file mode 100644 index 0000000..f7ba421 --- /dev/null +++ b/content/tech/spec/activitypub/gotchas/_index.md @@ -0,0 +1,2 @@ ++++ ++++ \ No newline at end of file diff --git a/content/tech/spec/activitypub/gotchas/orderedItems.md b/content/tech/spec/activitypub/gotchas/orderedItems.md new file mode 100644 index 0000000..412d653 --- /dev/null +++ b/content/tech/spec/activitypub/gotchas/orderedItems.md @@ -0,0 +1,21 @@ +## DO NOT CONFUSE ITEMS AND ORDEREDITEMS + +`items` indicates that this is a Collection + +however, `orderedItems` is valid on both Collection and OrderedCollection! it is defined like this in the as2 context: + +```json +{ + "orderedItems": { + "@id": "as:items", + "@type": "@id", + "@container": "@list" + } +} +``` + +so it is basically just an alias for `items` where it MUST be an array, and the array's order matters. (if it were `@container: set`, then it MUST be an array, but the array's order does not matter.) + +### tangent: a Collection may be ordered without being an OrderedCollection + +OrderedCollection is defined as strictly reverse chronological by ActivityPub. however, other orderings are valid on regular Collections. the use of the `orderedItems` term allows plain-JSON implementations to do exactly this. \ No newline at end of file diff --git a/content/tech/spec/activitypub/gotchas/validating-@context.md b/content/tech/spec/activitypub/gotchas/validating-@context.md new file mode 100644 index 0000000..d8db9de --- /dev/null +++ b/content/tech/spec/activitypub/gotchas/validating-@context.md @@ -0,0 +1,3 @@ +## DO NOT VALIDATE @CONTEXT IF YOU DO NOT UNDERSTAND JSON-LD + +you CANNOT check for the presence of `https://www.w3.org/ns/activitystreams` AT ALL -- the activitystreams context may be included within another context hosted elsewhere. or it may be excluded (although it SHOULD NOT). just ignore this property entirely if you don't understand it \ No newline at end of file diff --git a/content/tech/spec/activitypub/gotchas/validating-id.md b/content/tech/spec/activitypub/gotchas/validating-id.md new file mode 100644 index 0000000..0a5f2a0 --- /dev/null +++ b/content/tech/spec/activitypub/gotchas/validating-id.md @@ -0,0 +1,22 @@ +## DON'T BE STRICT ABOUT VALIDATING ID + +do NOT try to be overly strict about dereferencing IDs. some IDs may not be on your domain, or otherwise they may not be present at all, or they may be explicitly null. null or missing id indicates a transient activity. + +### example: Follow semantics + +Follows are realistically transient. it is therefore enough to: + +- keep track of local state +- mutate state based on activities + +if you receive an Accept/Reject Follow, check ONLY for the following: + +- actor +- type == Accept/Reject +- object.actor +- object.type == Follow +- object.object == actor + +if object is inlined, you don't need to check that object.id is local. the above is enough information, PROVIDED THAT you have a local pending follow request. if you do not have a pending follow, then DO NOT process an incoming Accept Follow. however, you may receive a Reject Follow at any time, indicating that you should destroy that follow relationship. note that you may also receive an Undo Accept Follow by some implementations. this is discouraged but should be handled as well + + \ No newline at end of file diff --git a/content/tech/spec/activitypub/gotchas/validating-type.md b/content/tech/spec/activitypub/gotchas/validating-type.md new file mode 100644 index 0000000..690284a --- /dev/null +++ b/content/tech/spec/activitypub/gotchas/validating-type.md @@ -0,0 +1,32 @@ +## DO NOT CHECK TYPES DURING VALIDATION + +an Actor has an `inbox` and `outbox`. that's it. + +an Activity has an `actor`. that's it. + +a Collection has `items` or `orderedItems`. that's it. + +etc + +## DON'T PANIC WHEN YOU SEE A TYPE YOU DON'T UNDERSTAND + +say you understand tags of type Mention and Hashtag and Emoji. someone sends you a `tag` array with a raw Link. DON'T PANIC. the document is still valid. just filter out anything you don't understand, something like + +```python +UNDERSTOOD_TAG_TYPES = set("Mention", "Hashtag", "Emoji") +document = json.loads(...) +tags = document.get('tag', []) +tags = [ + tag + for tag in tags + if tag['type'] in UNDERSTOOD_TAG_TYPES +] +# do whatever you need to now +``` + +```javascript +const UNDERSTOOD_TAG_TYPES = new Set(["Mention", "Hashtag", "Emoji"]) +let document = ... +tags = document.tag.filter(tag => tag.type in UNDERSTOOD_TAG_TYPES) +// do whatever you need to now +``` \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/MAX_FOLLOWERS.md b/content/tech/spec/activitypub/ideas/MAX_FOLLOWERS.md new file mode 100644 index 0000000..22cc55e --- /dev/null +++ b/content/tech/spec/activitypub/ideas/MAX_FOLLOWERS.md @@ -0,0 +1,5 @@ +## max followers + +https://github.com/mastodon/mastodon/issues/20089 + +max followers? mastodon has a MAX_FOLLOWS and MAX_FOLLOW_RATIO so why not MAX_FOLLOWERS? \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/Message.md b/content/tech/spec/activitypub/ideas/Message.md new file mode 100644 index 0000000..d0c154b --- /dev/null +++ b/content/tech/spec/activitypub/ideas/Message.md @@ -0,0 +1,45 @@ +## Message type + +Message is an IntransitiveActivity? + +```yaml +id: https://alice.social/activities/1 +actor: https://alice.social +type: Message +to: https://bob.social +content: "hello bob" +``` + +representing rooms with context: + +```yaml +id: https://alice.social/activities/1 +actor: https://alice.social +type: Message +to: https://rooms.social/rooms/1/audience # inbox forwarding, probably +content: "hello room" +context: https://rooms.social/rooms/1 # verify inclusion how? +``` + +making the context into an actor: + +```yaml +id: https://alice.social/activities/1 +actor: https://alice.social +type: Message +to: # inbox forwarding, definitely + - https://rooms.social/rooms/1 # we need to deliver to the room actor + - https://rooms.social/rooms/1/followers # rooms.social or the room actor's client will forward to participants +content: "hello room" +context: https://rooms.social/rooms/1 +``` + +use of bto/bcc? + +```yaml +id: https://alice.social/activities/1 +actor: https://alice.social +type: Message +content: "hello room" # arguably only the content needs to be signed? no need for canonicalization +context: https://rooms.social/rooms/1 +``` \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/_index.md b/content/tech/spec/activitypub/ideas/_index.md new file mode 100644 index 0000000..f7ba421 --- /dev/null +++ b/content/tech/spec/activitypub/ideas/_index.md @@ -0,0 +1,2 @@ ++++ ++++ \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/do-not-inline.md b/content/tech/spec/activitypub/ideas/do-not-inline.md new file mode 100644 index 0000000..caa64bf --- /dev/null +++ b/content/tech/spec/activitypub/ideas/do-not-inline.md @@ -0,0 +1,11 @@ +### maybe objects shouldn't be inlined tho + +spritely magenc golem ocappub etc etc etc + +```yaml +id: alice.example/54078934249073290847321094/activity/1 +actor: alice.example +type: Create +object: alice.example/54078934249073290847321094 # how to fetch this? +cc: [alice.example/followers, Public] +``` \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/exclude-followers.md b/content/tech/spec/activitypub/ideas/exclude-followers.md new file mode 100644 index 0000000..09df4c2 --- /dev/null +++ b/content/tech/spec/activitypub/ideas/exclude-followers.md @@ -0,0 +1,13 @@ +## exclude your own followers? send to someone else's followers? send to a room? + +[tldr i should choose if i want my reply to go to my followers, or their followers, or just them, or all of the above, or whatever] + +1. alice makes a post and sends it to her followers or whatever. optionally it can be made available to the public? + +1. bob replies to alice, they send that to alice only, and *maybe* optionally to bob's followers. **this should be up to the user (bob).** + +1. alice can then choose to redistribute that reply. **this should be up to the recipient (alice)** and also **this should be made clear to the replier (bob)**. + +note that alice might allow bob's replies to be automatically redistributed, if alice trusts bob. + +- bob may not want his reply to be redistributed though? so there should be a way for bob to signal that bob is okay with alice forwarding bob's message to alice's followers. perhaps by explicitly addressing alice's followers? bob might also make his reply explicitly anonymous and transient by using id: null or not providing id. either way, **this should be an explicit decision** \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/jokes/_index.md b/content/tech/spec/activitypub/ideas/jokes/_index.md new file mode 100644 index 0000000..f7ba421 --- /dev/null +++ b/content/tech/spec/activitypub/ideas/jokes/_index.md @@ -0,0 +1,2 @@ ++++ ++++ \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/jokes/s2s-Block-reminder.md b/content/tech/spec/activitypub/ideas/jokes/s2s-Block-reminder.md new file mode 100644 index 0000000..90c12fc --- /dev/null +++ b/content/tech/spec/activitypub/ideas/jokes/s2s-Block-reminder.md @@ -0,0 +1,7 @@ +## Block/Mute reminders + +anytime someone muted tries to reply, you respond with an Ignore + +anytime someone blocked tries to interact, you respond with a Block + +what if you do this *every single time* they try to reply/interact \ No newline at end of file diff --git a/content/tech/spec/activitypub/ideas/jokes/s2s-Ignore.md b/content/tech/spec/activitypub/ideas/jokes/s2s-Ignore.md new file mode 100644 index 0000000..d4dc60e --- /dev/null +++ b/content/tech/spec/activitypub/ideas/jokes/s2s-Ignore.md @@ -0,0 +1,3 @@ +## Federated mutes + +as2 has an Ignore type so you could theoretically federate out mutes in the same way that blocks are currently federated \ No newline at end of file