Wren Linux

wren-linux / extras / claude-code.sh

#!/bin/bash
# retired: An AI agent belongs behind the strongest wall Wren Linux has, and on this one it shares your kernel and your ~/projects: wrenctl install claude-vm. Its --remove leaves your files in ~/projects.
# What is left of this extra is its --remove, for whoever still has it set up: update.sh offers it,
# and so does wrenctl remove claude-code. It can't be installed any more; the rest of it was deleted.
# Usage: bash claude-code.sh --remove
# summary: Claude Code, sandboxed in its own user
# security: most
# category: dev
# status: bin:claude-sandbox bin:claude-unsandboxed bin:git file:/usr/local/libexec/claude-session
. "$(dirname -- "${BASH_SOURCE[0]}")/../lib.sh"
. "$WREN_DIR/lib/claude-repo.sh"
. "$WREN_DIR/lib/claude-accounts.sh"
if [ "${1:-}" = --manual ]; then echo "Retired: only --remove is left (bash extras/claude-code.sh --remove)."; exit; fi
need_user

AGENT=claude-agent
AGENT_HOME=/home/$AGENT
SESSION=/usr/local/libexec/claude-session
SUDOERS=/etc/sudoers.d/wren-claude
FIREWALL=/etc/wren/claude.nft
UNIT=/etc/systemd/system/wren-claude-firewall.service
PROJECTS=$HOME/projects

# account NAME: sets AGENT, AGENT_HOME, PROJECTS and the sudoers file for it
account() {
    claude_account_valid "$1" || die "An account name is small letters and digits, a letter first, at most 15 (e.g. work)."
    AGENT=$CLAUDE_ACCOUNT_PREFIX$1
    AGENT_HOME=/home/$AGENT
    PROJECTS=$HOME/projects-$1
    ACCOUNT_SUDOERS=/etc/sudoers.d/wren-claude-$1
}

# remove_account NAME: its sudo rule, its access to your folders, and (if you
# say so) the user and its home; what it made in its folder becomes yours
remove_account() {
    account "$1"
    sudo rm -f "$ACCOUNT_SUDOERS"
    if command -v setfacl >/dev/null; then
        setfacl -x "u:$AGENT" "$HOME" 2>/dev/null || true
        [ -d "$PROJECTS" ] && setfacl -R -x "u:$AGENT,d:u:$AGENT" "$PROJECTS" 2>/dev/null
    fi
    if id "$AGENT" >/dev/null 2>&1; then
        if ask "Delete the user $AGENT and its home folder $AGENT_HOME (its Claude login, memories, ssh key and settings)?" n; then
            sudo pkill -KILL -u "$AGENT" 2>/dev/null || true
            [ -d "$PROJECTS" ] && sudo find "$PROJECTS" -user "$AGENT" -exec chown -h "$(id -un):$(id -gn)" {} +
            sudo userdel -r "$AGENT" 2>/dev/null || sudo userdel "$AGENT"
            note "Removed the user $AGENT."
        else
            sudo pkill -KILL -u "$AGENT" 2>/dev/null || true
            note "Kept the user $AGENT and $AGENT_HOME (it can't be started: its sudo rule is gone)."
        fi
    fi
    note "${PROJECTS/#$HOME/\~} stays, with your files."
}

if [ "${1:-}" = --remove ]; then
    # the other accounts first, each with its own question
    mapfile -t accounts < <(claude_account_users | cut -d: -f1)
    for u in "${accounts[@]}"; do remove_account "${u#"$CLAUDE_ACCOUNT_PREFIX"}"; done
    AGENT=claude-agent AGENT_HOME=/home/claude-agent PROJECTS=$HOME/projects
    remove_bin claude-sandbox
    remove_bin claude-unsandboxed
    remove_claude_chooser
    # claude-pull and claude-share are shared with extras/claude-vm.sh; keep them if that sandbox
    # is still set up (its session wrapper is the tell)
    [ -e /usr/local/libexec/claude-vm-session ] || { remove_bin claude-pull; remove_bin claude-share; }
    remove_bin git
    sudo systemctl disable --now wren-claude-firewall.service >/dev/null 2>&1 || true
    sudo nft delete table inet wren_claude 2>/dev/null || true
    sudo rm -f "$SUDOERS" "$SESSION" "$FIREWALL" "$UNIT"
    remove_no_abstract
    remove_net_check
    sudo systemctl daemon-reload >/dev/null 2>&1 || true
    sudo rmdir /usr/local/libexec /etc/wren 2>/dev/null || true
    # take back what it was allowed to reach
    if command -v setfacl >/dev/null; then
        setfacl -x "u:$AGENT" "$HOME" 2>/dev/null || true
        [ -d "$PROJECTS" ] && setfacl -R -x "u:$AGENT,d:u:$AGENT" "$PROJECTS" 2>/dev/null
    fi
    if id "$AGENT" >/dev/null 2>&1; then
        if ask "Delete the user $AGENT and its home folder $AGENT_HOME (its Claude login, ssh keys and settings)?" n; then
            sudo pkill -KILL -u "$AGENT" 2>/dev/null || true
            # whatever it made in ~/projects becomes yours
            [ -d "$PROJECTS" ] && sudo find "$PROJECTS" -user "$AGENT" -exec chown -h "$(id -un):$(id -gn)" {} +
            sudo userdel -r "$AGENT" 2>/dev/null || sudo userdel "$AGENT"
            note "Removed the user $AGENT."
        else
            note "Kept the user $AGENT and $AGENT_HOME."
        fi
    fi
    # (claude-code itself stays while extras/claude-regular.sh needs it)
    apt_purge claude-code acl nftables ripgrep jq shellcheck python3-venv
    remove_claude_repo_if_unused
    note "Removed the sandboxed Claude Code. Your own settings stay in ~/.claude, and ~/projects is untouched."
    exit
fi

die "Retired: use claude-vm (wrenctl install claude-vm). Only --remove is left."