audiotheca — Specification

version 0.1

This document specifies the audiotheca protocol: a music-library domain layer over a metatheca vault. audiotheca models a personal audio collection as artists, releases, tracks, encodings (the audio originals it received), and playlists — all realized as metatheca entries — with metadata recorded as facts in the audio/ namespace, original audio and artwork stored as metatheca content blobs, lower-fidelity renditions transcoded on demand into a local regenerable cache, and history inherited unchanged from metatheca.

The specification is language-agnostic. The reference implementation is in Rust (audiotheca-rs/).

For motivation, design choices, and the relationship to the substrate family, see README.md. For the layer this document builds on — entries, paths, facts, state chain, head, index — see ../../metatheca/spec/SPEC.md; for the storage primitives beneath, ../../syntheca/spec/SPEC.md and ../../apotheca/spec/SPEC.md. This document covers only what sits above metatheca.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.

1 Scope and terminology

This document specifies audiotheca Phase 1: a single local library over a single metatheca vault, covering ingestion (from existing files and from the import boundary), the entity and fact model, metadata-variant resolution, on-demand transcoding into a local cache, and query. Deferred items are listed in §12.

Terminology not redefined here is inherited from metatheca SPEC §1 and, transitively, syntheca and apotheca: vault, entry, entry ID (UUID v7), path, fact, state, state chain, head, index, cella, deposit/depositum, content blob, hash (BLAKE3).

1.1 Library

A library is the unit audiotheca owns: exactly one metatheca vault whose entries and facts conform to this specification. audiotheca MUST be the sole writer of audiotheca-domain entries and facts (§3). External tools MUST reach the library through the import boundary (§8), not by writing facts directly.

1.2 Durable store and derived cache

audiotheca uses two distinct stores, separated by whether their bytes are irreplaceable or regenerable:

A transcoded rendition is a pure function of its source deposit and encoder settings (§7.3); it is therefore derived state in the same sense as metatheca’s index and ext/ cellas, and is treated as cache, not vault content (§1.3 states the general contract for such state). The sole exception is a pinned rendition (§7.5), which is promoted into the durable store deliberately.

1.3 Auxiliary state

Beyond the derived cache, an implementation MAY maintain implementation-private auxiliary state — projections, indexes, or other derived structures that accelerate its own operation — under <vault>/ext/audiotheca/, outside any cella. These constraints make it legitimate:

  1. Observational neutrality. Auxiliary state MUST NOT change the observable behaviour of any operation: resolution and query answered from auxiliary state MUST return results identical to the same operation evaluated from the facts under the resolution rules (§5.1 — latest fact wins per metatheca projection).

  2. Disposability. Auxiliary state MUST be wholly derived: deleting it at any moment MUST NOT affect correctness, only performance. It is rebuilt or repaired only by the implementation that owns it.

  3. Containment. Auxiliary state MUST NOT be written into any cella, referenced from any fact, or otherwise become interchange surface. Another implementation MUST be able to operate on the same library while ignoring it entirely.

  4. Freshness. An implementation SHOULD key auxiliary state to the metatheca head (state hash) it reflects: content addressing makes staleness detection exact, and anything the auxiliary state cannot serve falls back to fact replay.

The derived cache and its cache index are the domain-specified instance of the same idea, carrying additional rules of their own (§1.2, §7, §7.3); this section governs any other private state an implementation keeps. Maintaining no auxiliary state is always conformant.

The reference implementation maintains a SQLite projection of entities and facts at <vault>/ext/audiotheca/audio.db, keyed by the head state it has projected: opening the library replays only the missing suffix of the state chain into it, and falls back to a full rebuild when the projected state is not an ancestor of the current head. The same contract was ratified for the search extensions in the logopsis and semopsis specs, whose auxiliary-state sections this one mirrors.

1.4 Deposit

Audio and artwork bytes are stored as content blobs addressed by BLAKE3 hash — apotheca deposita in a cella. Original audio and artwork are deposits in the durable store; transcoded audio is a deposit in the derived cache (or, if pinned, the durable store).

1.5 Entity and kind

An entity is a metatheca entry carrying an audio/kind fact (§3.1), of kind artist, release, track, encoding, or playlist. Artwork is not an entity (§4.4); transcoded renditions are not entities unless pinned (§7.5). An entry’s kind MUST NOT change over its lifetime.

