An Experiment - UUID v7 for TiddlyWiki

This is an experiment and not intended to be used for production wikis.

Giving Every Tiddler a Birth Certificate

The status quo

TiddlyWiki identifies tiddlers by title — and titles must be unique within a wiki. That’s fine and should stay that way. But titles alone can’t answer:

  • When you merge two wikis — are these two differently-titled tiddlers actually the same?

  • When you import — did this tiddler already exist under another name?

  • When you rename — how do external references (URLs, printed notes, conversations) find it again?

There’s no built-in way to say “this is the same tiddler” across wikis or across renames. A c32 field gives every tiddler a permanent identity that is independent of its title.

What this plugin does

The wikilabs/uuid7 plugin assigns every new tiddler a UUID v7 identifier — a 128-bit, globally unique, time-ordered ID defined in RFC 9562 (2024). It stores this ID in two formats:

c32 — Crockford Base32 (29 chars)

01KMP4-ND9W-E8Y8V5YM1DR0-01VG

c7 — Standard UUID hex (36 chars)

019d2c4a-b53c-723c-8d97-d40b70000770

The c32 field uses Crockford’s Base32 encoding in a 6-4-12-4 display format. Same 128 bits as c7, just more compact and a bit more human-friendly:

c7 (UUID hex) c32 (Crockford Base32) TW created
Length 36 chars 29 chars 17 chars
Sortable Yes Yes Yes
Unique Globally Globally Not guaranteed
URL-safe Yes Yes Yes
Human-readable Hard Better Readable (but not unique)

The c32 format adds only 12 characters over the existing created field — in exchange for guaranteed global uniqueness.

Mnemonic phrases

Every UUID is also encoded as 8 adjective-noun-verb triplets:

metal dog soars, clear hawk drifts, proud leaf seeks, ...

You can navigate to a tiddler by typing a few phrase words in the URL:

https://mywiki.com/#metal+dog+soars

Remembering tiddlers

You don’t need to memorize 29 characters. The 6-4-12-4 format is designed so you can remember just the first few and last few characters:

Remembered Example Safe up to
First 3 + last 4 (6 chars) 01K + 01VG ~5K tiddlers
First 5 + last 4 (8 chars) 01KMP + 01VG ~1M tiddlers
First group + last group (9 chars) 01KMP4 + 01VG millions

Navigation

c32 values work as wiki links and URL hashes — full or partial. Because the 6-4-12-4 format groups bits by meaning, partial matches act as ranged selections:


[[01KMP4]]                            → all tiddlers from the same ~17 min window
[[01KMP4-ND9W]]                       → narrows to the exact millisecond
[[E8Y8V5YM1DR0]]                     → double-click, copy, paste — 56 random bits, clash: 1 in ~72 quadrillion
[[01VG]]                              → last 4 chars — usually unique in any wiki
https://mywiki.com/#01KMP4            → URL: opens all tiddlers from that time window
https://mywiki.com/#01VG              → URL: opens the specific tiddler

The first group is a time-based range — sharing a link like #01KMP4 opens every tiddler created in that ~17-minute session. The last group is a random pinpoint#01VG almost certainly identifies one specific tiddler.

Input is case-insensitive with Crockford alias support (O0, I1, L1).

Filter operators

Both c32 and uuid7 filters have full feature parity:

[{!!c32}c32[ms]format:timestamp[YYYY-0MM-0DD]]  →  2026-03-26
[{!!c32}c32[phrase]]                             →  metal dog soars, clear hawk drifts, ...
[{!!c32}c32[check]]                              →  U  (mod 37 check symbol)
[{!!c32}c32[c7]]                                 →  019d2c4a-b53c-...  (convert to UUID)
[{!!c7}uuid7[c32]]                               →  01KMP4-ND9W-...  (convert to c32)

Backfill

Existing tiddlers get their c7 and c32 fields on import or via a startup backfill module (dry-run by default). Timestamps are derived from the existing created or modified field.

What it would need to be part of core

The plugin works today with zero core changes — it monkey-patches getCreationFields() at startup. But for first-class support, a small set of core changes would make it cleaner:

  1. boot.js — Register c32 as a built-in field type, so it’s available before plugins load (~5 lines)

  2. wiki.js getCreationFields() — Generate UUID v7 natively, store as c32 (Crockford Base32), and derive created from its timestamp. This eliminates the dual-field redundancy and makes the UUID the source of truth (~10 lines)

  3. Tiddler constructor — Enforce read-only: once a c32 is assigned, it never changes. This prevents accidental mutation through the UI (~5 lines)

Note: MWS already uses c7 (UUID hex) as a database index. The c32 format encodes the same 128 bits — conversion between c7 and c32 is lossless and trivial, so both can coexist.

That’s roughly 20 lines of core code. Everything else — display, sorting, filtering, serialization — works unchanged because tiddler.fields.created remains a Date object.

Backward compatibility

  • Existing tiddlers without UUIDs keep working — the field module falls back to parseDate
  • Code reading tiddler.fields.created gets a Date object as before
  • The UUID is an addition to the timestamp, not a replacement — nothing is lost

Try it

The plugin is available at wikilabs/uuid7 — install it, create a tiddler, and watch the c32 badge appear.

The plugin also provides a c7 field (standard UUID v7 hex format) with full feature parity to c32 — same filter operators, same navigation, same backfill. Both encode the exact same 128-bit value and are 100% compatible with the UUID v7 spec. Having both lets you explore which format you prefer — in the future only one may remain.

Links

This is fantastic! I really like this idea. I still want my human-readable urls, but this helps fill that gap, while adding a much-needed immutable id to tiddlers.

This was also my first exposure to Crockford’s base32, and I’m quite impressed. The check-digits are a little odd, expanding the alphabet for little gain, but otherwise wonderful.

I’ve only read the docs, but will try it out as soon as I’m done with my current time-consuming project.

Thank you very much for sharing!