Changelog

On this page
  1. 2026-10-27: what real tools need
  2. 2026-10-20
  3. 2026-10-19
  4. 2026-10-18

Changelog

What was decided in the specifications, and when, newest first. The specifications say only the rules; this is where their history lives. Core stays 1.0 (draft) and the modules keep their versions until they are final, so everything below is part of those versions.

2026-10-27: what real tools need

A review walked the tools on deejay.tools, the DJ library converters and three demo apps built on a home's Access API through the specification. What it changed:

Core

  • Items gain note (the person's own text), composers, album_artists (display text next to album), release_type on releases (album, single, ep, compilation, an open list), and the read-only play_count and last_played_at, which a home derives from listens.
  • key is written in ID3v2.4 TKEY notation (Am, F#, Dbm, o); enharmonic spellings are equal. Camelot and Open Key are for display.
  • Identifiers gain iswc (T3452468001).
  • file gains audio_hash and fingerprint (both { "algorithm", "value" }, fingerprint moved from the DJ module). The same hash in one source is a moved file; on two sources, one item. A shared fingerprint is a suggestion.
  • Lists gain folders (folder, parent, position), color, and filter for smart lists. A colour is a tag: an unordered list with a color. Entries gain note. Role history for history playlists. No core list kind, no version (mix name) field, no relations between tracks: iswc and catalogs cover "other versions of this song".
  • Ordered lists may repeat an item where the list's platform allows it.
  • The platform registry gives each platform's ref form, and whether it is global or personal. A Bandcamp ref is Bandcamp's numeric track or album id.
  • Combining adds audio_hash as a firm match, fingerprint as a suggestion, and label plus catalogue number for releases, except represses (different copies on the same source). Lists' parent and filter references follow the lists they name.
  • Listening history is kept by the home in ListenBrainz's format, apart from the collection.
  • A home's namespace keeps everything about the collection (hidden tracks and lists too), in files and through Access alike; only the state of changes not yet sent to a source stays inside the home. Exporting and letting a new home read through Access carry the same data.
  • An app may ask for what it sends to be held, and a held item keeps everything sent.
  • ext is for what the person would miss if they left the app, never an app's cache. A guide to where a property goes (nowhere, an app's namespace, a module's, the core). A future Credits module will use DDEX's role names.
  • No vendor in normative text: a home's namespace is com.example.home in examples.
  • A link on a registered platform (or a source with a descriptor) is a copy, on a source the home adds if the person has none: the platform's ids are stable and shared, so the copy can be played and matches exactly when the person later buys or saves it there. A shop page has no shared id, so its link goes in via. The source descriptor rule is unchanged.

Access

  • Entries of ordered lists are addressed by position, with a list version (a member and ETag, never in files); a stale version is 409 version_conflict. Moving an entry is one request. Entry note can be changed. A repeat an ordered list's platform refuses is reported in unchanged, like every idempotent write, not as already.
  • GET /lists/can answers can for every list in one request; every response carrying a list carries can; /changes reports a list whose can changed. Smart lists answer add: false.
  • /me gains user.name.
  • data_bytes covers only an app's own namespace, per app per record; module data has each module's own minimum (DJ: 1 MB per copy).
  • Who may write a namespace: the home's own, the home only; a module's, any app with the scope; an app's own, that app (by its client_id domain, or the first app to write it). Everyone reads everything, and the person's own edits are never restricted. 403 namespace_not_yours.
  • Apps may write rating as the person's value. Measured values go on the copy they came from.
  • Listens: POST /listens in ListenBrainz's submit format, GET /listens, scopes listens:write and listens:read.
  • Apps may be known by an address they own (OAuth Client ID Metadata Documents), alongside registered clients.
  • POST /items?hold=true asks for the item to be held. An item needs a link or identifiers. A link on a registered platform is a copy (the home adds the source if needed); a shop's link goes in via.
  • Webhooks are left to each home. Device sign-in waits until an app without a web address needs it.

DJ

  • DJ data lives on the copy whose audio was measured, file or stream.
  • One timing basis: milliseconds from the first real sample, after the gapless header's delay and padding.
  • Cue kinds hot, memory, loop, fade_in, fade_out, load, an open list.
  • Compact beat grids, [at_ms, bpm, beat_in_bar], markers only where something changes. The grid's bpm places beats; the item's is for display.
  • analysis gains energy, audio (preview or full), bpm and key.
  • Waveforms are never stored. Stems separated on the fly are the app's; stem files are file copies.

Other modules

  • Feeds: a people's feed carries entry notes and never item notes. Recommendations are a feed.
  • Folder: an item's body is its core note, a list's is its description; lists gain folder, position, color and a derived parent link; iswc, composers, album_artists and release_type are properties.
  • Catalog and Commerce: resolvers answer by copy as well as by identifier, for the platforms asked.
  • Delivery: files on the person's own devices are out of scope.

2026-10-20

  • Core. Ids are local, and collections combine by contents: sources by platform and stable account (new account_name for display; local sources never match on their own), copies by source and ref, items by a shared copy, then MusicBrainz, ISRC or UPC, then title and artists as a suggestion only. Conflicts are settled by the person, never by timestamps.
  • A collection may be all releases. Copies gain position (vinyl sides), and tracks on sources without track ids use <release ref>/<position>. Physical copies gain format, format_details, condition and location; releases gain catalog_number and country.
  • Ownership is a claim, and the person's correction wins (provenance origin user). New role wanted. Roles stay few; apps use namespaced values of their own (com.example.app/borrowed), shown as the nearest core value.
  • Dates: how item, copy and entry added_at relate; updated_at removed (kept as unknown data from older files); provenance on copies and entries, with new origins estimated and inferred.
  • The header carries no sync state; counts splits recordings and releases; optional collection name.
  • New modules: Share (sending music to someone) and Collector (a copy's history).
  • Access. The cursor travels in the OMC-Cursor header of /collection, never in the file; can per app and list, never stored; lookups by content; POST /import; add-only apps (library:add); views by record type. DJ gains mixes.
  • Feeds. Feeds are JSON Feed 1.1 (and optionally Atom or RSS) with OMC records in their entries; push is WebSub; homes may publish a person's list as a feed.
  • Folder. omc and id replace omc_type and omc_id (still read); notes made by hand (no id, a template, shelf); the collection's name names the folder.

2026-10-19

  • Homes, apps and sources: a collection has exactly one home, apps change it in place within a view the person gives each connection, and sources are places with their own copy that the home syncs with. Connections, views, sync positions, change attribution and id maps are the home's state and never appear in records or files. via is written once, for people, on items and lists.
  • How core fields and extensions relate: core stands alone, an extension refines and never contradicts, core wins, a missing extension is derived from core. A host ignores writes to its own namespace instead of refusing them.
  • List entries carry an optional copy ref on lists that live on a source, and an unordered list holds an item once per copy there. Found rebuilding real playlists from a home's export: Spotify Liked Songs held 38 pairs of copies the home knew as one recording each.
  • From a real collection of 7,567 items and 411 lists: title may be empty (platforms return tracks they no longer serve without one), and a local file's ref must at least replace the home directory with ~ when the producer doesn't know the source folder. The Folder module round-trips that whole collection record for record.
  • Access. Telling an app's own writes apart, with by on change entries and a cursor on write responses.
  • Folder. Readers accept standard Markdown links and every Obsidian link style. The layout is free, so a folder per artist or letter needs no specifying. Tested on a 7,991-record collection rearranged by artist (354 repeated file names) and as a pile of randomly named notes.

2026-10-18

Found while building the reference validator, the examples and the first Access implementation:

  • Lists on a source get an optional ref (the source's own list id). saved allows one favourites list per kind of item (Spotify Liked Songs and Saved Albums). Ownership values are an open list like roles and kinds.
  • A host's own ext namespace is the host's and read-only to apps; app data is everything that isn't the host's own field. PATCH is an RFC 7396 merge patch, host fields must be sent unchanged (422 field_not_writable), and host-managed dates are ignored.
  • A host may refuse data it can't keep (size, copies on sources it can't hold, entry data) with an error, never silently.
  • The export header is per export; keeping unknown data applies to records.
  • The platform registry gains itunes and shelf.
  • Access scopes library:read, lists:read, lists:write, library:write.