1.6 Original and derived encoding

An original encoding is an audio rendition the library received, not one it produced — its lineage3.4) is ripped (from a CD via the import boundary, AccurateRip-verified, lossless) or imported (a pre-existing file, any format/quality). Originals are entities in the durable store.

A derived rendition is one audiotheca produced by transcoding (§7). Derived renditions live in the derived cache and are not entities (§1.5), except when pinned (§7.5), in which case they are encoding entities with lineage transcoded.

1.7 Primary and master

The primary of a track is its source of record: the original encoding that transcodes derive from and that the track resolves to when no profile is given. By default the primary is the highest-fidelity original under the ordering of §4.7; a track MAY override it with an audio/primary fact (§3.9).

A master is a primary whose fidelity is lossless. A track is not required to have a master (§4.7); a lossy primary is valid.

1.8 Metadata variant

A metadata variant is a named rendering of an entity’s metadata, identified by a short lowercase token. The canonical variant is utf8 and MUST be present for any entity bearing metadata. A derived variant (Phase 1 defines ascii) is computed from the canonical variant by transliteration, with manual pins5).

1.9 Encoding profile

An encoding profile is a predicate over an encoding’s technical facts (§3.5) — codec and bitrate constraints — interpreted as a ceiling for selection (§9.2). A profile MAY be attached to a playlist (§3.13) or passed to a query, export, or playback operation.

1.10 Canonical name and alias

A canonical name is a derived, normalized form of a display name, used for deduplication and query. It is produced by a frozen, versioned canonicalizer (vault setting canonicalizer_version, pinned at genesis like an analyzer): from the display name —

  1. lowercase;

  2. strip leading articles (Phase 1: English the, a, an);

  3. ascii-ize via the §5.2 transliteration;

  4. delete apostrophes;

  5. replace each run of remaining non-alphanumeric characters with a single -;

  6. collapse repeated - and trim leading/trailing -.

Examples: The Rolling Stonesrolling-stones; AC/DCac-dc; Sgt. Pepper's…sgt-peppers-….

Changing the canonicalizer is a versioned migration, not a silent edit: because every canonical name shifts, an implementation MUST treat a canonicalizer_version change as a re-derivation of all aliases.

An alias is a metatheca path (a core/path fact) pointing to an entity, spelled in canonical form. An entity MAY have several aliases (metatheca’s many-paths-to-one-entry); link adds one and mv renames one. Aliases are hierarchical, mirroring the reference chain: <artist-alias> for an artist, <artist-alias>/<release-alias> for a release, <artist-alias>/<release-alias>/<track-alias> for a track (e.g. miles-davis/kind-of-blue/so-what). The hierarchy namespaces aliases (so a band and an album that canonicalize alike do not collide) and provides navigation.

Identity is always the entry ID (§2.1); a canonical name or alias is a derived secondary key and MUST NOT be used as the stable identity. Two distinct entities MAY share a canonical name (caught by path uniqueness and disambiguated); the entry IDs keep them distinct.

2 Entities and identifiers

2.1 Entry IDs

Every audiotheca entity is identified by its metatheca entry ID (UUID v7; metatheca SPEC §2.1). References between entities carry entry IDs in canonical RFC 4122 string form.

2.2 References

Two kinds of reference, and the distinction is normative:

Audio and artwork MUST be referenced by hash; artists, releases, and tracks MUST be referenced by entity identity. An implementation MUST NOT reference an artist, release, or track by the hash of any of its current facts or audio, nor by its canonical name or alias (§1.10).

3 The audio/ fact namespace (current draft)

All audiotheca facts are metatheca facts whose kind begins audio/, each targeting one entry, serialized as canonical JSON (metatheca SPEC §3.1). Projection follows metatheca’s rule: for a given (target, kind, …discriminators), the latest fact in state order wins. Field shapes below are a current draft and MAY change before 0.1.

3.1 audio/kind

{ "kind": "audio/kind", "target": "<entry-id>", "value": "release" }

One per entry. value ∈ {artist, release, track, encoding, lyrics, playlist}.

3.2 audio/field — one metadata field

