Compare commits

..

1 Commits

Author SHA1 Message Date
claude[bot]
088e4b214f docs(filebrowser_quantum): stop advertising direct access on port 8071
config.yaml declares ingress_port: 8071 but no ports: key, so nothing is
published to the host network. ingress_port is the internal port the
Supervisor ingress proxy connects to on the add-on's private IP, and Home
Assistant only renders the Network card for add-ons that declare ports:.
Direct access at <your-ip>:8071 has therefore never worked; the README was
carried over from the sibling filebrowser add-on, which does declare
ports: 8080/tcp: 8071.

Correct the three README claims rather than publishing a port, since the
app is configured with server.baseURL set to the ingress entry and would
not serve correctly on a plain published port without further work.

Closes #2978

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 03:30:05 +00:00
26 changed files with 45 additions and 233 deletions

View File

@@ -228,7 +228,7 @@ jobs:
echo "... done"
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "GitHub bot : README updated"
default_author: github_actions

View File

@@ -125,7 +125,7 @@ jobs:
- name: Analyse and fix
if: steps.batch.outputs.count != '0'
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
uses: anthropics/claude-code-action@1623c36729ac1cd5895198cded705a287de7db79 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange. The scheduled path

View File

@@ -237,7 +237,7 @@ jobs:
# Get stars evolution
wget -S -O .github/starsevol.svg "https://api.star-history.com/svg?repos=alexbelgium/hassio-addons&type=Date" || true
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "GitHub bot : graphs updated"
default_author: github_actions

View File

@@ -64,7 +64,7 @@ jobs:
fetch-depth: 1
- name: Run Claude Code
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
uses: anthropics/claude-code-action@1623c36729ac1cd5895198cded705a287de7db79 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# AI_PR_TOKEN, not GITHUB_TOKEN, so a PR Claude opens triggers CI.

View File

@@ -135,7 +135,7 @@ jobs:
- name: Execute the plan
if: steps.bundle.outputs.has_plan == 'true'
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
uses: anthropics/claude-code-action@1623c36729ac1cd5895198cded705a287de7db79 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange, which 401s whenever

View File

@@ -59,7 +59,7 @@ jobs:
# Remove issues list
rm issueslist
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "Github bot : issues linked to readme"
default_author: github_actions

View File

@@ -166,7 +166,7 @@ jobs:
id: classify
if: github.event_name != 'issue_comment' || steps.claim.outputs.go == 'true'
continue-on-error: true
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
uses: anthropics/claude-code-action@1623c36729ac1cd5895198cded705a287de7db79 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Without this the action falls back to the OIDC -> Claude App token

View File

@@ -79,7 +79,7 @@ jobs:
- name: Address CodeRabbit comments
if: steps.claim.outputs.go == 'true'
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
uses: anthropics/claude-code-action@1623c36729ac1cd5895198cded705a287de7db79 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange, which 401s whenever

View File

@@ -95,7 +95,7 @@ jobs:
- name: Commit sanitize changes
id: sanitize_commit
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
commit: -u
message: "GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild]"
@@ -410,7 +410,7 @@ jobs:
done
- name: Commit changelog changes
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
commit: -u
message: "GitHub bot: changelog [nobuild]"

View File

@@ -18,7 +18,7 @@ jobs:
uses: erclu/check-crlf@v1
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "Github bot : CRLF corrected"
default_author: github_actions
@@ -50,7 +50,7 @@ jobs:
dos2unix -k "$f"
done
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "Github bot : CRLF corrected"
default_author: github_actions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Commit if needed
if: steps.calibre.outputs.markdown != ''
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
message: "Github bot : image compressed"
default_author: github_actions

View File

@@ -109,7 +109,7 @@ jobs:
#TOTAL3="$(awk '{SUM+=$2}END{print SUM}' Stats)"
- name: Commit if needed
uses: EndBug/add-and-commit@v11
uses: EndBug/add-and-commit@v10
with:
default_author: github_actions
message : "Github bot : stats updated"

View File

@@ -49,7 +49,6 @@ server {
sub_filter `/asset `%%ingress_entry%%/asset;
sub_filter "'/asset" "'%%ingress_entry%%/asset";
sub_filter \"/asset \"%%ingress_entry%%/asset;
sub_filter \"/auth \"%%ingress_entry%%/auth;
sub_filter window.location.origin} window.location.origin}%%ingress_entry%%;
}
}

View File

