{"@context":{"rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#","rdfs":"http://www.w3.org/2000/01/rdf-schema#","owl":"http://www.w3.org/2002/07/owl#","foaf":"http://xmlns.com/foaf/0.1/","dc":"http://purl.org/dc/elements/1.1/","dct":"http://purl.org/dc/terms/","sioc":"http://rdfs.org/sioc/types#","blog":"http://vocab.amy.so/blog#","as":"https://www.w3.org/ns/activitystreams#","mf2":"http://microformats.org/profile/","ldp":"http://www.w3.org/ns/ldp#","solid":"http://www.w3.org/ns/solid#","view":"https://terms.rhiaro.co.uk/view#","asext":"https://terms.rhiaro.co.uk/as#","dbp":"http://dbpedia.org/property/","geo":"http://www.w3.org/2003/01/geo/wgs84_pos#","doap":"http://usefulinc.com/ns/doap#","time":"http://www.w3.org/2006/time#"},"@graph":[{"@id":"https://rhiaro.co.uk/2015/12/1451604240","@type":"as:Add","as:actor":{"@id":"https://rhiaro.co.uk/about#me"},"as:object":{"@id":"https://apps.rhiaro.co.uk/burrow/"},"as:published":{"@type":"http://www.w3.org/2001/XMLSchema#datetime","@value":"2015-12-31T23:24:00+0000"},"as:summary":"Amy added https://apps.rhiaro.co.uk/burrow/ to https://rhiaro.co.uk/bookmarks/","as:tag":[{"@id":"https://rhiaro.co.uk/tags/activitypub"},{"@id":"https://rhiaro.co.uk/tags/activitypump"},{"@id":"https://rhiaro.co.uk/tags/activitystreams"},{"@id":"https://rhiaro.co.uk/tags/apps"},{"@id":"https://rhiaro.co.uk/tags/burrow"},{"@id":"https://rhiaro.co.uk/tags/checkins"},{"@id":"https://rhiaro.co.uk/tags/indieweb"},{"@id":"https://rhiaro.co.uk/tags/micropub"},{"@id":"https://rhiaro.co.uk/tags/slogd"}],"as:target":{"@id":"https://rhiaro.co.uk/bookmarks/"}},{"@id":"https://rhiaro.co.uk/2016/01/today-broke","@type":"as:Note","as:content":"
Today I broke my website completely and fixed it again. Rewrote lots of code in the process. I gained some functionality and lost some functionality. I may have lost more than I gained. Now I can do what I was planning to do tod.. oh. It's tomorrow.
","as:published":{"@type":"http://www.w3.org/2001/XMLSchema#dateTime","@value":"2016-01-02T06:56:58+00:00"},"as:summary":"Amy wrote about hacking, indieweb, slogd, micropub, activitypub, & activitypump","as:tag":[{"@id":"https://rhiaro.co.uk/tags/activitypub"},{"@id":"https://rhiaro.co.uk/tags/activitypump"},{"@id":"https://rhiaro.co.uk/tags/hacking"},{"@id":"https://rhiaro.co.uk/tags/indieweb"},{"@id":"https://rhiaro.co.uk/tags/micropub"},{"@id":"https://rhiaro.co.uk/tags/slogd"}]},{"@id":"https://rhiaro.co.uk/2016/03/socialwg5-summary","@type":"as:Article","as:content":"This post is my own opinion, and does not necessarily represent the opinion of the Social Web WG!
\r\nSee also day 1 minutes and day 2 minutes.
\r\nWe met in Boston on 16 and 17 March. What follows is more detail on my perspective of the main conversations we had over the two days. Clarifications and corrections welcome.
\r\nAS2 is inching closer to CR. Evan has made a validator at as2.rocks and done a lot of work on conformance criteria which we went through as a group and updated a little; mostly changing SHOULDs to MUSTs.
\r\nDiscussed and not necessarily resolved a few new open issues, including: considering dropping the Relationship
object and reviving it as an extension if necessary; a proposal for a new property to say when something was deleted; weakening the SHOULD requirement on name
; clarifying scope
and context
.
Stay tuned for a new working draft sometime soon.
\r\nThe most exciting thing I thought was agreeing on the potential for convergence between the create, update and delete parts of Activitypub and Micropub.
\r\nMicropub started life as a super small and simple way for clients and servers to agree how to create content on a website by POSTing form encoded parameters to an endpoint. As a result of this simplicity, there are dozens of client and server implementations, allowing people to use each others posting clients to add posts to their site, from simple text-only posts to photos, events, RSVPs, likes, bookmarks, reposts. When Micropub needed update and delete, it grew beyond what form-encoded parameters could sensibly handle, and added in a JSON syntax which I think to date only the editor has implemented.
\r\nActivitypub uses a JSON syntax (ActivityStreams2) from the outset for create, update and delete, and when you compare this with the Micropub JSON they look remarkably similar.
\r\nMy posting endpoint implements create the AP way, and endpoint discovery the MP way. It also catches Micropub form-encoded requests and translates them to AS2 JSON before proceeding, so I can still use simple Micropub clients. My posting clients burrow (checkins), obtainium (purchases), replicator (food) and seeulator (events, RSVPs, travel plans) all post AS2 JSON... after discovering the endpoint via rel=micropub
. Next on my list, and well overdue at this point, is adding update and delete to both server and clients.
So I proposed we write a document that unifies the common parts of AP and MP, iron out the smaller differences, and hope this coalesces into a small create/update/delete spec which both AP and MP can reference rather than duplicate. Because modularity is good, and common modules are better! I dubbed this temporarily (or is it?) SocialPub.
\r\nSo what's left in Micropub? I hear you cry. The super simple form-encoded create which is what made Micropub do so well in the first place is really what makes Micropub micro, so I'd like to see this be the bulk of the Micropub spec, with just a pointer to SocialPub for people who want to level up to JSON.
\r\nThere are still more than a few issues to be dealt with, though we handled a few during the meeting (such as media uploads). I'll be writing SocialPub up into the Social Web Protocols doc next week, stay tuned.
\r\nJessica and Chris demo'd Media Goblin federating with pump.io! Which is cool. Which brings them a huge step closer to implementing things with AS2/AP and federating that way. They discussed how one of their main impediments had been database schema migration.
\r\nAaron demo'd his Micropub editing UI, which allows partial edits on the post, only for data he is most likely to want to edit (tags, syndication URLs and date).
\r\nAaron also demonstrated a new event posting interface in Quill which uses Micropub, and showed how RSVPs from Woodwind (a feed reader) work via Webmention. Tantek and Ben also demo'd RSVPs from their sites. And Ben demo'd how he can post reactjis as replies, exemplified with the poop emoticon, and there is no question that the future of the social web is in safe hands.
\r\nFrank demonstrated federation between OwnCloud servers, which uses WebDAV and CalDAV, and talked through their access control.
\r\nWe also had a couple of admin/process related discussions. The first included agreeing to meet at TPAC in Lisbon in September as it already looks like there'll be critical mass to make it worthwhile.
\r\nSandro has made a list of issue labels for github which we painstakeingly went through to make sure everyone understands them and editors are willing to use them on specs. This should help people to figure out at a glance what the current state of a spec is from the issues, as well as help passers-by to jump in if they want to get involved.
","as:name":"5th Social WG F2F Summary","as:published":{"@type":"http://www.w3.org/2001/XMLSchema#dateTime","@value":"2016-03-29T10:43:39+01:00"},"as:tag":[{"@id":"https://rhiaro.co.uk/tags/activitypub"},{"@id":"https://rhiaro.co.uk/tags/activitystreams"},{"@id":"https://rhiaro.co.uk/tags/as2"},{"@id":"https://rhiaro.co.uk/tags/meeting"},{"@id":"https://rhiaro.co.uk/tags/micropub"},{"@id":"https://rhiaro.co.uk/tags/phd"},{"@id":"https://rhiaro.co.uk/tags/social+web+protocols"},{"@id":"https://rhiaro.co.uk/tags/social+web"},{"@id":"https://rhiaro.co.uk/tags/socialpub"},{"@id":"https://rhiaro.co.uk/tags/socialwg"},{"@id":"https://rhiaro.co.uk/tags/standards"},{"@id":"https://rhiaro.co.uk/tags/w3c"},{"@id":"https://rhiaro.co.uk/tags/webmention"}]},{"@id":"https://rhiaro.co.uk/2016/05/minimal","@type":"as:Article","as:content":"ActivityPub updates objects by posting an ActivityStreams2 Update
Activity to the authenticated user's outbox
(discovered from their profile).
I store my photo albums as ActivityStreams2 Collections
, so this morning I made a minimal AP-compliant update endpoint today that lets me add captions to them. This is just the server componant. It lets me do:
curl -vX POST -H \"Content-Type: application/activity+json\" -H \"Authorization: secret-token\" -d @as-update.json https://path/to/endpoint
\r\nwhere as-update.json
contains:
{\r\n \"@context\": \"http://www.w3.org/activitystreams#\",\r\n \"type\": \"Update\",\r\n \"name\": \"Amy captioned a photo.\",\r\n \"actor\": \"https://rhiaro.co.uk/about#me\",\r\n \"object\": {\r\n \"id\": \"https://uri/of/photo.jpg\",\r\n \"name\": \"A brand new caption.\"\r\n }\r\n}
\r\nSo far this replaces the entire object with the object embedded in the Activity. Hopefully we'll have a syntax to indicate partial updates in AP soon.
\r\nThe code (PHP) at https://path/to/endpoint
contains a few functions that are at the discretion of the server (how to verify the authenticated user can write, what to do with the activity once it gets it, and exactly how to perform the update):
function verify_token($token){\r\n // Magic to verify token passed in Authorization header here.\r\n // I check it against a protected file on the server that contains a randomly generated long string.\r\n return $response;\r\n}\r\n\r\nfunction store_activity($activity){\r\n // Arbitrary server logic to store the activity that was posted.\r\n // I just dump the JSON in a file.\r\n return true;\r\n}\r\n\r\nfunction make_update($activity){\r\n // Arbitrary server logic to perform the update on the object.\r\n // I parse the value of object in the activity to work out where its data is stored in the filesystem, then rewrite the appropriate JSON file.\r\n return true;\r\n}
\r\nAnd here's the overall flow:
\r\n// Authentication\r\n$headers = apache_request_headers();\r\nif(isset($headers['Authorization'])) {\r\n $token = $headers['Authorization'];\r\n $response = verify_token($token);\r\n $me = @$response['me'];\r\n $iss = @$response['issued_by'];\r\n $client = @$response['client_id'];\r\n $scope = @$response['scope'];\r\n}else{\r\n header(\"HTTP/1.1 403 Forbidden\");\r\n echo \"403: No authorization header set.\";\r\n exit;\r\n}\r\n\r\nif(empty($response)){\r\n // Something went wrong with verification\r\n header(\"HTTP/1.1 401 Unauthorized\");\r\n echo \"401: Access token could not be verified.\";\r\n exit;\r\n}elseif(stripos($me, \"rhiaro.co.uk\") === false || $scope != \"update\"){\r\n // The wrong person and scope was returned when the token was verified.\r\n header(\"HTTP/1.1 403 Forbidden\");\r\n echo \"403: Access token was not valid.\";\r\n exit;\r\n}else{\r\n\r\n // Verified, good to go..\r\n\r\n if(empty($_POST)){\r\n $post = file_get_contents('php://input');\r\n }\r\n\r\n if(isset($post) && !empty($post)){\r\n\r\n // Store activity\r\n $id = date(\"Y-m-d_h:i:s\").\"_\".uniqid();\r\n if(store_activity($post)){\r\n\r\n // Perform the update \r\n if(make_update($post)){\r\n header(\"HTTP/1.1 201 Created\");\r\n echo \"Resource updated\";\r\n }else{\r\n header(\"HTTP/1.1 500 Internal Server Error\");\r\n echo \"500: Could not make update (probably a permissions issue). \";\r\n }\r\n\r\n }else{\r\n header(\"HTTP/1.1 500 Internal Server Error\");\r\n echo \"500: Could not store activity log (probably a permissions issue).\";\r\n }\r\n\r\n }else{\r\n header(\"HTTP/1.1 400 Bad Request\");\r\n echo \"400: Nothing posted\";\r\n }\r\n\r\n}\r\n\r\n?>
\r\nObviously I've stripped out my implementation-specific stuff to make it easier to read. The actual code on my server is here: github/rhiaro/img/pub.php.
\r\nThe next thing I need to do is make a client that...
\r\noutbox
), streams
property, but to start with I'll probably just offer a URL input),Today I finished morph, a client for posting ActivityStreams2 Update activities to an endpoint. The server handles this update activity however it wants, but the obvious thing to do is take the object
of the activity and make the indicated changes.
So far it:
\r\nname
, published
and tags
to items in a Collection.Will be expanding its object editing abilities soon.
\r\nCode on github. See also: Minimal ActivityPub update endpoint.
","as:name":"Minimal ActivityPub update client","as:published":{"@type":"http://www.w3.org/2001/XMLSchema#dateTime","@value":"2016-05-30T23:28:56-04:00"},"as:tag":[{"@id":"https://rhiaro.co.uk/tags/activitypub"},{"@id":"https://rhiaro.co.uk/tags/activitystreams2"},{"@id":"https://rhiaro.co.uk/tags/activitystreams"},{"@id":"https://rhiaro.co.uk/tags/as2"},{"@id":"https://rhiaro.co.uk/tags/client"},{"@id":"https://rhiaro.co.uk/tags/cv"},{"@id":"https://rhiaro.co.uk/tags/hacking"},{"@id":"https://rhiaro.co.uk/tags/json"},{"@id":"https://rhiaro.co.uk/tags/morph"},{"@id":"https://rhiaro.co.uk/tags/photos"},{"@id":"https://rhiaro.co.uk/tags/php"},{"@id":"https://rhiaro.co.uk/tags/resume"},{"@id":"https://rhiaro.co.uk/tags/social"},{"@id":"https://rhiaro.co.uk/tags/socialwg"},{"@id":"https://rhiaro.co.uk/tags/swwg"}]},{"@id":"https://rhiaro.co.uk/tags/activitypub","@type":"as:Collection","as:totalItems":{"@type":"http://www.w3.org/2001/XMLSchema#nonNegativeInteger","@value":"28"}},{"@id":"https://rhiaro.co.uk/tags/activitypub?before=https://rhiaro.co.uk/2016/05/minimal-activitypub&limit=16","@type":"as:CollectionPage","as:items":[{"@id":"https://rhiaro.co.uk/2016/05/minimal-activitypub"},{"@id":"https://rhiaro.co.uk/2016/05/minimal"},{"@id":"https://rhiaro.co.uk/2016/03/socialwg5-summary"},{"@id":"https://rhiaro.co.uk/2016/01/today-broke"},{"@id":"https://rhiaro.co.uk/2015/12/1451604240"}],"as:name":"activitypub","as:next":{"@id":"https://rhiaro.co.uk/tags/activitypub?before=https://rhiaro.co.uk/2020/10/apconf-2020&limit=16"},"as:partOf":{"@id":"https://rhiaro.co.uk/tags/activitypub"}}]}