{
  "kind": "audio/field", "target": "<entry-id>",
  "variant": "utf8", "field": "title", "value": "Kind of Blue",
  "source": "musicbrainz", "pinned_from": null
}

Discriminators: (variant, field); latest wins, giving per-field editability.

Field names are not enumerated normatively. Conventional artist-level: name. Release-level: title, date, label, catalog, releasetype. Track-level: title, isrc. Which fields go on which kind is a matter of where they are recorded (§4), not a closed schema. Artist is not a field here — it is an entity reference (§3.14); a release’s or track’s artist is its referenced artist entity, whose own name is an audio/field.

3.3 audio/track-of — track → release placement

{ "kind": "audio/track-of", "target": "<track-id>",
  "release": "<release-id>", "disc": 1, "position": 3 }

One per track; a track MUST have exactly one in the current state.

3.4 audio/encoding-of — encoding → track, with lineage

{ "kind": "audio/encoding-of", "target": "<encoding-id>",
  "track": "<track-id>", "lineage": "imported" }

One per encoding. lineage ∈ {ripped, imported, transcoded}. ripped and imported are originals; transcoded appears only for a pinned rendition (§7.5) — unpinned transcodes are cache, not entities.

3.5 audio/tech — technical facts

{
  "kind": "audio/tech", "target": "<encoding-id>",
  "codec": "flac", "bitrate_bps": null, "mode": "lossless",
  "sample_rate_hz": 44100, "channels": 2,
  "duration_ns": 367000000000, "bytes": 28934112
}

codec ∈ {flac, mp3, wav, …}; mode ∈ {lossless, cbr, vbr}; bitrate_bps is null for lossless. The deposit hash and MIME come from the encoding’s metatheca core/blob-ref and are not duplicated.

3.6 audio/rip — rip provenance (ripped only)

{
  "kind": "audio/rip", "target": "<encoding-id>",
  "ripper": "whipper", "ripper_version": "0.10.0",
  "accuraterip_confidence": 7, "drive_offset": 6,
  "disc_id": "<musicbrainz-disc-id>",
  "ripped_at_ns": 1730000000000000000
}

accuraterip_confidence MAY be null if not AccurateRip-verified.

3.7 audio/import — import provenance (imported only)

{
  "kind": "audio/import", "target": "<encoding-id>",
  "original_filename": "03 So What.mp3",
  "imported_at_ns": 1730000000000000000
}

Records the file’s origin. The upstream fidelity is unverified: a file’s declared bitrate is the container’s claim, not measured (§9.3).

3.8 audio/transcode — transcode provenance (pinned only)

{
  "kind": "audio/transcode", "target": "<encoding-id>",
  "source_deposit": "<blake3-of-primary-audio>",
  "encoder": "lame", "encoder_version": "3.100",
  "params": "cbr-128/44100/stereo",
  "maintained_profile": null,
  "transcoded_at_ns": 1730000000000000000
}

Present only on pinned derived encodings (§7.5). For unpinned cache renditions the same tuple is the cache key (§7.3) but is held by the local cache index, not as a durable fact. maintained_profile is the identifier of the maintained profile (§7.6) that produced this rendition, or null for a manual pin; it lets the implementation find and refresh maintained renditions when the primary changes (§4.7).

3.9 audio/primary — primary override (optional)

{ "kind": "audio/primary", "target": "<track-id>",
  "encoding": "<encoding-id>" }

Overrides the default primary selection (§4.7). The referenced encoding MUST be an original of that track.

3.10 audio/artwork — release → artwork

{ "kind": "audio/artwork", "target": "<release-id>",
  "blob": "<blake3-of-image>", "role": "front", "mime": "image/jpeg" }

Discriminator: role (e.g. front, back, disc).

3.11 audio/lyrics-of — lyrics → track

{ "kind": "audio/lyrics-of", "target": "<lyrics-id>",
  "track": "<track-id>" }

One per lyrics entity (§4.5); a track has at most one current lyrics entity in Phase 1. The lyrics entity carries core/blob-ref to its UTF-8 plain-text deposit and core/mime (text/plain; charset=utf-8); it is the deposit that holds the text, never this fact.

3.12 audio/audio-id — track audio identity (optional)

{ "kind": "audio/audio-id", "target": "<track-id>",
  "pcm_hash": "<blake3-of-decoded-pcm>" }

