Skip to content

chore(deps): bump actions/setup-node from 6 to 7 in the workflow-dependencies group - #270

Merged
admonstrator merged 2 commits into
v2026.08.01from
dependabot/github_actions/main/workflow-dependencies-eefdb6dedd
Aug 6, 2026
Merged

chore(deps): bump actions/setup-node from 6 to 7 in the workflow-dependencies group#270
admonstrator merged 2 commits into
v2026.08.01from
dependabot/github_actions/main/workflow-dependencies-eefdb6dedd

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 3, 2026

Copy link
Copy Markdown
Contributor

Bumps the workflow-dependencies group with 1 update: actions/setup-node.

Updates actions/setup-node from 6 to 7

Release notes

Sourced from actions/setup-node's releases.

v7.0.0

What's Changed

Enhancements:

Bug fixes:

Documentation updates:

Dependency update:

New Contributors

Full Changelog: actions/setup-node@v6...v7.0.0

v6.5.0

What's Changed

Full Changelog: actions/setup-node@v6.4.0...v6.5.0

v6.4.0

What's Changed

Dependency updates:

New Contributors

Full Changelog: actions/setup-node@v6...v6.4.0

v6.3.0

What's Changed

Enhancements:

... (truncated)

Commits
  • 8207627 Migrate to ESM and upgrade dependencies (#1574)
  • 04be95c Add cache-primary-key and cache-matched-key as outputs (#1577)
  • 7c2c68d docs: Update caching recommendations to mitigate cache poisoning risks (#1567)
  • 6a61c03 Merge pull request #1569 from jasongin/update-actions-cache-5.1.0
  • 30eb73b Resolve high-severity audit issues
  • 4e1a87a Update dist
  • 360237f Strict equality
  • 4f8aac5 Bump @​actions/cache to 5.1.0, log cache write denied
  • f4a67bb Only use mirrorToken in getManifest if it's provided (#1548)
  • 0355742 Remove dummy NODE_AUTH_TOKEN export (#1558)
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

dependabot Bot added 2 commits August 1, 2026 20:29
Bumps the npm_and_yarn group with 1 update in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 5.0.7 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v5.0.7...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the workflow-dependencies group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 6 to 7
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: workflow-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file github-actions labels Aug 3, 2026
@admonstrator
admonstrator changed the base branch from main to v2026.08.01 August 6, 2026 19:15
@admonstrator
admonstrator merged commit 3735333 into v2026.08.01 Aug 6, 2026
5 checks passed
@admonstrator
admonstrator deleted the dependabot/github_actions/main/workflow-dependencies-eefdb6dedd branch August 6, 2026 19:15
admonstrator added a commit that referenced this pull request Aug 8, 2026
* chore(deps): bump brace-expansion (#264) (#265)

Bumps the npm_and_yarn group with 1 update in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 5.0.7 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v5.0.7...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(setup): strip API_KEY and JWT_SECRET from setup bootstrap payload (#261)

Background
----------
Security advisory GHSA-84cq-vv67-g8xx flagged secret disclosure via the setup
bootstrap: the window.__SETUP_BOOTSTRAP__ object serialised into the /setup page
could leak plaintext secrets. The core remote-unauthenticated vector is already
closed (degraded-state 500, provider tokens stripped, and the localhost /
ALLOW_REMOTE_SETUP guard from GHSA-v4jq). A re-audit, however, found the
production sanitizeConfigForBootstrap() in routes/setup.js stripped the seven
provider tokens but NOT API_KEY or JWT_SECRET -- both are persisted to runtime
config (built into finalConfig on setup completion) and can be merged into the
bootstrap config in the "env-configured-but-no-users" state. The existing
regression test only exercised a *local copy* of the sanitiser (which did include
API_KEY), so it passed while the real function silently omitted API_KEY and
JWT_SECRET.

Changes
-------
- Add services/bootstrapConfigSanitizer.js as the single source of truth,
  exporting sanitizeConfigForBootstrap() and BOOTSTRAP_SECRET_FIELDS. The strip
  list now includes API_KEY and JWT_SECRET in addition to the seven provider
  tokens (PAPERLESS_API_TOKEN, OPENAI_API_KEY, OLLAMA_API_KEY, CUSTOM_API_KEY,
  AZURE_API_KEY, OCR_API_KEY, MISTRAL_API_KEY).
- routes/setup.js: remove the local, incomplete sanitiser and import the shared
  module. The /setup call site is unchanged.
- tests/test-setup-bootstrap-security.js: exercise the REAL exported sanitiser
  instead of a simulated copy, and assert API_KEY/JWT_SECRET are stripped as an
  explicit regression guard. Also drop unused requires and attach the caught
  error as `cause` so the touched file passes eslint; Prettier reformatted the
  file.

Testing
-------
- node scripts/run-tests.js --test setup-bootstrap-security -> PASS
- node scripts/run-tests.js --all -> 45 passed, 6 skipped (server-dependent), 0 failed
- npx eslint + npx prettier --check on changed files -> clean
- OpenAPI drift check (regen-openapi.js + git diff) -> in sync (no API surface change)

Impact
------
Secrets can no longer be serialised into the client-delivered setup bootstrap,
closing the residual API_KEY / JWT_SECRET gap of GHSA-84cq. No behaviour change
for the normal setup flow; non-secret config is still passed through. The test
now fails if the production strip list regresses.

Upstream Status
---------------
Not submitted upstream (clusterzx/paperless-ai); specific to the next fork's
GHSA-84cq remediation.


Claude-Session: https://claude.ai/code/session_013SohUC9cKWyz2Ydx449MBC

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(deps): bump actions/setup-node (#246)

Bumps the workflow-dependencies group with 1 update in the / directory: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 6 to 7
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: workflow-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump the docs-tooling group across 1 directory with 3 updates (#254)

Bumps the docs-tooling group with 3 updates in the / directory: [eslint](https://github.com/eslint/eslint), [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) and [prettier](https://github.com/prettier/prettier).


Updates `eslint` from 10.7.0 to 10.8.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.7.0...v10.8.0)

Updates `eslint-plugin-jsdoc` from 63.0.13 to 63.3.0
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](gajus/eslint-plugin-jsdoc@v63.0.13...v63.3.0)

Updates `prettier` from 3.9.5 to 3.9.6
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.9.5...3.9.6)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docs-tooling
- dependency-name: eslint-plugin-jsdoc
  dependency-version: 63.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docs-tooling
- dependency-name: prettier
  dependency-version: 3.9.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: docs-tooling
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <git@admon.me>

* chore(deps): bump the ui-and-utils group across 1 directory with 2 updates (#253)

Bumps the ui-and-utils group with 2 updates in the / directory: [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit) and [globals](https://github.com/sindresorhus/globals).


Updates `express-rate-limit` from 8.5.2 to 8.6.1
- [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases)
- [Commits](express-rate-limit/express-rate-limit@v8.5.2...v8.6.1)

Updates `globals` from 17.7.0 to 17.8.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.7.0...v17.8.0)

---
updated-dependencies:
- dependency-name: express-rate-limit
  dependency-version: 8.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ui-and-utils
- dependency-name: globals
  dependency-version: 17.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: ui-and-utils
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <git@admon.me>

* chore(deps): bump the runtime-core group across 1 directory with 2 updates (#266)

* chore(deps): bump brace-expansion (#264)

Bumps the npm_and_yarn group with 1 update in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 5.0.7 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v5.0.7...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump the runtime-core group across 1 directory with 2 updates

Bumps the runtime-core group with 2 updates in the / directory: [axios](https://github.com/axios/axios) and [openai](https://github.com/openai/openai-node).


Updates `axios` from 1.18.1 to 1.19.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.18.1...v1.19.0)

Updates `openai` from 6.47.0 to 7.1.0
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/main/CHANGELOG.md)
- [Commits](openai/openai-node@v6.47.0...v7.1.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: runtime-core
- dependency-name: openai
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: runtime-core
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* style: apply prettier and eslint --fix to files touched by security fixes

Background
CI runs ESLint and Prettier over every file changed in a PR. The files
touched by the upcoming security fixes carry pre-existing formatting drift
(they predate the current Prettier setup), so any change to them would turn
the lint job red for reasons unrelated to that change.

Changes
- Ran `npx prettier --write` and `npx eslint --fix` over the files the
  security fixes touch: services/paperlessService.js, services/setupService.js,
  services/serviceUtils.js, routes/setup.js, scripts/run-tests.js and
  public/js/{history,playground-analyzer,settings,setup}.js.
- No behavioural change: formatting and auto-fixable lint rules only.

Testing
- node scripts/run-tests.js --all -> 45 passed, 6 skipped (server-dependent),
  0 failed.

Impact
Keeps the security commit that follows reviewable as a small diff and lets
the lint job pass on it.

Upstream Status
Not applicable - fork-local housekeeping.

* fix(security): resolve dependabot alerts and harden outbound requests, logging and DOM rendering

Background
Four open Dependabot alerts and a batch of CodeQL findings needed triage.
Reviewing the SSRF findings surfaced a real gap the existing validation did
not cover: validateApiUrl only inspects the initial URL, while axios follows
up to five redirects by default and maxRedirects was never set anywhere in
the production code. A 3xx response from a configured Paperless-ngx instance
could therefore redirect an authenticated request - including its API token -
to an arbitrary host such as a cloud metadata endpoint.

Changes
- Dependencies (package-lock.json, all transitive):
  - fast-uri 3.1.4 -> 3.1.5 (GHSA-7p8r-x3mc-p8w7, host confusion)
  - ip-address 10.2.0 -> 10.4.0 (GHSA-mwp4-54f8-5fhr, GHSA-4xrf-jv44-h6hh,
    GHSA-22jq-vg5j-6vgg - all SSRF/trust-boundary bypasses)
  - express-rate-limit 8.5.2 -> 8.6.1, which also realigns the installed tree
    with the ^8.6.1 range already declared in package.json
  npm audit now reports 0 vulnerabilities.
- Redirect hardening: new createRedirectGuard() in services/serviceUtils.js,
  wired into both axios clients in services/paperlessService.js. Redirects to
  the configured host stay allowed, as does an http -> https upgrade (common
  behind reverse proxies); off-host targets, cloud metadata endpoints,
  non-http(s) protocols and https -> http downgrades are rejected.
- ReDoS: new stripTrailingSlashes() replaces the /\/+$/ pattern at the three
  places CodeQL flagged as polynomial backtracking on user-provided values
  (paperlessService.js, setupService.js, routes/setup.js).
- Format-string injection: 12 console.* calls in services/paperlessService.js
  and routes/setup.js no longer interpolate user-controlled values into the
  format string; the values are passed as arguments via %s instead.
- DOM XSS: field names and tag chips are assigned via textContent instead of
  being interpolated into innerHTML (public/js/settings.js, public/js/setup.js),
  and backend error text in the playground message area is rendered as text
  (public/js/playground-analyzer.js).
- Link scheme check in public/js/history.js now also rejects vbscript:.
- generateBase32Secret() in routes/setup.js masks the low 5 bits instead of
  using modulo. The alphabet has exactly 32 entries, so this is equivalent -
  it just no longer reads as a biased-modulo pattern.
- New test tests/test-redirect-guard.js, registered in the security area of
  scripts/run-tests.js.

Testing
- node scripts/run-tests.js --all -> 46 passed, 6 skipped (server-dependent),
  0 failed.
- npm audit -> 0 vulnerabilities.
- npx eslint / npx prettier --check on the changed files -> clean, except for
  21 pre-existing eslint errors in legacy code that the follow-up commit fixes.
- node scripts/regen-openapi.js -> no drift (no API surface changed).

Impact
No behavioural change for correctly configured instances. Setups that rely on
a cross-host redirect from the configured Paperless-ngx URL will now fail with
an explicit "Redirect blocked" error instead of silently following it.

Upstream Status
Not reported upstream; the redirect gap exists in clusterzx/paperless-ai too.

* chore(lint): resolve pre-existing eslint errors in touched files

Background
The lint job checks every file changed in a PR. After the formatting pass,
21 eslint errors remained in the files the security fixes touch. They all
predate this branch, but they would still fail CI now that these files are
part of the diff.

Changes
- services/paperlessService.js
  - Dropped the unused `fs` and `path` requires.
  - Replaced unused catch bindings with optional catch binding.
  - `resolvedPublicUrl` is no longer initialized with a value that every
    branch overwrites.
  - `processTags` now attaches `{ cause: error }` when rethrowing, so the
    original error is no longer lost.
  - Removed `getAllDocumentIds()`. It referenced an undefined `page`
    variable, so every call threw a ReferenceError that its own catch block
    swallowed into an empty array. Nothing in the repository calls it; the
    working equivalent is `getAllDocumentIdsScan()`, which paginates properly.
- services/setupService.js
  - Replaced eight unused catch bindings with optional catch binding.
  - Removed the unused `JSON_STANDARD_PROMPT` local in `saveConfig()`; the
    prompt actually in use lives in config.mustHavePrompt.
- public/js/history.js
  - Declared the jQuery global via `/* global $ */` (DataTables is used here).
  - Replaced an unused catch binding and removed the unused `cfSection`.

Testing
- npx eslint on all changed files -> clean.
- npx prettier --check on all changed files -> clean.
- node scripts/run-tests.js --all -> 46 passed, 6 skipped (server-dependent),
  0 failed.

Impact
No behavioural change, with one exception worth calling out: the dead and
permanently broken `getAllDocumentIds()` is gone. Callers outside this
repository that relied on it were already receiving an empty array.

Upstream Status
Not reported upstream; the broken method exists in clusterzx/paperless-ai too.

* chore(deps): bump actions/setup-node from 6 to 7 in the workflow-dependencies group (#270)

* chore(deps): bump brace-expansion (#264)

Bumps the npm_and_yarn group with 1 update in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 5.0.7 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v5.0.7...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump actions/setup-node in the workflow-dependencies group

Bumps the workflow-dependencies group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 6 to 7
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: workflow-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump ip-address from 10.2.0 to 10.4.0 in the npm_and_yarn group across 1 directory (#271)

* chore(deps): bump brace-expansion (#264)

Bumps the npm_and_yarn group with 1 update in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 5.0.7 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v5.0.7...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump ip-address

Bumps the npm_and_yarn group with 1 update in the / directory: [ip-address](https://github.com/beaugunderson/ip-address).


Updates `ip-address` from 10.2.0 to 10.4.0
- [Release notes](https://github.com/beaugunderson/ip-address/releases)
- [Commits](beaugunderson/ip-address@v10.2.0...v10.4.0)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.4.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(scanner): keep scanning after a startup connection failure (#272) (#274)

* style: format server.js and schemas.js with prettier

Background
----------
Both files predate the prettier setup and were never reformatted. CI runs
`prettier --check` on every file changed in a PR, so any change to them fails
the lint job on formatting alone.

Changes
-------
- Ran `npx prettier --write server.js schemas.js`. No behavioural change:
  quoting, line wrapping and trailing commas only.

Testing
-------
- `node --check server.js` / `node --check schemas.js`
- `node scripts/run-tests.js --all`
- `node scripts/regen-openapi.js` produces no diff

Impact
------
None at runtime. Landed separately so the follow-up fix stays reviewable.

Upstream Status
---------------
Not applicable — fork-local formatting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgY7WruM1oXDMJZqVDWR9K

* fix(scanner): keep scanning after a startup connection failure (#272)

Background
----------
If Paperless-ngx was momentarily unreachable while the container started,
startScanning() logged "Failed to get own user ID. Abort scanning." and
returned early. That early return skipped both cron.schedule() calls, so
neither the document scan nor the reconciliation job was ever armed. The app
kept running and answered /health with {"status":"healthy"} while processing
nothing until someone restarted the container manually — the reporter lost
~10 days of document processing this way.

The preflight was decorative: its result (ownUserId) was threaded through
scanInitial()/scanDocuments() into processDocument(), which never used the
parameter. getOwnUserID() also returns null when the connection is fine but
PAPERLESS_USERNAME matches no user, which triggered the same permanent abort.

Two findings shaped the fix. getAllDocuments() swallows transport errors and
returns [], so an unreachable instance looks like a successful scan with
nothing to do — arming the cron alone would have kept the outage invisible.
And the /health handler that actually answers is the one in routes/setup.js;
the copy in server.js sat behind `app.use('/', setupRoutes)` and was dead code.

Changes
-------
- server.js: arm the scan cron before contacting Paperless-ngx, and arm the
  reconciliation cron independently of it. Drop the getOwnUserID() gate and
  the unused ownUserId parameter.
- server.js: add runInitialScanWhenReachable(), a detached startup retry with
  backoff (5s→5min, window via STARTUP_PAPERLESS_RETRY_MINUTES, default 30).
  Giving up now only defers the first scan to the next cron tick.
- server.js: replace scanInitial() with scanDocuments('initial'). The old
  duplicate ignored scanControl and could have run concurrently with a cron
  tick once the scheduler is armed first.
- server.js: every scan run probes connectivity via checkConnection() and
  reports its outcome; aborted runs are logged with the reason and interval.
- server.js: remove the unreachable /health handler and its @Swagger block.
- services/scanHealthService.js (new): singleton holding scanner health —
  armed state, run outcomes, consecutive failures, last probe result.
- services/paperlessService.js: add checkConnection(), a non-throwing probe
  that separates "host answered" from "token accepted".
- routes/setup.js: /health reports scanner and Paperless state and answers
  503 while degraded; /api/processing-status carries the same snapshot.
- views: dashboard shows a warning banner while the scanner is degraded,
  rendered via textContent only.
- config/config.js: HEALTHCHECK_STRICT (default yes),
  HEALTH_SCAN_FAILURE_THRESHOLD (default 3),
  STARTUP_PAPERLESS_RETRY_MINUTES (default 30).
- server.js: fix `if (config.reconciliationEnabled)` — parseEnvBoolean returns
  the string 'no', which is truthy, so RECONCILIATION_ENABLED=no never
  disabled reconciliation. Now compared against 'yes'.
- server.js: resolve three pre-existing eslint errors in the touched file.

Testing
-------
- tests/test-scanner-startup-resilience.js (new, 18 cases): health state
  machine, checkConnection() classification, and source assertions that the
  cron is armed independently of the preflight. Registered in
  scripts/run-tests.js under the `processing` area.
- node scripts/run-tests.js --all → 47 passed, 6 skipped (need a live server),
  0 failed.
- End-to-end against a dead Paperless-ngx: scheduler armed, per-interval
  "Scan aborted" lines instead of silence, /health → 503 degraded. Starting
  Paperless-ngx afterwards let the next tick complete and /health returned to
  200 healthy — without restarting the app.
- Counter-check: DISABLE_AUTOMATIC_PROCESSING=yes stays 200 healthy with no
  scheduler.
- eslint + prettier clean; OPENAPI/openapi.json regenerated (83 paths
  unchanged, ScannerHealth/PaperlessHealth added).

Impact
------
Behaviour change for monitoring: /health now answers 503 when automatic
processing is enabled but the scanner is not working — no scheduler armed, or
HEALTH_SCAN_FAILURE_THRESHOLD consecutive failed runs (default 3, roughly
three scan intervals). Set HEALTHCHECK_STRICT=no to keep 200 and read the
scanner/paperless fields from the body instead. The Docker healthcheck now
reports unhealthy for a stalled scan loop, which is what issue #272 asks for.

Deployments that set RECONCILIATION_ENABLED=no will see reconciliation
actually stop; it had been running regardless.

The new env vars and the /health contract still need documenting in
admonstrator/paperless-ai-next-docs.

Upstream Status
---------------
Not reported upstream. clusterzx/paperless-ai carries the same early return in
startScanning().

Closes #272

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgY7WruM1oXDMJZqVDWR9K

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(release): add v2026.08.01 changelog entries and bump version (#275)

Background
----------
config/changelog.js feeds the "What's New" modal shown after an update. The
scanner resilience work in this branch is user-visible — a permanently dead
scan loop, a health endpoint that now answers 503, and a new dashboard banner —
so it needs an entry there, not just in the docs site.

The file's header comment requires the newest RELEASES block to stay in sync
with PAPERLESS_AI_VERSION, which still read v2026.07.04.

Changes
-------
- config/changelog.js: new v2026.08.01 block covering the startup scan fix, the
  RECONCILIATION_ENABLED=no fix, the /health scanner reporting (linking to
  https://zettelrob.be/getting-started/monitoring/) and the dashboard banner.
- config/config.js: PAPERLESS_AI_VERSION bumped to v2026.08.01 to match.
- config/changelog.js was not prettier-clean before this change; reformatted so
  the CI lint job passes (one pre-existing line, quote style only).

Testing
-------
- `node -e "require('./config/changelog')"` → version v2026.08.01, four entries
- PAPERLESS_AI_VERSION and changelog.version verified identical
- node scripts/run-tests.js --all → 47 passed, 6 skipped, 0 failed
- eslint + prettier clean; regen-openapi.js produces no diff (the spec pins its
  own info.version at 1.0.0 and does not track the app version)

Impact
------
After deploying v2026.08.01, users see the What's New modal once with these
entries. The version string is now shown as v2026.08.01 across the UI, so tag
the release accordingly.

Upstream Status
---------------
Not applicable — fork-local release metadata.


Claude-Session: https://claude.ai/code/session_01CgY7WruM1oXDMJZqVDWR9K

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(deps): bump the runtime-core group with 2 updates (#267)

Bumps the runtime-core group with 2 updates: axios and openai.

Merged from PR #267, which targets `main` and is therefore based on an
older baseline than this release branch. The branch already carries
axios 1.19.0 (via #266), so only the openai range moves here.

Updates `openai` from ^7.1.0 to ^7.2.0
- openai/openai-node@v7.1.0...v7.2.0

`axios` stays at ^1.19.0 (already bumped in #266).

The lockfile resolves openai to 7.4.0, the newest release inside the
^7.2.0 range at install time.

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: runtime-core
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump the docs-tooling group with 3 updates (#268)

Bumps the docs-tooling group with 3 updates: eslint, eslint-plugin-jsdoc
and prettier.

Merged from PR #268, which targets `main` and is therefore based on an
older baseline than this release branch. The branch already carries
eslint 10.8.0 and prettier 3.9.6 (via #254 and later bumps), so only the
eslint-plugin-jsdoc range moves here.

Updates `eslint-plugin-jsdoc` from ^63.0.13 to ^63.3.2
- gajus/eslint-plugin-jsdoc@v63.0.13...v63.3.2

`eslint` stays at ^10.8.0 and `prettier` at ^3.9.6 (already bumped).
`globals` keeps the branch value ^17.8.0 instead of the older ^17.7.0
carried in this PR's baseline.

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-version: 63.3.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docs-tooling
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump js-yaml to 4.3.1 to clear GHSA-5p4m-2wfm-xmqj

Background
----------
npm audit reported one high-severity advisory on this branch: js-yaml
4.3.0 is vulnerable to quadratic CPU consumption while resolving `!!omap`
nodes (GHSA-5p4m-2wfm-xmqj, CVE-2026-59870, CVSS 7.5). The package is not
a direct dependency — it is pulled in transitively:

  swagger-jsdoc 6.3.0
    -> @apidevtools/swagger-parser 12.1.0
      -> @apidevtools/json-schema-ref-parser 14.0.1
        -> js-yaml ^4.1.0

swagger-jsdoc has no newer stable release (6.3.0 is latest; 7.0.0 is still
at rc.6), so waiting for an upstream bump was not an option. The advisory
range is >=4.0.0 <4.3.1 and the dependent's range is ^4.1.0, so the patched
release resolves inside the existing constraint — no override or forced
major upgrade needed.

Changes
-------
- package-lock.json: js-yaml 4.3.0 -> 4.3.1 (transitive, lockfile only).
  package.json is untouched.

Testing
-------
- npm audit -> found 0 vulnerabilities (was 1 high)
- node scripts/regen-openapi.js -> 83 documented paths, no diff against the
  committed spec (js-yaml is on the swagger-jsdoc code path, so this is the
  relevant regression check)
- swagger-jsdoc smoke test -> 83 paths generated, js-yaml resolves to 4.3.1
- node scripts/run-tests.js --all -> 46 passed, 0 failed, 7 skipped
  (server-dependent tests and poppler-render-real)
- prettier clean

Impact
------
No behaviour change. Removes the only outstanding npm audit finding on this
branch; a malicious or malformed YAML document with many `!!omap` entries can
no longer stall the swagger spec parser.

Upstream Status
---------------
Not applicable — lockfile-only maintenance, nothing to send upstream.

* fix(prompts): render existing tags as names instead of "[object Object]" (#262)

Background
----------
Issue #262 reported that the "Pre-existing tags:" block of the prompt sent to
the LLM contained only "[object Object], [object Object], ..." - repeated once
per tag - while "Pre-existing correspondents:" and "Pre-existing document
types:" were formatted correctly. The model therefore never received a usable
reference list of existing tags and kept creating near-duplicates.

Root cause is an inconsistent array shape for existingTags. paperlessService
.getTags() and the list*Names() helpers all return entity objects, but only
some call sites reduce them to plain names:

  - server.js scanDocuments(), mistralOcrService._runAiAnalysis() and the
    playground endpoint map to .name and are correct.
  - routes/setup.js processQueue() passed the raw objects straight through.
    That path serves the rescan/reprocess buttons (rescanDocumentsByIds) and
    POST /api/webhook/document, so the regular scan was never affected.

The provider services then did existingTags.join(', '), which renders objects
as "[object Object]". Correspondents and document types only looked healthy
because ollamaService._buildPrompt() mapped them to .name defensively; in
openaiService/azureService/customService the correspondent list was
interpolated raw and broke on the same path.

The same inconsistency existed in the opposite direction:
RestrictionPromptService._formatTagsList() filtered on `tag.name`, so a plain
name list - what the scan loop, the OCR fallback and the playground pass -
was dropped entirely and %RESTRICTED_TAGS% resolved to an empty string.

Changes
-------
- serviceUtils.js: new exported toNameList() that normalizes tags,
  correspondents and document types to plain names, accepting both entity
  objects and strings.
- routes/setup.js: processQueue() now maps tags and correspondents to names
  before calling processDocument(); /manual/analyze reuses toNameList()
  instead of its inline .map(x => x.name) chains.
- ollamaService/openaiService/azureService/customService: all three
  "Pre-existing ..." lines go through toNameList(), which also removes the
  duplicated inline mapping in ollamaService._buildPrompt(). This is
  defense in depth - a future call site handing over objects can no longer
  produce "[object Object]".
- restrictionPromptService.js: _formatTagsList() and
  _formatCorrespondentsList() now share toNameList(), so %RESTRICTED_TAGS%
  works with either shape. The unused trailing `config` parameter was dropped
  from processRestrictionsInPrompt() and its four call sites.
- tests/test-prompt-existing-data-serialization.js: new offline regression
  test, registered in the `prompts` area.
- tests/test-native-install-log-paths.js: the response-log-path assertion is
  now whitespace tolerant, because Prettier reflows the long path.join(...)
  call in the touched service files across several lines.
- The five touched service files were brought up to the repo's ESLint and
  Prettier baseline, as CI lints whole changed files: Prettier reformat,
  removal of unused requires/variables, bare `catch {}` for unused bindings
  and `{ cause: error }` on rethrown parse errors.

Testing
-------
- node scripts/run-tests.js --all -> 47 passed, 7 skipped (server- and
  tool-dependent), 0 failed.
- The new test was confirmed to fail against the unfixed code, once per bug
  direction: "[object Object]" in the prompt with entity objects, and an empty
  %RESTRICTED_TAGS% with plain names.
- npx eslint / npx prettier --check over every changed file: clean.
- node scripts/regen-openapi.js: no drift (no API surface change).

Impact
------
Rescan/reprocess and webhook-triggered analysis now send the real tag names to
the AI provider, and %RESTRICTED_TAGS% resolves correctly on the scan, OCR and
playground paths. No configuration, database or API changes.

Upstream Status
---------------
Not reported upstream; the affected processQueue() path exists in
clusterzx/paperless-ai as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(prompts): wire %RESTRICTED_DOCUMENT_TYPES% placeholder into prompt restrictions (#127)

Background
----------
Issue #127 reports that document types are not properly passed to the model.
RestrictionPromptService only knew %RESTRICTED_TAGS% and
%RESTRICTED_CORRESPONDENTS%, so a custom SYSTEM_PROMPT asking for a restricted
document type list had no way to receive one - %RESTRICTED_DOCUMENT_TYPES%
stayed in the prompt as a literal token.

This builds on the toNameList() normalization added for #262 in the preceding
commit, so the new formatter is a one-liner instead of another hand-rolled
object/string branch.

Changes
-------
- restrictionPromptService.js: new _formatDocumentTypesList() plus the
  %RESTRICTED_DOCUMENT_TYPES% replacement in _replacePlaceholders().
  processRestrictionsInPrompt() takes the document type list as its fourth
  argument - the slot the now-removed dead `config` parameter used to occupy.
- ollamaService/openaiService/azureService/customService: all four call sites
  forward their document type list. ollama and custom already passed it in
  that position by accident; openai and azure never did.
- tests/test-restricted-document-types-placeholder.js: new assertion test,
  registered in the `prompts` area. Beyond the formatter it also drives the
  placeholder through ollamaService._buildPrompt(), because a provider that
  forgets to forward its list would otherwise resolve the placeholder to an
  empty string with no test noticing.
- tests/test-restriction-service.js, tests/test-updated-service.js: pass a
  document type list instead of the removed `config` argument and cover the
  new placeholder. test-restriction-service.js additionally stops printing
  result.processedPrompt / result.restrictionPrompts, which had been logging
  "undefined" on every run since the function returns a plain string.
- CLAUDE.md: document the new placeholder, and point at toNameList() as the
  way to normalize entity lists.

Testing
-------
- node scripts/run-tests.js --all -> 48 passed, 7 skipped (server- and
  tool-dependent), 0 failed.
- The new test was confirmed to fail when the fourth argument is dropped from
  a provider call site.
- npx eslint / npx prettier --check over every changed file: clean.
- node scripts/regen-openapi.js: no drift (no API surface change).

Impact
------
%RESTRICTED_DOCUMENT_TYPES% now resolves for all four AI providers. Prompts
that do not use the placeholder are unaffected. No configuration, database or
API changes.

Upstream Status
---------------
Not reported upstream; clusterzx/paperless-ai has the same gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(release): add changelog entries for #262 and #127

Background
----------
The v2026.08.01 release block in config/changelog.js was created in d55ed67
before the prompt fixes landed, so the two user-visible changes from #262 and
the new placeholder from #127 were missing from the "What's New" modal.

PAPERLESS_AI_VERSION and the changelog version were already at v2026.08.01 and
stay unchanged - this only fills in the entries for that release.

Changes
-------
- config/changelog.js: three entries added to the v2026.08.01 block, keeping
  the New -> Fix -> Improvement grouping used by the other release blocks.
  - New: %RESTRICTED_DOCUMENT_TYPES% placeholder (#127)
  - Fix: existing tags reach the AI as names on the rescan/webhook path (#262)
  - Fix: %RESTRICTED_TAGS% no longer resolves to an empty list (#262)

Testing
-------
- require('./config/changelog') resolves to v2026.08.01 with seven entries and
  matches config.PAPERLESS_AI_VERSION.
- Entries carry no markup beyond what the modal already renders via innerHTML.
- node scripts/run-tests.js --all -> 48 passed, 7 skipped, 0 failed.
- npx eslint / npx prettier --check on config/changelog.js: clean.

Impact
------
The update modal now lists all changes shipped in v2026.08.01. No behaviour,
configuration or API changes.

Upstream Status
---------------
Not applicable - fork-specific release notes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github-actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant