Skip to content

Data Model

A library is the top-level entity — a music collection with its own identity (library_id), name, and encryption key. It lives at a library home on disk (~/.bae/libraries/{uuid}/), where the desktop app writes the authoritative database.

A library can optionally have a cloud home — a single cloud location (Google Drive folder, S3 bucket, etc.) that acts as the collaborative hub for multi-device sync and multi-user access. One cloud home per library, configured in config.yaml. See Cloud Home for details.

Release files can live in one or more of these locations:

  • Library home (~/.bae/libraries/{uuid}/storage/) — local copy, available offline
  • Cloud home (cloud-home/storage/) — encrypted copy, available for sync
  • Unmanaged — files stay wherever they are on disk, bae just indexes them

A release can be local only, cloud only, or both. Cloud-only files are not available for offline playback.

~/.bae/libraries/{uuid}/
config.yaml # device-specific settings (not synced)
library.db # SQLite -- all metadata
images/ab/cd/{id} # library images (covers, artist photos)
storage/ab/cd/{file_id} # release files
manifest.json # library identity (library_id, name, encryption fingerprint)

config.yaml holds device-specific settings. It is not synced. Credentials go in the OS keyring, not here.

Files under images/ and storage/ use a hash-based layout: the first 2 characters of the ID form the prefix directory, the next 2 form a subdirectory. No filenames or extensions on disk — original filenames and content types live in the database. Same layout in both the library home and cloud home.

The cloud home mirrors the library’s data and adds sync and access control machinery:

PathPurpose
snapshot.db.encFull database snapshot (bootstrap for new devices)
changes/{device_id}/{seq}.encIncremental sync changesets
heads/{device_id}.json.encPer-device sequence numbers
images/ab/cd/{id}Library images (encrypted)
storage/ab/cd/{file_id}Release files (encrypted)
membership/{pubkey}/{seq}.encMembership chain entries
keys/{user_pubkey}.encPer-user wrapped encryption keys

Managed by KeyService, using the OS keyring (on macOS, the protected data store with iCloud Keychain sync). All entries are namespaced by library_id unless noted otherwise.

EntryScopePurpose
encryption_master_keyper-libraryFile and metadata encryption
cloud_home_credentialsper-librarySerialized enum: S3 access+secret, OAuth token, or none (iCloud)
discogs_api_keyper-libraryDiscogs API access
server_passwordper-librarySubsonic API authentication
followed_password:{id}per-libraryPer-followed-library password
bae_user_signing_keyglobalEd25519 signing key
bae_user_public_keyglobalEd25519 public key

Whatever files came with a release: audio, images, CUE sheets, logs, etc. These are the user’s data. bae stores them exactly as imported so they can be ejected intact or seeded as torrents.

Images that bae creates and manages, separate from release files:

  • Release covers — display art for album grids, detail views, playback. One per release. May come from a file in the release or from MusicBrainz/Discogs. bae makes its own copy, so it can crop or resize without affecting the original.
  • Artist images — fetched from external sources during import.

New library: On first run, the desktop app shows a welcome screen. The user creates a new library, which generates a UUID, creates the directory structure, and launches the app. The storage/ directory starts empty — files appear as the user imports.

Restore from cloud home: The user provides cloud home credentials and the encryption key. bae downloads the snapshot, applies any newer changesets, and downloads images. Local storage/ starts empty — release files stream from the cloud home on demand.

Going from local to cloud: The user signs in with a cloud provider. bae generates an encryption key (if needed), pushes a full snapshot, images, and release files, then switches to incremental sync.