An encoder-independent identity from decoded PCM. Optional; reserved so a future Recording entity (§12) can be introduced without changing the track model.

3.13 Playlist facts

{ "kind": "audio/playlist-meta", "target": "<pl-id>",
  "name": "thumbdrive", "note": null }
{ "kind": "audio/playlist-tracks", "target": "<pl-id>",
  "tracks": ["<track-id>", "<track-id>"] }
{ "kind": "audio/playlist-profile", "target": "<pl-id>",
  "codec": "mp3", "max_bitrate_bps": 128000 }

audio/playlist-tracks carries the full ordered membership, rewritten wholesale on edit. audio/playlist-profile is optional.

3.14 audio/artist — release/track → artist(s)

{ "kind": "audio/artist", "target": "<release-or-track-id>",
  "artists": [ { "artist": "<artist-id>", "role": "main" } ] }

One per target, carrying the full ordered artist list (rewritten wholesale on edit, like audio/playlist-tracks). role ∈ {main, feat}. References artist entities by entity identity (§2.2).

Artist resolution mirrors the metadata hierarchy (§5.1): a track’s effective artist is its own audio/artist if present, else its release’s. A normal album records the artist once on the release; a various-artists compilation records "Various Artists" on the release and each track’s actual artist(s) on the track.

4 The entity model

4.1 Release

A release (audio/kind = release) carries release-level audio/field facts, one audio/artist fact (§3.14) referencing its artist(s), and zero or more audio/artwork facts. It has no audio and no core/blob-ref. Referenced by entity identity from its tracks. Its alias is the path <artist-alias>/<release-alias>1.10).

4.2 Track

A track (audio/kind = track) carries track-level audio/field facts, exactly one audio/track-of fact, optionally one audio/artist3.14; absent ⇒ inherits the release’s artist), optionally one audio/primary and one audio/audio-id. It has no core/blob-ref; its audio is held by its original encodings. A track MUST have at least one original encoding. Its alias is the path <artist-alias>/<release-alias>/<track-alias>, where the track segment MAY incorporate the position to disambiguate same-titled tracks within a release.

4.3 Encoding

An encoding (audio/kind = encoding) is the only content-bearing entity. It MUST have a metatheca core/blob-ref to its audio deposit, exactly one audio/encoding-of3.4), and one audio/tech3.5). A ripped original additionally carries audio/rip; an imported original carries audio/import; a pinned transcoded encoding carries audio/transcode.

4.4 Artwork

Artwork is a deposit referenced by hash from a release (§3.10), not an entity. Identical artwork bytes dedup automatically.

4.5 Lyrics

Lyrics (audio/kind = lyrics) are plain, unsynchronized UTF-8 text held as a deposit: the entity carries core/blob-ref to the text, core/mime, one audio/lyrics-of fact (§3.11) naming its track, and one reserved metatheca path .audiotheca/lyrics/<track-id> — an entry is projection-visible only with a live path, and visibility is what admits the text into the search corpus below. It has no audio. Replacing a track’s lyrics is a new deposit plus a new core/blob-ref on the same entity (latest-wins), so the entity’s identity is stable across edits. Synchronized (timed) and multi-language lyrics are out of Phase 1 (§12).

The entity-plus-blob-ref-plus-path shape, rather than an artwork-style hash reference inside a fact body, is what places lyrics in the metatheca ext/ search corpus: the search extensions enumerate the current (entry, blob) pairs of the projection-visible entries — at least one live path and a current blob — so a lyrics deposit is indexable and a search hit resolves entry → lyricslyrics-of → track. The same rule keeps audio out: encodings are deliberately path-less, so their binary deposits never enter the corpus. Identical text across tracks dedups to one deposit in the cella.

4.6 Playlist

A playlist (audio/kind = playlist) carries one audio/playlist-meta, one audio/playlist-tracks, and optionally one audio/playlist-profile. It references tracks by entity identity and has no audio.

4.7 Primary, master, and fidelity

Fidelity ordering. Encodings are ordered by fidelity: lossless outranks lossy; among lossy, higher bitrate_bps outranks lower. This order is computed from audio/tech.

Primary. The primary of a track is, by default, its highest-fidelity original encoding under this ordering (ties broken by preferring ripped over imported, then by earliest entry ID). An audio/primary fact (§3.9) overrides the default.

