activitypub-tutorial.txt 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. ActivityPub tutorial
  2. ====================
  3. ActivityPub is two things:
  4. - *A server to server federation protocol*
  5. (so decentralized websites can share information)
  6. - *A client to server protocol*
  7. (so users can communicate with ActivityPub using servers,
  8. from a phone or desktop or web application or whatever)
  9. ActivityPub implementations can implement just one of these things or
  10. both of them. However, once you've implemented one, it isn't too many
  11. steps to implement the other, and there are a lot of benefits to both
  12. (making your website part of the decentralized social web, and being
  13. able to use clients and client libraries that work across a wide variety
  14. of social websites).
  15. In ActivityPub, every actor (users are represented as "actors" here)
  16. has:
  17. - An INBOX: How they get messages from the world
  18. - An OUTBOX: How they send messages to others
  19. .--------.
  20. .--. | INBOX | <- receive messages
  21. ; o o; '--------'
  22. '. .'
  23. / \
  24. | | .--------.
  25. '----' | OUTBOX | -> send messages
  26. '--------'
  27. These are endpoints, or really, just URLs which are listed in the
  28. ActivityPub actor's ActivityStreams description. (More on
  29. ActivityStreams later.)
  30. Here's an example of the record of our friend Alyssa P. Hacker:
  31. {"@context": "https://www.w3.org/ns/activitystreams",
  32. "type": "Person",
  33. "id": "https://social.example/alyssa/",
  34. "name": "Alyssa P. Hacker",
  35. "preferredUsername": "alyssa",
  36. "summary": "Lisp enthusiast hailing from MIT",
  37. "inbox": "https://social.example/alyssa/inbox/",
  38. "outbox": "https://social.example/alyssa/outbox/",
  39. "followers": "https://social.example/alyssa/followers/",
  40. "following": "https://social.example/alyssa/following/",
  41. "likes": "https://social.example/alyssa/likes/"}
  42. ActivityStreams objects are JSON-LD documents. If you know what
  43. JSON-LD is, you can take advantage of the cool linked data approaches
  44. provided by JSON-LD. If you don't, don't worry, JSON-LD documents and
  45. ActivityStreams can be understood as plain old simple JSON. (If
  46. you're going to add extensions, that's the point at which JSON-LD
  47. really helps you out.)
  48. So, okay. Alyssa wants to talk to her friends, and her friends want
  49. to talk to her! Luckily these "inbox" and "outbox" things can help us
  50. out. They both behave differently for GET and POST. Let's see how
  51. that works:
  52. actor send messages
  53. reads incoming to actor
  54. messages (federation!)
  55. | |
  56. V V .---.
  57. .--------. .-. .' '.
  58. .--. .-[GET]--- | INBOX | <--[POST]--- .' '- ;
  59. ; o o; <-' '--------' .' '.
  60. '. .' ; REST OF THE ;
  61. / \ '. WORLD .-'
  62. | | --. .--------. '._ .'
  63. '----' '-[POST]-> | OUTBOX | ---[GET]---> '-__---_---'
  64. '--------'
  65. ^ ^
  66. | |
  67. actor sends outside
  68. messages / posts world can read
  69. content messages from actor
  70. Hey nice, so just as a recap:
  71. - You can POST to someone's inbox to send them a message
  72. (server-to-server / federation only... this *is* federation!)
  73. - You can GET from your inbox to read your latest messages
  74. (client-to-server; this is like reading your social
  75. network stream)
  76. - You can POST to your outbox to send messages to the world
  77. (client-to-server)
  78. - You can GET from someone's outbox to see what messages they've
  79. posted (or at least the ones you're authorized to see).
  80. (client-to-server and/or server-to-server)
  81. Of course, if that last one (GET'ing from someone's outbox) was the
  82. only way to see what people have sent, this wouldn't be a very
  83. efficient federation protocol! Indeed, federation happens usually by
  84. servers posting messages sent by actors to actors on other servers'
  85. inboxes.
  86. Let's see an example! Let's say Alyssa wants to catch up with her
  87. friend, Ben Bitdiddle. She lent him a book recently and she wants to
  88. make sure he returns it to her. Here's the message she composes, as
  89. an ActivityStreams object:
  90. {"@context": "https://www.w3.org/ns/activitystreams",
  91. "type": "Note",
  92. "to": ["https://chatty.example/ben/"],
  93. "attributedTo": "https://social.example/alyssa/",
  94. "content": "Say, did you finish reading that book I lent you?"}
  95. This is a note addressed to Ben. She POSTs it to her outbox.
  96. .--.
  97. ; o o; .---------.
  98. '. .' | ALYSSA'S|
  99. / \ ---[POST]--> | OUTBOX |
  100. | A | '---------'
  101. '----'
  102. Since this is a non-activity object, the server recognizes that this
  103. is an object being newly created, and does the courtesy of wrapping it
  104. in a Create activity. (Activities sent around in ActivityPub generally
  105. follow the pattern of some activity by some author being taken on some
  106. object. In this case the activity is a Create of a Note object,
  107. posted by a Person.)
  108. {"@context": "https://www.w3.org/ns/activitystreams",
  109. "type": "Create",
  110. "id": "https://social.example/alyssa/posts/a29a6843-9feb-4c74-a7f7-081b9c9201d3",
  111. "to": ["https://chatty.example/ben/"],
  112. "author": "https://social.example/alyssa/",
  113. "object": {"type": "Note",
  114. "id": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
  115. "attributedTo": "https://social.example/alyssa/",
  116. "to": ["https://chatty.example/ben/"],
  117. "content": "Say, did you finish reading that book I lent you?"}}
  118. Alyssa's server looks up Ben's ActivityStreams actor object, finds his
  119. inbox endpoint, and POST's her object to his inbox.
  120. .----------.
  121. | ALYSSA'S |'. .--.
  122. | SERVER | | .-------. ;o o ;
  123. |----------|.| | BEN'S | '. .'
  124. | ======== | | ---[POST]-----> | INBOX | / \
  125. | ======== | | '-------' | B |
  126. | ======== | | '----'
  127. | |.;
  128. '----------'
  129. Technically these are two separate steps... one is client to server
  130. communication, and one is server to server communication (federation).
  131. But, since we're using them both in this example, we can abstractly
  132. think of this as being a streamlined submission from outbox to inbox:
  133. .--. .--.
  134. ; o o; .---------. .-------. ;o o ;
  135. '. .' | ALYSSA'S| | BEN'S | '. .'
  136. / \ | OUTBOX | --------> | INBOX | / \
  137. | A | '---------' '-------' | B |
  138. '----' '----'
  139. Cool! A while later, Alyssa checks what new messages she's gotten.
  140. Her phone polls her inbox via GET, and amongst a bunch of cat videos
  141. posted by friends and photos of her nephew posted her sister, she sees
  142. the following:
  143. {"@context": "https://www.w3.org/ns/activitystreams",
  144. "type": "Create",
  145. "id": "https://chatty.example/ben/p/51086",
  146. "to": ["https://social.example/alyssa/"],
  147. "inReplyTo": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
  148. "author": "https://chatty.example/ben/",
  149. "object": {"type": "Note",
  150. "id": "https://chatty.example/ben/p/51085",
  151. "attributedTo": "https://chatty.example/ben/",
  152. "to": ["https://social.example/alyssa/"],
  153. "content": "<p>Argh, yeah, sorry, I'll get it back to you tomorrow.</p>
  154. <p>I was reviewing the section on register machines,
  155. since it's been a while since I wrote one.</p>"}}
  156. Alyssa is relieved, and likes Ben's post:
  157. {"@context": "https://www.w3.org/ns/activitystreams",
  158. "type": "Like",
  159. "id": "https://social.example/alyssa/posts/5312e10e-5110-42e5-a09b-934882b3ecec",
  160. "to": ["https://chatty.example/ben/"],
  161. "author": "https://social.example/alyssa/",
  162. "object": "https://chatty.example/ben/p/51086"}
  163. She POSTs this message to her outbox. (Since it's an activity, her
  164. server knows it doesn't need to wrap it in a Create object.)
  165. Feeling happy about things, she decides to post a public message to
  166. her followers. Soon the following message is blasted to all the
  167. members of her followers collection, and since it has the special
  168. Public group addressed, is generally readable by anyone.
  169. {"@context": "https://www.w3.org/ns/activitystreams",
  170. "type": "Create",
  171. "id": "https://social.example/alyssa/posts/9282e9cc-14d0-42b3-a758-d6aeca6c876b",
  172. "to": ["https://social.example/alyssa/followers/",
  173. "https://www.w3.org/ns/activitystreams#Public"],
  174. "author": "https://social.example/alyssa/",
  175. "object": {"type": "Note",
  176. "id": "https://social.example/alyssa/posts/d18c55d4-8a63-4181-9745-4e6cf7938fa1",
  177. "attributedTo": "https://social.example/alyssa/",
  178. "to": ["https://social.example/alyssa/followers/",
  179. "https://www.w3.org/ns/activitystreams#Public"],
  180. "content": "Lending books to friends is nice. Getting them back is even nicer! :)"}}