As mentioned in Blog Post URIs, I'm a bit concerned about people replying to the 'wrong' URI for a post. That is, using the URL of a HTML page with the post rendered within it, rather than the post's permalink. I couldn't find a Microformats2 term for permalink, which I thought would be the obvious solution. The webmention 'protocol' says that when I recieve a webmention: Check the target is a valid URI on my site after following redirects. In my case, upon recieving a target of a https://rhiaro.co.uk/yyyy/mm/id, to validate it, rather than 'following a redirect' I'll actually be getting it's foaf:topic from the triplestore, which is fine. But in accepting this as the target I'm confirming to the sender that this is the correct URI to reply to. Hypothetically this non-permalink could break, breaking the reply. The permalink URI is one I'm promising won't break, so it's in everybody's interests to send the reply there. But logically, if somebody is linking to me in a post of their own, they're going to want to link to the rendered version, rather than whatever my raw version will look like (probably crappy, but that's on me, and maybe part of the problem). Replying to shortlinks Because I'm a linked data nut, my problem derives from a deliberate differentiation between a blog post and a page about a blog post. The connection between them (or from one to the other) can be discovered through RDF, but there is no HTTP redirection. I suspect this is a very much minority use-case for #indieweb folks. However, use of shortlinks is widespread (and you can see redirects are accounted for in the webmention protocol), and so as soon as someone's URL shortner breaks (or moves) permanently, unless they store the post contents and metadata themselves, they might have to have to think about this a bit. Solution? I'd like to be able to send back the correct URI (303 redirect style) so the sender can either: try again with the correct URI, or update their system with the correct URI, knowing I've accepted the webmention against the correct URI on my end (more like a (2NN)[http://tools.ietf.org/html/draft-prudhommeaux-http-status-2nn-00] than a 303). I haven't looked at any implementations yet or thought about how to do it, so maybe I can just do this. I'm not sure how to anticpate people handling it. Straw poll: What do you/your system do if you send a webmention and it's rejected, though you're sure the target is valid? And if you're going to send your reply as a webmention from your own system, please target this post's permalink - the URI without a year and a month in :)
I've been thinking about adopting a polyphasic sleep cycle for some time. I really just want more hours in the day. In theory I like mornings, but I usually struggle to get up. When I do, it's very rewarding. Usually I'll drag myself out of bed an hour before the first thing I have to be at (meeting, seminar), which could be anywhere between 0830 and early afternoon. If I don't have anything specificially scheduled, I won't set an alarm, figuring I trust my body to wake up when it's ready. When I do this, I usually sleep a solid 10 hours and feel pretty shitty when I wake up. Regardless of when I get up, I start to crash around 1500. By crash, I mean suddely find myself staring, zombielike, at Buzzfeed and Facebook and stuck in a constant cycle of clicking mindless linkbate and scrolling through social feeds. Ugh. If I catch myself, I'll probably call it a day and go to do something physical like make food or clean. Sometimes (usually only possible if I have some decent food in my office) instead I push through the zombie and hit a second wave of productivity in the early evening. I start to wake up, feel more alert, and can get sucked into coding or researching something until the early hours of the morning. That feels great, but sometimes I have to make myself stop work before midnight so I can go home and get enough sleep to be awake for my first scheduled thing the next day. Sometimes I fail at stopping, only get a few hours sleep and feel like shit the next day. Occasionally I'll find time to take a nap if I'm feeling completely out of it, but naps often overrun by many hours, and then I hate myself for 'wasting' even more time asleep. So I want to figure something out that lets me fit in my nighttime productivity, and see the sunlight, and not oversleep or undersleep. Setting a rigid timetable for sleep and naps will help with this, I think. If I know I'm trying to be consistent, and naps are purposeful and scheduled, my willpower should kick in get me up when I need to be. In theory, anyway. I know these things look much different in the cold hard light of day[*][#cold-hard-light] than they do from a warm, comfy bed... just 5 more minutes... I'm aware that transitioning to polyphasic is going to be a nightmare, but if I keep putting it off until I don't have anything important to do, I'll never do it. Right now, I know I'm not travelling until at least March, so I've got time to adjust on my own schedule (and figure out how to handle conferences when I come to it). I've pulled all-nighters and had remarkably productive periods of sleep deprivation before, so I figure I'll cope. Oh, to be young. I read a lot of people have problems with actually being able to fall asleep for naps, but I have this superpower whereby I can go to sleep easily on any surface, in any situation, with any background noise, so I'm not worried about that. The schedule Turns out the reason some people can get away with fewer than eight hours is because there are different types of sleep, and not all of them are necessary to being alert when you're awake. So the 'science' is to cut out the useless sleep and train your body to fall straight into useful sleep. This is possible by paying attention to your natural circadian and ultradian rhythms, and reading websites by people who have already figured this stuff out. Sleep cycles in 1.5 hour blocks for most people, and the useful types are SWS (Slow Wave Sleep) and REM (Rapid Eye Movement). Knowing when and how long for these are likely to occur, over the course of a 24 hour period and in relation to each other, can help you to time your sleep sessions to maximise them. The Everyman 3 schedule consists of 4.5 hours sleep broken into a 'core' 3.5 hours, plus three 20 minute naps at approximately 4-6 hour intervals. I read that SWS happens best around dusk, so the core sleep should be around 2100 to 0030, and naps (when you get your REM) at 0410, 0810 and 1440. This is flexible of course, and once you've forced yourself into a pattern you can adjust by listening to your body (eg. move your nap an hour earlier or later if you're consistently tired/awake at certain times). It also needs to be adapted into the schedule of daily life. There are versions pre-planned for people with 9 to 5 jobs, but my schedule can be pretty irregular. There are certain things I have to do on certain days at different times. I think the day I'm going to have the most problem fitting around is Friday, when I have a 0930 seminar. Anyway, term hasn't started yet so most things aren't kicking off for another one to two weeks, so I have time to figure it out. So for now, I've put the Everyman 3 schedule in my calendar, and will ease into it. I might make my core sleep longer to start with, depending on how I feel when I wake up after 3.5 hours, and maybe have 40 minute naps instead of 20. I'm probably also going to shift the schedule mentioned above later by 2 hours, as I already know I won't be able to go to bed at 2100 any night. Logging For now I'm going to dump my sleep sessions into [this google calendar](https://www.google.com/calendar/embed?src=s2nceddftg5vclefkgdoc9is6o%40group.calendar.google.com&ctz=Etc/GMT ) for quick reference alongside the rest of my schedule, but I'll also start tracking through this site at /llog when things are up and running properly. Other stuff Diet and exercise are obviously important, and have an impact on how well one can sleep. Part of altering your sleep cycle is retraining your body to do things like digestion at different times. Sensible advice I've read so far seems to be to have a main meal at least two hours before the core sleep, and use breakfast to train your body to expect to be awake. Also not to exercise too close to core sleep, but exercising or eating right before naps is okay. * Metaphorical cold hard light of day. Most of these decisions I've made around 4am during a Scottish winter, so I have to use my imagination.
# Microformats2 RDF Ontology I'm serializing the [microformats2](http://microformats.org/wiki/microformats2) vocabulary into RDF; [repo on github](https://github.com/rhiaro/mf2rdf). **Disclaimer:** This post is (not necessarily coherant) notes as I go along. Useful notes by [Tom Morris](https://tommorris.org/) about [mf2 to RDF mapping on the microformats wiki](http://microformats.org/wiki/microformats2-parsing-rdf). I updated the namespace to use the one suggested there (`http://microformats.org/profile/`). Everything starting with `h-` looks like it's a class. Typically RDF classes are capitalised so this makes me cringe, but I'll cope. Woah, there are a lot of properties. Seventy unique properties. I didn't realise. On the wiki most don't have a description, many are duplicated in the list, and none of them seem to have their own description page (and thus no URIs to refer to them with). Pasting them into a spreadsheet was the quickest way to de-dup and alphabetise them. Now I'm working through one at a time, adding descriptions and mapping them to existing properties in [FOAF](http://xmlns.com/foaf/0.1/) (because many are obvious), [VCard](http://www.w3.org/2006/vcard/ns#) (because many (all?) are derived from this), [ActivityStreams 2.0](http://rawgit.com/jasnell/w3c-socialwg-activitystreams/extended-vocabulary/activitystreams2-vocabulary.html) (because SocialWebWG) and hesitantly [Dublin Core](http://purl.org/dc/elements/1.1/) and [SIOC](http://rdfs.org/sioc/types#). Where they map to something well-described in RDF, I don't bother with `rdfs:comment` and `domain` and `range` of their own, but I'll add for those with no good `sameAs` mappings. * Is `dt-reviewed` same as `dt-published`? If not, why do reviews get special treatment with regards to differentiation between when they're written and published, and nothing else does? * Is there an official definition of `e-content`? Couldn't find one on the wiki, so made one up that is compatible with AS2.0 content, with additional caveat about including markup, which is explicit on the mf2 wiki: "The contents of something like a post, including markup and embedded elements." * Are `e-description` and `e-instructions` sub-properties of `e-content`? I keep getting caught out by AS2.0 terms that are in the JSON-LD document but are actually deprecated (the JSON-LD contains no information other than property and class names and sometimes types; I have to remember to check the written docs). Eg. `as2:author` -> `as2:attributedTo`. * I couldn't find an AS2.0 equivalent property for `category`. Am I missing something? Oh wait, `tag` will do it. * How does `p-description` from `p-summary`? * Wiki says `p-education` should contain a nested `h-card` (of school and location). Doesn't that mean it should be an `e-` or have I misunderstood that completely? * It also says `p-education` is "an education `h-calendar` event" but `h-calendar` doesn't exist, it's `h-event`. Probably just a typo? Ditto `p-experience`. * ActivityStreams2.0 doesn't have a `name` property for `Actors` (only `displayName`). Interesting. * Wiki says `label` 'new in vCard4' but vCard4 says it's deprecated, and in any case was for address labels, so not sure what use this is in mf2. * What's `p-note` for specifically? * Why is `p-reviewer` not just `p-author`? * Maybe I just don't know how to read the vCard4 spec, but the mf2 wiki says `sex` and `gender-identity` are part of it, but all I can see is `hasGender`. AS2.0 has `rating` but it's explicitly defined as "a non-negative decimal number between 0.0 and 5.0 (inclusive) with one decimal place of precision". MF2 says it's a number between 1 and 5. I think even less specification is better, as ratings come in come in many forms. Anyway, because I'm not venturing into SKOS, I'm `owl:sameAs`ing them for the time being. **Update:** Dropped all domains and ranges because all properties in microformats2 are actually global.
I'm serializing the microformats2 vocabulary into RDF; repo on github. Disclaimer: This post is (not necessarily coherant) notes as I go along. Useful notes by Tom Morris about mf2 to RDF mapping on the microformats wiki. I updated the namespace to use the one suggested there (http://microformats.org/profile/). Everything starting with h- looks like it's a class. Typically RDF classes are capitalised so this makes me cringe, but I'll cope. Woah, there are a lot of properties. Seventy unique properties. I didn't realise. On the wiki most don't have a description, many are duplicated in the list, and none of them seem to have their own description page (and thus no URIs to refer to them with). Pasting them into a spreadsheet was the quickest way to de-dup and alphabetise them. Now I'm working through one at a time, adding descriptions and mapping them to existing properties in FOAF (because many are obvious), VCard (because many (all?) are derived from this), ActivityStreams 2.0 (because SocialWebWG) and hesitantly Dublin Core and SIOC. Where they map to something well-described in RDF, I don't bother with rdfs:comment and domain and range of their own, but I'll add for those with no good sameAs mappings. Is dt-reviewed same as dt-published? If not, why do reviews get special treatment with regards to differentiation between when they're written and published, and nothing else does? Is there an official definition of e-content? Couldn't find one on the wiki, so made one up that is compatible with AS2.0 content, with additional caveat about including markup, which is explicit on the mf2 wiki: "The contents of something like a post, including markup and embedded elements." Are e-description and e-instructions sub-properties of e-content? I keep getting caught out by AS2.0 terms that are in the JSON-LD document but are actually deprecated (the JSON-LD contains no information other than property and class names and sometimes types; I have to remember to check the written docs). Eg. as2:author -> as2:attributedTo. I couldn't find an AS2.0 equivalent property for category. Am I missing something? Oh wait, tag will do it. How does p-description from p-summary? Wiki says p-education should contain a nested h-card (of school and location). Doesn't that mean it should be an e- or have I misunderstood that completely? It also says p-education is "an education h-calendar event" but h-calendar doesn't exist, it's h-event. Probably just a typo? Ditto p-experience. ActivityStreams2.0 doesn't have a name property for Actors (only displayName). Interesting. Wiki says label 'new in vCard4' but vCard4 says it's deprecated, and in any case was for address labels, so not sure what use this is in mf2. What's p-note for specifically? Why is p-reviewer not just p-author? Maybe I just don't know how to read the vCard4 spec, but the mf2 wiki says sex and gender-identity are part of it, but all I can see is hasGender. AS2.0 has rating but it's explicitly defined as "a non-negative decimal number between 0.0 and 5.0 (inclusive) with one decimal place of precision". MF2 says it's a number between 1 and 5. I think even less specification is better, as ratings come in come in many forms. Anyway, because I'm not venturing into SKOS, I'm owl:sameAsing them for the time being. Update: Dropped all domains and ranges because all properties in microformats2 are actually global.
Making parrot toys out of cardboard, twine, and... mostly parts of old, dismantled parrot toys.
Amy wrote about http://vocab.amy.so/blog#Doing, http://vocab.amy.so/blog#Doing, mbaking, parrot, & Tigo
Finally you can get my blog post content in a number of different formats. You'll notice alternative URLs (noted currently as 'Permalink') in the metadata section of a normal view of a post. Hit this up with no Accept header, and you'll get neat markdown (which is what I authored it in; the 'source', as it were). Browsers automatically send Accept: text/html, in which case they are redirected to the rendered HTML version of the post you're probably reading now. But here are some alternatives you can try: curl https://rhiaro.co.uk/llog/micropub-test -> plain markdown. curl -H "Accept: application/json" https://rhiaro.co.uk/llog/micropub-test because there's obviously not enough JSON in the world. Note this isn't correct JSON-LD yet. I'll sort that out another time. curl -H "Accept: text/turtle" https://rhiaro.co.uk/llog/micropub-test Ohemgee! RDF! It's what you've all been waiting for, I know. For those of you who like angular brackets, you could try: curl -H "Accept: rdf/xml" https://rhiaro.co.uk/llog/micropub-test Relevance to micropub Briefly discussed with Tantek, Aaron and Bret at IWC Cambridge and on IRC about how a micropub client could fetch post content for editing. Many people aren't editing raw HTML as it's presented on the page, but maybe markdown or some other syntax, so a client needs to be able to discover this (the 'source') to present it to be edited. Now if someone makes their client check a post for a rel="source" or shoots off a request with Accept: text/plain (less likely, since static sites can't do conneg) then they'll get my markdown directly (uh, when I actually put rel="source" in my HTML, which I haven't yet). As a related aside, I also return source="markdown" if a micropub client asks my endpoint q=source.
Just listening to / reading discussions and debates in W3C groups is tiring; taking part even a tiny bit is exhausting, and I haven't decided if the reward is worth the brain-melt yet. I hope so. I admire all these people who manage to do standards work full time.
Amy wrote about http://vocab.amy.so/blog#Doing, http://vocab.amy.so/blog#Doing, socialwg, w3c, standards, & working group
I meant to watch House of Cards this evening, but I accidentally implemented likes, reposts, bookmarks and proper display of replies on my site.
Amy wrote about http://vocab.amy.so/blog#Doing, http://vocab.amy.so/blog#Doing, hacking, indieweb, & slogd
If you can see this, my micropub endpoint works :D And if you can see this, I can also edit posts.
Amy wrote about http://vocab.amy.so/blog#Doing, http://vocab.amy.so/blog#Doing, hacking, indieweb, test, & micropub
"Micropub is an open API standard that is used to create posts on one's own domain using third-party clients. Web apps and native apps (e.g. iPhone, Android) can use Micropub to post short notes, photos, events or other posts to your own site, similar to a Twitter client posting to Twitter.com." (Micropub on Indiewebcamp) This means I can now publish blog posts to my site using other peoples' posting clients, of which there are several. Previously, I was ftping markdown files to my server, then running a slightly unreliable PHP script to process the queue based on when the files were last updated. Which mostly worked, but when it didn't it was a pain to untangle. IndieAuth I wasn't expecting to have a micropub endpoint any time soon because it needs you to be able to sign into your site with IndieAuth. Fortunately, thanks to amazing work of aaronpk and others, most of the legwork has been done here, and you can delegate your IndieAuth; in your site : Micropub Then create a micropub script (eg. micropub.php) and make it discoverable in the same way: Initially I wanted my endpoint at https://rhiaro.co.uk/micropub and set an .htaccess rule to redirect to the PHP script. This doesn't work; POST requests can't follow redirects. Next, here's a bare minimum PHP script to store things posted to your site with a micropub client. This just dumps the POST request to a text file, which is where I started to see if it would work. What I actually do now is turn posts and metadata into triples and insert them into my triplestore, but you probably don't want to hear about that :) <? // Tell client where I can syndicate to if(isset($_GET['q']) && $_GET['q'] == "syndicate-to"){ header('Content-type: application/x-www-form-urlencoded'); echo "syndicate-to[]=twitter.com%2Frhiaro"; exit; } // Check for post if(!empty($_POST)){ $headers = apache_request_headers(); // Check token is valid $token = $headers['Authorization']; $ch = curl_init("https://tokens.indieauth.com/token"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, Array( "Content-Type: application/x-www-form-urlencoded" ,"Authorization: $token" )); $response = Array(); parse_str(curl_exec($ch), $response); curl_close($ch); // Check for scope=post // Check for me=https://rhiaro.co.uk $me = $response['me']; $iss = $response['issued_by']; $client = $response['client_id']; $scope = $response['scope']; if(empty($response)){ header("HTTP/1.1 401 Unauthorized"); exit; }elseif($me != "https://rhiaro.co.uk" || $scope != "post"){ header("HTTP/1.1 403 Forbidden"); exit; // Check that something was posted }elseif(empty($_POST['content']){ header("HTTP/1.1 400 Bad Request"); echo "Missing content"; }else{ // DO YOUR THING HERE // ie. insert post content and metadata into your store, write it to a file, whatever you do to add it to your site. // For demonstration purposes, let's dump the POST request into a file and return the URL of the file. $fn = "posts/".time().".txt"; $h = fopen($fn, 'w'); foreach($_POST as $k => $v){ $data .= "[$k] => $v "; } fwrite($h, $data); fclose($h); // Set headers, return location header("HTTP/1.1 201 Created"); header("Location: ".$fn); } } ?> I got this far by working through the instructions as part of setting up Quill, so that's a good place to start!
Recently I've been trying to wrap my head around the implicit vs. explicit post types debate (in the Social Web WG, indieweb community, and elsewhere). Here is some analysis of the different perspectives, and report on my own experiences so far. tl;dr I think each side of the debate about explicit vs implicit types are misaligned, due to conflation of Objects and Activities. I think Objects (like blogposts) needn't have explicit types, but Activities (like the act of creating a post) should do. Thanks to Tantek, Aaron, elf Pavlik, Chris and everyone whose conversations I've listened in on in #social and #indiewebcamp IRC channels for discussing this stuff with/around me. Indieweb: focus on UI Indieweb development starts with thinking about UI. The reason for thinking about different kinds of posts comes from a desire to display some posts differently to others. But rather than declaring them outright, one can infer how to display them based on the properties of a post (see also IndieWeb Posts by aaronpk. "explicit post/object types are architecture-centric design, rather than user-centric design" - tantek, #indiewebcamp irc Posting UI Another rationale against explicit post types is that it encourages posting clients to constrain users regarding what kind of posts they can create. "...content types dictate the kinds of content you actually allow people to create with your software" - benwerd, The tyranny of content types Most mainstream social networks now have a generic posting interface, that allows one to add things like photos, videos, people tags, etc, without forcing you to choose what type of post you want to make first. Software architecture Some people started with explicit post types and ran into problems when structuring their software around this idea. TODO: Citation from aaronpk in irc in March/April that I can't find right now. URL design If you include a slug for a post type in your URLs and decide to change the post type, this is definitely a pain. I figured this pretty early and dropped it from Slog'd. See also: Update URL Scheme issue for p3k. On the other hand: "but including type [in URL] because I like clustering by type in a day" - tantek, #indiewebcamp irc ... which I think relates to querying, discussed later. Clarification: content-based vs intent-based post types Often photo-type-post vs article-type-post is thrown up: ie. if you start with an article, then edit it to add a photo, does the post type change? I completely agree that typing based on content doesn't make sense here. When I talk about post types, I'm referring to types for like, bookmark, repost, log/metrics (eg. food, sleep), where they may look like a note in terms of content, but aren't. I display different icons for all of these at the moment, and you'll note that twitter for example displays retweets differently in the timeline. Post all the things Conclusion: in the indieweb community, so far, everything created is considered a post (a h-entry in microformats), and display differences can be inferred from properties of the post. an article is a post with a title. a like is a post with a like-of. a reply is a post with in-reply-to. etc. ActivityStreams2.0 ActivityStreams2.0 has explicit object types and explicit activity types. It's easy to conflate them; I think this is what is happening when discussing types in AS2 with indieweb people, and I certainly had this problem for a while. Taking an action generates an Activity, which might result in an Object (like a post). Beyond posts Contrary to indieweb stuff so far, AS2 allows for the idea that not every action on the social web results in a post. Having been immersed in indieweblogworld for ages, this was briefly a hard sell, but then I started to think of social actions (activites) I might want to take - that it's reasonable to want to publish in a feed - that I probably don't need to create a post for. Eg: edit a post (or a wiki page) git commits follow someone reading/viewing/watching something blocking someone / reporting content joining or leaving a group I don't publish any of these activities on my site yet. There are some activities I do publish with posts (or plan to imminently), that don't necessarily (I might need convincing on some fronts) need to be posts: likes reposts bookmarks eating, drinking, sleeping (See Llog). exercise, specifically running and yoga listening to music acquiring physical objects arriving at a destination (aka checkin) Activities vs objects An Activity can be generated for any action. Publishing a blogpost could generate in a Create activity, for which the object is the post created. Bookmarking a page could generate a Save activity, for which the object is the page and the result is a post about my having bookmarked the page. Editing a post could generate a Update activity for which the object is the post being edited, but there is no new post as a result. Liking a post could generate a Like activity for which the object is the post being liked, but there is no new post as a result (if you're into that sort of thing, so far I'm not). I strongly feel that all Activity should have URIs so that if it doesn't result in a post, it can still be interacted with (eg. you can like my edit). (However, this presents another point for confusion: when do I like a post, and when do I like the fact you published it (the create activity). Anyway, this is for another time..). Since activities are generated as a result of an action, not explicitly created by a user (unlike objects), arguments for not explicitly typing them fall down: you're unlikely to need to edit an activity, causing a need to change its type. post creation UIs have nothing to do with this. Instead, UIs for interacting with posts such as webactions - but typing in these seems to be acceptable. If you want to generate an activity stream from your actions, we do get an additional argument for explicit types of objects: it makes it much easier to generate a suitable Activity. Querying Explicit types for objects or activities make it easy to query by type. Inferring types - particularly from a combination of properties - complicates this a lot. I recently temporarily dropped quicklinks to /articles and /notes in Slogd after removing sioc:BlogPost and sioc:MicroblogPost as explicit post types made it suddenly more complicated to get them. Given a stream of many people's activities from all over the place how do you filter for specific things? Human-friendly display Displaying an activity (the fact that someone did something) is different to displaying an object. You might want to form a sentence ("alice likes bob's tweet") without caring what the properties of the object or possible result are. It's easier to figure out how to display it if it just has a type, without needing to follow links to the object or possible result to infer what's going on. Generating activity streams I guess... If you want to take a bunch of objects and generate an activity stream from them, and they don't have explicit types, it's totally up to you (an implementation detail, aka plumbing) how you decide what types the corresponding Activitys have. I'm not sure if it would be nice to have something in the AS2.0 spec to help with consistently deciding how to infer activity types from object properties. Maybe it doesn't matter. ...Wait, is this ISSUE-4? Lack of explicit mention of activities or objects makes it hard to tell. Conclusions Every creation of a post should (can) generate an Activity. Not every Activity-generating action needs to result in a post, but it's cool if it does. Activitys need types. Objects (like posts) do not (necessarily). If you don't want to generate a stream of all of your possible actions, but rather a feed of objects (as appears to be the case for indiewebbers so far) then you don't need activities, and this conversation is basically irrelevant... but if you think someone else might generate an activity stream from your objects, you're at their mercy regarding Activity types. Next? I should probably generate an AS2.0 feed from my posts. Start integrating non-post-resulting activities into it. Other notes I went through the lists of Activity types and Object types in current draft of AS2.0 vocab and did a first pass at things I am using, might use and probably won't use. I'll publish this in another post. I drew lots of diagrams of how I'd turn my different types of posts into Activitys based on their properties (how I'd infer activity types from object properties). I'll also type these up for another post. Activity is a subclass of Object in AS2. Practical and conceptual differences could be discussed in more depth, but include things like ephemerality of activities vs objects. Further discussion (possibly): https://lists.w3.org/Archives/Public/public-socialweb/2015Apr/0048.html
Testing a post with a location, although lat/long isn't displayed yet, so you won't see it. (Yet).
Amy wrote about http://vocab.amy.so/blog#Doing, http://vocab.amy.so/blog#Doing, hacking, indieweb, test, micropub, & location
# Generating an ActivityStream ## Part one: posts-for-all-the-things If you've been paying attention to me IRL or in IRC lately, you'll have had me try to persuade you that activities and posts are basically the same thing. People advocating post-centric views and people advocating activity-centric views are really describing the same thing from different angles, and with slightly different data models. An activity looks generally like: ![Activity structure](https://rhiaro.co.uk/posts/201505activitystream/activity_blank.png) And a post looks generally like: ![Post structure](https://rhiaro.co.uk/posts/201505activitystream/post_blank.png) Colour coding indicates what correlates between the two. Differences: | | Activity | Post | | ---------------------------------- | ---------------------------------- | --------------------------- | | **type** | explicit | implied by properties | | **relationship to another object** | `object` | value of arbitrary property | | **meta** | some on activity, some on `result` | all on post | But you can see that the data they contain are basically the same. Bear with me. ### What are activities for? To propagate changes through a network. To tell people that somebody did something. ### What are posts for? Posts are content, an end in themselves. ### Who cares? As someone who just wants to publish stuff and interact on the web, do I care if I'm creating and pushing around posts, or activities? Probably not. I just want to do stuff, and have people see it. As a developer, I want to post objects around that contain as much data as needed, and no more. I want the right people to know when something has changed. Whether I prefer to do this with activities or posts (or JSON or HTML) is, as far as I see it, personal preference. I don't think there's an inherant advantage to one over the other. ### So which to start with? Starting with a blog perspective, the most important thing is that people see my content. Posts are my content, so I start there. * I create a post, which are objects with various properties. * If I want to explicitly notify someone of a post - because I'm replying to them, favouriting, bookmarking, sharing, tagging, etc, I send them a [webmention](http://indiewebcamp.com/webmention). * The reciever is then free to act upon this as they like: display my post on their site, update their own internal friends list, add my post to a collection, ignore it completely, or whatever. * If I want to make a general announcement that something has changed on my site (eg. a new post) I either use PuSH and the hub lets subscribers know, or I don't do anything, and wait for interested parties to pull (visit my homepage, or open their reader) and find out for themselves. ![A new post](https://rhiaro.co.uk/posts/201505activitystream/post_create.png) For humans, the posts are displayed in html, and for machines they're marked up with minimal microformats, which allows a reader such as [Woodwind](http://reader.kylewm.com/) to parse out the relevant information and display however necessary. (Mine are also avaiable as RDF, but to the best of my knowledge nobody has built a blog reader that consumes RDF yet). In the ActivityStreams model, rather than creating posts directly, the user performs some *activity* which may generate as a side-effect a post. The activty could be something like `Post`, `Share`, `Like` (with many other terms currently in the vocab), with an optional `target` of an object being acted upon, and an optional `result` of a new object being created. Upon creation, the *activity* is sent by the server to the servers of anyone who is expected to receive it - either a default list such as people who have subscribed to the author, or a specific list of other people specified when the activity is created, or anyone mentioned or replied-to etc. Anyone for whom the activity ends up on their inbox might get a notification, and their server can fetch the associated post for them. ![A new create activity](https://rhiaro.co.uk/posts/201505activitystream/activity_create.png) Not all activities generate a `result` post and some activities generate a `result` that is the same as the `object`. In these cases, I would argue that an activity == a post... Currently, when I like something I create a like post, that looks like this: ![Like post](https://rhiaro.co.uk/posts/201505activitystream/post_like.png) A like *activity* looks like this: ![Like activity](https://rhiaro.co.uk/posts/201505activitystream/activity_like.png) See how similar they are?! So given this apparent direct mapping between posts and activities... ### Posts as activities Dreaming of interoperability with [ActivityPump](http://w3c-social.github.io/activitypump/) implementations, I want to generate AS2.0 compliant activities that I can send out. I stuck to super basic representations for now, nesting as little as possible (figuring everything with a URI can be dereferenced anyway, so all of the data doesn't need to be present in the first layer of JSON). So for a new post such as: ``` Post title This is a post! 15th May 2015 13:06 Amy Guy ``` The activity generated looks like: ``` { "@type": "Post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://rhiaro.co.uk/2015/05/a-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/a-post" } } ``` With no explicit audience specified, an ActivityPump compliant server would post this to the inboxes of all of my followers. *Their* server would see the new activity and insert it into their feed; they could click it and view the object (the post). Note that the `object` of the activity and the `result` here are the same, so there's some redundancy. For a like post - because I start with posts, I keep the `result` of the activity as the like post and retain this redundancy, though this isn't necessary and (see diagram above) you could in fact consider the activity to be equivalent to the like post, and use the ID of the activity to interact with it (if someone wanted to like my like, etc). I have no explicit post types, so it's implicitly considerd a 'like' post due to the `like-of` microformats property: ``` I like Jessica's post. 15th May 2015 13:06 Amy Guy ``` And the activity: ``` { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/a-like" } } ``` In this case, as well as any explicit or default audience specified, my server would post this activity to Jessica's inbox, whether Jessica is following me or not, as it's a like of her post. Her server can notify her, insert this into her feed, and it's also her server's job to send this activity out to everyone (probably Jessica's followers) who recieved the *original* post (the one I'm liking), so the 'like' counter can be incremented consistently for everyone, whether they know you or not. Without activities (my current setup), I'd send a webmention to Jessica, and there's nothing specified about how her server should handle this (beyond checking it's valid), so propagating the like out to everyone who had seen her post wouldn't necessarily happen. However, she is likely to display the number of 'likes' on her post, so a reader could *pull* the new number of likes each time anyone viewed it (or poll continuously if it wanted a live-update), which puts a lot of burden on the *client* rather than the server. ### Microformats to ActivityStreams So I generate [activities like this for all of my posts](https://rhiaro.co.uk/activitiesjson.php) so far. As I removed all explicit post types from my storage, I rely only on the *properties* of a post to decide how to display it. I implemented similar rules to generate the activity types required by AS2.0. These rules cascade: * `name` exists (object has a title) -> `Post` * `location` exists -> `Arrive` * `like-of` exists -> `Like` * `bookmark-of` exists -> `Save` * `repost-of` exists -> `Share` * otherwise -> `Post` * `in-reply-to` exists and has `category` "rsvp" -> `Accept` * otherwise -> `Respond` I also check nameless posts for `eat` and `sleep` tags, as these indicate a lifelog post for eating or sleeping, and use types `_:Consume` and `_:Sleep` (my own namespace, as AS2.0 doesn't have these types. TODO: Reuse an existing vocab for these). I'll be adding running, yoga, hiking, listening to music and committing code imminantly, so I might need more activity types. I probably need to model these better than relying on tags (but... maybe not. We'll see). ### Known issues If you noticed that the `object` of a `Consume` activity is a blog post, you're a semantic pedant, and you're right, this doesn't make sense. The next best option given the data I currently store is the object being blank node with a label of the contents of the post, eg: ``` { "@type": "Consume", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": { "title": "An apple" }, "result": { "@id": "https://rhiaro.co.uk/2015/05/a-nom" } } ``` But ideally the `object` would have it's own URI. This probably *isn't* something I'm going to do any time soon. Note an object (a post) is still created as a `result` of the `Consume` activity. This isn't necessary - as with likes, the pure activity could be sent and displayed to people - but since I start with posts, I wouldn't drop them. If you're squinting at my `location` -> `Arrive` rule, note my [own special rules for displaying checkins](https://rhiaro.co.uk/2015/05/checkins). This is subject to change. ## Part two: Maybe not posts-for-all-the-things ### Activities as posts Sometimes, when you want to do something on the web, it doesn't *feel* like making a post. For example, if I want to delete a blog post, I need to tell everyone who received it that it's deleted. In indiewebland, you just delete the post, return a 410, and send a webmention to anyone who needs it. However, this doesn't leave any record of when it was deleted (or when it was created). You might have noticed I like [tracking *everything*](https://rhiaro.co.uk/all), so the ActivityPump way of deleting - which retains a trace of the deletion - appeals to me: ``` { "@type": "Delete", "@id": "https://rhiaro.co.uk/2015/05/delete-bad-post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://rhiaro.co.uk/2015/05/bad-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/bad-post", "deleted": "2015-05-15T13:06:00+02:00" } } ``` (In the current ActivityPump spec, the `result` is a shell of the object that is being deleted, so the URI of the deleted post is retained with a deletion date attached, but all other properties deleted). But when you think about deleting a post, you're *not* thinking about creating a delete post which references the post you want to delete (if this *is* how you think about it, I'd love to hear from you). You want to hit delete, and have everyone suitably notified. The same for updates/edits/modifications (of your *own* post - creating a diff post as an edit to somene else's post or a wiki page is a different matter). Nonetheless, if I wanted to display the delete activity for humans, I could mark it up in HTML with microformats, just as if it were a post, something like: ``` I deleted a post. 15th May 2015 13:06 Amy Guy ``` Note: * I have invented microformat property `u-delete-of`, this doesn't currently exist. * There is content in the HTML version that is not present in the JSON version. Probably a `content` (or existing `displayName`) property on the activity could be used for this. Where this content comes from could be implementation-dependant, with some sensible default fallback. So *even though they're basically the same* thinking about 'delete' as an *activity* rather than a post feels easier *and* it is still possible to treat as a post for display if you need to. At the moment I store a modified date and just update this whenever I edit a post. For now I plan to add something that checks all posts for a `updated` date and slots `Update` activities into the stream (even though there is no explicit `update-of` post). When I can update posts properly with micropub, I will generate the update activity then. There are other activities ((un)follow/add/remove/join/leave) that I also think would be simpler to think of in this way. I don't currently know of anyone in indieweb who is displaying posts for these marked up with microformats, though I have a couple of [experimantal follows posts](https://rhiaro.co.uk/follows) with `u-follow-of`. ## Next Once I have AS2.0 compliant activities, I could post them to inboxes of [ActivityPump]() compliant servers. Someone should implement one please :) ## Addendum Certain activities (like `Follow`) may trigger special side-effects (a user is added to another users `followers` collection according to pump.io and ActivityPump (not ActivityStreams)). In indieweb, if you want to follow someone you type their URL into a reader and it fetches their content. What's missing is the reader being able to post a follow activity to your site via micropub so that others (including who you have just followed) can be notified of your follow, and if you have a public list of follows, that can be updated too. This is out of scope for this post, but I think core side-effects like this need to be clearly defined, and we also need a clear way of defining new side-effects as extensions.
Part one: posts-for-all-the-things If you've been paying attention to me IRL or in IRC lately, you'll have had me try to persuade you that activities and posts are basically the same thing. People advocating post-centric views and people advocating activity-centric views are really describing the same thing from different angles, and with slightly different data models. An activity looks generally like: And a post looks generally like: Colour coding indicates what correlates between the two. Differences: Activity Post type explicit implied by properties relationship to another object object value of arbitrary property meta some on activity, some on result all on post But you can see that the data they contain are basically the same. Bear with me. What are activities for? To propagate changes through a network. To tell people that somebody did something. What are posts for? Posts are content, an end in themselves. Who cares? As someone who just wants to publish stuff and interact on the web, do I care if I'm creating and pushing around posts, or activities? Probably not. I just want to do stuff, and have people see it. As a developer, I want to post objects around that contain as much data as needed, and no more. I want the right people to know when something has changed. Whether I prefer to do this with activities or posts (or JSON or HTML) is, as far as I see it, personal preference. I don't think there's an inherant advantage to one over the other. So which to start with? Starting with a blog perspective, the most important thing is that people see my content. Posts are my content, so I start there. I create a post, which are objects with various properties. If I want to explicitly notify someone of a post - because I'm replying to them, favouriting, bookmarking, sharing, tagging, etc, I send them a webmention. The reciever is then free to act upon this as they like: display my post on their site, update their own internal friends list, add my post to a collection, ignore it completely, or whatever. If I want to make a general announcement that something has changed on my site (eg. a new post) I either use PuSH and the hub lets subscribers know, or I don't do anything, and wait for interested parties to pull (visit my homepage, or open their reader) and find out for themselves. For humans, the posts are displayed in html, and for machines they're marked up with minimal microformats, which allows a reader such as Woodwind to parse out the relevant information and display however necessary. (Mine are also avaiable as RDF, but to the best of my knowledge nobody has built a blog reader that consumes RDF yet). In the ActivityStreams model, rather than creating posts directly, the user performs some activity which may generate as a side-effect a post. The activty could be something like Post, Share, Like (with many other terms currently in the vocab), with an optional target of an object being acted upon, and an optional result of a new object being created. Upon creation, the activity is sent by the server to the servers of anyone who is expected to receive it - either a default list such as people who have subscribed to the author, or a specific list of other people specified when the activity is created, or anyone mentioned or replied-to etc. Anyone for whom the activity ends up on their inbox might get a notification, and their server can fetch the associated post for them. Not all activities generate a result post and some activities generate a result that is the same as the object. In these cases, I would argue that an activity == a post... Currently, when I like something I create a like post, that looks like this: A like activity looks like this: See how similar they are?! So given this apparent direct mapping between posts and activities... Posts as activities Dreaming of interoperability with ActivityPump implementations, I want to generate AS2.0 compliant activities that I can send out. I stuck to super basic representations for now, nesting as little as possible (figuring everything with a URI can be dereferenced anyway, so all of the data doesn't need to be present in the first layer of JSON). So for a new post such as: Post title This is a post! 15th May 2015 13:06 Amy Guy The activity generated looks like: { "@type": "Post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://rhiaro.co.uk/2015/05/a-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/a-post" } } With no explicit audience specified, an ActivityPump compliant server would post this to the inboxes of all of my followers. Their server would see the new activity and insert it into their feed; they could click it and view the object (the post). Note that the object of the activity and the result here are the same, so there's some redundancy. For a like post - because I start with posts, I keep the result of the activity as the like post and retain this redundancy, though this isn't necessary and (see diagram above) you could in fact consider the activity to be equivalent to the like post, and use the ID of the activity to interact with it (if someone wanted to like my like, etc). I have no explicit post types, so it's implicitly considerd a 'like' post due to the like-of microformats property: I like Jessica's post. 15th May 2015 13:06 Amy Guy And the activity: { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/a-like" } } In this case, as well as any explicit or default audience specified, my server would post this activity to Jessica's inbox, whether Jessica is following me or not, as it's a like of her post. Her server can notify her, insert this into her feed, and it's also her server's job to send this activity out to everyone (probably Jessica's followers) who recieved the original post (the one I'm liking), so the 'like' counter can be incremented consistently for everyone, whether they know you or not. Without activities (my current setup), I'd send a webmention to Jessica, and there's nothing specified about how her server should handle this (beyond checking it's valid), so propagating the like out to everyone who had seen her post wouldn't necessarily happen. However, she is likely to display the number of 'likes' on her post, so a reader could pull the new number of likes each time anyone viewed it (or poll continuously if it wanted a live-update), which puts a lot of burden on the client rather than the server. Microformats to ActivityStreams So I generate activities like this for all of my posts so far. As I removed all explicit post types from my storage, I rely only on the properties of a post to decide how to display it. I implemented similar rules to generate the activity types required by AS2.0. These rules cascade: name exists (object has a title) -> Post location exists -> Arrive like-of exists -> Like bookmark-of exists -> Save repost-of exists -> Share otherwise -> Post in-reply-to exists and has category "rsvp" -> Accept otherwise -> Respond I also check nameless posts for eat and sleep tags, as these indicate a lifelog post for eating or sleeping, and use types _:Consume and _:Sleep (my own namespace, as AS2.0 doesn't have these types. TODO: Reuse an existing vocab for these). I'll be adding running, yoga, hiking, listening to music and committing code imminantly, so I might need more activity types. I probably need to model these better than relying on tags (but... maybe not. We'll see). Known issues If you noticed that the object of a Consume activity is a blog post, you're a semantic pedant, and you're right, this doesn't make sense. The next best option given the data I currently store is the object being blank node with a label of the contents of the post, eg: { "@type": "Consume", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": { "title": "An apple" }, "result": { "@id": "https://rhiaro.co.uk/2015/05/a-nom" } } But ideally the object would have it's own URI. This probably isn't something I'm going to do any time soon. Note an object (a post) is still created as a result of the Consume activity. This isn't necessary - as with likes, the pure activity could be sent and displayed to people - but since I start with posts, I wouldn't drop them. If you're squinting at my location -> Arrive rule, note my own special rules for displaying checkins. This is subject to change. Part two: Maybe not posts-for-all-the-things Activities as posts Sometimes, when you want to do something on the web, it doesn't feel like making a post. For example, if I want to delete a blog post, I need to tell everyone who received it that it's deleted. In indiewebland, you just delete the post, return a 410, and send a webmention to anyone who needs it. However, this doesn't leave any record of when it was deleted (or when it was created). You might have noticed I like tracking everything, so the ActivityPump way of deleting - which retains a trace of the deletion - appeals to me: { "@type": "Delete", "@id": "https://rhiaro.co.uk/2015/05/delete-bad-post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://rhiaro.co.uk/2015/05/bad-post", "result": { "@id": "https://rhiaro.co.uk/2015/05/bad-post", "deleted": "2015-05-15T13:06:00+02:00" } } (In the current ActivityPump spec, the result is a shell of the object that is being deleted, so the URI of the deleted post is retained with a deletion date attached, but all other properties deleted). But when you think about deleting a post, you're not thinking about creating a delete post which references the post you want to delete (if this is how you think about it, I'd love to hear from you). You want to hit delete, and have everyone suitably notified. The same for updates/edits/modifications (of your own post - creating a diff post as an edit to somene else's post or a wiki page is a different matter). Nonetheless, if I wanted to display the delete activity for humans, I could mark it up in HTML with microformats, just as if it were a post, something like: I deleted a post. 15th May 2015 13:06 Amy Guy Note: I have invented microformat property u-delete-of, this doesn't currently exist. There is content in the HTML version that is not present in the JSON version. Probably a content (or existing displayName) property on the activity could be used for this. Where this content comes from could be implementation-dependant, with some sensible default fallback. So even though they're basically the same thinking about 'delete' as an activity rather than a post feels easier and it is still possible to treat as a post for display if you need to. At the moment I store a modified date and just update this whenever I edit a post. For now I plan to add something that checks all posts for a updated date and slots Update activities into the stream (even though there is no explicit update-of post). When I can update posts properly with micropub, I will generate the update activity then. There are other activities ((un)follow/add/remove/join/leave) that I also think would be simpler to think of in this way. I don't currently know of anyone in indieweb who is displaying posts for these marked up with microformats, though I have a couple of experimantal follows posts with u-follow-of. Next Once I have AS2.0 compliant activities, I could post them to inboxes of [ActivityPump]() compliant servers. Someone should implement one please :) Addendum Certain activities (like Follow) may trigger special side-effects (a user is added to another users followers collection according to pump.io and ActivityPump (not ActivityStreams)). In indieweb, if you want to follow someone you type their URL into a reader and it fetches their content. What's missing is the reader being able to post a follow activity to your site via micropub so that others (including who you have just followed) can be notified of your follow, and if you have a public list of follows, that can be updated too. This is out of scope for this post, but I think core side-effects like this need to be clearly defined, and we also need a clear way of defining new side-effects as extensions.
# Micropubbing with json If micropub used JSON instead of (/as well as) form encoding, what might submissions look like? Aaaron Parecki brainstormed about this on our [Social APIs brainstorming doc](https://github.com/w3c-social/Social-APIs-Brainstorming/blob/master/README.md). I'm going to repeat his examples for create/update/delete here, compare them with equivalent AS2.0 json, then explore other post 'types'. Note: I've left out `@context` from all AS examples because we agreed when missing, this is implied to be "https://www.w3.org/ns/activitystreams". ## Create a new post ``` { "object": { "content": "hello world", "category": ["indieweb"], "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" } } ``` No URL is given so it's implied that a new post is being created. Most of this is Aaron's example but I added the explicit published date and author, but these could also be added automatically by the micropub endpoint as the current time and the authenticated user who is sending the request. **ISSUE:** `actor` vs `author` may never get agreed upon. I think `actor` is too abstract and `author` is too specific. I'm a fan of `agent` to mean 'whatever caused this to happen' (be it human, group, process or bot) but I know that's not an easy sell either. I think one side is going to have to suck it up, and I don't know who this is going to be. AS also has `attributedTo` which may be a reasonable compromise but seems a bit clunky. Response per micropub spec: `201 Created` and the URL of the post in the `Location` header. An AS2.0 Activity for creating a new post would look like: ``` { "@type": "Post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": { "content": "hello world", "category": ["indieweb"] } } ``` Response per ActivityPump spec: "This has the side effect that of the object that they are posting is being created." - either I missed something more specific in the spec or this remains to be clarified (I'm not criticising, AP is very much under-development!). I prefer the first one. Key differences: * Explicit `type` on the activity (whereas `Post` or `Create` is implied in the micropub example). * Metadata (eg. `published` and `actor`) are attached to the activity, not the object. There has been a bit of discussion about whether things like audience targeting (`to`) would be better off attached to the object rather than the activity; I'm inclined to think there's *no* metadata related to the creating of an object that can't/shouldn't be attached to the object itself, rendering the activity redundant. I keep asking people of examples that contradict this, but so far nobody has given me one. ## Update a post Aaron's micropub example: ``` { "url": "http://example.com/post/1", "object": { "content": "hello moon" } } ``` ActivityStreams: ``` { "@type": "Update", "object": "http://example.org/post/1" } ``` But the latter *doesn't* contain the updates made to the object... I might be missing something, but there are no specifics in the ActivityPump spec about this; presumably the user updates the object directly and the `Update` activity is *generated from that*, which is a curiously object-centric happening in an activity-centric world. Neither contain the updated date; presumably for micropub the endpoint is expected to fill this gap automatically, and possibly in the AS example the activity is meant to have a `published` property which could also automatically be filled in by the server. I'd like to be explicit about the updated date, though I don't know if this is a *must* or a *should* or a *may*. An alternative I like is: ``` { "object": { "@id": "http://example.org/post/1", "updated": "2015-05-23T15:00:00+02:00", "content": "hello moon" } } ``` * `id` is on the object as in AS, rather than outside it as in micropub, so what you're sending is *just* the object. * It's implicitly an update (no `type`) because an object with an `id` was supplied. * The new `content` is submitted, which will replace what exists. * The `updated` date is explicit. **ISSUE:** I feel like persuading microformats afficondos to use `@id` instead of `url` might be a toughie. ## Delete a post Aaron's example: ``` { "url": "http://example.com/post/1", "action": "delete" } ``` ActivityStreams: ``` { "@type": "Delete", "object": "http://example.org/post/1" } ``` They look pleasantly similar :) ActivityPump specifies that "this must leave a shell of the object that will be displayed in activities which reference the deleted object. If the deleted object is requested the server should respond with the HTTP 410 Gone status code." The empty 'shell' with the deleted date is useful if you want to preserve when you deleted things. Discussed this in #indiewebcamp IRC a bit, and [tantek](http://tantek.com) suggested that emptying a post of all metadata but the `updated` property would suffice for implying a delete. An alternative to the examples above could be: ``` { "object": { "@id": "http://example.org/post/1", "updated": "2015-05-23T15:00:00+:02:00", "...": "...." } } ``` where `"...": "..."` means... well, we need to define update behaviour before we can define delete-via-update behaviour. If omitting properties during an update removes them, then we don't need to send empty values for a delete. But if partial updates work, and the properties you send are changed and all others remain the same (which I think is better) then we need to send empty values for everything to delete them. This also allows the possibility of replacing `content` with say "This post has been deleted" or "this post was removed due to its offensive nature" or "I spelt literally every word wrong and it seemed easier just to forget about it" and so on, which could be useful for smarter display of deleted posts (ie. as normal posts that have been updated to say they've been deleted). I'm sure someone will have something to say about the semantics of delete vs update; if so, please back it up with practical examples/use cases :) We also need to consider that someone deleting a post may *not* want to leave a trace, even if just an `updated` date and simply return a 410 if someone tries to access. This should be allowed, with the caveat that it's not possible to advise other servers that may have consumed the post that it's gone if they don't actively try to retrieve it and see the 410. ## Liking a post Or posting a like. Depending on whether you have object- or activity-tinted glasses. New grounds, so I'll start with what exists in ActivityStreams: ``` { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", } ``` Remember in microformats we have the `like-of` property which would have the same value as `object` does above, this time, and the 'like' is a first-class post object in itself. So submitting one via micropub could look like: ``` { "object": { "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" "like-of": "https://theperplexingpariah.co.uk/2015/05/a-post" } } ``` These are *preeetttty* different with regards to how terms are being used. A more AS-friendly version where the like is still treated as a first-class object would be: ``` { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", "result": { "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" } } ``` Even though a like post is implicitly created through the `result` you have to go via the activity that generated it to find out what it's a like *of* so it doesn't really stand on its own. In the same way as audience targeting and other metadata is useful attached to the *object* I think having the like relation directly between the objects is practical. For semantic pedants, one post is a `like-of` another post, so it's closer to having a like *type* than being a like *verb*. But it's an implicit type, rather than an explicit one. Just to reiterate: I'm *not* saying 'post A likes post B'. That would be silly. 'Post A like-of post B' pretty easily implies the author of post A likes post B. It's not worth trying to overthink that, and you don't need RDF inferencing to make sense of it. In summary, even for things other than creating posts, I think just sending an object works. You can attach other things like `"content": "I like Jessica's post"` too if you wanted, for nicer display. ## Checkin The closest example in ActivityStreams is an `Arrive` activity: ``` { "@context": "https://www.w3.org/ns/activitystreams", "@type": "Arrive", "actor": { "@type": "Person", "displayName": "Sally" }, "location": { "@type": "Place", "displayName": "Work" }, "origin": { "@type": "Place", "displayName": "Home" } } ``` I have never needed `origin` with a checkin so I'm going to ignore that for now. I think I want to send to my micropub endpoint something more like: ``` { "object": { "published": "2015-05-23T13:00:00+02:00", "location": "https://rhiaro.co.uk/home", "content": "I got home" } } ``` or ``` { "object": { "published": "2015-05-23T13:00:00+02:00", "location": "http://dbpedia.org/resource/Edinburgh", "content": "Finally back in Auld Reekie!" } } ``` These are just like regular posts, but with a location property. Which is magically compatible with ActivityStremas *and* Microformats! Where `location` is *always* a URI, so that display name, latitude, longitude, etc, can be attached to that and don't need to be included in the checkin. I'm a big fan of not nesting. If you submit a checkin with a nested venue object, the endpoint has to try to create the venue before it can do the checkin, which means it needs to attempt to de-dup venues as part of the checkin process; and if the creation fails then there's more hassle... there's probably a smart way of designing the UI plus use of javascript that can make this pretty smooth, but it's definitely not something I want to have to deal with. ## RSVP In AS we have `Accept` which is supposed to have an `object` of `Invite` which in turn has an `object` of `Event` (whether these are all nested in the `Accept` activity or just referenced by URI doesn't really matter, the structure is the same. I included the nesting here cos I just copied the example directly from the AS vocabulary spec): ``` { "@context": "https://www.w3.org/ns/activitystreams", "@type": "Accept", "actor": { "@type": "Person", "displayName": "Sally" }, "object": { "@type": "Invite", "actor": "http://john.example.org", "object": { "@type": "Event", "displayName": "A Party!" } } } ``` While it's all semantically-pedantically very lovely, I reckon the microformats way of just having an RSVP be a regular post with a `reply-to` another regular post which happens to have a `location`, `start` and `end` (thus making it an event) is easier to follow and generally simpler (and effective as evidenced by all the people posting and RSVPing to indie events). In JSON, I think that'd look like: ``` { "object": { "published: "2015-05-23T13:00:00+02:00", "reply-to": "http://indiewebcamp.com/2015/Edinburgh", "rsvp": "Yes", "content": "Really looking forward to IWC Edinburgh!" } } ``` Microformats *does* have invitations too though - what's the fun of RSVPing to an event if you can't invite your friends! Invite posts can be part of an event post, part of an RSVP post, or a post by themselves. That's because all we're really doing to create them is adding `invitee` properties for everyone you want to invite, and then sending them webmentions. If your invitation is not also the original event, you can send the original event post a webmention too (since you've mentioned it, after all) which could allow it to update its own list of who has been invited. Your invitee should reply with their RSVP both to your invitation and the event (which they can do with one post). [Brainstorming about this, which is by no means sent in stone, on IWC wiki](http://indiewebcamp.com/invite). Here is an event with a list of invitees: ``` { "object": { "published: "2015-05-23T13:00:00+02:00", "name": "IndieWebCamp Edinburgh", "start": "2015-07-25T10:00:00+01:00" "end": "2015-07-26T18:00:00+01:00", "location": "http://dbpedia.org/resource/Edinburgh", "content": "Bring your friends to IWC Edinburgh!", "invitee": ["http://tantek.com", "http://theperplexingpariah.co.uk", "http://aaronpk.com"] } } ``` ## Consuming Neither ActivityStreams nor Micorformats have things to do with eating and drinking, but this is [something I log on my site](https://rhiaro.co.uk/llog). My micropub endpoint currently recognises Aaron's `p3k-food` property so I'll use that here, but I'd prefer it to be something more generic.. ``` { "object": { "published": "2015-05-23T17:00:00+02:00", "p3k-food": "A burrito", "category": ["mexican","burrito","yum"] } } ``` A potentially useful fall back for something that wants to display this post but doesn't understand `p3k-food` could just to be to treat it as `content`. Yes, I do desire to tag my food posts. The value of `p3k-food` could also be a handy URI for the food, if one existed, which when dereferenced could give you things like different language display names, calorie count, cuisine affiliations, ingredients, etc. ## Conclusion My micropub endpoint will accept JSON and expect an `object` with a bunch of properties. From these properties my server can infer how to treat them with regards to who to send webmentions to (ie. who to explicitly notify that this object was created; if I was implementing ActivityPump I'd send the whole object to these people's inboxes) and how to display them. It dosen't need explicit types, or properties spread over activities and objects to do this. In anther post I'll run through this for (un)follows, adding to and removing from collections, exercise posts, music listens, code commits, travel plans, offers and requests, and see if they hold up the same. If you have examples of online social stuff you do that you don't think you could publish and propagate this way, *please* tell me all about it. I really want to know.
If micropub used JSON instead of (/as well as) form encoding, what might submissions look like? Aaaron Parecki brainstormed about this on our Social APIs brainstorming doc. I'm going to repeat his examples for create/update/delete here, compare them with equivalent AS2.0 json, then explore other post 'types'. Note: I've left out @context from all AS examples because we agreed when missing, this is implied to be "https://www.w3.org/ns/activitystreams". Create a new post { "object": { "content": "hello world", "category": ["indieweb"], "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" } } No URL is given so it's implied that a new post is being created. Most of this is Aaron's example but I added the explicit published date and author, but these could also be added automatically by the micropub endpoint as the current time and the authenticated user who is sending the request. ISSUE: actor vs author may never get agreed upon. I think actor is too abstract and author is too specific. I'm a fan of agent to mean 'whatever caused this to happen' (be it human, group, process or bot) but I know that's not an easy sell either. I think one side is going to have to suck it up, and I don't know who this is going to be. AS also has attributedTo which may be a reasonable compromise but seems a bit clunky. Response per micropub spec: 201 Created and the URL of the post in the Location header. An AS2.0 Activity for creating a new post would look like: { "@type": "Post", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": { "content": "hello world", "category": ["indieweb"] } } Response per ActivityPump spec: "This has the side effect that of the object that they are posting is being created." - either I missed something more specific in the spec or this remains to be clarified (I'm not criticising, AP is very much under-development!). I prefer the first one. Key differences: Explicit type on the activity (whereas Post or Create is implied in the micropub example). Metadata (eg. published and actor) are attached to the activity, not the object. There has been a bit of discussion about whether things like audience targeting (to) would be better off attached to the object rather than the activity; I'm inclined to think there's no metadata related to the creating of an object that can't/shouldn't be attached to the object itself, rendering the activity redundant. I keep asking people of examples that contradict this, but so far nobody has given me one. Update a post Aaron's micropub example: { "url": "http://example.com/post/1", "object": { "content": "hello moon" } } ActivityStreams: { "@type": "Update", "object": "http://example.org/post/1" } But the latter doesn't contain the updates made to the object... I might be missing something, but there are no specifics in the ActivityPump spec about this; presumably the user updates the object directly and the Update activity is generated from that, which is a curiously object-centric happening in an activity-centric world. Neither contain the updated date; presumably for micropub the endpoint is expected to fill this gap automatically, and possibly in the AS example the activity is meant to have a published property which could also automatically be filled in by the server. I'd like to be explicit about the updated date, though I don't know if this is a must or a should or a may. An alternative I like is: { "object": { "@id": "http://example.org/post/1", "updated": "2015-05-23T15:00:00+02:00", "content": "hello moon" } } id is on the object as in AS, rather than outside it as in micropub, so what you're sending is just the object. It's implicitly an update (no type) because an object with an id was supplied. The new content is submitted, which will replace what exists. The updated date is explicit. ISSUE: I feel like persuading microformats afficondos to use @id instead of url might be a toughie. Delete a post Aaron's example: { "url": "http://example.com/post/1", "action": "delete" } ActivityStreams: { "@type": "Delete", "object": "http://example.org/post/1" } They look pleasantly similar :) ActivityPump specifies that "this must leave a shell of the object that will be displayed in activities which reference the deleted object. If the deleted object is requested the server should respond with the HTTP 410 Gone status code." The empty 'shell' with the deleted date is useful if you want to preserve when you deleted things. Discussed this in #indiewebcamp IRC a bit, and tantek suggested that emptying a post of all metadata but the updated property would suffice for implying a delete. An alternative to the examples above could be: { "object": { "@id": "http://example.org/post/1", "updated": "2015-05-23T15:00:00+:02:00", "...": "...." } } where "...": "..." means... well, we need to define update behaviour before we can define delete-via-update behaviour. If omitting properties during an update removes them, then we don't need to send empty values for a delete. But if partial updates work, and the properties you send are changed and all others remain the same (which I think is better) then we need to send empty values for everything to delete them. This also allows the possibility of replacing content with say "This post has been deleted" or "this post was removed due to its offensive nature" or "I spelt literally every word wrong and it seemed easier just to forget about it" and so on, which could be useful for smarter display of deleted posts (ie. as normal posts that have been updated to say they've been deleted). I'm sure someone will have something to say about the semantics of delete vs update; if so, please back it up with practical examples/use cases :) We also need to consider that someone deleting a post may not want to leave a trace, even if just an updated date and simply return a 410 if someone tries to access. This should be allowed, with the caveat that it's not possible to advise other servers that may have consumed the post that it's gone if they don't actively try to retrieve it and see the 410. Liking a post Or posting a like. Depending on whether you have object- or activity-tinted glasses. New grounds, so I'll start with what exists in ActivityStreams: { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", } Remember in microformats we have the like-of property which would have the same value as object does above, this time, and the 'like' is a first-class post object in itself. So submitting one via micropub could look like: { "object": { "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" "like-of": "https://theperplexingpariah.co.uk/2015/05/a-post" } } These are preeetttty different with regards to how terms are being used. A more AS-friendly version where the like is still treated as a first-class object would be: { "@type": "Like", "published": "2015-05-15T13:06:00+02:00", "actor": "https://rhiaro.co.uk/about#me", "object": "https://theperplexingpariah.co.uk/2015/05/a-post", "result": { "published": "2015-05-15T13:06:00+02:00", "author": "https://rhiaro.co.uk/about#me" } } Even though a like post is implicitly created through the result you have to go via the activity that generated it to find out what it's a like of so it doesn't really stand on its own. In the same way as audience targeting and other metadata is useful attached to the object I think having the like relation directly between the objects is practical. For semantic pedants, one post is a like-of another post, so it's closer to having a like type than being a like verb. But it's an implicit type, rather than an explicit one. Just to reiterate: I'm not saying 'post A likes post B'. That would be silly. 'Post A like-of post B' pretty easily implies the author of post A likes post B. It's not worth trying to overthink that, and you don't need RDF inferencing to make sense of it. In summary, even for things other than creating posts, I think just sending an object works. You can attach other things like "content": "I like Jessica's post" too if you wanted, for nicer display. Checkin The closest example in ActivityStreams is an Arrive activity: { "@context": "https://www.w3.org/ns/activitystreams", "@type": "Arrive", "actor": { "@type": "Person", "displayName": "Sally" }, "location": { "@type": "Place", "displayName": "Work" }, "origin": { "@type": "Place", "displayName": "Home" } } I have never needed origin with a checkin so I'm going to ignore that for now. I think I want to send to my micropub endpoint something more like: { "object": { "published": "2015-05-23T13:00:00+02:00", "location": "https://rhiaro.co.uk/home", "content": "I got home" } } or { "object": { "published": "2015-05-23T13:00:00+02:00", "location": "http://dbpedia.org/resource/Edinburgh", "content": "Finally back in Auld Reekie!" } } These are just like regular posts, but with a location property. Which is magically compatible with ActivityStremas and Microformats! Where location is always a URI, so that display name, latitude, longitude, etc, can be attached to that and don't need to be included in the checkin. I'm a big fan of not nesting. If you submit a checkin with a nested venue object, the endpoint has to try to create the venue before it can do the checkin, which means it needs to attempt to de-dup venues as part of the checkin process; and if the creation fails then there's more hassle... there's probably a smart way of designing the UI plus use of javascript that can make this pretty smooth, but it's definitely not something I want to have to deal with. RSVP In AS we have Accept which is supposed to have an object of Invite which in turn has an object of Event (whether these are all nested in the Accept activity or just referenced by URI doesn't really matter, the structure is the same. I included the nesting here cos I just copied the example directly from the AS vocabulary spec): { "@context": "https://www.w3.org/ns/activitystreams", "@type": "Accept", "actor": { "@type": "Person", "displayName": "Sally" }, "object": { "@type": "Invite", "actor": "http://john.example.org", "object": { "@type": "Event", "displayName": "A Party!" } } } While it's all semantically-pedantically very lovely, I reckon the microformats way of just having an RSVP be a regular post with a reply-to another regular post which happens to have a location, start and end (thus making it an event) is easier to follow and generally simpler (and effective as evidenced by all the people posting and RSVPing to indie events). In JSON, I think that'd look like: { "object": { "published: "2015-05-23T13:00:00+02:00", "reply-to": "http://indiewebcamp.com/2015/Edinburgh", "rsvp": "Yes", "content": "Really looking forward to IWC Edinburgh!" } } Microformats does have invitations too though - what's the fun of RSVPing to an event if you can't invite your friends! Invite posts can be part of an event post, part of an RSVP post, or a post by themselves. That's because all we're really doing to create them is adding invitee properties for everyone you want to invite, and then sending them webmentions. If your invitation is not also the original event, you can send the original event post a webmention too (since you've mentioned it, after all) which could allow it to update its own list of who has been invited. Your invitee should reply with their RSVP both to your invitation and the event (which they can do with one post). Brainstorming about this, which is by no means sent in stone, on IWC wiki. Here is an event with a list of invitees: { "object": { "published: "2015-05-23T13:00:00+02:00", "name": "IndieWebCamp Edinburgh", "start": "2015-07-25T10:00:00+01:00" "end": "2015-07-26T18:00:00+01:00", "location": "http://dbpedia.org/resource/Edinburgh", "content": "Bring your friends to IWC Edinburgh!", "invitee": ["http://tantek.com", "http://theperplexingpariah.co.uk", "http://aaronpk.com"] } } Consuming Neither ActivityStreams nor Micorformats have things to do with eating and drinking, but this is something I log on my site. My micropub endpoint currently recognises Aaron's p3k-food property so I'll use that here, but I'd prefer it to be something more generic.. { "object": { "published": "2015-05-23T17:00:00+02:00", "p3k-food": "A burrito", "category": ["mexican","burrito","yum"] } } A potentially useful fall back for something that wants to display this post but doesn't understand p3k-food could just to be to treat it as content. Yes, I do desire to tag my food posts. The value of p3k-food could also be a handy URI for the food, if one existed, which when dereferenced could give you things like different language display names, calorie count, cuisine affiliations, ingredients, etc. Conclusion My micropub endpoint will accept JSON and expect an object with a bunch of properties. From these properties my server can infer how to treat them with regards to who to send webmentions to (ie. who to explicitly notify that this object was created; if I was implementing ActivityPump I'd send the whole object to these people's inboxes) and how to display them. It dosen't need explicit types, or properties spread over activities and objects to do this. In anther post I'll run through this for (un)follows, adding to and removing from collections, exercise posts, music listens, code commits, travel plans, offers and requests, and see if they hold up the same. If you have examples of online social stuff you do that you don't think you could publish and propagate this way, please tell me all about it. I really want to know.
# Social Web WG 3rd face-to-face summary *This post is my own opinion, and does not necessarily represent the opinion of the Social Web WG!* **tl;dr:** There is increased understanding and expectation of convergence between Micropub, ActivityPump and SoLiD. We're all excited about working towards this convergence. There is increased agreement and understanding about similarities and differences between protocols, and where strong points of one protocol could plug weak points in another. More people are excited about implementing experiments to really figure out what works and what doesn't. See also [day 1 minutes](https://www.w3.org/wiki/Socialwg/2015-05-04-minutes) and [day 2 minutes](https://www.w3.org/wiki/Socialwg/2015-05-05-minutes) and [self-critique whiteboard](http://aaronparecki.com/uploads/whiteboard-20150505-161540.jpg). What follows is more detail on my perspective of the main conversations we had over the two days. Clarifications and corrections welcome. ## Issues in the tracker Closed a bunch of ActivityStreams2.0 issues with the general conclusion that issues raised in future should be more specific and actionable, rather than vague problems. An alternative for reporting this kind of issue wasn't mentioned specifically, but vague problems can probably go in brainstorming sections on the wiki somewhere (indieweb style) or into github issues. A note that we can always re-open issues if they turn out to be a problem in the future, but we shouldn't *keep* them open just in case they might be. ## Awesome user-story protocol walkthroughs [Aaron](http://aaronpk.com), Jessica and Andrei and Henry walked us through exactly how some of the user stories would be carried out according to the protocols [Micropub](https://github.com/aaronpk/Micropub/blob/master/user-stories/), [ActivityPump](https://github.com/w3c-social/activitypump/tree/master/userstories) and [SoLiD](https://github.com/linkeddata/SoLiD/blob/master/UserStories/) respectively. This was *super* useful, and if you weren't there you *need* to read the accompanying documents (linked just prior) and minutes (linked at the top of this post). Results of this were: * Proponants of each protocol started to really understand how the other protocols worked, in a way that just reading all the specs couldn't do. * Everyone really started to see where some protocols were strong and where their own weak points or gaps were. * Everybody started to express that their protocols aren't complete and entertain the idea that they could make changes. * We started to get a feeling of how the three protocols could complement each other well, and the feeling of competition between them was gone. * We started to get *really* excited about making this happen. I particularly noted: * Sandro said he could see how Micropub and SoLiD were similar/complementary (SoLiD advocates seem keen on adding webmention for notifications). * Aaron and Jessica agreed that ActivityPump and Micropub were actually way more inline than they realised before. * I can see the ways in which ActivityPump and SoLiD are similar, and Evan also noted the complementary nature of the two. That's a full triangle of connections! The protocols are all friends. The resolution, then, was that we won't rush to pick a base to work from, and instead will spend some more time (with regular updates to the group) tweaking each protocol to get closer to convergence, so everyone will be happier when a base does eventually need to get chosen. (*Exciting*, right?! This was like the best meeting of minds that has happened so far. If you weren't in the room you might have missed this, so... trust those of us who were). ## Extensibility There should be some kind of clear warning in the spec that extensibility won't work well if you're using plain JSON, not JSON-LD (implying the best way to do vocabulary extensibility is through URI namespacing, which hasn't neccessarily been agreed, but to me seems like a sensible way). It also needs to be clear that upon receiving terms that a server does not understand it should *not* drop the data, even if it ignores it - in case that data gets passed forward to a server that might understand it. We resolved "ISSUE-36, Remove all non AS2 namespaces from the normative context, keep the normative context limited to only AS2 vocabulary terms". I'm not totally sure what this means: that if there are terms we decide are crucial we will include AS versions of them even if they already existing another vocabulary (instead of reusing)? In the name of 'completeness' might we overload ActivityStreams, and because we consider AS 'complete' risk sabotaging easy extension? I still don't get everything that is throw around during extension mechanism conversations, so I could be missing something. Mostly I'd love to see someone implement AS and extend it to a specific domain, to see how this would work in practice. On a related note, I don't think I've seen documentation of the use-cases / origin of all of the vocabulary terms in AS2, does this exist? ## Activities vs objects binary is false The activity-oriented model of AS2 and the object-oriented model of microformats are actually basically the same but looked at from different perspectives. I think it's really important people try to see this, and not see them as conflicting. We're all trying to do the same thing, and there's no reason AS2-based and microformats2-based implementations can't interoperate. I'm going to work more on this, and others are too. We closed issues related to this as too vague, but it'll need revisiting when we have some more concrete proposals about what to do. By the end of the two days, people were starting to generally agree that this is the case. ## People and profiles We clarified that `Person` in AS2 does not need to refer to a real-world person, and agreed we should make this clearer in the spec (eg. it could be a fictional person, a persona or aspect of someone, etc). We resolved to add `Profile` to AS2 to enable talking about documents about people separately to talking about people, and to facilitate connecting one `Person` to multiple `Profile`s, which I think is useful but not everyone agrees. Thus `Profile` is at-risk until there are implementations. It's worth emphasising the two different parts of this discussion: `Person` vs. `Persona` is different from `Person` vs. `Profile` (ie. `Person` != `Persona` != `Profile`). There was some conflation of this during discussion. Whilst everyone tends to agree that there are three different things there, we disagree about which level of modelling is practically necessary. Figuring this out based on observing how existing social sites model this, implementing my own aspect-based profiles, and shunting my own data around between different profiles, is something I'm concentrating on over the next couple of months. ## Clients and servers Describing things as client *or* server can be misleading. Things *act* as a client or server depending on the perspective, and it doesn't necessarily matter if they *run* in the client or on the server. We either need to clarify or alter our terminology, perhaps rephrasing to describe which componants we see having which *responsibility*. ## Namespacing terms Not at all, with strings, with URIs? Uh... decent arguements for and against all, strong opinions all around. I dunno. ## Audience targeting vs group membership Subtle distinction between access control via the author of a post deciding who to send it to (or who can see it), and someone actively joining a group in order to see additional content. In ActivityPump (and pump.io) you specify `to` to decide who can see a note, eg. your followers. If someone new follows you, and someone else unfollows you, then you update your note, which list do the changes get propagated to? The original receivers of the note, or the current list of followers? I *think* this isn't specified and needs to be clarified. In ActivityPump, following someone is effectively adding yourself to a group (Collection) of their followers. ## Side effects and propagating changes to the social graph LDP doesn't have any side effects by itself, it's simply posting and getting of data to a generic store that doesn't understand the data itself. But for more domain specific purposes like social, LDP could be one layer with another layer that handles side effects like notifications and distribution of changes. Certain activities in ActivityPump cause specific side effects. This is a fixed list, and could do with being better described in the spec. These are things like: if A creates a follow activity of B, side effects are A is added to B's list of follwers, B is added to A's list of followings, B is notified that A followed them, and anyone with permission to see A's activities also sees that A followed B. From what I can tell, side effects tend to be updating/deleting objects or adding to/removing from collections. Micropub's side effects could include syndicating a post to other sites, or triggering webmentions (notifications). There is increasing discussion about having a following list that a reader can subscribe to (so you don't have to enter all of your subscriptions if you change reader) and keeping track of when you followed/unfollowed someone; actively doing one could trigger the other. My vague conclusion is that specifying an extensible way to describe side effects of activities would be useful, with perhaps some core musts and shoulds, but open ended for implementations to do more focused things (eg. if Floop-o-matic decide that posting a Floop automatically likes the second most recent post of everyone named Fred, then they could describe that in a spec-conformant way. Okay that was silly, but you know what I mean). ## RESTful vs endpoints LDP is RESTful; Micropub (and Webmention) use discoverable endpoints; ActivityPump mostly uses endpoints (specific paths rather than discoverable I think) with some things able to be done RESTfully. Endpoints mean services can be delegated to third-parties, and can therefore be used on static sites. Not sure if there's a specific benefit to RESTful (or RESTfulish) but with a bit of sideways thinking they could actually co-exist, allowing implementers to use the method they prefer. ## Cooperation++ General observation: We're all working towards the same thing. Nobody in the group is trying to sabotage this effort. I don't think it would hurt to listen more, and maybe imagine that you *want* someone else's [solution|use case|constraint|idea] to be better than yours, and try to fit it to your world, instead of just insisting one is the best. Every community has different constraints, and different use cases they're trying to solve. We can't elevate one set of requirements above another, but need to try to accept - or at least entertain the idea - that they're valid, without dismissing them outright just because you haven't experienced it directly. Periodically mentioning the business use cases from IBM and Boeing (for example) is helpful in reminding everyone that there are legitimate long-standing needs that probably aren't considered by most of us, but will be implemented if we support them, and will be implemented some non-standard way if we don't. ## Addendum TimBL made sure to remind everyone that JSON is 'in fashion' just as XML once was, and really everyone will regret not just using RDF from the start in the future :)
This post is my own opinion, and does not necessarily represent the opinion of the Social Web WG! tl;dr: There is increased understanding and expectation of convergence between Micropub, ActivityPump and SoLiD. We're all excited about working towards this convergence. There is increased agreement and understanding about similarities and differences between protocols, and where strong points of one protocol could plug weak points in another. More people are excited about implementing experiments to really figure out what works and what doesn't. See also day 1 minutes and day 2 minutes and self-critique whiteboard. What follows is more detail on my perspective of the main conversations we had over the two days. Clarifications and corrections welcome. Issues in the tracker Closed a bunch of ActivityStreams2.0 issues with the general conclusion that issues raised in future should be more specific and actionable, rather than vague problems. An alternative for reporting this kind of issue wasn't mentioned specifically, but vague problems can probably go in brainstorming sections on the wiki somewhere (indieweb style) or into github issues. A note that we can always re-open issues if they turn out to be a problem in the future, but we shouldn't keep them open just in case they might be. Awesome user-story protocol walkthroughs Aaron, Jessica and Andrei and Henry walked us through exactly how some of the user stories would be carried out according to the protocols Micropub, ActivityPump and SoLiD respectively. This was super useful, and if you weren't there you need to read the accompanying documents (linked just prior) and minutes (linked at the top of this post). Results of this were: Proponants of each protocol started to really understand how the other protocols worked, in a way that just reading all the specs couldn't do. Everyone really started to see where some protocols were strong and where their own weak points or gaps were. Everybody started to express that their protocols aren't complete and entertain the idea that they could make changes. We started to get a feeling of how the three protocols could complement each other well, and the feeling of competition between them was gone. We started to get really excited about making this happen. I particularly noted: Sandro said he could see how Micropub and SoLiD were similar/complementary (SoLiD advocates seem keen on adding webmention for notifications). Aaron and Jessica agreed that ActivityPump and Micropub were actually way more inline than they realised before. I can see the ways in which ActivityPump and SoLiD are similar, and Evan also noted the complementary nature of the two. That's a full triangle of connections! The protocols are all friends. The resolution, then, was that we won't rush to pick a base to work from, and instead will spend some more time (with regular updates to the group) tweaking each protocol to get closer to convergence, so everyone will be happier when a base does eventually need to get chosen. (Exciting, right?! This was like the best meeting of minds that has happened so far. If you weren't in the room you might have missed this, so... trust those of us who were). Extensibility There should be some kind of clear warning in the spec that extensibility won't work well if you're using plain JSON, not JSON-LD (implying the best way to do vocabulary extensibility is through URI namespacing, which hasn't neccessarily been agreed, but to me seems like a sensible way). It also needs to be clear that upon receiving terms that a server does not understand it should not drop the data, even if it ignores it - in case that data gets passed forward to a server that might understand it. We resolved "ISSUE-36, Remove all non AS2 namespaces from the normative context, keep the normative context limited to only AS2 vocabulary terms". I'm not totally sure what this means: that if there are terms we decide are crucial we will include AS versions of them even if they already existing another vocabulary (instead of reusing)? In the name of 'completeness' might we overload ActivityStreams, and because we consider AS 'complete' risk sabotaging easy extension? I still don't get everything that is throw around during extension mechanism conversations, so I could be missing something. Mostly I'd love to see someone implement AS and extend it to a specific domain, to see how this would work in practice. On a related note, I don't think I've seen documentation of the use-cases / origin of all of the vocabulary terms in AS2, does this exist? Activities vs objects binary is false The activity-oriented model of AS2 and the object-oriented model of microformats are actually basically the same but looked at from different perspectives. I think it's really important people try to see this, and not see them as conflicting. We're all trying to do the same thing, and there's no reason AS2-based and microformats2-based implementations can't interoperate. I'm going to work more on this, and others are too. We closed issues related to this as too vague, but it'll need revisiting when we have some more concrete proposals about what to do. By the end of the two days, people were starting to generally agree that this is the case. People and profiles We clarified that Person in AS2 does not need to refer to a real-world person, and agreed we should make this clearer in the spec (eg. it could be a fictional person, a persona or aspect of someone, etc). We resolved to add Profile to AS2 to enable talking about documents about people separately to talking about people, and to facilitate connecting one Person to multiple Profiles, which I think is useful but not everyone agrees. Thus Profile is at-risk until there are implementations. It's worth emphasising the two different parts of this discussion: Person vs. Persona is different from Person vs. Profile (ie. Person != Persona != Profile). There was some conflation of this during discussion. Whilst everyone tends to agree that there are three different things there, we disagree about which level of modelling is practically necessary. Figuring this out based on observing how existing social sites model this, implementing my own aspect-based profiles, and shunting my own data around between different profiles, is something I'm concentrating on over the next couple of months. Clients and servers Describing things as client or server can be misleading. Things act as a client or server depending on the perspective, and it doesn't necessarily matter if they run in the client or on the server. We either need to clarify or alter our terminology, perhaps rephrasing to describe which componants we see having which responsibility. Namespacing terms Not at all, with strings, with URIs? Uh... decent arguements for and against all, strong opinions all around. I dunno. Audience targeting vs group membership Subtle distinction between access control via the author of a post deciding who to send it to (or who can see it), and someone actively joining a group in order to see additional content. In ActivityPump (and pump.io) you specify to to decide who can see a note, eg. your followers. If someone new follows you, and someone else unfollows you, then you update your note, which list do the changes get propagated to? The original receivers of the note, or the current list of followers? I think this isn't specified and needs to be clarified. In ActivityPump, following someone is effectively adding yourself to a group (Collection) of their followers. Side effects and propagating changes to the social graph LDP doesn't have any side effects by itself, it's simply posting and getting of data to a generic store that doesn't understand the data itself. But for more domain specific purposes like social, LDP could be one layer with another layer that handles side effects like notifications and distribution of changes. Certain activities in ActivityPump cause specific side effects. This is a fixed list, and could do with being better described in the spec. These are things like: if A creates a follow activity of B, side effects are A is added to B's list of follwers, B is added to A's list of followings, B is notified that A followed them, and anyone with permission to see A's activities also sees that A followed B. From what I can tell, side effects tend to be updating/deleting objects or adding to/removing from collections. Micropub's side effects could include syndicating a post to other sites, or triggering webmentions (notifications). There is increasing discussion about having a following list that a reader can subscribe to (so you don't have to enter all of your subscriptions if you change reader) and keeping track of when you followed/unfollowed someone; actively doing one could trigger the other. My vague conclusion is that specifying an extensible way to describe side effects of activities would be useful, with perhaps some core musts and shoulds, but open ended for implementations to do more focused things (eg. if Floop-o-matic decide that posting a Floop automatically likes the second most recent post of everyone named Fred, then they could describe that in a spec-conformant way. Okay that was silly, but you know what I mean). RESTful vs endpoints LDP is RESTful; Micropub (and Webmention) use discoverable endpoints; ActivityPump mostly uses endpoints (specific paths rather than discoverable I think) with some things able to be done RESTfully. Endpoints mean services can be delegated to third-parties, and can therefore be used on static sites. Not sure if there's a specific benefit to RESTful (or RESTfulish) but with a bit of sideways thinking they could actually co-exist, allowing implementers to use the method they prefer. Cooperation++ General observation: We're all working towards the same thing. Nobody in the group is trying to sabotage this effort. I don't think it would hurt to listen more, and maybe imagine that you want someone else's [solution|use case|constraint|idea] to be better than yours, and try to fit it to your world, instead of just insisting one is the best. Every community has different constraints, and different use cases they're trying to solve. We can't elevate one set of requirements above another, but need to try to accept - or at least entertain the idea - that they're valid, without dismissing them outright just because you haven't experienced it directly. Periodically mentioning the business use cases from IBM and Boeing (for example) is helpful in reminding everyone that there are legitimate long-standing needs that probably aren't considered by most of us, but will be implemented if we support them, and will be implemented some non-standard way if we don't. Addendum TimBL made sure to remind everyone that JSON is 'in fashion' just as XML once was, and really everyone will regret not just using RDF from the start in the future :)