Ctrl K
ComunidadGuías…

docs/homarr-navidrome-contract.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Navidrome Homarr Link

## Source contract, not runtime proof

This slice implements #284 on the committed #262 reference contract.
As of 13 October 2026, source `4bb1f98dd85f1d99513624b3a3c0795836134966`
registers `navidromeTarget()` in `targets.go`. Shared registry and reconciliation
remain custodian-owned; the credential-safety gate below restricts activation.
The sensitive Link accepts exact Homarr API versions `2.5.1` and `2.5.2`,
with the catalog image resolved and verified by the reference owner, and
`deluan/navidrome:0.64.0`. Source was checked against Homarr commit
`ebbd4876d25a51151444a009ab596e1154d62880` and Navidrome tag commit
`1072e9f7eb27faef740df5f0bd7fb18898aa650e`. Navidrome reports its release
as `0.64.0 (<build commit>)`; the version check accounts for that suffix.
No images were run or image digests independently observed in this lane.
The reference owner's cached image evidence identifies Homarr OCI revision
`3d4150e59f600876b7e308f857b2f21572b75348`, distinct from the tagged baseline.
Its integration router and credential definitions were compared with the
tagged baseline and are byte-identical. This is scoped source equivalence,
not Navidrome runtime acceptance or whole-image equivalence.

## Native activation: credential-safe version gate #363

