docs/homarr-integration-coverage-research.md
Rama main · réplica de lectura del repositorio original.
# Homarr integration coverage research
Read-only research, **11 October 2026**, at Go baseline `5aafb4c1833fd4cd9caf0f7beaac31e625aafe22`. No implementation, tests, runtime inspection, installations, or live Homarr changes were performed by that research. The findings, implementation descriptions, matrices and unknowns below are **historical research-baseline observations**, not today's missing-integration inventory. Source links and the 39/41 matrices are retained as evidence.
## Follow-up: 12 October 2026
The [reconciled ticket index](homarr-ticket-index.md) now maps **39 App source slices** to PRs; that is not 39 runtime acceptances. Immich #262 is closed and [#309](https://github.com/ajnart/selfhost-skills/pull/309) merged at frozen reference `6b82c55fb890c11a1388bd4e5a2f10b632ff4f7c`, with evidence head `33e7fbccb7541d2bbf1d81968eb21c35a1b5bfd7`. The [final bounded proof](homarr-immich-proof.md#final-bounded-attempt) at unchanged production `89301aff17c86b767f5a45076881f2bb7b41db39` accepts authenticated Immich Users 1 / Photos 0 / Videos 0 / Storage 0: **1/39**, not populated-media, cross-kind or all-peer acceptance. Historical pending-review prose in the proof/contract predates the merged decision; the [merged guide](agents/homarr-integrations.md#acceptance-boundary) records that boundary.
Keep upstream identities distinct: the proof's actual Homarr OCI source is `3d4150e59f600876b7e308f857b2f21572b75348`, OCI version `main`, authenticated API `2.4.1`; the tagged research baseline below is `ebbd4876d25a51151444a009ab596e1154d62880`. The reference's 22-file comparison establishes only those compared files, not every peer contract. Peer PRs have different recorded heads and assembly stages; the frozen reference does not mean all 39 are normalized or registered. This follow-up reads source/docs/tracker metadata only and adds no runtime approval or proof.
## Research-baseline findings
**At the Go research baseline, the CLI did not wire every Homarr-supported integration.** It shipped **93 Catalog Apps**, of which **47 corresponded to Homarr's research-baseline service-integration kinds**. Homarr declared **9 Links: 8 actual integrations plus a Vaultwarden navigation tile**. Thus **39 shipped, upstream-supported Apps lacked native Homarr wiring at that baseline**. Generic navigation tiles covered installed Apps with an Address, not their authenticated integrations or widgets. Sources: [Catalog assembly][catalog], [registrations][registrations], [Go targets][targets], [generic tiles][tiles], and [upstream registry][upstream]. Counts are static historical source inventory, not runtime acceptance or a claim that 39 source slices remain absent.
On 11 October, the official website listed **91 integration guides**. The Homarr **v2.4.1 research baseline** had **89 registry entries, including development Mock: 88 real service/feed kinds**. The remaining three documentation entries were **Docker, Docker labels, and Kubernetes**, which used separate infrastructure/discovery mechanisms rather than the service registry. iCal is a feed, not installable software. Consequently neither “80 integrations” nor “91 installable Apps” was an accurate denominator. Sources: [documentation catalog observed during research][docs], [release][release], [registry][upstream], [creator registry][creators], [Docker][docker], [labels][labels], [Kubernetes][kubernetes]. Mutable documentation links are not a new 12 October inventory check.
The historical TypeScript implementation was substantially broader: **48 native descriptors**, a generic attachment engine, credential alternatives, and widget prerequisites. However its claimed coverage registry listed only **49 standard surfaces**, one external-only (UniFi), while its own cited upstream snapshot already contained **88 non-Mock kinds**. It did **not** cover every upstream integration. The Go research baseline had lost wiring for **39 Apps still shipped at that baseline**; a fortieth old descriptor, **Coolify**, had no Go Catalog App there. Descriptor existence did not imply successful automatic authentication or live widget acceptance. Sources: [old descriptors][old-descriptors], [old coverage registry][old-registry], [old seed engine][old-seed], [old credential resolver][old-mapping], [old pinned upstream][old-upstream].
## Provenance and boundaries
| Source | Immutable revision / scope |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Requested Go baseline, maincheck checkout | `5aafb4c1833fd4cd9caf0f7beaac31e625aafe22`; branch `fix/host-folders-app-email`. Read committed blobs for modified paths, not the user's uncommitted edits. |
| Local `main` ref observed during research | `86246cfe97673932de9d0cced20c7db9664b290e`. Diff against baseline showed **no changes in Apps, Catalog, service, helpers, status, activity, or the relevant TUI details/activity rendering**. Other TUI changes existed; baseline Onboarding line citations are explicitly baseline. No claim about an un-fetched remote head. |
| Historical TypeScript checkout | `/Users/ajnart/Documents/GitHub/selfhost-skills`, `feat/video-reels`, committed HEAD `56f3fbf6c972fa507e5cae7673dc730438714855`. Historical source, not the Go executable. |
| Homarr release observed during research | `v2.4.1`, published `2026-10-10T11:36:42Z`, commit `ebbd4876d25a51151444a009ab596e1154d62880`. Tagged research baseline, not the later inspected OCI source. |
| Homarr development source cross-check | `b925f75e62ff224bfedbe4344a65fcd4f9f567e4`; same 89 integration-kind keys as the tagged research baseline. |
| Historical upstream snapshot cited by TypeScript | `beffe794022c22c43dd458999ee2a016bb1df2e6`; fetched registry also had the same 89 keys. |
Inventory method: inspect `Catalog.Default`, generated Apps and batch registrations; extract App IDs from committed `Info` implementations (91 imported App packages, with `arr` exposing three Apps = 93 Apps); compare upstream kind/name aliases, not fuzzy branding. Count old descriptors with non-null `integrationKind`. No tests or binaries were run. Context7 quota was already exhausted; primary GitHub/docs and the read-only Homarr docs MCP were used instead.
Vocabulary and ownership follow [CONTEXT][context]: a **Link** belongs to its consuming App; Homarr **integration** means its authenticated/server-side connection. [ADR 0001][adr1] makes Apps compiled Go and Compose the running inventory; [ADR 0007][adr7] removes the old TypeScript product and explicitly does not migrate its old Projects. This investigation creates no approval to mutate anything.
## What the research-baseline Go implementation did
### Connections, authentication and widgets
All rows below were declared in baseline [`internal/apps/homarr/links.go:72–137`][targets]. Server routes used Compose names; browser Addresses were resolved independently (`links.go:900–914`). `stats` and `calendar` were consolidated across owned integrations ([shared widgets][shared]). These were implemented declarations, not proof each service worked at that time or works today.
| Catalog App / Homarr kind | Link | Credential source | Widgets and configured metrics |
| ---------------------------- | ------------------ | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Sonarr / `sonarr` | `link-sonarr` | Sonarr saved `api_key` | `calendar`, `mediaMissing`, stats: shows/downloaded/missing/queued |
| Radarr / `radarr` | `link-radarr` | Radarr saved `api_key` | `calendar`, `mediaMissing`, stats: movies/downloaded/missing/queued |
| Prowlarr / `prowlarr` | `link-prowlarr` | Prowlarr saved `api_key` | `indexerManager`, stats: indexers |
| Jellyfin / `jellyfin` | `link-jellyfin` | Jellyfin saved `api_key` | `mediaServer`, `mediaReleases`, stats: sessions |
| Seerr / `seerr` | `link-seerr` | Seerr saved `api_key` | `mediaRequests-requestList`, `mediaRequests-requestStats`, stats: pending/approved/available |
| qBittorrent / `qBittorrent` | `link-qbittorrent` | Shared Admin login username/password, not API-key mode | `downloads`, stats: download/upload/paused |
| Beszel / `beszel` | `link-beszel` | `beszel.AdminEmail(to)` and Admin password | `beszelSystemStats`, binds actual machine ID; CPU/memory/disk/network/GPU/Docker options |
| ntfy / `ntfy` | `link-ntfy` | ntfy saved `topic` and dedicated `homarr_token` as Homarr `apiKey` | `notifications`, stats: messages |
| Vaultwarden / no native kind | `link-vaultwarden` | None | Navigation tile only |
At that baseline, Homarr first-run Setup created/verified the Admin login, completed Home board onboarding, stored a Homarr API key, reconciled tiles, and placed Assistant/Notebook. Later REST/tRPC changes used the stored Homarr `ApiKey` header; the initial admin session used CSRF and cookies. Sources: [`homarr.go:195–374`][homarr-auth], [`homarr.go:374–440`][homarr-setup], [`links.go:158–192`][transport].
Research-baseline idempotency/configuration ownership:
- Apps matched by server-side `pingUrl`; browser `href` drift was repaired, and IDs retained (`links.go:750–897`). Integrations matched by **kind + internal Address**; credential fingerprints triggered `integration.update`, otherwise existing IDs were reused ([connection reconciliation][reconcile]). Matching was not a universal ownership proof: an existing manually created matching integration could be adopted/updated; removal selected the same kind/Address. Expansion needed an explicit adoption policy.
- Connection creation checked Homarr's returned **body error**, not HTTP 200 alone (`links.go:245–257`). ntfy's changed/new token was separately checked against `/v1/account`, because Homarr's connection test could accept HTTP 401 (`links.go:560–565,619–633`). Routine unchanged checks did not publish test notifications.
- Widget ownership was stored per integration; ambiguous legacy widget sets stopped with a recovery hint rather than deleting user widgets (`links.go:423–493`). Removal preserved unowned widgets and removed only the target binding from shared widgets (`links.go:708–744`). [Source][widget-ownership].
- Generic tiles used `self-host tile:<id>` markers; they reconciled installed Apps, removed marked stale tiles, and preserved unrelated records ([`installed.go:15–156`][tiles]). Installed inventory came from Project Compose fragments, included Unmanaged apps, and did not require Setup readiness ([`installed_apps.go:12–79`][installed]). Notebook listed installed Apps and explicitly disclaimed tile/widget success as integration acceptance ([`notebook.go:30–74`][notebook]).
- Successful target removal invoked consumer `Link.Remove` and forgot both link/unlink records (`lifecycle.go:149–190`); integration/widget cleanup lived in Homarr (`links.go:916–1035`). [Removal sources][unlink].
The [Homarr proof available to the research][proof] was a **7 October** scoped runtime record at `374bd1c813aa6f2d32580f1ae4fe2711a0ecb427`: ntfy/Jellyfin functionality and Gatus **navigation** add/remove, plus reconciliation/recovery. It was not proof of all eight declarations, all 47 candidate Apps, or compatibility with the tagged Homarr research baseline.
## Research-baseline coverage matrix
### A. Upstream-supported and shipped, but no native Homarr Link at the baseline: 39
Every historical row had **Catalog = yes; Homarr Link = no; implemented Homarr auth/widgets = none; generic navigation = when installed with an Address**. The [12 October index](homarr-ticket-index.md#app-source-slices) supersedes this absence status, not the baseline evidence. Credential column records **upstream requirements**, not credentials acquired by baseline Go. Each service name links to its official guide; [upstream registry][upstream] is authoritative for baseline alternative secret sets and [widget map][widget-map] for baseline native capabilities. [Catalog assembly][catalog]/[registrations][registrations] establish historical shipping; the exhaustive [nine-target list][targets] establishes baseline absent wiring.
`key` = Homarr `apiKey`; `login` = username/password; `/` separates alternatives. `stats` was supported for all registry kinds except iCal at this research version; additional examples are not exhaustive.
| Catalog ID / official service | Upstream auth alternatives | Additional native capability examples |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------------------------------- |
| `adguard-home` / [AdGuard Home](https://homarr.dev/docs/integrations/adguard-home/) | login | DNS summary/controls |
| `archive-team-warrior` / [ArchiveTeam Warrior](https://homarr.dev/docs/integrations/archiveteam-warrior/) | none / login | Warrior |
| `aria2` / [Aria2](https://homarr.dev/docs/integrations/aria2/) | none / key | Downloads |
| `audiobookshelf` / [Audiobookshelf](https://homarr.dev/docs/integrations/audiobookshelf/) | key | Audio stats |
| `bazarr` / [Bazarr](https://homarr.dev/docs/integrations/bazarr/) | key | Subtitles |
| `caddy` / [Caddy](https://homarr.dev/docs/integrations/caddy/) | none | stats |
| `dashdot` / [Dash.](https://homarr.dev/docs/integrations/dash-dot/) | none | System resources/disks/health |
| `deluge` / [Deluge](https://homarr.dev/docs/integrations/deluge/) | password | Downloads |
| `emby` / [Emby](https://homarr.dev/docs/integrations/emby/) | key | Media streams/releases |
| `frigate` / [Frigate](https://homarr.dev/docs/integrations/frigate/) | login | stats |
| `gatus` / [Gatus](https://homarr.dev/docs/integrations/gatus/) | none | stats |
| `glances` / [Glances](https://homarr.dev/docs/integrations/glances/) | none | System resources/health |
| `gluetun` / [Gluetun](https://homarr.dev/docs/integrations/gluetun/) | login / key / none | VPN |
| `gotify` / [Gotify](https://homarr.dev/docs/integrations/gotify/) | login | Notifications |
| `home-assistant` / [Home Assistant](https://homarr.dev/docs/integrations/home-assistant/) | key | Calendar/entity state/automation |
| `immich` / [Immich](https://homarr.dev/docs/integrations/immich/) | key | Server stats/album carousel |
| `lidarr` / [Lidarr](https://homarr.dev/docs/integrations/lidarr/) | key | Calendar |
| `linkwarden` / [Linkwarden](https://homarr.dev/docs/integrations/linkwarden/) | key | stats |
| `navidrome` / [Navidrome](https://homarr.dev/docs/integrations/navidrome/) | login | Audio stats/media streams |
| `netdata` / [Netdata](https://homarr.dev/docs/integrations/netdata/) | none | stats |
| `nextcloud` / [Nextcloud](https://homarr.dev/docs/integrations/nextcloud/) | login | Calendar/notifications |
| `nzbget` / [NZBGet](https://homarr.dev/docs/integrations/nzbget/) | login | Downloads |
| `paperless-ngx` / [Paperless-ngx](https://homarr.dev/docs/integrations/paperless-ngx/) | key | Documents |
| `patchmon` / [PatchMon](https://homarr.dev/docs/integrations/patchmon/) | `patchmonApiKey` + `patchmonApiSecret` | PatchMon |
| `peanut` / [PeaNUT](https://homarr.dev/docs/integrations/peanut/) | login / none | UPS |
| `pihole` / [Pi-hole](https://homarr.dev/docs/integrations/pi-hole/) | key / none | DNS summary/controls |
| `plex` / [Plex](https://homarr.dev/docs/integrations/plex/) | server key | Media streams/releases |
| `prometheus` / [Prometheus](https://homarr.dev/docs/integrations/prometheus/) | none / login | stats |
| `sabnzbd` / [SABnzbd](https://homarr.dev/docs/integrations/sabnzbd/) | key | Downloads |
| `slskd` / [Slskd](https://homarr.dev/docs/integrations/slskd/) | key | Downloads (read-only client) |
| `speedtest-tracker` / [Speedtest Tracker](https://homarr.dev/docs/integrations/speedtest-tracker/) | key | Speed tests |
| `tdarr` / [Tdarr](https://homarr.dev/docs/integrations/tdarr/) | none / key | Transcoding |
| `technitium-dns` / [Technitium DNS](https://homarr.dev/docs/integrations/technitium-dns/) | key / login | DNS summary/controls |
| `tracearr` / [Tracearr](https://homarr.dev/docs/integrations/tracearr/) | key | Media monitoring |
| `traefik` / [Traefik](https://homarr.dev/docs/integrations/traefik/) | none / login / key | Reverse proxy |
| `transmission` / [Transmission](https://homarr.dev/docs/integrations/transmission/) | login | Downloads |
| `umami` / [Umami](https://homarr.dev/docs/integrations/umami/) | login / key | Website analytics |
| `uptime-kuma` / [Uptime Kuma](https://homarr.dev/docs/integrations/uptime-kuma/) | none / slug / key / slug+key | Uptime |
| `wud` / [What's Up Docker](https://homarr.dev/docs/integrations/whats-up-docker/) | none / login | Container updates |
At the baseline, shipping the App was not sufficient to fill this table with automatic Links. Some own Setup remained Manual, some required tokens were not provisioned, and some API endpoints differed from the browser Address. Examples: [AdGuard admin Manual step](https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/adguard/adguard.go#L91-L100), [Home Assistant owner Manual step](https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homeassistant/homeassistant.go#L140-L152), [Plex private server-token verification](https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/plex/plex.go#L70-L120), and [Speedtest's intentionally narrow Check](https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/speedtesttracker/speedtesttracker.go#L301). The research recommended a separate credential/readiness audit before claiming straightforward additions.
### B. Supported by Homarr, not shipped in the Go research-baseline Catalog: 41
All historical entries here had **Catalog = no; declared Link/auth/widgets = none**. They could be connected manually in Homarr; an Unmanaged app could get a generic tile, not a native Link through the baseline typed scheduler. This was a Catalog scope gap, not “Homarr does not support it.” Exhaustive baseline comparison of [registry][upstream] against [Catalog registrations][registrations]:
| Group | Official supported kinds/services outside the baseline Catalog |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Media/downloads (16) | [Autobrr](https://homarr.dev/docs/integrations/autobrr/), [Bindery](https://homarr.dev/docs/integrations/bindery/), [FileFlows](https://homarr.dev/docs/integrations/fileflows/), [Jackett](https://homarr.dev/docs/integrations/jackett/), [Jellyseerr](https://homarr.dev/docs/integrations/jellyseerr/), [Jellystat](https://homarr.dev/docs/integrations/jellystat/), [Komga](https://homarr.dev/docs/integrations/komga/), [Maintainerr](https://homarr.dev/docs/integrations/maintainerr/), [Overseerr](https://homarr.dev/docs/integrations/overseerr/), [Readarr](https://homarr.dev/docs/integrations/readarr/), [RomM](https://homarr.dev/docs/integrations/romm/), [Stash](https://homarr.dev/docs/integrations/stash/), [Tube Archivist](https://homarr.dev/docs/integrations/tubearchivist/), [Unmanic](https://homarr.dev/docs/integrations/unmanic/), [xTeVe](https://homarr.dev/docs/integrations/xteve/), [Your Spotify](https://homarr.dev/docs/integrations/your-spotify/) |
| Collections/home/feeds (10) | [Anchor](https://homarr.dev/docs/integrations/anchor/), [Homebox](https://homarr.dev/docs/integrations/homebox/), [Karakeep](https://homarr.dev/docs/integrations/karakeep/), [Mealie](https://homarr.dev/docs/integrations/mealie/), [Miniflux](https://homarr.dev/docs/integrations/miniflux/), [Plant-it](https://homarr.dev/docs/integrations/plantit/), [Spoolman](https://homarr.dev/docs/integrations/spoolman/), [Tandoor](https://homarr.dev/docs/integrations/tandoor/), [Trilium](https://homarr.dev/docs/integrations/trilium/), [iCal](https://homarr.dev/docs/integrations/ical/) |
| Infrastructure/monitoring (15) | [Changedetection.io](https://homarr.dev/docs/integrations/changedetection/), [Coolify](https://homarr.dev/docs/integrations/coolify/), [Healthchecks](https://homarr.dev/docs/integrations/healthchecks/), [NetAlertX](https://homarr.dev/docs/integrations/netalertx/), [OpenMediaVault](https://homarr.dev/docs/integrations/open-media-vault/), [OPNsense](https://homarr.dev/docs/integrations/opnsense/), [Proxmox](https://homarr.dev/docs/integrations/proxmox/), [Scrutiny](https://homarr.dev/docs/integrations/scrutiny/), [Syncthing Relay](https://homarr.dev/docs/integrations/syncthing-relay/), [Synology](https://homarr.dev/docs/integrations/synology/), [TrueNAS](https://homarr.dev/docs/integrations/truenas/), [UniFi Controller](https://homarr.dev/docs/integrations/unifi-controller/), [Unraid](https://homarr.dev/docs/integrations/unraid/), [Wazuh](https://homarr.dev/docs/integrations/wazuh/), [llama.cpp](https://homarr.dev/docs/integrations/llama-cpp/) |
Do not conflate **Syncthing** with **Syncthing Relay**, **Ollama** with **llama.cpp**, or **FreshRSS** with **Miniflux**. Jellyseerr/Overseerr were distinct upstream kinds even though baseline self-host shipped Seerr. Coolify's old descriptor was historical evidence, not a Go research-baseline Catalog registration.
### C. Shipped Apps without a Homarr research-baseline service-integration kind: 46
At the baseline, **Link = none**, except Vaultwarden's explicitly tile-only Link. **Native Homarr auth/widgets = not applicable**; generic navigation remained available for Apps with Addresses. Compared against the complete baseline [upstream registry][upstream] and [Catalog][catalog]:
Actual Budget, Authelia, Authentik, BookStack, Borgmatic, Cloudflare Tunnel, Dockge, Docmost, Firefly III, Forgejo, FreshRSS, Grafana, HAProxy, Headscale, Homarr, Homebridge, Keycloak, Kopia, LLDAP, Loki, Mayan EDMS, MinIO AIStor, NetBird, Nginx Proxy Manager, Node-RED, Ollama, Open WebUI, Pangolin, Passbolt, PhotoPrism, Portainer, PostgreSQL, Renovate, restic, Scrypted, Seafile, Stalwart Mail Server, Stirling PDF, Syncthing, Tailscale, Unbound, Valkey, Vaultwarden, Vector, VictoriaMetrics, ZeroTier.
This meant **no native registry kind in the research snapshot**, not that a saved shortcut, public/custom HTTP widget, or another platform feature was impossible. Homarr's [Custom Widget request model](https://homarr.dev/docs/management/custom-widgets/requests-and-security/) is a separate capability and was not counted as implemented self-host Links.
## Why absent-peer Links appeared in the baseline terminal UI
This was a **service scheduling/state problem**, not evidence that Homarr created ntfy/Beszel resources. The later #265 merge is recorded in the [index](homarr-ticket-index.md#related-work); this section retains the original diagnosis.
1. Baseline Homarr returned every target unconditionally (`Links()` at `links.go:130–137`). The contract only required `Apply` once both Apps had finished Setup ([`app.go:108–116`][link-contract]).
2. `runSetup` called `applyLink` for **every** declared Link after successful own Setup/Check (`service.go:404–429`). `readyPeer` returned nil both for an **absent** App and for an **installed but unready** App (`service.go:525–543`). [Scheduling source][schedule].
3. Both cases called `h.Skip(link.ID, "waiting for <target> (install it, or finish its Setup)")` (`service.go:464–470`). `H.Skip` emitted a step event and recorded a persistent `skip`; recorder put it into `Steps` and `StepOrder` ([helpers skip][skip], [`service.go:315–328`][recorder]). Thus an optional peer that was never selected became a visible remembered task.
4. Status included **all** `StepOrder` entries, without peer eligibility filtering (`status.go:138–155`). Apps → Setup iterated all steps; Activity iterated all remembered steps. The activity projection even reclassified a skipped Link whose text started `waiting for ` as **NotRun**, retaining its row/target rather than hiding it. Sources: [Status][status], [Setup render][setup-render], [Activity rows][activity-rows], [Activity projection][activity-projection].
5. `skip` was **not** `manual`/`fail`: only those two needed attention, and `setupResult` ignored skips. Homarr could therefore report Setup OK alongside irrelevant waiting rows. Do **not** hide all skips: idempotent already-done steps also used `StepSkip` (`helpers.go:140–141`). [Step semantics][step-semantics], [helper semantics][skip].
6. Onboarding plan preview already filtered Links to present targets (baseline `onboarding.go:542–564`), demonstrating a usable presence concept distinct from readiness. Target removal forgot Link records, but a later baseline Homarr Setup created absent-target skips again. [Preview][preview], [unlink][unlink].
No target `Apply` executed when `readyPeer` was nil. Generic tile reconciliation selected **installed** Project Apps, so this baseline absent-peer waiting-row bug did not by itself create a phantom Homarr tile or integration. If the complaint instead concerned Homarr's browser board, stale owned resources or inventory mismatch needed a separate investigation; no live state was inspected by this research.
## Historical TypeScript finding and migration limits
- Historical `.agents/skills/self-host/workflows/selfhost/references/catalog/apps/<id>/homarr.json` files owned `integrationKind`, required/alternative secrets, widgets, stats metrics and prerequisites: **48 native mappings**. `packages/cli/src/lib/homarr-stack-mapping.ts:186–205` loaded them, and `:148–183` resolved saved credentials, preferring usable key modes. [Descriptor and resolver sources][old-descriptors], [old-mapping].
- `packages/cli/src/lib/homarr-stack-seed.ts:133–178` checked live `integration.getKinds` and exact credential alternatives. `:251–320` checked a stale-proof approval, created missing integrations and widgets, read back resources, preserved unrelated board items, and reported `renderedContentVerified: false`. This was real generic wiring code, not just an icon/name registry. [Source][old-seed].
- `homarrNativeEligibility` additionally gated exact recipe hashes and private HTTPS proofs, and widget selection required chosen IDs. Some connections could be blocked/tile-only; no honest “48 automatically working connections” conclusion followed. The historical checkout's descriptors and test expectations also differed in places (e.g. stats selections), so old tests were not a fresh passing contract. [Eligibility][old-mapping], [historical synthetic tests][old-tests].
- Historical registry's `checkedAt: 2026-10-03`, **49 standard entries**, zero special, and `externalOnly: ["unifiController"]` supported **48 bundled native descriptors + 1 external surface**, not upstream completeness. Its cited `beffe794…` registry already contained the additional 39 non-Mock kinds it omitted. The coverage report calculated its denominator from this **curated list**, rather than querying all upstream kinds (`catalog.ts:2114–2186`). [Registry][old-registry], [coverage calculation][old-coverage], [upstream counterexample][old-upstream].
- “Homarr Link” is Go vocabulary. TypeScript had catalog descriptors and attachment jobs, not this Go `app.Link` implementation. Research-baseline generic tiles preserved broad navigation, while native wiring narrowed from 48 descriptors to 8 integration targets. [ADR 0007][adr7] also ruled out assuming automatic migration of old Projects.
## Historical research recommendations
These were 11 October research recommendations and decision topics, **not new tickets, questions or authorization now**. Later source slices and decisions are tracked in the reconciled index.
1. **Absent-peer eligibility, separate from coverage.** `internal/service` should distinguish absent peers (no scheduled row) from installed/unready peers (visible wait). Decide a common rule for all Apps versus Homarr-only behavior. Preserve consumer ownership, failed/manual waits, future install-triggered inbound Links, existing JSON/persisted formats, and idempotent skip outcomes. Historical absent-peer records needed projection or cleanup semantics, and live Activity events should agree with remembered Status; TUI-only string hiding would leave underlying task pollution.
2. **Native wiring for the 39 baseline shipped candidates.** Keep declaration/connection/widget ownership in `internal/apps/homarr`; obtain credentials in the peer App's own Setup/helper boundary, not by bypassing `internal/project`. Define credential readiness, missing-key recovery, server routes/ports and least privilege per App before batches. Decide whether “coverage” means connection configured, chosen default widgets, or verified useful data. Navigation alone is not acceptance.
3. **Catalog expansion/external connections is separate.** Forty-one upstream kinds were outside the research-baseline Catalog, some host operating systems/appliances/feeds. Decide whether external and Unmanaged peers become typed connection inputs, remain manual, or get install definitions; do not promise a Compose App for every integration. Treat Docker/Kubernetes as separate, security-sensitive features: no casual socket mount under [AGENTS](https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/AGENTS.md#L25-L31).
4. **Compatibility contract before completeness claims.** Baseline Go shipped `ghcr.io/homarr-labs/homarr:dev`, a mutable tag (`homarr.go:29,49`), and mixed REST with internal tRPC/board payloads. Pin a supported upstream version/source contract or define a narrow compatibility check. The older implementation's live `integration.getKinds` validation was reusable precedent, not a reason to reintroduce the TypeScript runtime. [Baseline Go image][image], [old validation][old-seed].
5. **Widget policy and adoption.** Choose defaults versus opt-in, shared Stats/Calendar layout, and ID-dependent selectors (album/system/website/entity/automation). Preserve manually authored resources; define when matching integration records may be adopted, updated or removed. Broader wiring multiplies API/schema/credential churn and dashboard clutter even after absent-peer rows are fixed. [Baseline widget ownership][widget-ownership], [old prerequisites][old-mapping], [research-baseline widget compatibility][widget-map].
6. **Native versus arbitrary API access.** Baseline qBittorrent's Go username/password connection supported native downloads, but Homarr's research-baseline generic requests required an API key on qBittorrent 5.2+; iCal and TrueNAS could not be generic HTTP sources at all. A working native Link does not imply all MCP/Custom Widget actions work. [Official source-security guidance](https://homarr.dev/docs/management/custom-widgets/requests-and-security/), [baseline registry limitations][upstream].
Unknowns at the research date: deployed Homarr image digest/version; whether reported rows were terminal Setup/Activity or stale browser resources; baseline peer credential availability for every candidate; historical live acceptance outside scoped proofs; exact desired external-service/default-widget scope. None was inferred from private state. Later reference identities and bounded acceptance are recorded separately above, not retroactively substituted into this snapshot.
## Source index
[context]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/CONTEXT.md#L61-L76
[adr1]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/docs/adr/0001-compiled-apps-compose-is-truth.md
[adr7]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/docs/adr/0007-one-repo-go-cli-only.md
[catalog]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/catalog/default.go#L14-L27
[registrations]: https://github.com/ajnart/selfhost-skills/tree/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/catalog
[targets]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/links.go#L72-L137
[tiles]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/installed.go#L15-L156
[installed]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/service/installed_apps.go#L12-L79
[homarr-auth]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/homarr.go#L195-L374
[homarr-setup]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/homarr.go#L374-L440
[image]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/homarr.go#L25-L59
[transport]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/links.go#L158-L192
[reconcile]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/links.go#L544-L641
[widget-ownership]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/links.go#L423-L744
[shared]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/shared_widgets.go#L10-L43
[notebook]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/apps/homarr/notebook.go#L30-L74
[proof]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/docs/homarr-proof.md#L1-L26
[link-contract]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/app/app.go#L108-L116
[schedule]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/service/service.go#L401-L543
[recorder]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/service/service.go#L315-L328
[skip]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/helpers/helpers.go#L130-L182
[status]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/service/status.go#L138-L155
[setup-render]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/tui/details.go#L295-L333
[activity-rows]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/tui/activity_view.go#L92-L103
[activity-projection]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/activity/activity.go#L181-L192
[step-semantics]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/status/status.go#L4-L25
[preview]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/tui/onboarding.go#L542-L564
[unlink]: https://github.com/ajnart/selfhost-skills/blob/5aafb4c1833fd4cd9caf0f7beaac31e625aafe22/internal/service/lifecycle.go#L149-L190
[docs]: https://homarr.dev/docs/integrations/
[release]: https://github.com/homarr-labs/homarr/releases/tag/v2.4.1
[upstream]: https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/definitions/src/integration.ts#L82-L1049
[creators]: https://github.com/homarr-labs/homarr/blob/b925f75e62ff224bfedbe4344a65fcd4f9f567e4/packages/integrations/src/base/creator.ts
[widget-map]: https://github.com/homarr-labs/homarr/blob/b925f75e62ff224bfedbe4344a65fcd4f9f567e4/packages/definitions/src/widget-integration-map.ts#L35-L119
[docker]: https://homarr.dev/docs/integrations/docker/
[labels]: https://homarr.dev/docs/integrations/docker-labels/
[kubernetes]: https://homarr.dev/docs/integrations/kubernetes/
[old-descriptors]: https://github.com/ajnart/selfhost-skills/tree/56f3fbf6c972fa507e5cae7673dc730438714855/.agents/skills/self-host/workflows/selfhost/references/catalog/apps
[old-registry]: https://github.com/ajnart/selfhost-skills/blob/56f3fbf6c972fa507e5cae7673dc730438714855/.agents/skills/self-host/workflows/selfhost/references/catalog/homarr-integrations.json#L1-L62
[old-coverage]: https://github.com/ajnart/selfhost-skills/blob/56f3fbf6c972fa507e5cae7673dc730438714855/.agents/skills/self-host/workflows/selfhost/scripts/catalog.ts#L2114-L2186
[old-mapping]: https://github.com/ajnart/selfhost-skills/blob/56f3fbf6c972fa507e5cae7673dc730438714855/packages/cli/src/lib/homarr-stack-mapping.ts#L128-L250
[old-seed]: https://github.com/ajnart/selfhost-skills/blob/56f3fbf6c972fa507e5cae7673dc730438714855/packages/cli/src/lib/homarr-stack-seed.ts#L133-L320
[old-tests]: https://github.com/ajnart/selfhost-skills/blob/56f3fbf6c972fa507e5cae7673dc730438714855/packages/cli/tests/homarr-catalog-mappings.test.ts#L6-L138
[old-upstream]: https://github.com/homarr-labs/homarr/blob/beffe794022c22c43dd458999ee2a016bb1df2e6/packages/definitions/src/integration.ts