Redirect a Short ID to a Markdown File (not just for DEVONthink)

I like using links to Markdown files, and there are already several existing solutions, each with their pros and cons:

  • Wikilinks, like [[Note_Name]], can easily break if the note is moved or renamed. Additionally, they don’t work across DEVONthink databases or in apps like Apple Notes.
  • File paths can change.
  • Hookmark links, like hook://file/pxbWfCG2u?p=cnRmLzEx&n=External%20Sources.rtf, and DEVONthink links, such as x-devonthink-item://4F70343B-1153-4DED-97F2-F1388A754AB5, are much too long and tied to their respective apps. This makes them less flexible for workflows involving other tools.

I wanted links in the following format:
http://id/1A22832D
The links should be as short as possible while still remaining unique. Each link would point to a key in the frontmatter of a Markdown file, like this:

---
title: Test Note  
aliases: 1A22832D  
---

The Current Solution

At first glance, this solution may seem like a Rube Goldberg machine, but once it’s set up, it’s simple to use.

Generating the ID

Coming up with a unique alias for every note is cumbersome. While DEVONthink’s 32-character UUID is too long, the first 8 characters are unique enough for my needs.

Here’s a quick calculation:
Generate 10,000 random 8-character IDs from the hex set {0-9, a-f}.
The total possible unique IDs: 16^8 = 4,294,967,296. This means about 2 duplicates in 10,000 IDs, which is acceptable for my use case.

I wrote a script that:
1. Takes a DEVONthink UUID,
2. Shortens it to 8 characters,
3. Copies it to the clipboard.

Here’s how I use it in practice:
1. I copy a DEVONthink link.
2. I run my script (dlink) in the WARP terminal’s visor mode, and the short UUID is ready to paste.
3. I add the shortened UUID to the frontmatter of my notes.

With this setup, I can:

  • Search for the note in DEVONthink: aliases BEFORE 1A22832D
  • It recognises the key/search term aliases in DEVONthink: aliases==1A22832D
  • If I copy the ID to the alias field in the Inspector, I can use Wikilinks: [[1A22832D]].

A search with a URL looks like this
x-devonthink://search?query=aliases%20BEFORE%201A22832D

This makes it easy to create URLs for other apps like iA Writer, nvUltra, or Apple Shortcuts.

Redirecting the Link on macOS

Now, I wanted http://id/1A22832D to redirect to x-devonthink://search?query=aliases%20BEFORE%201A22832D.
I should be able to click on the short link in any note-taking app, and the search would open.

On macOS, this is possible with two apps: Redirect Web for Safari and Link Unshortener (by the developer of StopTheMadness). Both apps work well for this purpose.

For Link Unshortener, I use the following regex in the Redirects settings:

  • Match: /http://id/(.+)/
  • Replace: x-devonthink://search?query=aliases%20BEFORE%20$1
    Where $1 is replaced by the ID.

Redirecting the Link on macOS and iOS

The apps Redirect Web for Safari and StopTheMadness also work on iOS, creating redirects to usual web-URLs. However, they cannot handle app-specific URLs like devonthink://.

To solve this, I built a plugin for a self-hosted URL shortener, YOURLS. YOURLS can be installed via Docker on a Synology NAS. For example:
https://yourls.ralf.example.com/brett redirects to https://brettterpstra.com.

Normally, YOURLS creates a 1:1 mapping from the short URL to the target URL. My plugin, however, carries over the ID.

Now, https://yourls.ralf.example.com/1A22832D resolves to:
x-devonthink://search?query=aliases%20BEFORE%201A22832D

By combining YOURLS with Redirect Web for Safari, I can go from:
http://id/1A22832D → YOURLS → DEVONthink search.

The Tools You Need

To make this work, I use:
1. A script to generate the unique IDs.
2. Redirect Web for Safari for macOS/iOS or Link Unshortener for Mac only.
3. A self-hosted YOURLS instance with my plugin.
4. A search program that supports URLs, such as Apple Shortcuts, nvUltra, or DEVONthink.

The short scripts I’ve written are available to share. Ideas welcome!

By the way, I’ve been using the solution on the Mac for a year now. Only the extension with Yourls was added today to get it work on the iPhone (again).

