Proper comment privacy! Yay!

Okay, instead of trying to modify Isso to support thread IDs that are separate from page URIs, I ended up leveraging the way that Publ request routing works and just made all thread IDs consist of a /<signature>/<entry_id> path, where <signature> is computed from an HMAC signature on the entry ID and a secret key. So, now the thread ID is only visible to people who have access to the entry in the first place (as long as my signing key never leaks), and the fact that Isso only uses the thread ID when generating a reply email link isn’t a problem.

So, for example, this entry has an entry ID of 4678, and the generated thread ID is (for example) /890824f4d450d4ac/4678, so when someone gets a reply notification the email will say something like:

such-and-such <foo@bar.baz> wrote:

Good point!

Link to comment:

which will then redirect back here.

It’s not ideal, of course, but it works well enough.

Of course, to do this I had to migrate all of my thread IDs again, but hopefully this is the last time I’ll have to do that, and it also takes care of all my legacy Movable Type-era thread IDs. It does set a bad precedent that I’ll have to migrate thread IDs more in the future if I ever change my publishing system but the fact I was able to get away with not doing that for so long is a pretty good testament to my laziness, which I ended up having to pay interest on in the future anyway. So, lesson learned.

Also, this approach is even better privacy than what I was hoping to get out of the Disqus method; as it stood before, someone on my friends list (or who saw an Auth: * entry) could have theoretically figured out the way I was determining private thread IDs and used that to explore comments on entries they don’t have access to, and also there was an issue that if I ever took a public entry private, its thread ID would remain the same as when it was public. But this way, it’s unguessable as long as my HMAC key never leaks, and if my HMAC key does leak I can just reset it and regenerate the thread IDs. (Edit from the future: Ha. Haha. Ha hahaha ha haha. Ha.)

This approach is also useful for things other than Publ; my advice to anyone who’s using Isso for comments is that instead of using the actual entry URI as the thread ID, they should have some sort of stable mechanism for forwarding an opaque thread ID to the actual entry, and use that. This just happened to be really easy to implement for Publ since Publ already supports opaque ID chasing.

Incidentally, here’s the migration script I wrote:

""" generate the comment migration SQL output.

Converts the old shitty migrated thread IDs to HMAC-signed thread stubs


pipenv run python3 migrations/ | sqlite3 comments.db


import app
from publ import model, entry
from pony import orm

def do_all():
        for record in
            e = entry.Entry(record)

def print_migration(entry):
    sql = 'UPDATE threads SET uri="{tid}" '.format(tid=app.thread_id(entry))

    entry_id =
    old_id = entry.get('thread-id')
    if old_id:
        sql += f'WHERE uri="{old_id}";'
    elif entry.private:
        sql += f'WHERE uri LIKE "/{entry_id}-%";'
        sql += f'WHERE uri="/{entry_id}";'


app.thread_id is the function that generates the thread ID/URI from a publ.entry.Entry object, and is also visible to my templating system. I’ll be sure to add this stuff to the example Publ templates as an aside, since it’s a little tricky and non-obvious.


Before commenting, please read the comment policy.

Avatars provided via Libravatar