A+BLibrary format

Format

open-dj is the library format Open DJ publishes: tracks, cue points, beatgrids, playlists, pairings and play sessions as canonical JSON, where every analyzed value records who produced it and when. Inside the Mac app, the library records where each value came from; exporting or importing an open-dj file is a tool in a source checkout, and the Mac app has no import or export control yet. Deck A is what the format holds; Deck B is who reads and writes it today, and what is still planned.

Draft spec Works Not yet

Summarized from the private development repository (music-dj-tools): docs/architecture/open-dj.md and the spec it describes, open-dj/spec/v0.2/open-dj.md, at 487fa677d7, checked against the code on Sat 3 Oct 2026.

ADeck A / Version

Version 0.2, a draft.

The spec calls itself a draft and expects breaking changes before 1.0. Read it as a working proposal, not a frozen standard.

ItemValue
Spec version0.2-draft Draft
Version in every document"schema_version": "0.2", enforced by the JSON Schema
DatedThu 16 Apr 2026, last changed Fri 17 Apr 2026
EncodingJSON, written in RFC 8785 canonical form, checked against a JSON Schema (draft 2020-12)
LicenseSpec text and schemas CC BY 4.0; reference code Apache-2.0
Breaking changesAllowed at any minor version while the format is 0.x, each one logged in the changelog
ADeck A / Structure

Nine entities.

A library document holds tracks and the things DJs attach to them. The core section of the spec defines each one.

EntityWhat it holds
ProvenanceValueThe wrapper on every analyzed field: the value, its source, an optional confidence, and when it was set.
TrackIdentity (id, title, artists, album, ISRC, duration, file path, content hash), then BPM, key, energy and rating as provenance values, plus cues, beatgrid and vendor ids.
CuePointHot, memory, loop, grid, fade and load cues, with pad slot, name, color and length.
BeatGridAn origin and a BPM, or an explicit list of beat times for variable-tempo tracks.
PlaylistAn ordered list of track ids, an optional parent folder, and named play orders.
PlayOrderA named ordering of the same playlist for a different context, such as an opening set or peak time.
PairingA directed, sourced link from one track to another that mixes well with it.
SessionOne performance: when it started and ended, and the deck events in between.
TransitionA move from one deck to another inside a session: when, which tracks, which kind (cut, blend and so on) and how long.

The provenance rule

Analyzed fields are wrapped, so a corrected BPM keeps a record that a person set it, and the next app's analysis does not silently replace it. Identity fields are not wrapped: the doc calls them observed facts, and if they change, the track id typically changes too.

Extension fields start with x_. A tool that does not know one must keep it on a round trip, in every version.

the provenance envelopedocs/architecture/open-dj.md
ProvenanceValue<T> = { value, source, confidence?, modified_at }
one BPM a person set, with four vendor analyses kept beside itopen-dj/conformance/corpus-0.2/case-10-provenance-stress.open-dj.json
{
  "kind": "library",
  "schema_version": "0.2",
  "tracks": [
    {
      "artists": [
        "Test"
      ],
      "bpm": {
        "confidence": 1,
        "modified_at": "2026-03-11T18:42:11Z",
        "source": "manual",
        "value": 128.02,
        "x_prior_djay": {
          "modified_at": "2026-02-02T09:00:00Z",
          "source": "djay",
          "value": 128.04
        },
        "x_prior_mik": {
          "modified_at": "2026-02-02T09:00:00Z",
          "source": "mik",
          "value": 128.01
        },
        "x_prior_rekordbox": {
          "modified_at": "2026-02-02T09:00:00Z",
          "source": "rekordbox",
          "value": 128.03
        },
        "x_prior_serato": {
          "modified_at": "2026-02-02T09:00:00Z",
          "source": "serato",
          "value": 128
        }
      },
      "content_hash": "sha256:a9c250a9aab3e5b3deccc0da93b3926ae6fec1a4ceb8a531604fd7c849dff988",
      "duration_ms": 240000,
      "file_path": "/music/test/prov-test.flac",
      "isrc": "USPVN0000001",
      "title": "Provenance Test",
      "track_id": "e5169c5b8cf9082bbd039da915aae1f71f3b63a0"
    }
  ]
}

A test fixture from the spec's conformance set, with line breaks added for reading. On disk it is one canonical line with sorted keys and no spaces, which is what makes its SHA-256 stable.

ADeck A / Canonical form

Same library, same bytes.

The aim is that two tools writing the same library produce byte-identical files.

Why canonical JSON

The reference code writes RFC 8785 JSON: sorted keys, no whitespace, shortest round-trip numbers. The SHA-256 of those bytes is the document's hash. Without it, key order and whitespace would read as changes, the hash would drift, and a sync could not tell that nothing changed.

Two kinds of conformance test

Document tests check that each corpus file is already canonical, stays the same when written again, and validates. Adapter tests write a document through a vendor adapter and read it back; a failure means the adapter lost data it did not declare it would lose.

BDeck B / Who uses it

Read and written by Open DJ, for now.

Today the program that reads and writes open-dj files is Open DJ's own reference tool, run from a source checkout. This project knows of no other software that reads or writes the format.

PathStatusWhat it does
python -m apps.open_dj.cliWorksThe reference tool: validate, canonicalize and diff open-dj files, export from a vendor library, import into one. Runs from a source checkout.
Export from rekordbox, djay, Serato or TraktorPartialReads a vendor library and writes an open-dj file, from a source checkout. The Serato reader supports libraries from before Serato 4.0. The Serato and Traktor readers are checked on test files only.
Import into Serato or TraktorPartialWrites an open-dj file back into the vendor library. The code exists and is tested on synthetic files only; real-library sign-off is deferred, so do not run it against your real library. A dry run unless the caller passes both --live and --i-understand-the-risks. The Traktor writer has only that dry run and those two flags: no running-app check, backup, atomic write or read-back.
Import into rekordbox or djayNot yetDry runs, no live writes. Live writes to those apps go through Open DJ's separate sync writers, not through open-dj import.
The Mac appNot yetNo import or export control. Its settings can name open-dj as a preferred sync destination, but that writeback is marked not implemented.
BDeck B / Planned

Planned, not built.

The spec runs ahead of the code. These items are planned or open, and the architecture doc says so itself.

ItemStatusDetail
Version 1.0PlannedNeeds two independent implementations, all four vendor adapters passing a frozen corpus, a real library round trip, and a deprecation policy. None is met.
Rekordbox read and write adapterPlannedThe spec's own 0.2 milestone. Today rekordbox can be exported, not imported.
Larger conformance suite, sessions decisionPlannedA bigger test corpus and a decision on whether sessions become a separate spec.
Stable reference CLI, history privacy modePlannedIncluding a redaction mode for listening history.
Public spec sitePlannedThe schema ids already name a web address, but the spec says nothing is hosted there yet.
Second independent implementationPlannedFrom another author, tested head to head.
Two known gaps in the spec itself.

The worked example in Appendix A still says "schema_version":"0.1", so it fails the 0.2 schema. And the rekordbox and djay exporters often fill content_hash from file size, date and path rather than the audio bytes, and mark it x_content_hash_mode: "inferred".

The full spec

The spec, its JSON Schema, the conformance corpus and the deep-dive this page summarizes live in the Open DJ source, under open-dj/ and docs/architecture/open-dj.md. They will be linkable once the source opens; see slot S5 on Contributing.

A / Parity
Beyond / B { type: 'crossfader', value: 0.50 }