I have now taken things a step further. YOURLS, or rather the YOURLS plugin, fully resolves URLs of the type http://id/
With the YOURLS plugin, I can define any additional rules as needed. This eliminates the need for using “Redirect Web for Safari” entirely.

  • If an 8-character ID follows, it is converted into a DEVONthink URL.
  • If the link meets other patterns it can be expanded to shortcut URLs (shortcuts://) and other uses.
  • If “brett” follows, it is treated as a short URL and redirects to Brett’s website.
  • And so on…

I have set up a DNS record in Pi-hole, which redirects the domain “id” to the IP of the reverse proxy (the standard reverse proxy from Synology).
The reverse proxy forwards http://id:443 to http://192.168.1.33:8327, which is the IP and port of YOURLS.
Additionally I installed the YOURLS plugin “Allow Aliases” to enable IP-based YOURLS access

The advantage of this revised approach is that I no longer need any apps, and the link with the ID works on any device in the network.

the plugin:

<?php
/*
Plugin Name: ID Redirect
Description: Redirects 8-character alphanumeric IDs to DEVONthink search queries.
*/

// Hook to handle redirection when a keyword is not found
yourls_add_action('redirect_keyword_not_found', function($args) {
    $keyword = $args[0];

    // Check if the keyword is exactly 8 alphanumeric characters
    if (preg_match('/^[a-zA-Z0-9]{8}$/', $keyword)) {
        // Redirect to the DEVONthink URL
        header("Location: x-devonthink://search?query=$keyword", true, 302);
        exit;
    }
});
1 Like

This is really cool and a great idea. Do you want to share these scripts/plugins as a Gist or some place I could link to it from a Web Excursions?

Oh, BTW @rhsev , did you ever see my bookmark-cli project? Might be something you’d be interested in integrating:

https://github.com/ttscoff/bookmark-cli

I wrote about it years ago but the downloadable on that page won’t work with SIP enabled, so you have to compile from source.

It should be easily revivable as a Swift script if anyone had interest in looking into it.

1 Like

Nevermind, I guess that makes way longer strings than you’re looking for. Definitely not 8-char ids. But if you ever do want persistent bookmarks to files that survive renaming and moving, it’ll do the trick. Just compiled it under Sequoia and it still works.

Sure, I can share my short scripts on Gist. I just need to polish them a bit over the weekend and add some comments in the script.

Bookmark-cli sounds definitely interesting - I’ll take a closer look and get back in a few days. Thanks for the pointer!

Oh yeah, bookmark-cli could be used to store the bookmark in a file the way Hook files work. Then you could name them based on the short id and use them as an intermediary to the file. Outside of DEVONthink it could mean you could locate the file without adding frontmatter to it. Inside DEVONthink I don’t know how much use it would be.

But at that point you might as well just use Hookmark :slight_smile:

A few days ago I already created a small Ruby script. It works as a wrapper around bookmark-cli. It maps the very long bookmark IDs (nearly 2000 characters) to shorter 9-digit numbers and stores these mappings in a JSON file.
For easier lookup, the script also adds the short ID to the file’s Spotlight metadata. This way I get manageable IDs while keeping the robust file tracking of bookmark-cli. The script provides CLI commands to save, find and manage these bookmarks.
I will clean up the code a bit and share it, maybe someone finds it useful.

Here is the link to my first Gist GitHub Gist, a bookmarker based on Brett Terpstra’s bookmark-cli.

But at that point you might as well just use Hookmark :slight_smile:

Yes, but it gives me much shorter IDs and URLs. I’m even considering reducing the 9-digit IDs to just 4 digits, as I will never need more than 9,999 robust and unique links.

Awesome! I forked it and made a version that fit my own needs:

The changes I made are listed in the comments at the top. This is super handy, nice work!

Cool, thank you for the adjustments. I will use this as a template for upcoming scripts.

Made a few changes, mostly so I could run it from within a System Service (Quick Action). Needed a hardcoded path for the bookmark binary, among other things.

Added a simple script that can be used in a Quick Action to allow [[XXXXXX]] wiki linking from any application.

https://gist.github.com/ttscoff/87c1501e7325df387d10a9bd717beb48#file-wiki_link_service-rb

1 Like

Interesting to see what you can make out of it! An optional comment when creating a bookmark could be useful, as it would be stored in the bookmark JSON and displayed when listing the bookmarks.

I think the script is especially useful for bookmarking directories, especially since they may often move around.

Oh, I was tweaking before I saw your last note. I added some basic optparser handling so we can add something like --note "comment" handling to it instead of passing arguments.

In my latest version, if you pass a string after the path in the add command, it uses that as the alias instead of generating a numeric string. It still handles incrementing keys if one is repeated.

This allows me to name the bookmarks intuitively and then use the wiki link service to create links like [[My Markdown File]] and open the associated file. Made updates to the service script to match.

Digging this!

Great! How does this work with WikiLinks? Is it an extension, e.g., for VS Code?

Here the link to my other script that I mentioned in the initial post. Gist
This script retrieves the UUID and file path of the currently selected document in DEVONthink. It adds the shortened UUID to the document’s frontmatter under an aliases key and sets it as macOS metadata. This enables better cross-referencing and searchability of DEVONthink documents.
I briefly considered integrating this with bookmarker, but I think there is no need for JSON if the key is stored in the frontmatter of a markdown file

Edit: A selected markdown document needs a frontmatter key ‘aliases:’ before starting the script.

1 Like

The script I added as a second file in my fork of the gist can be added to an Automator Service or Shortcut as a run shell script action. It will parse for [[links]] and run them through bookmarker.

I’ve created a repo for the bookmarker script so that people (mostly @rhsev) can make pull requests with changes.

I made the Service script allow for multiple [[wiki links]] in the selected text, opening them all.

I also added a Makefile to bookmark-cli so that, assuming you have Xcode installed, you can just run make to build and install the bookmark tool.

By the way, these bookmarks created with bookmark/bookmarker will work on other machines, assuming the bookmarked file is synced (iCloud/Dropbox/etc.) to the same directory structure.