Master. A master is a primary in lossless mode. A track is not required to have one; a track whose best original is, e.g., a 256 kbps MP3 has that MP3 as its primary and no master, and is fully valid.

Upgrade. Acquiring a higher-fidelity original later (e.g. re-ripping a recovered CD) adds a new original; the default primary then promotes to it automatically, cached renditions derived from the old primary become stale and are regenerated on next request (§7.3), and any maintained renditions (§7.6) are re-derived from the new primary. No entity is restructured.

4.8 No cross-release audio sharing

An audio deposit MUST belong to exactly one original encoding, and thus to exactly one track and release. audiotheca does not model a recording shared across releases: distinct rips are never bit-identical, and a personal library needs no global-catalogue identity. A track’s only audio multiplicity is its set of originals plus its cached renditions; its only metadata multiplicity is its variants (§5). The rule scopes to audio deposits: a text deposit MAY be shared by several lyrics entities (§4.5) through content-address dedup.

4.9 Artist

An artist (audio/kind = artist) is a first-class entity so that a single, stable identity carries an editable name and propagates a rename to every release and track that references it (§3.14). It carries artist-level audio/field facts — chiefly name (the display name, e.g. "The Rolling Stones") — and has no audio. Its alias is the path <artist-alias>1.10), the canonical of its name.

On ingest (§8.1), an incoming artist is matched to an existing entity, in order of preference:

  1. by external stable ID (a MusicBrainz artist MBID, when the producer supplies one);

  2. otherwise by canonical name, compared separator-insensitively (so ac-dc matches acdc).

A match reuses the existing artist (and MAY add the incoming spelling as an additional alias); no match creates a new artist entity. Near-matches SHOULD be surfaced for confirmation rather than merged silently; the entry ID keeps genuinely-distinct same-canonical artists apart (§1.10).

5 Metadata variants and resolution

5.1 Effective metadata

The effective value of field F for track T in variant V is resolved in two stages.

Stage 1 — hierarchy (canonical variant). Within utf8: the latest track-level audio/field for F if present, else the latest release-level audio/field for F on T’s release, else absent. Track overrides release. (The artist is resolved analogously, but as an entity reference rather than a field — §3.14.)

Stage 2 — variant derivation. If V is canonical, the effective value is the Stage 1 result. If V is derived: if a pin exists for that field in V (an audio/field in variant V with non-null pinned_from), the pinned value wins; otherwise the value is transliterate(Stage-1 result).

Effective metadata MUST be computed on read and MUST NOT be stored as its own facts.

5.2 Transliteration

Derivation of ascii transliterates the canonical value to ASCII (the reference implementation uses deunicode or any_ascii). Transliteration MUST be deterministic for a fixed library and version, which SHOULD be recorded so a transliterator change is distinguishable from a content change.

5.3 Pins and staleness

A pin overrides transliteration for one field of one entity in one derived variant; it is an audio/field fact in that variant with pinned_from set to the canonical value at pin time. A pin is stale when the current Stage-1 canonical value differs from its pinned_from. The review workflow MUST surface stale pins (and fields whose auto-transliteration changed). A stale pin MUST continue to apply until explicitly changed; staleness is advisory.

5.4 Provenance and supersession

Each audio/field records its source3.2). When multiple writes set the same (target, variant, field), the latest fact wins per metatheca projection. Enrichment (§8.5) emits facts from stronger sources (musicbrainz, discogs, manual) to supersede weaker ones (embedded); an implementation MUST present such supersessions for review rather than applying silently, consistent with §5.3.

6 Audio-storage invariants

Normative, and the core of audiotheca. The consequence of the five rules below: an encoding’s audio hash is a function of audio alone — editing any metadata, at any level, in any variant, changes no audio deposit and re-hashes nothing.

6.1 Deposits are tag-free

A stored audio deposit — in either store, of any lineage — MUST contain only the audio payload and the codec-mandatory headers. It MUST NOT contain library metadata — ID3v1/ID3v2 frames, FLAC VORBIS_COMMENT/PICTURE blocks, APE tags, MP4 metadata atoms, embedded cover art — beyond what the bitstream format mandates (FLAC’s STREAMINFO, which is not a tag, is retained).

