Micropub Revisited

After making a comment on the fediverse about how my #100DaysToOffload streak would have come to an end if it wasn’t for being able to use micropub to post from my phone. I was asked about my setup and if I’d written about it.

This led me to 2 observations. My original post about setting it up was an incoherent ramble and that I’d never gotten around to implementing paging on my blog.

I didn’t feel like addressing the latter so thought I write up my current micropub setup instead.

Environment

This setup is for using the Indigenous Android app (or other micropub clients like Quill) to post content to my static website. I will attempt to stay on track by not going into the following details.

  • The site is re-deployed when changes are committed to the repository as is common with such a setup. This micropub setup works by adding files to the repository.
  • The site is already set up to use indieauth.com
  • The site is set up to display 5 different post types: Article, Note, Reply, Bookmark, Like. Micropub doesn’t care which, or how many the site understands. For example, the site doesn’t have a concept of Read, RSVP, or Check-in.
  • The site doesn’t yet automatically send webmentions for any replied, likes, or bookmark post types that are added via any means, micropub or otherwise. Webmentions and micropub are separate concepts.

The Micropub Server

The software I’m using to provide the micropub endpoint is IndieKit. I forked the repository and deployed to a heroku dyno by linking my fork to a dyno, not by using the deploy button.

I then added the 4 compulsory environment variables as well as the INDIEKIT_CONFIG_PATH variable to the dyno settings, as described in these instructions.

After deploying the dyno you will need to add the endpoint to your sites head.

<link rel="micropub" href="https://YOURDYNONAME.herokuapp.com/micropub">

Configuration

In order to tell IndieKit what post types go where in your website you will need to create a json config file at the INDIEKIT_CONFIG_PATH to override any defaults as described here. Here is my current config as an example.

{
  "post-types": {
    "note": {
      "name": "notes",
      "icon": "",
      "template": "indiekit/note.njk",
      "post": {
        "path": "content/notes/{{ published | date('X') }}.md",
        "url": "notes/{{ published | date('X') }}"
      }
    },
    "reply": {
      "name": "notes",
      "icon": "",
      "template": "indiekit/reply.njk",
      "post": {
        "path": "content/notes/{{ published | date('X') }}.md",
        "url": "notes/{{ published | date('X') }}"
      }
    },
    "article": {
      "name": "blog",
      "icon": "",
      "template": "indiekit/article.njk",
      "post": {
        "path": "content/blog/{{ name }}.md",
        "url": "blog/{{ name }}"
      }
    },
    "bookmark": {
      "name": "earmarks",
      "icon": "",
      "template": "indiekit/bookmark.njk",
      "post": {
        "path": "content/earmarks/{{ slug }}.md",
        "url": "earmarks/{{ slug }}"
      }
    },
    "like": {
      "name": "earmarks",
      "icon": "",
      "template": "indiekit/like.njk",
      "post": {
        "path": "content/earmarks/{{ slug }}.md",
        "url": "earmarks/{{ slug }}"
      }
    }
  }
}

In addition, the templates referenced here need to be added to your sites repository. Here is the content of my /indiekit/reply.njk file.

---
date: {{ published }}
replyTo: {{ inReplyTo }}
{%- if category %}
category:
{%- for item in category %}
- {{ item }}
{%- endfor %}
{%- endif %}
---
{{ content | safe }}

These are the templates for how IndieKit will create the markdown files that it writes to the repository.

Now IndieKit is listening on your endpoint for new posts and it knows what post types your site handles and what to do with the information it is passed for each one.

Indigenous

Now, in theory, we have a functional micropub endpoint we can publish using a client.

Once in Indigenous, add an account by signing up with your site url and going through the IndieAuth flow. You should then be able to use the write menu to access the list of post types.

Post types in Indigenous
Post types in Indigenous

In addition to this, in the settings, you can enable various share intents for certain post types meaning you can, for example reply to a blog post or bookmark a web page from your browsers share menu.

Indigenous share intents
Indigenous share intents

Bug

It is here where I discovered an issue. When leaving an empty date field Indigenous passes the current timestamp to the micropub endpoint but somewhere this is getting malformed.

I hacked around the issue by making a change in my IndieKit fork. Changing the /lib/microformats/derive-published.js file at line 17 to do a string replace.

const {DateTime} = require('luxon');

const config = require(process.env.PWD + '/app/config');

/**
 * Derives published date (based on microformats2 data, else the current date).
 *
 * @exports dervivePublished
 * @param {Object} mf2 microformats2 object
 * @returns {Array} Array containing ISO formatted date
 */
module.exports = mf2 => {
  let {published} = mf2.properties;
  const now = DateTime.local().toISO();

  if (published) {
	  published = DateTime.fromISO(published[0].replace(" ", "+"), {
		  locale: config.locale,
		  zone: 'utc'
    }).toISO();
    return new Array(published);
  }

  return new Array(now);
};

All Working

After fixing this, everything works swimmingly. Adding an article, note, reply, like, or bookmark in Indigenous sends the data to the micropub endpoint on my site. IndieKit grabs this data and creates a markdown file using the template for the given post type and then adds the file to the configured location in the git repository.

What I haven’t done

Aside from the lack of automatically sending webmentions when new likes, bookmarks, or replies are added, the main gap is I haven’t looked into the media endpoint, how it works or what I need to do to start using one.