docs/homarr-paperless-proof.md
Rama main · réplica de lectura del repositorio original.
# Paperless-ngx prerequisite and Homarr descriptor
Source status, 13 October 2026 (before this docs update):
`a463bd3dbed8856ccf990b94faaa6c9692e69615`.
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.
Peer slice for #289. The prerequisite runtime evidence below belongs to
11 October 2026 and implementation commit `368cc026`; the source-only ownership
and Manual recovery follow-up is dated 12 October 2026. Neither is complete
Homarr widget acceptance. #262's shared contract is now frozen at
`6b82c55fb890c11a1388bd4e5a2f10b632ff4f7c`; central wiring is complete, while new bounded
rendered/lifecycle acceptance remains held. The 13 October source-only SDK
completion adds native and Stats verification; it does not add runtime evidence.
## Implemented boundary
- Paperless-ngx Setup verifies the configured Admin login, then creates a
dedicated, randomly named Homarr account only when no account checkpoint,
username, password or token is saved. A pending checkpoint precedes the
remote write; the verified create response supplies its canonical account ID.
A random ownership marker is recorded with that ID and in the dedicated
account's First name field. Neither the shared Admin login nor another
account's metadata is modified.
- The account is active, not staff or a superuser, has no groups and has exactly
`view_global_statistics`, `view_tag`, `view_correspondent` and
`view_documenttype`. It cannot list document contents, upload, edit or delete
documents. Global statistics expose archive counts; taxonomy lists retain
Paperless's existing object-level visibility. Setup does not rewrite ownership
or object permissions.
- Reruns resolve only `/api/users/<recorded-id>/`, require that returned ID and
ownership marker to agree, and authenticate the saved password using the
returned canonical username. The Basic-auth and token-auth profiles must
carry the same marker. A profile alone returns no ID: marker correlation
with the exact administrator-authenticated detail record is the identity
evidence, not arbitrary token success or a username lookup.
- The current profile token is reused. `/api/token/` creates a reusable token
only when that verified profile has none. Production Setup never calls the
rotating `generate_auth_token` endpoint. The checkpoint, username, password
and verified `PAPERLESS_NGX_API_KEY` are stored through `helpers.H` and the
existing Project secret boundary, not in command arguments.
- Every Check reads account policy, ownership and current token afresh, then
authenticates against `/api/statistics/`, `/api/tags/`,
`/api/correspondents/` and `/api/document_types/`. Missing fields are not
accepted as zero. A null inbox count is valid when no inbox tag exists.
Missing/stale tokens are rediscovered on `self-host setup paperless-ngx`.
## Source-only ownership and Manual recovery follow-up
The production follow-up at `2b9753441043e85c979e3edf6b104542e4dc7804`
changed Paperless Setup/token code, its fixture and documentation. It also
added agent-authored regression cases inside existing test bodies without an
actual human request for new unit tests. The previous "no new cases" claim was
incorrect. The coverage cleanup removes those cases and their fault-injection
machinery; production code, dependencies, registry and shared verifiers remain
unchanged.
- A renamed selected account or lost saved username keeps the same recorded
ID, ownership marker, password and reusable token. The current canonical
username is saved only after authenticated ownership and data checks pass.
- Legacy name-only metadata cannot establish canonical provenance. It remains
Manual without adopting an existing account, adding an ownership marker,
replacing a token or rewriting permissions. Malformed/missing/pending
checkpoints likewise fail closed. A failed final checkpoint write cannot
cause a second account/token on retry.
- Verified credential denial, disabled status, MFA, identity disagreement and
policy drift are targeted retryable Manual steps. Setup never reactivates
users, disables MFA, resets passwords, changes existing permissions, rotates
tokens or deletes accounts. Infrastructure failures and malformed API/data
responses remain failed steps; they are not blanket-converted to Manual.
- The supported person recovery seam is the official 3.2.1 Users & Groups →
Edit user dialog: it displays the ID and exposes Password, First name and
Active controls. After confirming the recorded ID/marker privately with
`self-host credentials`, the person may restore the corresponding saved
password or Active status if intended, then retry Setup. MFA remains a
separate person decision; Setup does not clear it. The restored known pair
reuses its existing token. No account UI action was performed for this fix.
- Missing saved credentials or unverifiable old ownership require a verified
Project Backup or separately approved migration. This slice adds no private
Inputs, no `--set-file` ingestion and no credentials-only update command.
It does not claim an automatic old-format migration or a general replacement
credential recovery route.
Git history and the owning agent's recorded patches confirm that the extra
checkpoint/creation assertions, independent partial-write fixture, rename/key/
ownership/authentication/recovery/failure scenarios and exclusive fault flags,
counters and failing secret store were introduced by that agent, not by a
person or unknown concurrent work. They were removed without replacement.
The remaining test tree was compared with fixed implementation parent
`5aafb4c1833fd4cd9caf0f7beaac31e625aafe22`, not merely the last commit or the
list of function names. Every baseline test body/assertion, `newH` and the
plain in-memory secret store are unchanged. The only remaining fixture delta
supplies the actual account create/detail, reusable token, Basic/token profile
and four data endpoint responses needed by those existing cases, including the
returned ID, ownership marker and required profile fields.
Retained assertions cover `TestSetupThenCheckPass` (normal Setup/Check and no
failed steps), `TestRerunSkipsEveryStep` (all existing rerun steps skip), and
`TestFailedLoginHintNamesTheApp` (rejected administrator login and App-page
hint). The happy-path adapter includes the read-only credential responses;
there are no separate account-ID/token-count or least-permission assertions.
Rename/lost username or ID, stale/rejected credentials, uncertain provenance,
MFA/disabled accounts, permission drift, partial checkpoint writes and malformed
response guards now have source/schema/compile review only, not retained
regression cases or current runtime acceptance.
At `2b975344`, the source-only local gate passed on 12 October: focused
`go test ./internal/apps/paperlessngx/`, empty `gofmt -l .`, `go build ./...`,
`go vet ./...`, `go vet -tags e2e ./e2e/`, `go test ./...`,
`go test -race ./internal/tui/ ./internal/activity/ ./internal/apps/paperlessngx/`,
`bash -n install.sh` and `git diff --check`, with `GOFLAGS=-buildvcs=false`.
That run included the now-removed cases and is not final-tree retained coverage.
No Docker, browser, e2e runtime, installer, host probes or account actions ran.
The remaining-tree gate passed after the coverage cleanup: focused
`go test -count=1 ./internal/apps/paperlessngx/`, empty `gofmt -l .`,
`go build ./...`, `go vet ./...`, `go vet -tags e2e ./e2e/`,
`go test -count=1 ./...`,
`go test -race -count=1 ./internal/tui/ ./internal/activity/ ./internal/apps/paperlessngx/`,
`bash -n install.sh` and `git diff --check`, with `GOFLAGS=-buildvcs=false`.
These runs validate the remaining baseline cases and compilation, not the
removed regression scenarios or native/rendered/lifecycle acceptance.
## Homarr wiring handoff
`internal/apps/homarr/target_paperless_ngx.go` defines
`paperlessTarget() target` through the current SDK descriptor:
- Catalog ID `paperless-ngx`, upstream kind `paperlessNgx`.
- Compose-side Address `http://paperless-ngx:8000`; browser Address stays separate.
- Homarr credential `apiKey` from the peer's verified `api_key` secret.
- Native `paperlessNgx` widget showing document and inbox totals, inbox ratio,
correspondents, tags and document types. Shared Stats selects `documentsTotal`,
`documentsInbox`, `tagsCount`, `correspondentsCount` and `documentTypesCount`.
No guessed document IDs or custom JSX.
- Registry owner registered `paperlessTarget()` against the current SDK contract.
The Paperless completion makes no central registry, `links.go`, shared widget
or plugin/init changes beyond the forward SDK merge.
- The supported upstream widget default is 2 by 2 cells. The central placement
helper on this branch defaults unknown widgets to 1 by 1; the registry owner
must preserve a useful size when wiring this descriptor.
Source-only completion forward-merges SDK commit
`ecc21d9f95664ef6245863ddfb05930bf4cde3a3` and ports only the repaired aggregate's
Paperless descriptor/verifier, without importing its registry or other peers.
Read-only preflight and native verification authenticate the saved peer token
against statistics and taxonomy counts. Every source/native count must be an
explicit finite nonnegative integer; inbox count cannot exceed document count.
A null source inbox is valid only with explicit null `inbox_tag` and
`inbox_tags` metadata. Homarr's actual native schema normalizes this to zero;
an absent native field or native null remains invalid. The notice distinguishes
an unconfigured inbox from a processed inbox.
The native call is authenticated `widget.paperlessNgx.getStats` with the owned
integration ID. Its cached counts are validated independently of the direct
peer sample, so changing legitimate counts do not cause equality failures.
The pure `stats` callback receives the SDK's one completed central sample and
catalog, requires `count` for all five metrics, and validates integrality and
the inbox bound on that sample. It performs no HTTP calls or refreshes.
Freshness, advancing timestamps and selected-field presence stay SDK-owned.
Requested focused regression coverage uses the descriptor's production
`verifyData` seam without registry registration. It exercises authenticated
native calls and owned integration selection, valid independent cached counts,
missing/null fields, absent-inbox metadata, legitimate document zeros, native
credential denial, unchanged refresh timestamps, invalid first refreshed data,
and exactly one central catalog/refresh per successful invocation. Pure count
checks cover all five metrics, wrong units, negative/fractional/nonfinite values.
This extends the Homarr tests only; the peer App's retained baseline cases and
credential recovery coverage boundary above stay unchanged.
The final isolated Homarr run still needs to verify useful rendered data,
stable owned IDs on rerun, token-change propagation, preservation of manual
widgets/integrations and owned-only removal. Source declarations or a passing
connection test do not satisfy that acceptance.
## Isolated prerequisite evidence
Historical validated implementation commit: `368cc026` on
`feat/homarr-paperless-sol`. This run predates the canonical ID/marker and
Manual recovery changes; it does not validate those changes at runtime.
The only runtime fixture used Compose Project
`self-host-paperless-sol-93543`, loopback port `53415`, and one new disposable
Home under the approved OpenCode temporary directory. No existing Project,
private documents, browser profiles or credential stores were inspected.
Development Telemetry and update checks were disabled. The three containers
were limited to 2304 MiB total and two CPUs each, with an eight-minute deadline.
Only the existing production service/helper/Project boundaries performed
installation, Setup, credential persistence and Compose commands; the fixture
wrapper changed resource limits only.
Observed results:
- Fresh installation with Admin login `fixture-admin` passed Setup and Check,
including credential creation and fresh authenticated probes on an empty
archive. The prior hard-coded administrator lookup was corrected.
- A single synthetic document model, owned by the fixture administrator, with
an unowned inbox tag, correspondent and document type returned
`documents_total=1`, `documents_inbox=1` using the read-only token. All three
taxonomy endpoints passed Check. This proves document-count access, not OCR,
ingestion, physical document downloads or Homarr rendering.
- The same token received HTTP 403 for taxonomy writes and document-content
listing. Account policy checks confirmed the exact four permissions, no
groups and no staff/superuser status.
- A valid Setup rerun kept the token unchanged. Replacing the saved key with
an invalid synthetic value made fresh Check fail; Setup rediscovered the same
upstream token without rotation.
- A controlled upstream token rotation in the fixture made Check fail again;
Setup rediscovered that new token without rotating it a second time.
- Exact App response: Paperless-ngx `3.2.1`, API version `10`.
Image: `ghcr.io/paperless-ngx/paperless-ngx@sha256:5fa76604a81df6945086e0837b14b56543d137e8ce4f311cc5d9ebe907e74e79`.
The fixture also used the Catalog's `postgres:18` and `valkey/valkey:9-alpine`.
- Initial synthetic seeding needed the App key and database password passed
through the helper's environment because container exec bypasses entrypoint
`_FILE` resolution. The same fixture was resumed after that harness-only
correction; no second fixture was created. Its exact owned Compose Project
was taken down after each run, and a label-filtered check confirmed no owned
containers remained. Synthetic files were retained only in that disposable
Home; no broad prune or volume deletion occurred.
Local gate passed: empty `gofmt -l .`, `go build ./...`, `go vet ./...`,
`go vet -tags e2e ./e2e/`, `go test ./...`, race checks for
`internal/tui`, `internal/activity`, `internal/apps/paperlessngx` and
`internal/apps/homarr`, and `bash -n install.sh`. No e2e runtime suite or
installer was run. The first combined gate command hit its shell deadline
after the full test suite passed; the remaining race and shell checks passed
in a separate bounded command.
## Versions and sources
Paperless-ngx is pinned by this Catalog App to `3.2.1`. Its permission and
authentication contract is verified against these versioned sources:
- [REST authorization and API versioning](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/docs/api.md).
- [User creation and profile/token serializers](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src/paperless/serialisers.py).
- [Reusable token endpoint and rotating profile endpoint](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src/paperless/views.py).
- [Basic authentication and MFA boundary](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src/paperless/auth.py).
- [Official Edit user ID/password/status controls](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html).
- [Person-controlled MFA recovery](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/docs/usage.md#two-factor-authentication).
- [Statistics visibility and read-only permissions](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src/documents/permissions.py).
- [Statistics response](https://github.com/paperless-ngx/paperless-ngx/blob/v3.2.1/src/documents/views.py).
Homarr descriptor source contract: `2.4.1`, commit
`ebbd4876d25a51151444a009ab596e1154d62880`:
- [Authentication and all four requested data endpoints](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/integrations/src/paperless-ngx/paperless-ngx-integration.ts).
- [Native widget options](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/widgets/src/paperless-ngx/index.ts).
- [Native widget integration limit](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/definitions/src/widget-integration-map.ts).
- [Native widget size](https://github.com/homarr-labs/homarr/blob/ebbd4876d25a51151444a009ab596e1154d62880/packages/definitions/src/widget.ts).
Context7 was attempted but its monthly quota was exhausted; upstream source
was used instead. No dependencies were added. The retained Paperless fixture
only adapts the baseline cases to the new API prerequisite; the removed cases
and reduced coverage boundary are recorded above.