# Gotify Homarr implementation boundary Source status, 13 October 2026 (before this docs update): `4d73ed69a47bdf9025865f38014347ec7e9180c6`. The descriptor is normalized to the Stats SDK and registered in `internal/apps/homarr/targets.go`. This establishes source wiring only; native/rendered data, both installation orders and lifecycle acceptance remain unproved. Earlier dated evidence retains its original scope. Issue #282; source implementation, not live functional proof. ## Contract - Homarr: `ghcr.io/homarr-labs/homarr:v2.4.1`, source `ebbd4876d25a51151444a009ab596e1154d62880`. The shared version and `integration.getKinds` gates apply to the registered `gotifyTarget()`; registry and reconciliation remain custodian-owned. - Gotify: compiled `gotify/server:3.1.1`. No new dependencies or privileges. Runtime image digests are unrecorded because runtime verification is gated. - Native kind `gotify`, internal Address `http://gotify:80`; the tile and notification deep links use the separately resolved browser Address. - Exact credential shape: `username`, `password`, HTTP Basic authentication. No unauthenticated alternative, application token, or client token fallback. ## Account and visibility Gotify Setup verifies the existing Admin login, then creates one non-admin `self-host-homarr` account if that name does not exist. The generated password, username and returned numeric account ID are saved with `h.SetSecret` as `GOTIFY_HOMARR_PASSWORD`, `GOTIFY_HOMARR_USERNAME` and `GOTIFY_HOMARR_USER_ID`. The password is saved before creation so an ambiguous response cannot cause blind credential rotation. A valid recorded account is reused on every retry. A same-name account without a usable recorded login remains Manual. Restore the login and ID through the Project secret boundary only after deciding ownership privately; do not delete the account, applications or messages. Existing Projects can retry `self-host setup gotify` to create the missing account. Recreating a container does not reset a database's administrator. Messages belong to the user that owns their sending application. The new account cannot read the administrator's or other users' messages. Sending applications must be created while signed in as `self-host-homarr`; this is an intentional least-privilege visibility boundary, not server-wide delivery. Gotify does not provide a read-only username/password role: a standard user can manage its own resources, but cannot administer other users. Routine Setup/Check and Link verification never send, delete or acknowledge messages, create a client, subscribe a device, or enable public registration. ## Widgets and checks The defaults are native `notifications` with visible application logos and plain message formatting, plus the owned shared Stats entry `messages`. Both accept Gotify without an application selector; no identifier is guessed. The notifications widget remains distinct from ntfy and other connections. The #262 ownership contract preserves unrelated shared Stats/Calendar entries and user-authored integrations and widgets. Every Check probes the recorded non-admin identity and authenticated `GET /message?limit=100` afresh, including required message/paging fields. An explicit empty message array with size zero is valid; missing/null fields and rejected authentication are not zeros. The Link first verifies `/current/user` through Homarr's saved connection using the supported `integration.request` mutation with `method: GET`. It requires the exact integration ID/kind, upstream success with HTTP 200, the recorded numeric Gotify account ID and strict `admin: false`. Missing or malformed identity, a different account, and administrator credentials fail before native data can be accepted. Homarr redacts the saved username in the response; identity is established by the account ID, not by adopting that response name. This read does not create, rotate or alter any account. The Link polls `widget.notifications.getNotifications` scoped to the exact integration using the existing polling helper and a 30-second context deadline. It validates the result's integration identity, error state, native `integration.updatedAt` and message fields. Homarr caches successful notification reads for ten seconds; only an actual native timestamp within this polling attempt is accepted. Older, future, missing or malformed timestamps never turn cached empty messages into fresh success. A fresh authenticated empty array is valid. Polling only reads data, without invalidation, fabricated timestamps or notification sends. The SDK discovers the catalog and forces one advancing Stats refresh after native verification. The pure Gotify callback requires `messages` in `count` units, an integral value in 0–100 and a timestamp at or after this attempt started. Missing values, failed/stale/no-op refreshes fail centrally. This cache refresh changes no Gotify data. The `messages` count means retrieved notifications, capped at 100 by the adapter, not all-time volume, unread messages, or verified device delivery. It can differ from a separately fetched list when messages arrive between reads. ## Acceptance still required The descriptor is registered. Runtime acceptance remains pending the approved contained phase. Local checks and saved configuration do not prove a rendered widget. Next phase must use an approved disposable loopback-only Project, development Telemetry disabled, exact image digests recorded, and no person's credentials, private files, Project or devices. Use the isolated account and one scoped disposable application with one known fixture message, at most once; do not send routine test notifications or reuse a real device subscription. Verify that message through Homarr and the rendered notifications/Stats widgets. Then prove both installation orders, late-peer/retry, restart, credential updates, removal/reinstallation, stable owned IDs and preservation of user resources. Cleanup must remain scoped to the approved disposable resources. ## Local verification The local gate passed on the committed #262 reference `b8f5ea0b27c1641079b834b280cc28b067119b1d` plus this peer's changes: clean `gofmt -l .`, `go build ./...`, `go vet ./...`, `go vet -tags e2e ./e2e/`, `go test ./...`, `bash -n install.sh`, and `go test -race` for TUI, Activity, Gotify and Homarr, with `GOFLAGS=-buildvcs=false`. The existing Gotify fake was updated to cover its new account step, legitimate empty messages and rerun reuse; no new test functions or dependencies were added. These are source/local-fixture results, not Docker, native rendered widget or isolated lifecycle acceptance. At that historical correction, the source remained on that frozen base; no reference merge, registration, shared Stats normalization or common reconciliation edit was performed. The custodian then owned assembly onto the merged #309 reference `6b82c55fb890c11a1388bd4e5a2f10b632ff4f7c`, including its central post-leaf `freshStatsSnapshot` gate. At that correction, existing fixtures did not exercise the unregistered descriptor; fresh source review and approved native acceptance were still required. The source-status note above records subsequent SDK wiring. ## Official sources - [Homarr Gotify guide](https://homarr.dev/docs/integrations/gotify/) - [Gotify user/message scope](https://gotify.net/docs/) - [Gotify API documentation](https://gotify.net/api-docs) - [Homarr 2.4.1 credentials](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/definitions/src/integration.ts) - [Homarr Gotify adapter](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/gotify/gotify-integration.ts) - [Homarr notification query](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/widgets/notifications.ts) - [Homarr saved-connection request schema and response redaction](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/integration/integration-request.ts) - [Homarr integration request route](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/integration/integration-router.ts) - [Homarr saved-connection authentication and permission gate](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/api/src/router/integration/integration-http.ts) - [Homarr native notification cache and timestamps](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/request-handler/src/lib/request-handler.ts) - [Homarr Stats metric](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/stats/existing.ts) - [Homarr Stats refresh/error contract](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/request-handler/src/stats.ts) - [Gotify 3.1.1 account API](https://github.com/gotify/server/blob/v3.1.1/api/user.go) - [Gotify 3.1.1 message API](https://github.com/gotify/server/blob/v3.1.1/api/message.go)