From 20c7889a4e5fbcfde3ffb7701b0725b68b4718e6 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Tue, 7 Jul 2026 09:29:56 +0200 Subject: [PATCH] Remove binary assets from Claude Desktop add-on --- claude_desktop/CHANGELOG.md | 5 + claude_desktop/Dockerfile | 135 ++++++++++++++++++ claude_desktop/README.md | 60 ++++++++ claude_desktop/apparmor.txt | 72 ++++++++++ claude_desktop/build.json | 6 + claude_desktop/config.yaml | 82 +++++++++++ claude_desktop/rootfs/defaults/autostart | 1 + .../rootfs/etc/cont-init.d/20-folders.sh | 67 +++++++++ .../etc/cont-init.d/80-configuration.sh | 54 +++++++ .../etc/cont-init.d/81-claude_update.sh | 12 ++ .../rootfs/etc/cont-init.d/82-claude_tools.sh | 57 ++++++++ .../rootfs/etc/cont-init.d/90-ingress.sh | 40 ++++++ .../rootfs/etc/nginx/includes/mime.types | 96 +++++++++++++ .../etc/nginx/includes/proxy_params.conf | 15 ++ .../rootfs/etc/nginx/includes/resolver.conf | 1 + .../etc/nginx/includes/server_params.conf | 6 + .../rootfs/etc/nginx/includes/ssl_params.conf | 9 ++ .../rootfs/etc/nginx/includes/upstream.conf | 3 + claude_desktop/updater.json | 7 + 19 files changed, 728 insertions(+) create mode 100644 claude_desktop/CHANGELOG.md create mode 100644 claude_desktop/Dockerfile create mode 100644 claude_desktop/README.md create mode 100644 claude_desktop/apparmor.txt create mode 100644 claude_desktop/build.json create mode 100644 claude_desktop/config.yaml create mode 100644 claude_desktop/rootfs/defaults/autostart create mode 100755 claude_desktop/rootfs/etc/cont-init.d/20-folders.sh create mode 100755 claude_desktop/rootfs/etc/cont-init.d/80-configuration.sh create mode 100755 claude_desktop/rootfs/etc/cont-init.d/81-claude_update.sh create mode 100755 claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh create mode 100755 claude_desktop/rootfs/etc/cont-init.d/90-ingress.sh create mode 100644 claude_desktop/rootfs/etc/nginx/includes/mime.types create mode 100644 claude_desktop/rootfs/etc/nginx/includes/proxy_params.conf create mode 100644 claude_desktop/rootfs/etc/nginx/includes/resolver.conf create mode 100644 claude_desktop/rootfs/etc/nginx/includes/server_params.conf create mode 100644 claude_desktop/rootfs/etc/nginx/includes/ssl_params.conf create mode 100644 claude_desktop/rootfs/etc/nginx/includes/upstream.conf create mode 100644 claude_desktop/updater.json diff --git a/claude_desktop/CHANGELOG.md b/claude_desktop/CHANGELOG.md new file mode 100644 index 0000000000..b41d6a53ad --- /dev/null +++ b/claude_desktop/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## debianbookworm-1ae1f8ff-ls13 + +- Initial Claude Desktop add-on using LinuxServer Selkies, Home Assistant ingress, persistent sign-in data, runtime Claude Desktop updates, optional apt/pip additions, custom scripts, and bundled Claude Code optimization tools. diff --git a/claude_desktop/Dockerfile b/claude_desktop/Dockerfile new file mode 100644 index 0000000000..e2051a0709 --- /dev/null +++ b/claude_desktop/Dockerfile @@ -0,0 +1,135 @@ +#============================# +# ALEXBELGIUM'S DOCKERFILE # +#============================# +#=== Home Assistant Addon ===# + +################# +# 1 Build Image # +################# + +ARG BUILD_FROM +ARG BUILD_VERSION +FROM ${BUILD_FROM} + +################## +# 2 Modify Image # +################## + +# Set S6 wait time +ENV S6_CMD_WAIT_FOR_SERVICES=1 \ + S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ + S6_SERVICES_GRACETIME=0 + +USER root + +# load volume +VOLUME [ "/sys/fs/cgroup" ] + +# Set shell +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Allow UID and GID setting +# hadolint ignore=SC2015,DL4006,SC2013,SC2086 +RUN \ + usermod --home /config/data abc && \ + if [[ -d /etc/services.d ]] && ls /etc/services.d/*/run 1> /dev/null 2>&1; then sed -i "1a set +e" /etc/services.d/*/run; fi + +# Global LSIO modifications +COPY ha_lsio.sh /ha_lsio.sh +ARG CONFIGLOCATION="/config/data" +RUN chmod 744 /ha_lsio.sh && if grep -qr "lsio" /etc; then /ha_lsio.sh "$CONFIGLOCATION"; fi && rm /ha_lsio.sh + +################## +# 3 Install apps # +################## + +# Add rootfs +COPY rootfs/ / +RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \; + +# Uses /bin for compatibility purposes +# hadolint ignore=DL4005 +RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \ + if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi + +# Install Claude Desktop and Python tooling +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 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install token-optimization tool prerequisites and binaries at build time +RUN apt-get update && \ + apt-get install -y --no-install-recommends nodejs npm && \ + pip3 install --break-system-packages "headroom-ai[all]" && \ + curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh && \ + if [ -x /root/.local/bin/rtk ] && [ ! -x /usr/local/bin/rtk ]; then mv /root/.local/bin/rtk /usr/local/bin/rtk; fi && \ + if [ -x /usr/local/bin/rtk ]; then chmod +x /usr/local/bin/rtk; else echo "rtk binary was not installed on PATH"; exit 1; fi && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /root/.cache + +# Modules +ARG MODULES="00-banner.sh 00-global_var.sh 01-custom_script.sh 00-local_mounts.sh 00-smb_mounts.sh 90-dns_set.sh" + +# Automatic modules download +COPY ha_automodules.sh /ha_automodules.sh +RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh + +# Manual apps +ENV PACKAGES="nginx" + +# Automatic apps & bashio +COPY ha_autoapps.sh /ha_autoapps.sh +RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh + +################ +# 4 Entrypoint # +################ + +# Add entrypoint +COPY ha_entrypoint.sh /ha_entrypoint.sh +RUN chmod 777 /ha_entrypoint.sh + +# Install bashio +COPY bashio-standalone.sh /usr/local/lib/bashio-standalone.sh +RUN chmod 0755 /usr/local/lib/bashio-standalone.sh + +ENTRYPOINT [ "/usr/bin/env" ] +CMD [ "/ha_entrypoint.sh" ] + +############ +# 5 Labels # +############ + +ARG BUILD_ARCH +ARG BUILD_DATE +ARG BUILD_DESCRIPTION +ARG BUILD_NAME +ARG BUILD_REF +ARG BUILD_REPOSITORY +ARG BUILD_VERSION +ENV BUILD_VERSION="${BUILD_VERSION}" +LABEL \ + io.hass.name="${BUILD_NAME}" \ + io.hass.description="${BUILD_DESCRIPTION}" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.title="${BUILD_NAME}" \ + org.opencontainers.image.description="${BUILD_DESCRIPTION}" \ + org.opencontainers.image.vendor="Home Assistant Add-ons" \ + org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.url="https://github.com/alexbelgium" \ + org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \ + org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${BUILD_REF} \ + org.opencontainers.image.version=${BUILD_VERSION} + +#################### +# 6 HealthcheckNOT # +#################### diff --git a/claude_desktop/README.md b/claude_desktop/README.md new file mode 100644 index 0000000000..beced8c4f1 --- /dev/null +++ b/claude_desktop/README.md @@ -0,0 +1,60 @@ +# Home assistant add-on: Claude Desktop + +![Supports aarch64 Architecture][aarch64-shield] +![Supports amd64 Architecture][amd64-shield] +![Project Maintenance][maintenance-shield] + +Run the Claude Desktop Linux app inside a LinuxServer.io Selkies container and stream it through Home Assistant ingress. + +## Installation + +1. Add this repository to the Home Assistant add-on store. +2. Install **Claude Desktop**. +3. Start the add-on and open the web UI from the sidebar. +4. Sign in with your Claude account from the Desktop app. + +Claude Desktop sign-in requires a claude.ai plan that supports the Desktop app. API keys are not accepted by the Desktop application. Anthropic's Linux beta does not include Computer Use or dictation. + +## Features + +- Claude Desktop in single-app Selkies mode. +- Home Assistant ingress support. +- Persistent `$HOME` under `/config/data`, preserving Claude Desktop and Claude Code sign-in state across restarts. +- Optional runtime Claude Desktop updates from Anthropic's apt repository. +- Optional extra apt and pip package installation. +- Custom script support through the repository standard `claude_desktop.sh` script. +- Optional bundled Claude Code optimization tools: headroom, rtk, and caveman. +- Low-power defaults: GPU device mapping, `AUTO_GPU=1`, `SELKIES_FRAMERATE=30`, `/tmp` tmpfs, and `$HOME/.cache` redirected to `/tmp/cache`. + +## Options + +| Option | Default | Description | +| ------ | ------- | ----------- | +| `PUID` / `PGID` | `0` / `0` | User and group used for persistent data ownership. | +| `TZ` | | Optional timezone, for example `America/New_York`. | +| `KEYBOARD` | | Optional Selkies keyboard layout. | +| `PASSWORD` | | Optional password for direct Selkies ports. Set this before exposing ports `3000` or `3001`. | +| `DRINODE` | | Optional GPU device override for Selkies. | +| `DNS_server` | `8.8.8.8` | DNS server used by the standard DNS module. | +| `auto_update` | `true` | Check Anthropic's apt repository and upgrade `claude-desktop` at add-on startup. | +| `install_headroom` | `true` | Make the baked-in `headroom` command available and log a usage hint. | +| `install_rtk` | `true` | Configure the rtk Claude Code `PreToolUse` hook in the persistent Claude Code settings. | +| `install_caveman` | `true` | Install the caveman Claude Code plugin into the persistent Claude Code home. | +| `additional_apps` | | Comma-separated Debian apt packages to install at startup, for example `htop,git`. | +| `additional_pip` | | Comma-separated pip packages to install at startup. Installs use `--break-system-packages`. | +| `data_location` | `/config/data` | Persistent home directory location. Keep this persistent so Claude sign-in survives restarts. | +| `networkdisks`, `cifsusername`, `cifspassword`, `cifsdomain` | | Standard SMB mount options. | +| `localdisks` | | Standard local disk mount option. | +| `env_vars` | `[]` | Extra environment variables to export into the container. This can override `SELKIES_*` defaults. | + +## Custom scripts + +The add-on includes the repository standard custom-script executor. On first start, it seeds a `claude_desktop.sh` file in the add-on config directory from the shared template. Commands in that script run during startup, allowing local customization without rebuilding the image. + +## Data and cache locations + +Persistent state is stored in the configured `data_location`. Claude Desktop stores sign-in data below `~/.config/Claude`, and Claude Code/tool configuration is stored below `~/.claude`. Volatile cache data is redirected to `/tmp/cache` through `$XDG_CACHE_HOME` and `$HOME/.cache`. + +[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg diff --git a/claude_desktop/apparmor.txt b/claude_desktop/apparmor.txt new file mode 100644 index 0000000000..d7fdfea13f --- /dev/null +++ b/claude_desktop/apparmor.txt @@ -0,0 +1,72 @@ +#include + +profile webtop_kde_addon flags=(attach_disconnected,mediate_deleted) { + #include + + capability chown, + capability dac_override, + capability dac_read_search, + capability fowner, + capability setgid, + capability setuid, + capability sys_chroot, + capability sys_admin, + file, + signal, + mount, + umount, + remount, + network udp, + network tcp, + network dgram, + network stream, + network inet, + network inet6, + network netlink raw, + network unix dgram, + + +# S6-Overlay + /init ix, + /run/{s6,s6-rc*,service}/** ix, + /package/** ix, + /command/** ix, + /run/{,**} rwk, + /dev/tty rw, + /bin/** ix, + /usr/bin/** ix, + /usr/lib/bashio/** ix, + /etc/s6/** rix, + /run/s6/** rix, + /etc/services.d/** rwix, + /etc/cont-init.d/** rwix, + /etc/cont-finish.d/** rwix, + /init rix, + /var/run/** mrwkl, + /var/run/ mrwkl, + /dev/i2c-1 mrwkl, + # Files required + /dev/fuse mrwkl, + /dev/sda1 mrwkl, + /dev/sdb1 mrwkl, + /dev/nvme0 mrwkl, + /dev/nvme0n1 mrwkl, + /dev/nvme1 mrwkl, + /dev/mmcblk0p1 mrwkl, + /dev/* mrwkl, + /udev/* mrwkl, + /tmp/** mrkwl, + /dev/fuse/** mrkwl, + /dev/** mrkwl, + /sys/firmware/** mrkwl, + + # Data access + /data/** rw, + + # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container + ptrace (trace,read) peer=docker-default, + + # docker daemon confinement requires explict allow rule for signal + signal (receive) set=(kill,term) peer=/usr/bin/docker, + +} diff --git a/claude_desktop/build.json b/claude_desktop/build.json new file mode 100644 index 0000000000..5ab1765eeb --- /dev/null +++ b/claude_desktop/build.json @@ -0,0 +1,6 @@ +{ + "build_from": { + "aarch64": "ghcr.io/linuxserver/baseimage-selkies:arm64v8-debianbookworm", + "amd64": "ghcr.io/linuxserver/baseimage-selkies:amd64-debianbookworm" + } +} diff --git a/claude_desktop/config.yaml b/claude_desktop/config.yaml new file mode 100644 index 0000000000..816609f030 --- /dev/null +++ b/claude_desktop/config.yaml @@ -0,0 +1,82 @@ +arch: + - aarch64 + - amd64 +audio: true +description: Claude Desktop streamed through a browser with LinuxServer Selkies +devices: + - /dev/dri + - /dev/dri/card0 + - /dev/dri/card1 + - /dev/dri/renderD128 + - /dev/fuse +environment: + AUTO_GPU: "1" + FM_HOME: /config/data + HOME: /config/data + PGID: "0" + PUID: "0" + SELKIES_FRAMERATE: "30" + START_DOCKER: "false" + TITLE: Claude Desktop + shm_size: 1gb +image: ghcr.io/alexbelgium/claude_desktop-{arch} +ingress: true +init: false +map: + - addon_config:rw + - share:rw + - media:rw + - ssl +name: Claude Desktop +options: + env_vars: [] + DNS_server: 8.8.8.8 + PGID: 0 + PUID: 0 + additional_apps: "" + additional_pip: "" + auto_update: true + data_location: /config/data + install_caveman: true + install_headroom: true + install_rtk: true +panel_admin: false +panel_icon: mdi:robot-happy +ports: + 3000/tcp: null + 3001/tcp: null +ports_description: + 3000/tcp: Web interface + 3001/tcp: Web interface https +privileged: + - SYS_ADMIN + - DAC_READ_SEARCH +schema: + env_vars: + - name: match(^[A-Za-z0-9_]+$) + value: str? + DNS_server: str? + DRINODE: list(/dev/dri/card0|/dev/dri/card1|/dev/dri/card2|/dev/dri/renderD128|/dev/dri/renderD129|)? + KEYBOARD: list(da-dk-qwerty|de-de-qwertz|en-gb-qwerty|en-us-qwerty|es-es-qwerty|fr-ch-qwertz|fr-fr-azerty|it-it-qwerty|ja-jp-qwerty|pt-br-qwerty|sv-se-qwerty|tr-tr-qwerty)? + PASSWORD: str? + PGID: int + PUID: int + TZ: match([A-Z][a-z]*./[A-Z][a-z]*.)? + additional_apps: str? + additional_pip: str? + auto_update: bool + cifsdomain: str? + cifspassword: str? + cifsusername: str? + data_location: str? + install_caveman: bool + install_headroom: bool + install_rtk: bool + localdisks: str? + networkdisks: str? +slug: claude_desktop +tmpfs: true +udev: true +url: https://github.com/alexbelgium/hassio-addons +version: "debianbookworm-1ae1f8ff-ls13" +video: true diff --git a/claude_desktop/rootfs/defaults/autostart b/claude_desktop/rootfs/defaults/autostart new file mode 100644 index 0000000000..a76d2f564e --- /dev/null +++ b/claude_desktop/rootfs/defaults/autostart @@ -0,0 +1 @@ +claude-desktop --no-sandbox diff --git a/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh b/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh new file mode 100755 index 0000000000..989d953f2f --- /dev/null +++ b/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh @@ -0,0 +1,67 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# shellcheck disable=SC2046 +set -e + +# Define user +PUID=$(bashio::config "PUID") +PGID=$(bashio::config "PGID") + +# Check data location +LOCATION=$(bashio::config 'data_location') + +if [[ "$LOCATION" = "null" || -z "$LOCATION" ]]; then + LOCATION="/config/data" +else + LOCATIONOK="" + for location in "/share" "/config" "/data" "/mnt"; do + if [[ "$LOCATION" == "$location"* ]]; then + LOCATIONOK=true + fi + done + + if [ -z "$LOCATIONOK" ]; then + LOCATION="/config/data" + bashio::log.fatal "Your data_location value can only be set in /share, /config, /data or /mnt. It will be reset to the default location : $LOCATION" + fi +fi + +bashio::log.info "Setting data location to $LOCATION" + +for file in /etc/s6-overlay/s6-rc.d/*/run; do + if [ "$(sed -n '1{/bash/p};q' "$file")" ]; then + sed -i "1a export HOME=$LOCATION" "$file" + sed -i "1a export FM_HOME=$LOCATION" "$file" + sed -i "1a export XDG_CACHE_HOME=/tmp/cache" "$file" + fi +done + +for folders in /defaults /etc/cont-init.d /etc/services.d /etc/s6-overlay/s6-rc.d; do + if [ -d "$folders" ]; then + sed -i "s|/config/data|$LOCATION|g" $(find "$folders" -type f) &> /dev/null || true + fi +done + +sed -i "s|^\(abc:[^:]*:[^:]*:[^:]*:[^:]*:\)[^:]*|\1$LOCATION|" /etc/passwd + +if [ -d /var/run/s6/container_environment ]; then printf "%s" "$LOCATION" > /var/run/s6/container_environment/HOME; fi +if [ -d /var/run/s6/container_environment ]; then printf "%s" "$LOCATION" > /var/run/s6/container_environment/FM_HOME; fi +if [ -d /var/run/s6/container_environment ]; then printf "%s" "/tmp/cache" > /var/run/s6/container_environment/XDG_CACHE_HOME; fi +{ + printf "%s\n" "export HOME=\"$LOCATION\"" + printf "%s\n" "export FM_HOME=\"$LOCATION\"" + printf "%s\n" "export XDG_CACHE_HOME=\"/tmp/cache\"" +} >> ~/.bashrc + +bashio::log.info "Creating $LOCATION" +mkdir -p "$LOCATION" /tmp/cache +chmod 755 /tmp/cache + +if [ -e "$LOCATION/.cache" ] && [ ! -L "$LOCATION/.cache" ]; then + rm -rf "$LOCATION/.cache" +fi +ln -sfn /tmp/cache "$LOCATION/.cache" + +bashio::log.info "Setting ownership to $PUID:$PGID" +chown -R "$PUID":"$PGID" "$LOCATION" /tmp/cache +chmod -R 700 "$LOCATION" diff --git a/claude_desktop/rootfs/etc/cont-init.d/80-configuration.sh b/claude_desktop/rootfs/etc/cont-init.d/80-configuration.sh new file mode 100755 index 0000000000..83a5f83333 --- /dev/null +++ b/claude_desktop/rootfs/etc/cont-init.d/80-configuration.sh @@ -0,0 +1,54 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +# shellcheck disable=SC2015 +set -e + +if bashio::config.has_value 'additional_apps'; then + bashio::log.info "Installing additional apps :" + NEWAPPS=$(bashio::config 'additional_apps') + if command -v "apt-get" &> /dev/null; then + apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null || bashio::log.warning "Unable to update apt package lists" + fi + for packagestoinstall in ${NEWAPPS//,/ }; do + bashio::log.green "... $packagestoinstall" + if command -v "apk" &> /dev/null; then + apk add --no-cache "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found") + elif command -v "apt-get" &> /dev/null; then + apt-get install -yqq --no-install-recommends "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found") + elif command -v "pacman" &> /dev/null; then + pacman --noconfirm -S "$packagestoinstall" &> /dev/null || (bashio::log.fatal "Error : $packagestoinstall not found") + fi + done +fi + +if bashio::config.has_value 'additional_pip'; then + for p in $(bashio::config 'additional_pip' | tr ',' ' '); do + bashio::log.green "... pip: $p" + pip3 install --break-system-packages "$p" || bashio::log.fatal "Error: pip package $p failed" + done +fi + +if bashio::config.has_value 'TZ'; then + TIMEZONE=$(bashio::config 'TZ') + bashio::log.info "Setting timezone to $TIMEZONE" + ln -snf /usr/share/zoneinfo/"$TIMEZONE" /etc/localtime + echo "$TIMEZONE" > /etc/timezone +fi || (bashio::log.fatal "Error : $TIMEZONE not found. Here is a list of valid timezones : https://manpages.ubuntu.com/manpages/focal/man3/DateTime::TimeZone::Catalog.3pm.html") + +if bashio::config.has_value 'KEYBOARD'; then + KEYBOARD=$(bashio::config 'KEYBOARD') + bashio::log.info "Setting keyboard to $KEYBOARD" + if [ -d /var/run/s6/container_environment ]; then printf "%s" "$KEYBOARD" > /var/run/s6/container_environment/KEYBOARD; fi + printf "%s\n" "KEYBOARD=\"$KEYBOARD\"" >> ~/.bashrc +fi || true + +if bashio::config.has_value 'PASSWORD'; then + bashio::log.info "Setting password to the value defined in options" + PASSWORD=$(bashio::config 'PASSWORD') + passwd -d abc + echo -e "$PASSWORD\n$PASSWORD" | passwd abc +elif ! bashio::config.has_value 'PASSWORD' && [[ -n "$(bashio::addon.port "3000")" ]] && [[ -n $(bashio::addon.port "3001") ]]; then + bashio::log.warning "SEVERE RISK IDENTIFIED" + bashio::log.warning "You are opening an external port but your password is not defined" + bashio::log.warning "You risk being hacked ! Please disable the external ports, or use a password" +fi diff --git a/claude_desktop/rootfs/etc/cont-init.d/81-claude_update.sh b/claude_desktop/rootfs/etc/cont-init.d/81-claude_update.sh new file mode 100755 index 0000000000..8fd3dd7862 --- /dev/null +++ b/claude_desktop/rootfs/etc/cont-init.d/81-claude_update.sh @@ -0,0 +1,12 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +if bashio::config.true 'auto_update'; then + bashio::log.info "Checking for Claude Desktop updates..." + if apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null && apt-get install -y --only-upgrade claude-desktop &> /dev/null; then + bashio::log.info "Claude Desktop version: $(dpkg-query -W -f='${Version}' claude-desktop)" + else + bashio::log.warning "Update check failed (offline?), keeping current version" + fi +fi diff --git a/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh new file mode 100755 index 0000000000..451d10eec0 --- /dev/null +++ b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh @@ -0,0 +1,57 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +mkdir -p "$HOME/.claude" + +if bashio::config.true 'install_headroom'; then + if command -v headroom &> /dev/null; then + bashio::log.info "headroom $(headroom --version 2> /dev/null || true) available. Use 'headroom wrap claude' or MCP mode from Claude Code." + else + bashio::log.warning "headroom is not available" + fi +fi + +if bashio::config.true 'install_rtk'; then + if command -v rtk &> /dev/null; then + if [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk' "$HOME/.claude/settings.json"; then + bashio::log.info "rtk Claude Code hook already configured" + else + bashio::log.info "Configuring rtk Claude Code hook" + rtk init -g || bashio::log.warning "rtk hook configuration failed" + fi + else + bashio::log.warning "rtk is not available" + fi +elif [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk' "$HOME/.claude/settings.json"; then + bashio::log.info "Removing rtk Claude Code hook" + python3 - <<'PY' || bashio::log.warning "Unable to remove rtk hook automatically" +import json +from pathlib import Path +path = Path.home() / ".claude" / "settings.json" +data = json.loads(path.read_text()) +hooks = data.get("hooks", {}) +for event, entries in list(hooks.items()): + if isinstance(entries, list): + hooks[event] = [entry for entry in entries if "rtk" not in json.dumps(entry)] + if not hooks[event]: + hooks.pop(event, None) +if hooks: + data["hooks"] = hooks +else: + data.pop("hooks", None) +path.write_text(json.dumps(data, indent=2) + "\n") +PY +fi + +if bashio::config.true 'install_caveman'; then + if [ -d "$HOME/.claude/plugins/caveman" ] || find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -print -quit | grep -q .; then + bashio::log.info "caveman Claude Code plugin already configured" + else + bashio::log.info "Installing caveman Claude Code plugin" + curl --connect-timeout 10 --max-time 60 -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash || bashio::log.warning "caveman install failed (offline?)" + fi +else + bashio::log.info "Disabling caveman Claude Code plugin" + find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -exec rm -rf {} + 2> /dev/null || true +fi diff --git a/claude_desktop/rootfs/etc/cont-init.d/90-ingress.sh b/claude_desktop/rootfs/etc/cont-init.d/90-ingress.sh new file mode 100755 index 0000000000..5b533f43e2 --- /dev/null +++ b/claude_desktop/rootfs/etc/cont-init.d/90-ingress.sh @@ -0,0 +1,40 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +NGINX_CONFIG=/etc/nginx/sites-available/ingress.conf +SUBFOLDER="$(bashio::addon.ingress_entry)" + +# Ensure subfolder ends with a trailing slash (except for root) +if [[ -n "${SUBFOLDER}" && "${SUBFOLDER}" != "/" ]]; then + [[ "${SUBFOLDER}" == */ ]] || SUBFOLDER="${SUBFOLDER}/" +else + SUBFOLDER="/" +fi + +cp /defaults/default.conf "${NGINX_CONFIG}" + +# Keep only the first (non-SSL) server block +awk -v n=2 '/^[[:space:]]*server[[:space:]]*\{/{n--} n>0' "${NGINX_CONFIG}" > tmpfile +mv tmpfile "${NGINX_CONFIG}" + +# Disable IPv6 listeners for ingress proxying +sed -i '/listen \[::\]/d' "${NGINX_CONFIG}" + +# Adapt ports and upstream paths for Home Assistant ingress +sed -i "s|3000|$(bashio::addon.ingress_port)|g" "${NGINX_CONFIG}" +sed -i "s|SUBFOLDER|/|g" "${NGINX_CONFIG}" +sed -i "s|CWS|8082|g" "${NGINX_CONFIG}" +sed -i "s|REPLACE_HOME|${HOME:-/root}|g" "${NGINX_CONFIG}" +sed -i "s|REPLACE_DOWNLOADS_PATH|${HOME:-/config}|g" "${NGINX_CONFIG}" +sed -i '/proxy_buffering/a proxy_set_header Accept-Encoding "";' "${NGINX_CONFIG}" +sed -i '/proxy_buffering/a sub_filter_once off;' "${NGINX_CONFIG}" +sed -i '/proxy_buffering/a sub_filter_types *;' "${NGINX_CONFIG}" +sed -i '/proxy_buffering/a sub_filter "vnc/index.html?autoconnect" "vnc/index.html?path=%%path%%/websockify?autoconnect";' "${NGINX_CONFIG}" +sed -i "s|%%path%%|${SUBFOLDER:1}|g" "${NGINX_CONFIG}" + +# Avoid content encoding on proxied responses to keep Selkies happy +sed -i '/proxy_buffering/a \ + proxy_set_header Accept-Encoding "";' "${NGINX_CONFIG}" + +cp "${NGINX_CONFIG}" /etc/nginx/sites-enabled diff --git a/claude_desktop/rootfs/etc/nginx/includes/mime.types b/claude_desktop/rootfs/etc/nginx/includes/mime.types new file mode 100644 index 0000000000..7c7cdef2d1 --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/mime.types @@ -0,0 +1,96 @@ +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/svg+xml svg svgz; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/webp webp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + + font/woff woff; + font/woff2 woff2; + + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.oasis.opendocument.graphics odg; + application/vnd.oasis.opendocument.presentation odp; + application/vnd.oasis.opendocument.spreadsheet ods; + application/vnd.oasis.opendocument.text odt; + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx; + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx; + application/vnd.wap.wmlc wmlc; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/claude_desktop/rootfs/etc/nginx/includes/proxy_params.conf b/claude_desktop/rootfs/etc/nginx/includes/proxy_params.conf new file mode 100644 index 0000000000..1990d49596 --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/proxy_params.conf @@ -0,0 +1,15 @@ +proxy_http_version 1.1; +proxy_ignore_client_abort off; +proxy_read_timeout 86400s; +proxy_redirect off; +proxy_send_timeout 86400s; +proxy_max_temp_file_size 0; + +proxy_set_header Accept-Encoding ""; +proxy_set_header Connection $connection_upgrade; +proxy_set_header Host $http_host; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-NginX-Proxy true; +proxy_set_header X-Real-IP $remote_addr; diff --git a/claude_desktop/rootfs/etc/nginx/includes/resolver.conf b/claude_desktop/rootfs/etc/nginx/includes/resolver.conf new file mode 100644 index 0000000000..70f4982b9b --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/resolver.conf @@ -0,0 +1 @@ +resolver 127.0.0.11 ipv6=off; diff --git a/claude_desktop/rootfs/etc/nginx/includes/server_params.conf b/claude_desktop/rootfs/etc/nginx/includes/server_params.conf new file mode 100644 index 0000000000..09c06543ea --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/server_params.conf @@ -0,0 +1,6 @@ +root /dev/null; +server_name $hostname; + +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; +add_header X-Robots-Tag none; diff --git a/claude_desktop/rootfs/etc/nginx/includes/ssl_params.conf b/claude_desktop/rootfs/etc/nginx/includes/ssl_params.conf new file mode 100644 index 0000000000..6f15005998 --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/ssl_params.conf @@ -0,0 +1,9 @@ +ssl_protocols TLSv1.2; +ssl_prefer_server_ciphers on; +ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA; +ssl_ecdh_curve secp384r1; +ssl_session_timeout 10m; +ssl_session_cache shared:SSL:10m; +ssl_session_tickets off; +ssl_stapling on; +ssl_stapling_verify on; diff --git a/claude_desktop/rootfs/etc/nginx/includes/upstream.conf b/claude_desktop/rootfs/etc/nginx/includes/upstream.conf new file mode 100644 index 0000000000..b292326bd7 --- /dev/null +++ b/claude_desktop/rootfs/etc/nginx/includes/upstream.conf @@ -0,0 +1,3 @@ +upstream backend { + server 127.0.0.1:8080; +} diff --git a/claude_desktop/updater.json b/claude_desktop/updater.json new file mode 100644 index 0000000000..204f59e472 --- /dev/null +++ b/claude_desktop/updater.json @@ -0,0 +1,7 @@ +{ + "source": "github", + "upstream_repo": "linuxserver/docker-baseimage-selkies", + "github_fulltag": true, + "slug": "claude_desktop", + "paused": false +}