6.2 Ripped originals are byte-exact

A ripped original MUST be the exact byte output of the ripper; it MUST NOT be rewritten, re-tagged, or re-encoded.

6.3 Imported originals may be normalized

An imported original MAY be normalized to pure audio on ingest: the implementation MUST preserve the encoded audio frames bit-exact (for MP3, including the leading Xing/LAME info frame) and MUST remove only metadata blocks/tags. The normalized deposit’s audio is identical to the input’s; only non-audio metadata is dropped. Standard tags are captured as embedded-sourced facts, every embedded picture is kept as artwork, and embedded unsynchronized lyrics become a lyrics entity’s deposit (§8.2, §4.5); nonstandard tags are dropped without capture. The implementation MUST NOT alter audio samples.

6.4 Transcoded renditions are tag-free

A transcoded rendition MUST be produced with the encoder emitting no metadata tags (audio-only output).

6.5 Export may inject tags

On export10), tags MAY be injected into the exported copy, which is transient and never stored as a deposit.

7 Transcoding and the derived cache

7.1 Pipeline

Every transcode is decode → PCM → (resample if the target rate differs) → encode. There is no compressed-domain bitrate reduction. Reference stack: Symphonia (decode), mp3lame-encoder (MP3), hound (WAV), rubato (resample).

7.2 Producing a rendition

To satisfy a request not already cached:

  1. Select the source: the track’s primary4.7). An implementation MUST NOT transcode from a derived rendition.

  2. Decode the primary’s deposit to PCM; resample if required.

  3. Encode with fixed, recorded parameters; output MUST be tag-free (§6.4).

  4. Deposit the output into the derived cache (content-addressed; dedup automatic).

  5. Record the rendition in the local cache index under its cache key (§7.3). No durable fact is written unless the rendition is pinned (§7.5).

7.3 Cache key and reproducibility

A rendition’s cache key is

hash(primary_deposit)  ⊕  canonical(encoder, encoder_version, params)

Before producing a rendition the implementation MUST consult the cache index for this key and reuse a hit. Because encoder output is reproducible only for a fixed encoder version and parameters, a change in either MUST yield a new rendition rather than being treated as equivalent. When a track’s primary changes (§4.7), cache entries keyed on the former primary’s deposit become unreachable and MAY be evicted.

7.4 Direction rules

The fidelity ordering of §4.7 bounds transcoding:

7.5 Pinning

An implementation MAY pin a specific rendition: promote it from the derived cache into the durable store as an encoding entity with lineage transcoded, core/blob-ref, audio/tech, and audio/transcode3.8). A pinned rendition syncs with the durable store like any original. Pinning is opt-in per rendition and off by default; its purpose is serving a fixed rendition from a machine that lacks the primary — resolution (§9.2) serves a pinned rendition when no original satisfies the profile, so the purpose holds without re-transcoding. Pinning is idempotent: a rendition that already exists durably is never duplicated (§4.8). Pinned renditions remain subject to §6 (tag-free).

7.6 Maintained profiles

A library MAY be configured with a list of maintained profiles — a vault-level setting maintained_profiles, a possibly-empty list of encoding profiles (§1.9) the library keeps materialized as durable, pinned renditions. The default is the empty list, in which case all transcoding is on-demand into the derived cache (§7.2) and nothing extra is stored.

A maintained profile is proactive, durable pinning: it applies the same resolution as §7.4 (ceiling semantics, down-convert only, derive from the primary, never lossless-from-lossy), but materializes the result ahead of demand as a pinned rendition (§7.5) carrying maintained_profile3.8) rather than on first request. Maintained renditions live in the durable store and sync to remote backends, so a transcoding-incapable client (e.g. a mobile player reading the vault from an R2 bucket) can resolve and stream them directly without decoding the primary.

Lifecycle:

A maintained profile a track cannot meet under the direction rules (§7.4) MUST be reported at materialization time, never skipped silently.

Maintained profiles are bounded by construction: each maintains at most one current durable rendition per track — demotion on upgrade is what preserves the bound. This is distinct from durably storing arbitrary on-demand transcodes, which §1.2 forbids — the bound is what makes the storage cost predictable and the policy safe to sync.

8 Ingestion and the import boundary

8.1 The ingest core

