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.
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.
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.
| Item | Value |
|---|---|
| Spec version | 0.2-draft Draft |
| Version in every document | "schema_version": "0.2", enforced by the JSON Schema |
| Dated | Thu 16 Apr 2026, last changed Fri 17 Apr 2026 |
| Encoding | JSON, written in RFC 8785 canonical form, checked against a JSON Schema (draft 2020-12) |
| License | Spec text and schemas CC BY 4.0; reference code Apache-2.0 |
| Breaking changes | Allowed at any minor version while the format is 0.x, each one logged in the changelog |
Nine entities.
A library document holds tracks and the things DJs attach to them. The core section of the spec defines each one.
| Entity | What it holds |
|---|---|
ProvenanceValue | The wrapper on every analyzed field: the value, its source, an optional confidence, and when it was set. |
Track | Identity (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. |
CuePoint | Hot, memory, loop, grid, fade and load cues, with pad slot, name, color and length. |
BeatGrid | An origin and a BPM, or an explicit list of beat times for variable-tempo tracks. |
Playlist | An ordered list of track ids, an optional parent folder, and named play orders. |
PlayOrder | A named ordering of the same playlist for a different context, such as an opening set or peak time. |
Pairing | A directed, sourced link from one track to another that mixes well with it. |
Session | One performance: when it started and ended, and the deck events in between. |
Transition | A 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.
docs/architecture/open-dj.mdProvenanceValue<T> = { value, source, confidence?, modified_at }
open-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.
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.
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.
| Path | Status | What it does |
|---|---|---|
python -m apps.open_dj.cli | Works | The 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 Traktor | Partial | Reads 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 Traktor | Partial | Writes 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 djay | Not yet | Dry runs, no live writes. Live writes to those apps go through Open DJ's separate sync writers, not through open-dj import. |
| The Mac app | Not yet | No import or export control. Its settings can name open-dj as a preferred sync destination, but that writeback is marked not implemented. |
Planned, not built.
The spec runs ahead of the code. These items are planned or open, and the architecture doc says so itself.
| Item | Status | Detail |
|---|---|---|
| Version 1.0 | Planned | Needs 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 adapter | Planned | The spec's own 0.2 milestone. Today rekordbox can be exported, not imported. |
| Larger conformance suite, sessions decision | Planned | A bigger test corpus and a decision on whether sessions become a separate spec. |
| Stable reference CLI, history privacy mode | Planned | Including a redaction mode for listening history. |
| Public spec site | Planned | The schema ids already name a web address, but the spec says nothing is hosted there yet. |
| Second independent implementation | Planned | From another author, tested head to head. |
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.