Runroller UI

I recently released a simple API to un-shorten URLs. A few people wanted a super-simple interface to this, so I whipped one up: https://keita.blog/unroll/. Enjoy!

Some notes about the tools I’ve used:

  • React — I’ve used React in portions of sites before, but this is the first, albeit simple, full-page React app I’ve made.
  • Brunch — used by default in Phoenix apps, it’s just what I’m used to these days.

Just like the service that runs the API, the UI is also open-source. Hack away!

Link Unroller Service

As a small side project, I recently launched a “link unroller” service. This is a very simple service. You give it a URI, and it follows any redirect chain for you. Then it spits out the final URI via a friendly JSON API.

Give it a spin:

https://unroll.kbys.me/unroll?uri=http://bit.ly/1QZ6acT

Basically, all you do is send a GET request to:

https://unroll.kbys.me/unroll?uri=<URI to unroll>

Done. If there are no problems, you will get a JSON response:

{
  "uri":"http://bit.ly/1QZ6acT",
  "unrolled_uri":"https://keita.blog/",
  "redirect_path":[
    "http://bit.ly/1QZ6acT",
    "http://keita.blog/"
  ],
  "error":false
}

The unrolled_uri parameter is the final link in the chain, and the redirect_path is an array of the links that were traversed.

If you’d like to take a look at the code, make some contributions, or submit some bugs, please head over to the GitHub page.

Technical details:

  • The server is in Tokyo.
  • Written in Elixir.
  • Backend responses are ~ 600 microseconds on a cache hit.

Policy details:

  • Up to 7 redirects will be followed.
  • The request will time out after 20 seconds and return an error.
  • 301 redirects are cached forever, regardless of Cache-Control or Expires headers present in the response.
  • 302 redirects will honor caching headers, with a minimum TTL of 1 minute (this is for DoS protection on my side)
  • 200 responses are cached for 1 hour.

App.net Object Sync

I recently released my first app on the Mac App Store, Toki, and I decided that talking about the inner workings of the sync mechanism I’m using would not only be interesting, but helpful for me to think about some of the problems I’m having1.

Toki uses an App.net sync mechanism that I’ve been thinking about and working on for a while — I thought that Toki would be a good low-profile app to test out some ideas in real-world use.

Although Toki is a production app, the sync portion is not feature-complete yet. I’m working on ironing out the basic bugs (planned to be released in 1.0.1 and 1.0.2 releases), before moving on to the more advanced features. My “end goal” for the App.net sync mechanism is an open-source multi-platform library that anyone can use in their own app to use the App.net backend to keep their objects synchronized2. From now on (and until I think of a better name), I will call this sync mechanism “App.net Object Sync” or “ADNOS”.

ADNOS uses the private message / channel feature of the App.net API, using a channel for the app namespace, and a message representing each object.

There are two fundamental steps of the sync protocol (as of version 1.0.2, still in development):

  1. Pulling remote changes: ADNOS pulls all new messages from the channel, processing them one by one, creating or updating local records as necessary. If the record is deemed “duplicate”, it will overwrite local changes3. If the record was newly created in the local database, ADNOS will also record the App.net Message ID in the local database.
  2. Pushing local changes: ADNOS makes a new message post for each record that does not have a App.net Message ID recorded in the local database. When the message is successfully created, the message ID is set.

This has some limits, some inherent in the protocol, and some inherent in the App.net backend:

  1. Message creation is rate-limited to 20 messages per minute4.
  2. Each message is limited to 8192 bytes. Less, actually, because of serialization and metadata.
  3. Recreating the database requires a full playback of messages — this takes time, especially on databases with more than 200 objects.

These limits are pretty much unavoidable. I work around the rate-limiting problem by queueing sync up requests, and pausing the queue when X-RateLimit-Remaining gets dangerously low. The queue is re-started after X-RateLimit-Reset elapses. Any app that plans on using ADNOS will want to be aware of these limitations before implementing it.

And, now for the features I’m planning on implementing (in no particular order):

  • Proper conflict resolution.
  • Streaming support for pulling new changes.
  • Updating and deleting previously synced items. This isn’t implemented yet, but should be relatively easy.

I’ll be writing more as I progress. I hope to get some sort of open-source library out there in the near future.


  1. This is inspired by the “Vesper Sync Diary” series of blog posts by Brent Simmons

  2. There already has been some work been done with a Ruby-based command-line client.

  3. Conflict resolution is not applicable in the context of Toki, but is high on the list of features I want to implement.

  4. This is also why Toki works with such low-resolution data.

Retina, Please!

A few days ago, I posted a small JavaScript snippet.

Retina, Please! is that JavaScript snippet on steroids. It is a combination of JavaScript and PHP to allow your Retina users to only download Retina images, while non-Retina users only download the standard-resolution version.

Check it out: https://github.com/keichan34/KKRetinaPlease

WordPress Theme Writers: just require it in your functions.php. I may package it into a real plugin later on.

I’ve tested it with CakePHP as well.