@@ -49,7 +49,6 @@ server {
sub_filter `/asset `%%ingress_entry%%/asset;
sub_filter "'/asset" "'%%ingress_entry%%/asset";
sub_filter \"/asset \"%%ingress_entry%%/asset;
sub_filter \"/auth \"%%ingress_entry%%/auth;
sub_filter window.location.origin} window.location.origin}%%ingress_entry%%;
}
}

View File

@@ -1,25 +1,4 @@
## 07308545.1 (17-08-2026)
- Minor bugs fixed
## 07308543.1 (17-08-2026)
- Fix the "For your security, sign in again" prompt recurring on every restart again. The v1.37
`safeStorage` patch (`86-claude_safestorage.sh` / `claude-safestorage-patch.js`) only knew how
to inject its opt-in after a leading `"use strict"` directive in the app's main bundle, and
refused to patch anything else. Confirmed live on the running add-on (Claude Desktop
1.30096.1): the shipped main bundle (`.vite/build/index.pre.js`) no longer opens with a
`"use strict"` directive at all — it now opens directly with a bare IIFE — so the patcher has
been silently refusing to patch on every boot, `safeStorage.isEncryptionAvailable()` stayed
`false`, and the app's own log kept showing `Encryption not available, returning empty env
vars` exactly as before v1.37. `applyPatch()` now falls back to inserting the opt-in as the
bundle's first real statement when no directive is present, skipping past any leading BOM,
hashbang, or banner comment first so a directive hidden behind a comment is still found and
protected rather than pushed out of position zero. Verified by copying the live production
`app.asar` and running the patcher against it directly: the previously-refused bundle now
patches successfully, the marker lands correctly, a second run reports "Already patched", and
targeted unit tests cover the bare-IIFE, comment-hidden-directive, hashbang, and
unterminated-comment cases.
- One-time step after upgrading, same as v1.37: the previously-stored session is already stale,
so complete one sign-in from a computer; it then persists across restarts.
## 07308545 (2026-08-15)
- Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases)
- Upstream tag : v3.2.2+claude1.30096.1

View File

@@ -13,10 +13,8 @@ streamed desktop.
offline until a fresh sign-in was done from a computer). v1.35 switched to
`--password-store=basic` plus a cont-init script that re-syncs the persistent openbox
`autostart` from the image on every boot — **but that flag alone does nothing**, and the bug
survived it untouched. v1.37 added the application-side opt-in the `basic` backend requires;
see "Why v1.35 did not work" below. That patcher then regressed silently when upstream's
bundle output changed shape — fixed again in 07308543.1; see "Why v1.37 stopped working"
below.
survived it untouched. Actually fixed in v1.37, which adds the application-side opt-in the
`basic` backend requires; see "Why v1.35 did not work" below.
- **Planned only:** Problem A (in-desktop browser for OAuth) is intentionally not implemented.
The image ships no browser; complete the login with the user-side workaround below.
@@ -172,40 +170,10 @@ The third row is the one that matters: it is the restart survival this add-on ne
reaches upgrades, not just fresh installs.
4. `gnome-keyring` stays out of the Dockerfile.
### Why v1.37 stopped working
`claude-safestorage-patch.js` only knew how to inject its opt-in *after* a leading
`"use strict"` directive in the app's main bundle, and refused to patch (leaving the app
unpatched and the session un-persisted) if that directive wasn't there. Confirmed live on the
running add-on: Claude Desktop 1.30096.1's main bundle (`.vite/build/index.pre.js`) no longer
opens with a `"use strict"` directive — it now opens directly with a bare IIFE
(`(function(){try{var e=typeof window...`). Upstream's build output changed shape at some point
after v1.37 shipped, the patcher's one injection point stopped existing, and it had been
silently refusing to patch on every boot since — the app's `main.log` kept showing exactly the
same `Encryption not available, returning empty env vars` warning documented above, and the
session went back to not surviving restarts.
`applyPatch()` now falls back to inserting the opt-in as the bundle's first real statement when
no `"use strict"` directive is found, rather than refusing outright. It skips past any leading
BOM, hashbang, or banner comment first (`skipPrologue()`), so a directive hidden behind a
comment is still found and protected instead of being pushed out of the first-statement
position by a naive prepend — Vite/esbuild banners commonly put a license comment ahead of the
directive. A bundle with no directive at all has nothing to protect, so prepending the opt-in
there is unconditionally safe: the injected code is a complete `try{}catch(e){}` statement, and
a statement can never merge with what follows it via ASI the way a bare expression could.
Verified by copying the live production `app.asar` and running the patcher against it directly
(outside the container's boot sequence): the previously-refused bundle now patches
successfully, the marker lands at the front of the main entry, a second run correctly reports
"Already patched" (idempotent), and unit tests cover the bare-IIFE, comment-hidden-directive,
hashbang, and unterminated-comment cases.
### One-time step after upgrading
The previously-stored session is already stale. Complete **one** sign-in from a computer
(mobile still can't finish the OAuth flow itself, per Problem A) — the session then persists
normally and dispatch stays online regardless of which device connects first afterward. This
applies again after the 07308543.1 fix above, since the affected sessions were never persisted
in the first place.
normally and dispatch stays online regardless of which device connects first afterward.
---
@@ -217,10 +185,7 @@ in the first place.
- `claude_desktop/rootfs/etc/cont-init.d/86-claude_safestorage.sh` and
`claude_desktop/rootfs/usr/local/bin/claude-safestorage-patch.js` — new in v1.37; the
app-side `safeStorage` opt-in that makes `--password-store=basic` actually take effect.
`claude-safestorage-patch.js` updated again in 07308543.1 to also patch bundles with no
leading `"use strict"` directive, and to look past leading comments/hashbang when deciding
whether one is present.
- `claude_desktop/Dockerfile` — corrected stale comment (gnome-keyring is not installed).
- `claude_desktop/CHANGELOG.md` / `config.yaml` — v1.35, then v1.37, then 07308543.1.
- `claude_desktop/CHANGELOG.md` / `config.yaml` — v1.35, then v1.37.
Problem A (in-desktop browser for OAuth) remains planned-only; not touched by this change.

View File

@@ -136,5 +136,5 @@ schema:
slug: claude_desktop
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "07308545.1"
version: "07308545"
video: true

View File

@@ -128,98 +128,24 @@ function integrityOf(buf, blockSize) {
return { algorithm: 'SHA256', hash: sha256(buf), blockSize, blocks };
}
// Any of the four ECMAScript LineTerminator code points — not just "\n". A //-comment or an ASI
// boundary ends at the first of these, and using a bare "\n" search for that would let a CR- or
// U+2028/U+2029-terminated line swallow real code as "still the comment/still on this line" and
// misplace the insertion point deep inside the bundle instead of before it.
const LINE_TERMINATOR = /[\n\r\u2028\u2029]/;
/* Skip a leading BOM and hashbang line. Only meaningful at byte 0 — called once, before any
* directive scanning. */
function skipBomAndHashbang(source) {
let i = source.charCodeAt(0) === 0xfeff ? 1 : 0; // BOM
if (source.startsWith('#!', i)) {
const m = LINE_TERMINATOR.exec(source.slice(i));
i += m ? m.index + 1 : source.length - i;
}
return i;
}
/* Skip whitespace and comments starting at i. Returns the next index, or -1 for an unterminated
* block comment (caller refuses rather than guesses). */
function skipWhitespaceAndComments(source, i) {
for (;;) {
const rest = source.slice(i);
const ws = /^\s+/.exec(rest);
if (ws) {
i += ws[0].length;
continue;
}
if (rest.startsWith('//')) {
const m = LINE_TERMINATOR.exec(rest);
i += m ? m.index + 1 : rest.length;
continue;
}
if (rest.startsWith('/*')) {
const end = rest.indexOf('*/');
if (end === -1) return -1;
i += end + 2;
continue;
}
return i;
}
}
// A single-line string literal: no raw line terminator in its content (a real one would need an
// escaped line continuation, which this deliberately doesn't special-case — failing to match
// just means the prologue scan below stops there, which is always safe, see applyPatch).
const STRING_LITERAL = /^(['"])(?:\\.|(?!\1)[^\\\n\r\u2028\u2029])*\1/;
/* Scan the bundle's full leading directive prologue: every consecutive ExpressionStatement made
* of nothing but a string literal, per how ECMAScript directives actually work. A directive
* prologue can hold more than one entry, and "use strict" only has to appear *somewhere* in it,
* not first — so this treats every leading directive as needing protection, not just one
* specifically named "use strict". Returns the index right after the full prologue (which is
* also correct as "no prologue, insert here" when there wasn't one), or -1 when a leading string
* literal isn't cleanly terminated as its own statement — ambiguous whether it's a directive at
* all, refused rather than guessed at. */
function scanDirectivePrologue(source, start) {
let i = start;
for (;;) {
const next = skipWhitespaceAndComments(source, i);
if (next === -1) return -1;
const rest = source.slice(next);
const m = STRING_LITERAL.exec(rest);
if (!m) return next; // not a directive; prologue (possibly empty) ends here
const after = rest.slice(m[0].length);
if (after[0] === ';') {
i = next + m[0].length + 1;
} else if (after === '' || LINE_TERMINATOR.test(after[0])) {
i = next + m[0].length;
} else {
return -1; // "use strict" + x and friends: not unambiguously a directive
}
}
}
/* Insert the opt-in right after the bundle's full leading directive prologue (BOM/hashbang, then
* any run of string-literal-only statements — "use strict" among them if present). It must go
* *after* the whole prologue, not just after the first entry: a directive prologue only takes
* effect when its members are the very first statements, so inserting between two of them, or
* ahead of all of them, would silently drop the file out of strict mode just as surely as
* inserting ahead of a lone "use strict" would.
/* Insert the opt-in after the bundle's leading "use strict" directive. It must go *after* it: a
* directive prologue only takes effect as the very first statement, so prepending would silently
* drop the whole main process out of strict mode.
*
* When there is no prologue at all (observed from Claude Desktop 1.30096.1 onward, whose main
* entry opens with a bare IIFE instead), there is nothing to preserve: PATCH lands at the same
* position anyway, as the file's first real statement. A `try{}catch(e){}` statement can never
* merge with whatever follows via ASI — unlike a bare expression, a statement is not a valid
* left-hand side for anything a following token could continue — so this is unconditionally
* safe once placed after any banner comment / hashbang / directive prologue. */
* Returns null — meaning "refuse to patch" — for anything that is not unambiguously a directive.
* `"use strict" + x` is an expression, not a directive, and injecting into it would produce a
* syntax error, so the directive is only accepted when it is terminated by its own semicolon, a
* line break, or end of input. */
function applyPatch(source) {
const start = skipBomAndHashbang(source);
const end = scanDirectivePrologue(source, start);
if (end === -1) return null;
return source.slice(0, end) + PATCH + source.slice(end);
const m = /^\s*(['"])use strict\1(;?)/.exec(source);
if (!m) return null;
const rest = source.slice(m[0].length);
const terminated = m[2] === ';' || rest === '' || /^[\r\n]/.test(rest);
if (!terminated) return null;
// Supply the terminator when the directive relied on ASI; without it the injected code would
// continue the string-literal expression instead of following it.
const sep = m[2] === ';' ? '' : ';';
return source.slice(0, m[0].length) + sep + PATCH + rest;
}
function writeAll(fd, buf) {
@@ -277,7 +203,7 @@ function main() {
const patchedSource = applyPatch(original);
if (patchedSource === null) {
fail(`${mainRel} opens with an ambiguous "use strict"-like string literal; refusing to patch`);
fail(`${mainRel} does not begin with a recognized "use strict" directive; refusing to patch`);
}
const patched = Buffer.from(patchedSource, 'utf8');

View File

@@ -1,10 +1,7 @@
## 1.5.1.1 (2026-08-16)
- Expose the web UI on host port 8071, reachable at `<your-ip>:8071`
(redirects to `/filebrowser_quantum/`). Direct access is served by a new,
separate nginx vhost that proxies to the same backend Ingress already uses;
Ingress itself, and the app's own base URL, are unchanged.
## 1.5.1.1 (2026-08-17)
- Documentation: the add-on is Ingress-only and does not publish port 8071, so the README no longer advertises direct access at `<your-ip>:8071`
## 1.5.1 (2026-08-08)
- Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases)

View File

@@ -42,11 +42,11 @@ comparison to installing any other Home Assistant add-on.
1. Click the `Save` button to store your configuration.
1. Start the add-on.
1. Check the logs of the add-on to see if everything went well.
1. Access the web UI through the sidebar or at `<your-ip>:8071/filebrowser_quantum/`.
1. Access the web UI through the Home Assistant sidebar.
## Configuration
The web UI can be found at `<your-ip>:8071` (redirects to `/filebrowser_quantum/`) or through the Home Assistant sidebar when using Ingress.
The web UI is reached through the Home Assistant sidebar (Ingress). This add-on does not publish a port on your network, so it has no **Network** section in its configuration page and is not reachable at `<your-ip>:8071`.
**Default credentials:**
- Username: `admin`
@@ -69,7 +69,7 @@ The web UI can be found at `<your-ip>:8071` (redirects to `/filebrowser_quantum/
## Setup
1. Start the add-on and wait for it to initialize.
1. Access the web interface through the Home Assistant sidebar or at `<your-ip>:8071`.
1. Access the web interface through the Home Assistant sidebar.
1. Log in using the default credentials:
- Username: `admin`
- Password: `admin`

View File

@@ -97,10 +97,6 @@ options:
default_user_scope: "/"
panel_admin: false
panel_icon: mdi:file-search
ports:
8072/tcp: 8071
ports_description:
8072/tcp: Web UI port
privileged:
- SYS_ADMIN
- DAC_READ_SEARCH

View File

@@ -43,13 +43,6 @@ declare ingress_interface
declare ingress_port
#declare keyfile
# The app's own baseURL is always the Supervisor ingress-entry path — this is
# unchanged from before. FileBrowser Quantum has no known "ignore baseURL for
# routing" leniency the way classic filebrowser's app does, so ingress access
# is left completely untouched here. Direct ip:port access is handled below by
# a second, separate nginx vhost (direct.conf) that rewrites a fixed public
# path onto this same ingress-entry baseURL, instead of changing the baseURL
# itself.
FB_BASEURL=$(bashio::addon.ingress_entry)
export FB_BASEURL
@@ -66,15 +59,6 @@ sed -i "s|%%protocol%%|${ADDON_PROTOCOL}|g" /etc/nginx/servers/ingress.conf
sed -i "s|%%port%%|${ingress_port}|g" /etc/nginx/servers/ingress.conf
sed -i "s|%%interface%%|${ingress_interface}|g" /etc/nginx/servers/ingress.conf
sed -i "s|%%subpath%%|${FB_BASEURL}/|g" /etc/nginx/servers/ingress.conf
# --- Direct ip:port access (separate from ingress, see comment above) ---
# Publishes a second nginx vhost on a fixed internal port (published to the
# host as 8071 via config.yaml's `ports:`), at a fixed public path
# (/filebrowser_quantum/), that proxies to the same backend the ingress vhost
# uses. This keeps the app's own baseURL, and therefore ingress, unchanged.
sed -i "s|%%protocol%%|${ADDON_PROTOCOL}|g" /etc/nginx/servers/direct.conf
sed -i "s|%%subpath%%|${FB_BASEURL}/|g" /etc/nginx/servers/direct.conf
mkdir -p /var/log/nginx && touch /var/log/nginx/error.log
############################

View File

@@ -1,24 +0,0 @@
server {
listen 0.0.0.0:8072 default_server;
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
client_max_body_size 0;
location = / {
return 302 /filebrowser_quantum/;
}
location = /filebrowser_quantum {
return 301 /filebrowser_quantum/;
}
location /filebrowser_quantum/ {
add_header Access-Control-Allow-Origin *;
proxy_connect_timeout 30m;
proxy_send_timeout 30m;
proxy_read_timeout 30m;
proxy_pass %%protocol%%://backend%%subpath%%;
}
}

View File

@@ -1,8 +1,4 @@
## 3.4.1.1 (2026-08-16)
- Fixed `404: Not Found` when clicking **Discover** in the sidebar through ingress (#2975). Seerr's Discover link points at `/`, which nginx rewrote to the ingress entry without a trailing slash; Home Assistant only routes ingress on `/api/hassio_ingress/<token>/…`, so the request was rejected by Home Assistant before reaching the add-on. Only ingress was affected; the directly published port 5055 always worked.
## 3.4.1 (2026-08-01)
- Update to latest version from seerr-team/seerr (changelog : https://github.com/seerr-team/seerr/releases)
## 3.3.0.1 (2026-07-28)

View File

@@ -96,4 +96,4 @@ schema:
slug: seerr
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/seerr
version: "3.4.1.1"
version: "3.4.1"

View File

@@ -46,14 +46,9 @@ server {
# Do not rewrite every response type blindly.
sub_filter_types text/html application/javascript text/javascript application/json;
# The trailing slash is required: Home Assistant routes ingress on
# "/api/hassio_ingress/{token}/{path:.*}", so the bare entry without it
# matches no route and Home Assistant answers its own plain-text
# "404: Not Found" before the request ever reaches this add-on. Seerr's
# Discover link is href="/", so without the slash every click on it 404s.
sub_filter 'href="/"' 'href="$app/"';
sub_filter 'href="/"' 'href="$app"';
sub_filter 'href="/login"' 'href="$app/login"';
sub_filter 'href:"/"' 'href:"$app/"';
sub_filter 'href:"/"' 'href:"$app"';
sub_filter '\/_next' '%%ingress_entry_escaped%%\/_next';
sub_filter '/_next' '$app/_next';
sub_filter '/api/v1' '$app/api/v1';