Leveraging IndieWeb to Avoid Storing Others' Data

Owning your own data is great. I’ve been using this website as the central IndieWeb point of my online life for over five years, and I love it. However, the joy of owning your own website comes bundled with great responsibility: as the website owner, I am responsible for what’s on my site and for what’s stored “under the hood” to make this website work.

It’s not a huge issue as long as I only post my own content on my site, but the cool thing about the IndieWeb — as opposed to “regular” Web — is its social aspect, the ability to interact with other people running other websites. To do that I usually need to put some of the data that belongs to other people onto my website. And that always makes me uncomfortable.

Let’s say I wanted to mention Aaron Parecki1, one of the prominent IndieWeb leaders, on my site. Of course, I could simply make a link to his website, mark it up as a person mention and call it a day, but it would look incredibly dull. If it was on a social network like Twitter or Facebook, there would be a popup with Aaron’s profile picture and some information from his profile. I want my mention of Aaron to look fancy, too. OK, I don’t know how to make a fancy popup card (yet), but I could at least have a picture of him near his name, the way it’s done when a user is mentioned on the IndieWeb wiki.

I could parse the representative h-card Aaron has on his website and get the necessary information and the link to his profile picture, and link to it directly (like IndieWeb wiki does), but that would break if Aaron were to change his profile picture URL in the future. To avoid this I’d have to copy the picture itself to my website, but then it wouldn’t update if Aaron updated the picture on his site. Obviously, if I wanted to show any other information from his profile I’d have to save that information somewhere on my site, and it wouldn’t get updated when Aaron updates his profile.

There are legal considerations, too. It looks like every jurisdiction on the planet has some law describing what the personal data is and how it is supposed to be stored. I don’t want to break any law if I can help it, but I can’t realistically attempt to wrap my head around all that legislation and make sure I do nothing wrong. After all, I simply don’t want to store Aaron’s data on my website, that’s what his website is for!

I’ve been thinking about this all for quite a while, and couldn’t help but come to the obvious conclusion: it’s not my website where Aaron’s data is required, it’s the visitor’s device! I only need a way to instruct the visitor’s device to go to Aaron’s website and fetch the necessary information there, and turn the dull simple link into something more fancy. That’s what JavaScript is for, isn’t it?

Kudos to the great people in the Russian IndieWeb chatroom2; they immediately identified the potential issues (like CORS) I’d be facing while implementing the solution I had in mind, and also gave me an idea about building a caching proxy server to mitigate those issues. This actually made the whole project feasible: I don’t speak much JavaScript, but I do speak a little Go, so I felt much more confident, now that I could avoid writing a lot of JavaScript code.

Some JavaScript is still required, of course, so if you’re reading this with JavaScript turned off or in your RSS reader, all you can see below is an icon and a name linked to Aaron’s website. But if you’re reading this on my site with JavaScript enabled, there’s Aaron’s photo instead of an icon, and hovering your mouse over the link will give you his full name in the tooltip:


And the beauty of it is: all I have on my website is the link to Aaron’s website! It’s your browser that, via the JavaScript magic, asks the proxy for Aaron’s name and picture and shows those to you. Should Aaron change his profile, the new information will show up here.

All it takes is a simple script

let persons = document.getElementsByClassName("person-mention");
for (let person of persons) {
    let u = person.getElementsByClassName("u-url")
    if (u.length == 1) {
        let url = u[0].attributes.href.value;
        let req = 'https://indieweb-glue.evgenykuznetsov.org/api/hcard?url=';
        req += url;
        const r = encodeURI(req);
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                let hcdata = JSON.parse(this.responseText);
                if (hcdata.uphoto == null) {} else {
                    let i = person.getElementsByTagName("i");
                    if (i.length > 0) {
                        let uri = 'https://indieweb-glue.evgenykuznetsov.org/api/photo?url=';
                        uri += hcdata.source;
                        const urienc = encodeURI(uri);
                        const svg = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"%3E%3C/svg%3E';
                        let pic = '<img class="u-photo photo lazyload" src="' + svg + '" data-src="' + urienc + '">';
                        i[0].outerHTML = pic;
                if (hcdata.pname == null) {} else {
                    let a = person.getElementsByTagName("a");
                    if (a[0].attributes.title == null) {
                        a[0].setAttribute("title", hcdata.pname);
        xmlhttp.open("GET", r, true);

and the IndieWeb-glue proxy server I put up3. You’re welcome to use it, too, if you want to. It doesn’t do much yet, but it is, of course, open-source, the code is available on GitHub, and everybody who feels like joining in on development is extremely welcome. And, by the way, I need to thank Ruxton for calling me on some mistakes I did early in the development process.

Yay! That mention of Ruxton shows up with a photo, too! That’s definitely a nice start.

One day I’ll be showing webmentions in the same manner…

  1. Aaron, I hope you don’t mind I chose you for this example. If you do, I’m sincerely sorry and will re-write this post to mention someone else instead. ↩︎

  2. There used to be a link to the Telegram group (where it all took place) here, but it is bridged to a Matrix room now. Since Matrix is better than Telegram (if only because you can have a peek without registering an account), that’s where the link is pointing now. ↩︎

  3. It was running on Heroku for two years, but in 2022 Heroku decided to scrap the free tier, so I moved it to the same VPS I host this website on. ↩︎



Comments can be sent as webmentions or by email.