Ingest consumes (pure audio bytes + a metadata bundle) and, within a single metatheca head advance: matches or creates the artist(s) (§4.9), release, and track, assigning their aliases (§1.10); deposits the audio as an original encoding (§4.3) with technical and lineage/provenance facts; emits the bundle’s audio/field facts (per variant, each tagged with its source) and its audio/artist reference fact(s); deposits/references artwork; and materializes any maintained profiles for the new track (§7.6). Import MUST be all-or-nothing with respect to the head; on failure before the advance the vault is unchanged (orphaned deposits MAY remain pending GC). audiotheca is the sole writer (§1.1).

A metadata bundle has two sources (§8.2, §8.3); both converge on this core.

8.2 Plain-file path (built-in extraction)

For existing files, audiotheca itself reads the file’s embedded tags (the reference implementation reads them via Symphonia’s metadata support), producing a metadata bundle whose fields carry source: embedded. All standard tags that map to field names are captured, and every embedded picture becomes release artwork — the front-typed (or first) picture as front, the rest by their declared type. Embedded unsynchronized lyrics (ID3v2 USLT, Vorbis UNSYNCEDLYRICS) are not a field: they become the track’s lyrics entity and deposit (§4.5). It then normalizes the file to pure audio per §6.3 and deposits it as an imported original. This path requires no network and no external tool.

8.3 Manifest path (external producer)

A producer such as ripcli supplies audio files together with an import manifest — the rich bundle channel, carrying what embedded tags cannot: per-field metadata provenance, rip/import provenance, artist references with canonical names and MBIDs, and variant pins. ripcli MUST NOT write vault facts or deposits directly; it targets import.

Package. A manifest and the files it references form one directory; file references are relative paths resolved against the manifest’s location — a reference MUST NOT be absolute and MUST NOT escape the package directory. The manifest is JSON (matching the canonical-JSON fact convention and trivially produced/consumed by both ends).

Authority. audiotheca is authoritative for anything derivable from the bytes: it computes each deposit’s hash and audio/tech from the actual file. The manifest therefore omits tech; an optional per-file blake3 is accepted only as an integrity check that the handed-off file matches. Likewise audiotheca derives each entity’s authoritative canonical name from its display name via the frozen canonicalizer (§1.10); a manifest-supplied canonical is only a match hint and the in-manifest reference key, never authoritative.

Shape. A manifest declares a list of artists (each with name, canonical, optional mbid) referenced by canonical from releases and tracks, and a list of releases; each release carries metadata, artist, artwork, and tracks; each track carries disc, position, optional artist, metadata, originals, and an optional lyrics file reference — plain UTF-8 text that becomes the track’s lyrics entity (§4.5), replacing the current one on a targeted track. Each {value, source} maps to one audio/field; each original maps to one encoding entity with its lineage and provenance. A release/track artist is a single canonical (or null on a track, to inherit the release’s); for multiple or featured artists it MAY instead be a list of { artist, role } with role ∈ {main, feat} (§3.14).

{
  "manifest_version": 1,
  "artists": [
    { "name": "Miles Davis", "canonical": "miles-davis", "mbid": "…" }
  ],
  "releases": [
    {
      "target": null,
      "artist": "miles-davis",
      "metadata": { "utf8": {
        "title":   { "value": "Kind of Blue", "source": "musicbrainz" },
        "date":    { "value": "1959",         "source": "discogs" },
        "label":   { "value": "Columbia",     "source": "discogs" }
      } },
      "artwork": [
        { "file": "front.jpg", "role": "front" }
      ],
      "tracks": [
        {
          "disc": 1, "position": 1, "artist": null,
          "metadata": { "utf8": {
            "title": { "value": "So What", "source": "musicbrainz" }
          } },
          "originals": [
            {
              "file": "01-so-what.flac",
              "lineage": "ripped",
              "blake3": "…optional integrity check…",
              "rip": {
                "ripper": "whipper", "ripper_version": "0.10.0",
                "accuraterip_confidence": 7, "drive_offset": 6,
                "disc_id": "…", "ripped_at_ns": 1730000000000000000
              }
            }
          ]
        }
      ]
    }
  ]
}