The shared [diagnostic Address privacy issue #363](https://github.com/ajnart/selfhost-skills/issues/363)
is enforced through `requireCredentialSafeHomarr(ctx, from, hint)`. Each Link
Check/apply first reads authenticated `info.getInfo`; only exact versions
`2.5.1` and `2.5.2` proceed. Older, unknown, malformed or unreadable versions
return retryable Manual before any Navidrome request, compatibility-kind query,
credential submission, tile/widget mutation, native query or Stats refresh.
Back up Homarr before changing its version, then retry `self-host setup homarr`.
Do not manually create an integration or bypass the hold. Standalone Navidrome
regular-account Setup/Check and owned Link removal remain separate and operational.

Historical Homarr OCI-source revision
`3d4150e59f600876b7e308f857b2f21572b75348` and tag `v2.5.0`, commit
`ed7b4bc00a77945f733a421efca2a58563d0d351`, retain native Navidrome's
`u=username` and `p=password` query values in `ResponseError.url` on a non-2xx
response. The integration decorator sanitizes its own diagnostic copy but
rethrows an error containing the original cause. Media Server settling logs
that cause, recursive formatting retains the Address, and console logging is
enabled. A valid direct probe and regular credentials cannot prevent a later
rejected native response on those versions.

[Homarr PR #7170](https://github.com/homarr-labs/homarr/pull/7170) fixes the
shared HTTP diagnostic boundary: `ResponseError.url` now passes through
`redactCredentialUrl`, which removes both `u` and `p` before the downstream
error/decorator/settling/formatter chain. Stable `2.5.1` source
`c2e95f8d05b87dae42e9cd638dd7d4822b7cd608` and `2.5.2` source
`7eb9a417a60d759adce5f7ac15ceb594aefad471` contain the fix; the relevant
privacy and Navidrome sources are unchanged between those tags.
The reference owner's [published-image and authenticated API observations](homarr-integration-contract.md#supported-upstream)
confirm both releases. These source and narrow version observations authorize
the finite gate; they are not a Navidrome native/privacy, browser or lifecycle
acceptance claim. A mutable latest tag does not admit an unreviewed version.

After the safe-version gate passes, the Link again runs `ReadHomarrData` as
its preflight: it freshly verifies the exact regular account, native role,
library access, administrative denial and complete Subsonic data before any
Homarr mutation. The selected credential and native/Stats validators retain
their existing ownership and semantic checks.

The focused transport fixture covers actual `add` and `linked` paths for
older, unfixed, future, missing and unreadable versions: one authenticated
version request, zero provider requests and no other Homarr requests. Fixed
`2.5.1`/`2.5.2` fixtures pass the gate and perform all nine native account/data
requests; a rejected regular credential still requires Manual after login.
The fixed-version regression failed against the obsolete unconditional hold
and passes with the shared gate. Those synthetic fixtures do not establish
native runtime acceptance.

## Credentials and minimum-role limitations

Homarr's native `navidrome` kind requires exactly `username` and `password`.
It does not accept a Navidrome UI JWT, an API key, or a precomputed salted
Subsonic token in place of the password. Navidrome Setup preserves the shared
Admin login for administration and creates a dedicated regular account named
`self-host-homarr` through the pinned native `POST /api/user` route with
`isAdmin: false`. The account receives Navidrome's default libraries; Setup
never expands that access or changes another user. The consuming Link uses
only its Project-owned `NAVIDROME_HOMARR_USERNAME` and
`NAVIDROME_HOMARR_PASSWORD`, with native user ownership recorded in
`NAVIDROME_HOMARR_USER_ID`, through the existing private helpers boundary.
Credentials are persisted before creation so an interrupted response can be
recovered by login without creating a duplicate or rotating the password.
A conflicting existing user, lost/rejected credential or changed identity
requires account-preserving Manual recovery.

Fresh native login and `GET /api/user/{id}` must confirm the exact saved ID,
username, explicit `isAdmin: false` and accessible library IDs. The regular
JWT must receive HTTP 403 from the administrative `GET /api/library` route.
This is an authenticated, read-only scope probe; Setup and Check do not try
an administrative mutation as a verification step.

Before any native widget query, the Link reads `integration.byId` with
`{"id": integrationID}`. This requires Homarr full integration access and
returns `secrets` entries with `kind`, `value` and `updatedAt`: `username`
is a public string, while the private `password` value is `null`. The Link
requires the returned ID and kind, exactly those two credential entries,
and the public username to equal the Project's selected regular Homarr user.
It also requires the recorded owned integration ID and internal Address
`http://navidrome:4533`.
A missing/malformed identity or a different valid account stops as Manual
with instructions to restore this Link's owned integration to that regular
account. Matching library/session data cannot adopt another account. It does
not read, print or claim to compare the redacted password.

A regular Navidrome user can call all selected Subsonic endpoints without
administrator privileges. This uses the lowest supported native role, but
Navidrome has **no read-only account role or per-endpoint password scope**.
The account can still perform ordinary user operations such as playlist
edits, streaming and permitted personal-account changes. Native user
creation and library administration require an administrator. These limits
must not be described as a read-only credential.

Self-host probes authenticate with fresh `t = MD5(password + salt)` and `s`
in a POST form, with `u`, `v=1.16.1`, `c=self-host`, and `f=json`.
The password is not sent in these probes and credentials never enter their
request Addresses. Transport, parse and upstream errors are reduced to
fixed diagnostics without response bodies or credential-bearing query text.
All HTTP goes through helpers and follows no redirects.

**Upstream limitation:** Homarr v2.4.1 itself uses GET query parameters
`u`, `p`, `v=1.16.1`, `c=homarr`, and `f=json`. It does not implement salted
authentication. Its requests stay on `http://navidrome:4533` inside the
Project, separate from the browser Address. Do not publish or capture raw
Homarr/Navidrome request logs: upstream URLs may carry the password.
This implementation does not fix Homarr's upstream logging or transport.

## Useful data and widgets

Navidrome Setup and Check freshly read authenticated `ping`,
`getMusicFolders`, `getScanStatus`, `getAlbumList2`, `getArtists`, and
`getNowPlaying`. Accessible library IDs are discovered, not invented.
The scan must have completed without a recorded error. It is not started
by these probes. Empty music folders are valid only after a completed scan.

Standalone readiness is bounded to six Subsonic requests per probe and one
representative album page: `alphabeticalByName`, `size=1`, `offset=0`.
Its explicit `X-Total-Count` must be nonnegative; the page must contain one
schema-valid album when nonempty and no album when the total is zero.
Library, artist and session validation are shared with the consumer probe.
This verifies authentication and representative API/schema readiness within
the existing one-minute deadline, not complete library counts. No sampled
count is returned as Stats data, and readiness never pages through the full
album library. The persisted `homarr-api` step ID remains compatible; its
label names API readiness and its recovery hint names only Navidrome Setup.

The prepared native verifier requires complete counts through
`ReadHomarrData`: `alphabeticalByName`, 500 albums per page, at most 20
pages, reconciled against a stable `X-Total-Count`. Missing `songCount`,
missing payload objects, repeated albums, changing totals and truncated
pages fail instead of becoming zeros. Libraries over 10,000 albums fail
that consuming Link explicitly because the adapter truncates them; that
limit does not affect standalone Navidrome readiness.
Artists are counted from the ID3 index; song counts are summed per album.
These are library counts, not listening time or play counts.

Navidrome's exact `getArtists` failure code 70 with message
`Library not found or empty` is accepted only with an explicit zero album
count in the same accessible-library scope after authentication and library
discovery succeed. The completed scanner's `getScanStatus.count` sums songs
across every library, including libraries inaccessible to the regular user;
it must not determine whether that user's accessible libraries are empty. Scan
count/schema/completion checks remain, and missing or malformed successful
artist objects still fail. Account-scoped album/artist counts and the native
Homarr Stats provider keep their existing meanings.
`nowPlaying: {}` means zero sessions in the pinned API; an absent
`nowPlaying` object, failed request, or incomplete populated entry fails.

Default widgets are `audioStats`, `mediaServer` and shared `stats` entries
`artistCount`, `albumCount`, `songCount`. No `sessions` Stats metric exists
for this Homarr kind. Media Server shows all returned audio sessions,
without bitrate/location toggles: the adapter supplies neither playback
position nor a playing/paused state. No album/media selection is required.

The Link verifies the credential-safe Homarr version before provider requests,
then preflights the direct API before any Homarr mutation. Native verification retains saved regular Homarr identity/destination, freshly authenticated complete Subsonic
library/session readiness, `widget.audioStats.getStats` count semantics, and
`widget.mediaServer.getCurrentStreams` integration/session identifiers and audio
schema. Direct, native and Stats counts and session lengths are independently
sampled; their volatile values are not required to equal each other.
`verifyNavidromeStats` receives the shared SDK's one catalog-backed forced
advancing Stats sample. All three selected metrics require `count` units and
finite nonnegative integral values; album counts retain the 10,000-album bound.
Within each native/Stats sample, zero albums require zero songs and populated
albums require artists.
Explicit zeros and empty session arrays remain valid. The central timestamp rule
requires a finite positive advancing timestamp; this leaf adds no wall-clock
limit. Errors remain secret-safe.
Recorded IDs, ownership, shared widget preservation and removal use #262's
unchanged reconciliation. No user media, playback, artwork retrieval or
external music account is used by Setup or Check.

## Acceptance still required

No Docker, browser, installer, live media seeding, playback or lifecycle
mutation was run in this lane. Existing focused tests use an authenticated
fake with the pinned empty-library response, POST salted authentication,
and fresh Setup/Check requests. Existing test bodies verify:

- Standalone Setup/Check use six requests and one single-album page each
  for 10,001 albums; growing to 60,001 albums preserves that exact bound.
- The consumer rejects the oversized explicit total after four requests,
  but reconciles a supported 503-album library across both complete pages
  with actual artist/album/song/session counts.
- Authenticated empty-library readiness and complete consumer zeros remain
  valid; a null representative `songCount` fails after four requests rather
  than becoming a zero or successful readiness result.

The request-bound control failed before the correction and passed afterward,
without simulated delays. That earlier readiness correction added no test file,
function or named case.
Descriptor HTTP fixtures now invoke the real `verifyData` seam with the existing
`statsFixture`. They cover populated independently changing samples, zero counts
and empty sessions, invalid counts/units/catalog entries, the album bound, no-op
refreshes, saved account/destination/owned-ID Manual guards and malformed native
counts/sessions. Native failures acquire no Stats; semantic Stats failures use
one catalog and one forced refresh. Focused App/Homarr tests, build, vet and
formatting/diff checks are the source gate. Native adapter, Stats metrics and
nullable session schema were rechecked at OCI source revision
`3d4150e59f600876b7e308f857b2f21572b75348`. These fixtures are not functional,
rendered or lifecycle proof.

The focused regular-account fixtures also verify that Homarr never receives
administrator credentials, populated/zero native data reaches exactly one
Stats sample, role/identity/scope drift stops verification, creation-response
loss recovers without duplicate users, and missing/rejected secrets or a
conflicting existing user preserve all users and passwords. The native
user-create fixture denies an administrative mutation under the regular JWT.
That fixture is source-contract evidence, not a real-server denial proof.

The scoped-empty regression supplies global scan count 37 from an inaccessible
library while the selected regular user's library has zero albums and the
exact native empty-artist result. It fails before the scope correction and
passes afterward through complete Homarr reads and standalone Setup/Check.
Absent/malformed artists and empty artists alongside accessible albums remain
rejected. This is a source fixture, not native multi-library runtime proof.

After the parent accepts the bounded reference runtime, a separately
approved disposable Project must record resolved image digests and prove:

- Both installation orders, absent/unready peers, late-peer installation
  and retry after scan readiness.
- Owned small audio is scanned and the real Homarr Audio Stats, shared
  Stats and Media Server results render correctly. A disposable listening
  session needs explicit playback approval, not an invented session ID.
- Valid empty data versus missing fields, rejected credentials, unsupported
  versions and failed/unfinished scanning.
- Rerun/restart stability, credential changes without account changes,
  preservation of user-authored resources and other shared bindings, and
  removal/reinstallation without duplicates or stale bindings.

The regular-account role and selected-identity guards are source-validated.
No native runtime or rendered acceptance is claimed by this PR.

## Official sources

- [Homarr credentials](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/definitions/src/integration.ts)
- [Homarr saved integration response](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/integration/integration-router.ts)
- [OCI-revision saved integration response](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/api/src/router/integration/integration-router.ts)
- [OCI-revision credential definitions](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/definitions/src/integration.ts)
- [Homarr adapter](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/navidrome/navidrome-integration.ts)
- [Audio Stats options](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/widgets/src/audio-stats/index.ts)
- [Stats metrics](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/stats/existing.ts)
- [Stats refresh](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/widgets/stats.ts)
- [Media Server query](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/widgets/media-server.ts)
- [Navidrome POST and authentication](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/middlewares.go)
- [Navidrome routes and roles](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/api.go)
- [Navidrome library/artist data](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/browsing.go)
- [Navidrome album/session data](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/album_lists.go)
- [Navidrome response schema](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/responses/responses.go)
- [Navidrome reported version](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/consts/version.go)

- [Navidrome native user routes and administrative library guard](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/nativeapi/native_api.go)
- [Navidrome native login identity and JWT](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/auth.go)
- [Navidrome user schema](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/model/user.go)
- [Navidrome user creation, role policy and default library assignment](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/persistence/user_repository.go)

- [Navidrome global scan counters](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/scanner/controller.go)
- [Navidrome scan-status response](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/library_scanning.go)
- [Navidrome default-library selection](https://github.com/navidrome/navidrome/blob/1072e9f7eb27faef740df5f0bd7fb18898aa650e/server/subsonic/helpers.go)

- [Navidrome native query and rejected response](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/integrations/src/navidrome/navidrome-integration.ts)
- [ResponseError retains the response Address](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/common/src/errors/http/response-error.ts)
- [Decorator sanitizes one copy and retains the cause](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/integrations/src/base/errors/decorator.ts)
- [Media Server integration settling](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/api/src/settle-integrations.ts)
- [Media Server native query](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/api/src/router/widgets/media-server.ts)
- [Recursive error formatting](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/core/src/infrastructure/logs/format/error.ts)
- [String metadata formatting](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/core/src/infrastructure/logs/format/metadata.ts)
- [Console and conditional Redis transports](https://github.com/homarr-labs/homarr/blob/3d4150e59f600876b7e308f857b2f21572b75348/packages/core/src/infrastructure/logs/transports/index.ts)

- [Fixed ResponseError diagnostic boundary](https://github.com/homarr-labs/homarr/blob/7eb9a417a60d759adce5f7ac15ceb594aefad471/packages/common/src/errors/http/response-error.ts)
- [Fixed username/password query redaction](https://github.com/homarr-labs/homarr/blob/7eb9a417a60d759adce5f7ac15ceb594aefad471/packages/common/src/errors/http/credential-url.ts)