Building a Webfinger Client for my Personal Cloud


Summary

This post shows how the Webfinger client for the Kynetx CloudOS allows you to link two personal clouds. The post also shows how the client was built.

webfinger icon

Last week I walked through the theory on using Webfinger for discovery in personal clouds. The problem we're solving is how one personal cloud discovers another so that they can create a peer-to-peer relationship. Over the weekend, I decided to make it real and build a Webfinger client for personal clouds running the Kynetx CloudOS. This blog post will show how Webfinger helps with discovery in personal clouds and also show how the Kynetx CloudOS Webfinger client works.

The User Experience

I put together a short (1 min) screencast to show the user experience inside the personal cloud desktop. After launching the Webfinger client and typing in a Webfinger identifier, we are asked if we want to connect to the personal cloud associated with that webfinger.

Discovery allows two personal clouds to be connected in a peer-to-peer relationship with only an easily transmitted and remembered identifier. We could link our clouds by me telling you "my personal cloud ID is phil@windley.org". You'd type that in the Webfinger client and we'd be off. (See The Personal Cloud Desktop in Action for a look at how the desktop itself works.)

A Webfinger Client

Here's how I built the client. To make the Webfinger client real, I first decided to changed the set up I built for last week's post to use JSON per Appendix A of the RFC for Web Host Meta. JSON is a little easier to deal with than XML. Now, when you go to host-meta.json on windley.org, you see this:

{"host": "windley.org",
 "links":[{"rel": "lrdd",
           "href": 
             "http://windley.org/describe?type=json&uri={uri}",
           "title": "Resource Descriptor"
          }
         ]
}

And the resource descriptor for the Webfinger ed@windley.org returns

{"subject": "acct:ed@windley.org",
 "links": [ {"rel": "http://kynetx.org/rel/well-known-eci",
             "href": "76161abcd-18d4-00163ebfeeee",
             "name": "David Orcutt"
            }
          ]
}

I used Ed Orcutt's AppTemplate ruleset to build the client. AppTemplate shows how to write an app that runs in the CloudOS, including form interactions. I won't go into the details of using the AppTemplate here—we'll save that for another post. But I did want to show the code that performs the Webfinger interactions.

The first step is to extract the host from the Webfinger identifier so we can query its host-meta file:

host = event:attr('webfinger_uri').extract(re#.+@(.+)$#).head();

Next, we retrieve the host-meta.json file at that domain and extract the lrdd template from the contents:

host_url = "http://#{host}/.well-known/host-meta.json";
host_meta = http:get(host_url).pick("$.content").decode();
template = host_meta.pick("$.links[?(@.rel eq 'lrdd')].href");

We're using a common pattern in working with JSON files: process the results of the http:get() call with a pick() to retrieve the contents and then decode() to resulting string to return JSON. Technically, we should first check to make sure the "host" entry in the host-meta file matches the domain we retrieved, but I'm skipping that step here.

The next step is to put the Webfinger identifier in the template, retrieve the contents of the resulting URL as JSON, and then get any entry that is labeled as a well-known-eci. (See the KRL documentation for details on pick() and it's little language, JSONPath.) Recall that an ECI is an event channel identifier and the well-known ECI provides a way to contact a personal cloud. So, getting our hands on it allow us to contact the personal cloud associated with it.

acct_url = template.replace(
            re#\\{uri\\}#, 
            "acct:#{event:attr('webfinger_uri')}");
webfinger = http:get(acct_url).pick("$.content").decode();
webfinger_entry = webfinger.pick(
   "$.links[?(@.rel eq 'http://kynetx.org/rel/well-known-eci')]",
   true).head();

The final step is to get the actual HREF representing the ECI and the name associated with it for use later in the program.

well_known_eci = webfinger_entry{'href'};
eci_name = webfinger_entry{'name'};

If we find a Webfinger entry, we raise a explicit:wke_found event, passing along the well_known_eci and eci_name, so other rules can process it. If not, we raise the explicit:wke_not_found event so we can let the user recover.

if(webfinger_entry) then {
  noop();
}
fired {
  raise explicit event wke_found 
    attributes 
    event:attrs().put({"wke": well_known_eci, 
                       "eci_name": eci_name});
} else {
  raise explicit event wke_not_found 
    attributes event:attrs();
}

And that's it. The client is fairly simple to build and the AppTemplate made creating the app for the CloudOS easy as well. We now have a way of using Webfinger identifiers for linking personal clouds.