Skip to content
Rymflux Documentation
Esc
navigateopen⌘Jpreview
On this page

ADR-003: Content IDs — UUIDv4

ADR-003: Content IDs — UUIDv4

Status

Accepted

Context

Every content item stored in the library, queue, playlist, or history must have a stable, source-independent identifier (FR-ID-1). The ID must not encode the source plugin (Constraint C4). Options:

  • UUIDv4: Random 128-bit UUID. No encoding of source, time, or content. Standard library support in every language.
  • UUIDv5 (content-hash): Deterministic UUID derived from hashing content metadata (e.g., SHA-1 of title + artist + album). Same content from different plugins would produce the same UUID automatically.
  • ULID: 128-bit, sortable, encodes timestamp. Good for time-ordered identifiers but adds ordering semantics the library doesn’t need.
  • Plugin-scoped ID + foreign ID tuple: (plugin_id, foreign_id) as the primary identifier. Simple but violates Constraint C4 (content is not independent of source).

Decision

Use UUIDv4 for all content identifiers. The ID is generated by the core at the moment a content item is first saved to the library (not by the plugin). The resolution table maps content UUID to one or more (plugin_id, foreign_id) tuples.

Deduplication is handled by an advisory layer: before assigning a new UUID, the search manager checks existing resolution table entries for matching (title, artist, duration) across plugins. If a match is found, the existing UUID is reused. This is best-effort (Constraint A6).

Alternatives considered

  • UUIDv5 (content-hash): Appealing in theory but unreliable in practice. Plugins return inconsistent metadata for the same track (different title casing, missing artist, different duration due to encoding). A hash mismatch means a duplicate UUIDv5 under a slightly different metadata key, which is worse than a duplicate UUIDv4 (UUIDv5 collisions are silent; UUIDv4 duplicates are explicit and mergeable).
  • ULID: Adds timestamp sorting as a side effect. If the library needs time-ordered IDs, an added_at column is the explicit and correct solution. Encoding time in the ID creates coupling between ordering semantics and identity.
  • (plugin_id, foreign_id) tuple: Simple, violates the core platform philosophy (Principle 1). If a track moves from Plugin A to Plugin B, every library reference to (A, foreign_id) breaks. UUIDv4 breaks this coupling.

Consequences

Positive:

  • Truly source-independent. The UUID carries no information about provenance.
  • UUIDv4 is supported in every language’s standard library or well-known package.
  • The resolution table can be updated independently of library items (if a plugin changes its foreign_id scheme, only the resolution table updates).

Negative:

  • Same content from two plugins requires the deduplication layer to discover the match. Without it, the same track gets two UUIDs.
  • UUIDv4 is not human-readable or debuggable. Logging and diagnostics reference UUIDs, not human-friendly names. Mitigation: the diagnostics system always includes title + artist alongside the UUID.
  • Random UUIDs are not sortable. All ordering uses explicit timestamp columns (added_at, played_at).

References

  • Constraint C4 (constraints-assumptions.md)
  • FR-ID-1, FR-CORE-3 (functional.md)
  • Principle 1 (vision.md)

Was this page helpful?