The thing I was most excited about at IndieWebCamp Brighton was coming up with a Private Webmention extension to Webmention. The version we outlined in Brighton was drastically simplified from previous iterations of potential ways to send private Webmentions.
Nearly a week after speccing it out, I now have a first draft implementation of sending. My goal this week was to finish implementing sending private Webmentions, to get some real-world feedback on the spec.
First, since I use the Telegraph API to send Webmentions, I had to add the ability for it to pass through the new "code" and "realm" values. The neat thing about doing it this way is if you want to use Telegraph to send Webmentions for you, you don't have to give it access to your private posts.
I then had to add the concept of private posts to p3k. One of the challenges I've been facing with p3k is adding the concept of user accounts and other people logging in. To do so, I would need some sort of user database (likely treating the person's URL as the unique identifier for their identity in my system), and then would need to associate users with posts to keep track of who can see what. Then the next challenge would be writing the queries to return different items in various feeds people are viewing when logged in. This all sounded terribly complicated, and there were a number of implementation decisions I didn't want to make just yet.
I decided to scrap that whole idea and do the simplest possible thing instead. I realized that the way the Private Webmention spec is written, I don't actually need "user accounts" to send a private Webmention at all. Instead, all I need to do is to be able to generate and verify tokens that can fetch a specific page. I don't need these tokens associated with users or even domain names.
This simplified my implementation a lot. It meant a relatively small amount of self-contained code to generate the authorization codes and access tokens. The access tokens are locked to a specific post URL, so each token issued can only be used to view a specific post. This is obviously not useful as a generic login mechanism, but it's absolutely sufficient to have a Webmention receiver verify a private Webmention!
Also worth noting is that my implementation does not currently take advantage of the "realm" value. This means every private Webmention I send will require the receiver obtain a new access token. Once I add the of concept of user accounts and mapping posts to users, then I'll be able to generate a "realm" value for that particular user so they will be able to reuse access tokens to fetch additional posts. This is a good future optimization, but not necessary for a first draft implementation.
Next up I need to implement receiving private Webmentions. Since I use webmention.io to handle receiving Webmentions, I'll be adding support for receiving private Webmentions to it.
I am also looking forward to others implementing receiving private Webmentions so that I can start sending some! If you're interested, take a look at the spec, as well as the implementation guide. Hop in our chat if you're not already there and feel free to ask questions!
My DSLR camera doesn't have GPS, so normally all my photos would not include the location of where I was when I took the photo. I used to use the Eye-Fi card that did geotagging, but that is no longer supported in the new "mobi" line. I could get an external GPS unit for my camera, but that sounds cumbersome and would only work with that one camera.
Since I already track everywhere I go, I figured I could use this data to geotag my photos when I upload them to Flickr. It turns out, due to the limitations of Exif, the metadata format that digital cameras use to store information about photos, it wasn't so easy.
Exif lets the camera write arbitrary text data into a jpg when it saves it. There are a handful of standard properties that most cameras write, such as the time the photo was taken, the camera settings such as shutter speed, f-stop, etc, and GPS location if the camera knows where it is. My thought was that if I know when the photo was taken, I can find out where I was at that time, and then add the GPS data to the photo.
Unfortunately, the format for storing dates in Exif does not support specifying a timezone offset. The format for dates is YYYY:MM:DD HH:MM:SS. Without the timezone offset, this series of numbers corresponds to many different actual points in time, depending on which timezone you interpret it as. So what I need is a way to turn the camera time into a specific point in time in order to find out where I was at that time.
I realized that since I have a complete log of my GPS coordinates, I should have enough information to piece this together. Essentially the question I am asking is "where was I when my clock read 7:00pm on July 16 2016?" Note that there are two parts to the answer: my location, and the absolute point in time. It's kind of like solving an equation where there are three variables and you know two of them. The three variables are: my location, the clock time, and the timezone offset. If we knew my location and the clock time, we could find the timezone offset. If we knew the timezone offset and the clock time, then we could find my location.
Where was I when my clock read "7:00pm on July 16 2016"?
If we knew what timezone I was in, then "7:00pm on July 16, 2016" becomes a single reference to an absolute point in time. But we don't know what timezone I was in yet, so there are actually 24 possible absolute points in time this could be. (I'm simplifying this problem slightly by ignoring the 30-minute offset timezones.)
The solution is to find my location (which includes the absolute point in time) at all 24 possible points in time, find the timezone offset that corresponds to each location, then find the location where its timezone offset matches the candidate offset. Below is an example:
Offset-less time in question: 2016-05-12 16:00:00
This could be any of the absolute points in time:
(I left out some of the less common timezone offsets I frequent for the sake of clarity in this example.) Now let's query my GPS database to find out what my local time actually was at each of these points in time:
|Potential Time||Time from GPS||Location|
|2016-05-12 16:00:00 -23:00||2016-05-13 10:59:03 -04:00||New York|
|2016-05-12 16:00:00 -22:00||2016-05-13 10:00:00 -04:00||New York|
|2016-05-12 16:00:00 -07:00||2016-05-12 19:00:00 -04:00||New York|
|2016-05-12 16:00:00 -08:00||no data|
|2016-05-12 16:00:00 -06:00||2016-05-12 17:59:21 -04:00||New York|
|2016-05-12 16:00:00 -05:00||2016-05-12 16:59:53 -04:00||New York|
|2016-05-12 16:00:00 -04:00||2016-05-12 15:59:57 -04:00||New York|
|2016-05-12 16:00:00 -03:00||2016-05-12 14:52:46 +02:00||France|
|2016-05-12 16:00:00 +00:00||2016-05-12 14:52:46 +02:00||France|
|2016-05-12 16:00:00 +01:00||2016-05-12 14:52:46 +02:00||France|
|2016-05-12 16:00:00 +02:00||2016-05-12 14:52:46 +02:00||France|
|2016-05-12 16:00:00 +22:00||2016-05-11 19:15:41 +02:00||Düsseldorf|
|2016-05-12 16:00:00 +23:00||2016-05-11 18:46:26 +02:00||Düsseldorf|
(Note that the times aren't an exact match, because my GPS device doesn't log a point every second. In reality it's more like every second when I'm moving and have a good GPS lock, and when I'm not moving, it records less data. Also on plane flights I sometimes lose the GPS signal part way through the flight which is why many of the rows in this case show the same time from my GPS.)
As you can see by comparing the potential timezone on the left with the actual timezone on the right, there are two offsets that match (highlighted in yellow), so we need to determine which is the correct one. This happens when I am traveling on a plane and cross timezones very quickly.
If we take the two candidates and look at the actual time difference in seconds between the timestamps described, the answer becomes obvious.
|Potential Time||Time from GPS||Difference|
|2016-05-12 16:00:00 -04:00
|2016-05-12 15:59:57 -04:00
|2016-05-12 16:00:00 +02:00
|2016-05-12 14:52:46 +02:00
From this, I can conclude that when my clock read "2016-05-12 16:00:00" it was at "2016-05-12 16:00:00 -0400" when I was in New York.
Most of the time only one offset matches, so this last step isn't necessary. It's only when I quickly cross timezones that there are potentially more than one match.
Since I want to be able to use this to geotag photos, it makes sense to include it as an API in the same system that stores my GPS logs. I encapsulated this logic in my GPS server, Compass with a simple API that returns the answer given an offset-less time. Now I can use it in my geotagging script!
At IndieWebCamp Nürnberg, I didn't end up spending time on my home page design, despite a great day 1 discussion on the topic. Instead, I made a few fixes to my site, and built a quick indie reader in IRC, which shows me posts from people I'm following, and lets me reply to them from IRC as well.
It's ugly, but it works!
When I type !reply in IRC, my bot makes a Micropub request to my server which creates the post.
After I got home, I was inspired to spruce up my home page design since there were quite a few great ideas from people. Jeremy Keith added sparklines to his home page, showing activity in a few different kinds of posts. I liked that idea, and wanted to do the same. I also moved my bio to the top of my home page, since that's more of the primary content of the page. Now my posts appear below the header block.
I also added four mini previews of posts, showing the last night's sleep, the last thing I ate, the last photo I posted, and the next event. I think this does a good job of showing a little more about me than just my bio and just the last few notes I've posted.
Now my home page looks like this! I think it's a great improvement over what it was before, when it just started with the latest post.
Overall, IndieWebCamp Nürnberg was a huge success and I had a great time! I'm looking forward to going back in a couple weeks for Düsseldorf! Thanks to everyone who made the event possible!
I just launched an update to p3k which adds an integrated authorization server. This means that now when I sign in to Micropub apps like Quill, it will redirect me to my own server where I can have more fine-grained control over the access I am granting the application.
My new authorization endpoint displays the scopes the application is requesting, and lets me modify the scope when granting the request. This means if an application I don't yet trust requests the ability to update or delete my posts, I can un-check those boxes but still allow it to create posts.
I also have the ability to have all posts from an application be added to a specific channel, rather than showing up in my main feed. I use the concept of "channels" to create different lists of posts depending on what kind of content the post has. For example, /photos contains all of my photo posts, and /travel contains travel plans, plane flight and train logs, and events that occur outside of Portland.
Now that I have a built-in authorization endpoint, I'm looking forward to adding more features to it, such as setting the default privacy of posts so I can allow an application to create posts that are only visible to me (or a specific audience) unless I make them public.