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 :)
# 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.
Amy added http://microformats.org/wiki/social-network-portability to https://rhiaro.co.uk/bookmarks/
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
Render reposts of tweets properly
Amy wrote about http://vocab.amy.so/blog#Todo, http://vocab.amy.so/blog#Todo, hacking, slogd, twitter, microformats, microformats2, mf2, & parser
# Checkins When is it a checkin (with a note) and when is it a note (with a location)? Hot topic. I differentiate them for display purposes (currently I just show a map-marker icon next to checkins, but I'm also going to want to show them on a map and stuff). For now, my rule is: * If a post has a `location` property, which should be a URL of a place (venue/city/country/vague), it's a checkin. * A checkin *may* have a `latitude` and `longitude` (my vague checkins, eg. 'home', do not). * A checkin *may* have `content`. * If a post has a `latitude` and `longitude` and *no* `location`, it's a note with a location. * A note *should* have content. My inner semantic pedant is unhappy with attaching a lat and lon to a post directly, so I might rethink this at some point.
When is it a checkin (with a note) and when is it a note (with a location)? Hot topic. I differentiate them for display purposes (currently I just show a map-marker icon next to checkins, but I'm also going to want to show them on a map and stuff). For now, my rule is: If a post has a location property, which should be a URL of a place (venue/city/country/vague), it's a checkin. A checkin may have a latitude and longitude (my vague checkins, eg. 'home', do not). A checkin may have content. If a post has a latitude and longitude and no location, it's a note with a location. A note should have content. My inner semantic pedant is unhappy with attaching a lat and lon to a post directly, so I might rethink this at some point.
# 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.
Take note weary travelers, Three's Feel At Home plan does not include Germany. Silly of me to just assume it did. I spent £5 on IRC and reading the microformats wiki.
Amy wrote about travel, microformats2, three, & 3g