mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-08-30 08:43:31 +02:00
Merge pull request #2823 from alexbelgium/fix/claude-desktop-startup-and-keyring
fix(claude_desktop): fix startup crash loop and persist sign-in via g…
This commit is contained in:
@@ -1,3 +1,15 @@
|
||||
## 1.4 (07-07-2026)
|
||||
|
||||
- Persist Claude Desktop sign-in: bundle gnome-keyring/libsecret/dbus-x11 and start an unlocked Secret Service in the desktop session, and launch with --password-store=gnome-libsecret. Fixes "Your sign-in won't be saved on this device. Install and unlock a system keyring". The keyring DB lives on persistent storage (/config/data), so the session survives restarts.
|
||||
|
||||
## 1.3 (07-07-2026)
|
||||
|
||||
- Fix startup crash loop ("All subprocesses terminated. Exiting."):
|
||||
- Make the Selkies desktop init oneshots (init-video, init-selkies-config) tolerant so a partially-permitted device/permission op in the HA sandbox no longer fails add-on bringup.
|
||||
- Pre-create /tmp/selkies_js.log so the base image's "chmod 777 /tmp/selkies*" calls never fail on an empty glob.
|
||||
- Reconcile XDG_RUNTIME_DIR to the tmpfs runtime dir instead of persistent storage.
|
||||
- Run Claude Desktop with --disable-dev-shm-usage and drop the misplaced shm_size env var (Home Assistant ignores shm_size), fixing Electron renderer crashes on the default 64 MB /dev/shm.
|
||||
|
||||
## 1.2 (07-07-2026)
|
||||
- Add baked-in git/GitHub CLI support with optional startup credential configuration.
|
||||
|
||||
|
||||
@@ -56,7 +56,13 @@ RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; f
|
||||
RUN curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc && \
|
||||
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" > /etc/apt/sources.list.d/claude-desktop.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends claude-desktop python3-pip git gh && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
claude-desktop \
|
||||
python3-pip \
|
||||
gnome-keyring \
|
||||
libsecret-1-0 \
|
||||
dbus-x11 \
|
||||
git gh && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
118
claude_desktop/SIGN_IN.md
Normal file
118
claude_desktop/SIGN_IN.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Claude Desktop sign-in — fix plan (not implemented)
|
||||
|
||||
Two related sign-in problems when Claude Desktop runs inside the LinuxServer Selkies
|
||||
streamed desktop.
|
||||
|
||||
**Status:**
|
||||
- **Problem B (keyring persistence) — IMPLEMENTED** in v1.4 (Dockerfile + `rootfs/defaults/autostart`).
|
||||
- **Problem A (in-desktop browser for OAuth) — PLAN ONLY, intentionally not implemented.**
|
||||
The image ships no browser; complete the login with the user-side workaround below.
|
||||
|
||||
---
|
||||
|
||||
## Problem A — Cannot complete login
|
||||
|
||||
Symptoms:
|
||||
- **"Continue with Google"** does nothing.
|
||||
- **"Continue with email"** sends a link that "must be opened from the same machine".
|
||||
|
||||
### Root cause
|
||||
Claude Desktop signs in through an **external web page** (Google OAuth or the email magic
|
||||
link) and then hands the token back to the app via the **`claude://` URL scheme**
|
||||
(auto-registered on first launch). Both paths need:
|
||||
1. a **web browser inside the streamed desktop** to render the auth page, and
|
||||
2. a **default-browser association** so the app's `xdg-open <url>` call goes somewhere.
|
||||
|
||||
The Selkies base image ships **no browser** and no default handler, so `xdg-open` has
|
||||
nothing to dispatch to → Google "does nothing", and the email link's final `claude://…`
|
||||
redirect only works on the machine running the app (the container), which currently can't
|
||||
render/handle it.
|
||||
|
||||
### Fix (image level)
|
||||
In `claude_desktop/Dockerfile`, install a browser and register defaults so the whole flow
|
||||
completes inside the session:
|
||||
- `apt-get install -y --no-install-recommends chromium` (Debian bookworm package name;
|
||||
`firefox-esr` is an alternative).
|
||||
- Set the default browser and confirm the scheme handlers, e.g. as an s6 oneshot or in the
|
||||
desktop `autostart` (runs as the session user with `$DISPLAY`/dbus available):
|
||||
```sh
|
||||
xdg-settings set default-web-browser chromium.desktop
|
||||
xdg-mime default claude-desktop.desktop x-scheme-handler/claude
|
||||
```
|
||||
- Verifying expectations (for reference, not a test step):
|
||||
`x-scheme-handler/https` → `chromium.desktop`, `x-scheme-handler/claude` →
|
||||
`claude-desktop.desktop`.
|
||||
|
||||
Then login is done entirely in the streamed desktop: **Continue with Google** opens
|
||||
Chromium in-session and the `claude://` redirect lands back in the app; or paste the email
|
||||
magic link into the in-session Chromium (not a phone).
|
||||
|
||||
### User-side workaround (no rebuild)
|
||||
- Add-on Configuration → `additional_apps: chromium`, restart (installed by
|
||||
`rootfs/etc/cont-init.d/80-configuration.sh`).
|
||||
- Run the two `xdg-settings`/`xdg-mime` commands once in an in-session terminal, or add them
|
||||
to the custom script `/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`.
|
||||
|
||||
---
|
||||
|
||||
## Problem B — "Your sign-in won't be saved on this device"
|
||||
|
||||
Symptom:
|
||||
> Install and unlock a system keyring (such as GNOME Keyring), then restart the app.
|
||||
|
||||
### Root cause
|
||||
Claude Desktop (Electron) persists its auth token with `safeStorage`, which on Linux
|
||||
encrypts via the **Secret Service API** (`org.freedesktop.secrets`) provided by
|
||||
gnome-keyring/kwallet. The Selkies base runs only a **system** D-Bus
|
||||
(`svc-dbus` → `dbus-daemon --system`) and has **no keyring daemon**, so `safeStorage` is
|
||||
unavailable and the token cannot be stored → the app warns and forgets the session on
|
||||
restart.
|
||||
|
||||
Note: the desktop itself already runs under a **session** bus —
|
||||
`root/defaults/startwm.sh` launches `dbus-launch --exit-with-session openbox-session`, so
|
||||
apps started from the openbox `autostart` inherit `DBUS_SESSION_BUS_ADDRESS`. The keyring
|
||||
must be started **inside that session** so it registers the secrets service on the same bus
|
||||
Claude Desktop uses. No extra `dbus-launch` is needed.
|
||||
|
||||
### Fix (image level)
|
||||
1. `claude_desktop/Dockerfile`: install
|
||||
`gnome-keyring libsecret-1-0 dbus-x11` (dbus-x11 provides `dbus-launch`, already used by
|
||||
startwm; libsecret-1-0 is the backend Electron dlopens).
|
||||
2. `claude_desktop/rootfs/defaults/autostart`: start & unlock an empty-password keyring
|
||||
before launching the app, and force the libsecret backend. Proposed content:
|
||||
```sh
|
||||
# Start + unlock a gnome-keyring secrets service so Claude Desktop can persist sign-in.
|
||||
# Runs inside the openbox session's dbus (startwm.sh: dbus-launch --exit-with-session).
|
||||
eval "$(printf '' | gnome-keyring-daemon --login)" 2>/dev/null || true
|
||||
eval "$(gnome-keyring-daemon --start --components=secrets)" 2>/dev/null || true
|
||||
dbus-update-activation-environment --all >/dev/null 2>&1 || true
|
||||
|
||||
claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret
|
||||
```
|
||||
- `--login` reads the password from stdin (empty here), creating and unlocking the login
|
||||
keyring on first run and starting the daemon in login mode; `--start --components=secrets`
|
||||
then exposes the Secret Service and exports `GNOME_KEYRING_CONTROL`/`SSH_AUTH_SOCK`.
|
||||
- `--password-store=gnome-libsecret` forces Electron to use the libsecret backend instead
|
||||
of falling back to plaintext.
|
||||
3. Persistence: the keyring DB lives in `$HOME/.local/share/keyrings/` and `HOME=/config/data`
|
||||
(persistent add-on storage), so the empty-password login keyring survives restarts and is
|
||||
re-unlocked automatically each boot by the same `autostart` line — the sign-in then sticks.
|
||||
|
||||
### User-side workaround (no rebuild)
|
||||
- Add-on Configuration → `additional_apps: gnome-keyring, libsecret-1-0, dbus-x11`, restart.
|
||||
- Add the keyring-start lines above to the custom script
|
||||
`/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`, and relaunch Claude Desktop
|
||||
with `--password-store=gnome-libsecret` (e.g. edit the in-session openbox autostart).
|
||||
|
||||
---
|
||||
|
||||
## Files this plan would touch (when implemented)
|
||||
- `claude_desktop/Dockerfile` — install `chromium`, `gnome-keyring`, `libsecret-1-0`,
|
||||
`dbus-x11`; set default browser + scheme handlers.
|
||||
- `claude_desktop/rootfs/defaults/autostart` — keyring bootstrap + `--password-store` flag.
|
||||
- `claude_desktop/CHANGELOG.md` / `config.yaml` version bump.
|
||||
|
||||
## Open questions before implementing
|
||||
- Bundle **chromium** vs **firefox-esr** as the in-image browser?
|
||||
- Empty-password keyring (fully unattended) vs. reuse the add-on `PASSWORD` option to lock
|
||||
the keyring with the same password?
|
||||
@@ -18,7 +18,6 @@ environment:
|
||||
SELKIES_FRAMERATE: "30"
|
||||
START_DOCKER: "false"
|
||||
TITLE: Claude Desktop
|
||||
shm_size: 1gb
|
||||
image: ghcr.io/alexbelgium/claude_desktop-{arch}
|
||||
ingress: true
|
||||
init: false
|
||||
@@ -79,5 +78,5 @@ slug: claude_desktop
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.2"
|
||||
version: "1.4"
|
||||
video: true
|
||||
|
||||
@@ -1 +1,16 @@
|
||||
claude-desktop --no-sandbox
|
||||
#!/usr/bin/env sh
|
||||
# Persist Claude Desktop sign-in across restarts.
|
||||
# Claude Desktop (Electron) stores its auth token via safeStorage, which on Linux needs the
|
||||
# Secret Service API (org.freedesktop.secrets). Provide it with gnome-keyring on the openbox
|
||||
# session bus (startwm.sh runs `dbus-launch --exit-with-session`), unlocked with an empty
|
||||
# password so it comes back automatically after a restart. The keyring DB lives under
|
||||
# $HOME/.local/share/keyrings, and HOME=/config/data is persistent add-on storage, so the
|
||||
# saved sign-in survives add-on restarts.
|
||||
eval "$(gnome-keyring-daemon --start --components=secrets 2>/dev/null)" || true
|
||||
printf '' | gnome-keyring-daemon --unlock 2>/dev/null || true
|
||||
export GNOME_KEYRING_CONTROL SSH_AUTH_SOCK
|
||||
dbus-update-activation-environment --all >/dev/null 2>&1 || true
|
||||
|
||||
# --password-store=gnome-libsecret forces Electron onto the libsecret backend instead of
|
||||
# falling back to plaintext (and warning that the sign-in will not be saved).
|
||||
claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret
|
||||
|
||||
@@ -70,6 +70,11 @@ mkdir -p "$LOCATION" /tmp/cache "$XDG_RUNTIME_DIR"
|
||||
chmod 755 /tmp/cache
|
||||
chmod 700 "$XDG_RUNTIME_DIR"
|
||||
|
||||
# Pre-create the Selkies joystick log so the base image's "chmod 777 /tmp/selkies*"
|
||||
# calls (in init-selkies-config and svc-de) never fail on an empty glob.
|
||||
touch /tmp/selkies_js.log
|
||||
chmod 777 /tmp/selkies_js.log
|
||||
|
||||
if [ -e "$LOCATION/.cache" ] && [ ! -L "$LOCATION/.cache" ]; then
|
||||
rm -rf "$LOCATION/.cache"
|
||||
fi
|
||||
@@ -78,3 +83,24 @@ ln -sfn /tmp/cache "$LOCATION/.cache"
|
||||
bashio::log.info "Setting ownership to $PUID:$PGID"
|
||||
chown -R "$PUID":"$PGID" "$LOCATION" /tmp/cache "$XDG_RUNTIME_DIR"
|
||||
chmod -R 700 "$LOCATION"
|
||||
|
||||
# The base init-selkies-config script overrides XDG_RUNTIME_DIR to $HOME/.XDG, which lands
|
||||
# on persistent storage and conflicts with the tmpfs runtime dir set above. Re-assert the
|
||||
# tmpfs value at the end of that oneshot so the app and desktop agree on one valid dir.
|
||||
SELKIES_CONFIG_RUN="/etc/s6-overlay/s6-rc.d/init-selkies-config/run"
|
||||
if [ -f "$SELKIES_CONFIG_RUN" ] && ! grep -q 'XDG_RUNTIME_DIR override reconciled' "$SELKIES_CONFIG_RUN"; then
|
||||
printf '\n# XDG_RUNTIME_DIR override reconciled\nprintf "%%s" "%s" > /run/s6/container_environment/XDG_RUNTIME_DIR\n' "$XDG_RUNTIME_DIR" >> "$SELKIES_CONFIG_RUN"
|
||||
fi
|
||||
|
||||
# The Selkies desktop init oneshots do best-effort device/permission setup (mknod
|
||||
# /dev/input/*, chmod /tmp/selkies*, /dev/dri perms) that is only partially permitted in the
|
||||
# HA add-on sandbox. A non-zero exit from a oneshot fails add-on bringup and crash-loops the
|
||||
# container, so make these two tolerant and always report success. Longruns (svc-*) are left
|
||||
# untouched so s6 keeps supervising them with their real exit codes.
|
||||
for oneshot in init-video init-selkies-config; do
|
||||
run="/etc/s6-overlay/s6-rc.d/$oneshot/run"
|
||||
if [ -f "$run" ] && ! grep -q '^set +e$' "$run"; then
|
||||
sed -i "1a set +e" "$run"
|
||||
printf '\nexit 0\n' >> "$run"
|
||||
fi
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user