A track with artist: null inherits the release artist (§3.14); a compilation sets each track’s artist and the release artist to "Various Artists". target (optional) is an existing entry ID to append to or upgrade an existing entity (e.g. attaching a re-ripped master to an existing track, §4.7); absent ⇒ create new. A targeted entity’s metadata and artwork are emitted like any other (latest-wins, §5.4); fields whose {value, source} equal the current fact are skipped, and supersessions MUST be reported. lineage: "imported" replaces the rip block with an import block (§3.7), which is optional: original_filename defaults to file and imported_at_ns to the ingest time. For ripped, §8.4 applies.

Validation. audiotheca MUST reject the entire manifest (all-or-nothing, §8.1) unless: every referenced file exists and matches its blake3 if given; every originals entry’s lineage and provenance are consistent (rippedrip, importedimport); every rip block carries only §3.6 fields with their declared types; every lyrics reference is valid UTF-8; every artist referenced by a release/track is declared in artists; the canonical utf8 variant is present wherever metadata is given; any target resolves to an entity of the expected kind; and each track has disc and position. Re-running an import is safe but not idempotent: the duplicate-deposit rule (§4.8) makes a rerun that re-ingests already-bound bytes fail validation — for create-new and targeted imports alike, nothing is ever duplicated, but the rerun is refused rather than a no-op.

8.4 Pristine handoff

ripped master files referenced by a manifest MUST be unmodified ripper output (§6.2) and MUST be deposited byte-for-byte; imported originals follow the §6.3 normalization. Manifest metadata is recorded as facts; it MUST NOT be written into the audio bytes.

8.5 Enrichment as a post-import pass

Online metadata enrichment is not a precondition of import. An enricher (a ripcli mode, or a sibling tool) MAY read the library, query databases, and emit stronger-sourced audio/field facts that supersede embedded ones (§5.4), under review. This mirrors metatheca’s "run an extractor over existing entries later" model and keeps the plain-file path (§8.2) fully offline.

9 Query and encoding profiles

9.1 Metadata query

Queries resolve against metatheca’s index projection of audio/ facts, in a chosen variant (default utf8), using effective values (§5.1). The query language is not fixed in Phase 1; it MUST support at least predicates over release- and track-level fields and over encoding technical facts.

9.2 Profile resolution

An encoding profile (§1.9) is a predicate over audio/tech interpreted as a ceiling (§7.4). Applying it to a track yields: the highest-fidelity original at or below the ceiling; else an existing pinned rendition (§7.5) at or below the ceiling — preferring one derived from the current primary with parameters matching the profile’s transcode plan, else the highest-fidelity admitted one, whose staleness MUST be reported (§7.6); else a down-transcode of the primary; else an unsatisfiable result, which MUST be reported explicitly. Whether resolution may transcode on demand or must use only existing encodings MUST be explicit at the call site, never silent.

9.3 Fidelity query

Because fidelity is computable from each track’s primary, an implementation SHOULD expose a query for tracks whose primary is not lossless — the re-acquisition backlog. Such results reflect declared fidelity (§3.7); audiotheca does not verify true audio bandwidth in Phase 1.

10 Export and playback

10.1 Export

export materializes encodings (resolved through a profile, §9.2) as files outside the library. Tags MAY be injected into the exported copies (§6.5) in the container-native scheme (Vorbis comments for FLAC, ID3 for MP3), derived from effective metadata (§5.1) in a chosen variant. When tags are injected, the track’s lyrics (§4.5) MUST be among them wherever the scheme has a slot (ID3v2 USLT, Vorbis UNSYNCEDLYRICS; RIFF INFO has none and omits them) — lyrics extracted to a deposit at ingest come back at the tag-carrying egress, so the §6.3 strip loses nothing system-wide. Exported copies are transient and never deposited back.

10.2 Playback

Playback resolves a track or playlist — by default to the primary (local listening does not require transcoding), or through a profile for constrained output — and decodes for the audio device. The reference implementation decodes via Symphonia and plays via rodio/cpal. Playback is read-only with respect to the vault. Detailed playback behavior is otherwise out of Phase 1 normative scope.

11 CLI surface

The reference CLI is audiotheca (suggested alias aud); the verb set is provisional and non-normative.

12 Deferred (out of Phase 1)

Inherited deferrals from metatheca Phase 1 (no public hook trait, no GC, deposit-only durability) apply transitively.