A self-hosted web application that provides mobile-friendly access to Claude Code running on your local machine with GPU support.
Note: This is an unofficial community project and is not affiliated with, endorsed by, or supported by Anthropic. Claude Code is a product of Anthropic, but this web interface is an independent project.
Read more: Clawed Abode: Claude Code is Too Cloudy
- Run Claude Code sessions from any device with a web browser
- Access local GPU resources for AI workloads
- Persistent sessions with isolated git clones
- Simple password-based authentication (single user)
- Session tracking with IP addresses and login history
- Clean session lifecycle management
- Mobile-friendly interface
- Voice input (browser speech recognition) and read-aloud with Kokoro TTS
This application runs Claude Code in bypassPermissions mode, which means Claude can execute arbitrary code, install packages, and modify files without confirmation. You should:
- Run this on a dedicated machine or dedicated user account - not your personal workstation
- Never run as root - always use a dedicated unprivileged user
- Use a fine-grained GitHub token scoped to only the repositories you want to expose
- Use Tailscale or similar for remote access - never expose port 3000 directly to the internet
- A Linux host with systemd user services (for the session process scopes and the service unit),
sudofor creating the user, and Git 2.31+ (clones pass credentials viaGIT_CONFIG_*) - Node.js 22.22.1+; the setup below installs it via nvm
sudo useradd -m -s /bin/bash clawedabode
# Let the user's systemd services run without a login session
sudo loginctl enable-linger clawedabode
sudo -u clawedabode -i# Install Node.js (e.g., via nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
source ~/.bashrc
nvm install 22
# Install pnpm
corepack enable && corepack prepare pnpm@latest --activate
# Install and authenticate Claude Code
npm install -g @anthropic-ai/claude-code
claude setup-tokengit clone https://github.com/brendanlong/clawed-abode.git
cd clawed-abode
pnpm install
cp .env.example .envEdit .env and set PASSWORD_HASH, GITHUB_TOKEN and CLAUDE_CODE_OAUTH_TOKEN as described below. The full variable list is under Configuration.
Copy the token printed by claude setup-token in step 2 (run it again if you need to) into .env as CLAUDE_CODE_OAUTH_TOKEN.
Use a Fine-grained Personal Access Token for security:
- Go to https://github.com/settings/personal-access-tokens/new
- Select "Fine-grained personal access token"
- Under "Repository access", select "Only select repositories" and choose the repos you want to use
- Under "Permissions" > "Repository permissions", set:
- Contents: Read and write (for push/pull, and to list branches)
- Metadata: Read-only (automatically included)
- Issues: Read-only (to browse issues when starting a session)
- Pull requests: Read-only (for the session PR status indicator)
- Generate the token and add it to your
.envfile
Public repos work even without these permissions, so a token missing Contents only fails on private repos — where branch listing and cloning break.
pnpm hash-password your-secure-passwordAdd the output to your .env file:
PASSWORD_HASH="JGFyZ29uMmlkJHY9MTkkbT02NTUzNix0PTMscD00JC4uLg=="pnpm prisma migrate deploypnpm run build
pnpm startVisit http://localhost:3000 from the server itself (see Remote Access for the URL to use from other devices).
Stop the foreground server from step 5 first (Ctrl-C); the service binds the same port.
First, find the full path to your Node.js binary:
nvm which 22
# Example output: /home/clawedabode/.nvm/versions/node/v22.22.1/bin/nodeCreate ~/.config/systemd/user/clawed-abode.service, replacing the node path with the output of nvm which 22:
[Unit]
Description=Clawed Abode
After=network.target
[Service]
Type=simple
WorkingDirectory=%h/clawed-abode
ExecStart=%h/.nvm/versions/node/v22.22.1/bin/node node_modules/next/dist/bin/next start
Restart=always
RestartSec=5
Environment=NODE_ENV=production
# Listen on loopback only: Tailscale Serve is the sole ingress, so the
# X-Forwarded-For header used for login rate limiting is always the one it sets.
Environment=HOSTNAME=127.0.0.1
[Install]
WantedBy=default.targetsystemctl --user daemon-reload
systemctl --user enable --now clawed-abode.servicejournalctl --user -u clawed-abode.service -f./scripts/update.shThis pulls the latest code, installs dependencies, applies database migrations, rebuilds, and restarts the service. A plain git pull + restart is not enough — next start serves the prebuilt .next bundle, so without a rebuild you keep running the old code. If your service isn't named clawed-abode.service, set CLAWED_ABODE_SERVICE.
Read-aloud needs a Kokoro speech server (see TTS_BASE_URL below). Hosted OpenRouter works, but its latency varies from about 1 to 10 s per request. A local Kokoro-FastAPI on the CPU synthesizes a sentence in about 0.5 s, roughly 5× faster than playback, so audio starts in under a second. We use the CPU image: the GPU is faster, but it would hold 0.4–1.3 GB of VRAM permanently to save about half a second.
scripts/kokoro.container runs it as a podman Quadlet user service on 127.0.0.1:8880, started at boot. It relies on the lingering enabled in step 1. The first start pulls the image, about 3 GB.
mkdir -p ~/.config/containers/systemd
cp scripts/kokoro.container ~/.config/containers/systemd/
systemctl --user daemon-reload # Quadlet generates kokoro.service; its [Install] section enables it
systemctl --user start kokoro.service
curl http://127.0.0.1:8880/healthThen add the following to .env and restart clawed-abode.service:
TTS_BASE_URL="http://127.0.0.1:8880/v1"
TTS_MODEL="kokoro"
TTS_MAX_CONCURRENCY=1 # one request at a time, so they don't compete for CPU coresLogs: journalctl --user -u kokoro.service.
tailscale serve 3000Access at https://<machine-name>.<tailnet-name>.ts.net
tailscale funnel 3000Note: HTTPS is required for clipboard copy and browser notifications.
The schema in src/lib/env.ts is authoritative; it is validated once at startup and the server refuses to boot on an invalid value.
| Variable | Description | Default |
|---|---|---|
PASSWORD_HASH |
Base64-encoded Argon2 hash for auth; logins fail without it | None |
DATABASE_URL |
SQLite database path | file:./data/dev.db |
GITHUB_TOKEN |
GitHub Fine-grained PAT; without it repo/branch/issue pickers and PR status are unavailable | None |
CLAUDE_CODE_OAUTH_TOKEN |
Claude Code OAuth token (claude setup-token); can instead be set in the Settings UI |
None |
CLAUDE_MODEL |
Default Claude model (overridable per repo/session in Settings) | opus[1m] |
SESSION_BRANCH_PREFIX |
Prefix for session git branches | claude/ |
ENCRYPTION_KEY |
32+ char key for encrypting secrets; required before any secret env var or MCP header can be saved | None |
APP_URL |
Public URL the browser reaches this app on; only used to build the MCP OAuth redirect URI | Derived from request |
CODE_SERVER_URL |
Base URL of a code-server instance; enables the "Open in VS Code" button (see scripts/setup-code-server.sh) |
None |
PUBLIC_FILES_PORT / PUBLIC_FILES_URL |
Loopback port and browser URL for serving each session's public/ directory; set both or neither (see scripts/expose-public-files-tailscale.sh) |
None |
TTS_BASE_URL |
OpenAI-compatible speech API serving Kokoro (OpenRouter or a local Kokoro-FastAPI); enables read-aloud | None |
TTS_API_KEY |
Bearer key for TTS_BASE_URL, if it needs one |
None |
TTS_MODEL |
Model name sent to TTS_BASE_URL (kokoro for Kokoro-FastAPI) |
hexgrad/kokoro-82m |
TTS_MAX_CONCURRENCY |
Speech requests per message run at once; use 1 for a local CPU server |
4 |
LOG_LEVEL |
Minimum server log level: debug, info, warn, or error |
info |
pnpm run dev starts the dev server with hot reload; pnpm test:run runs every test suite (what CI runs). After editing prisma/schema.prisma, pnpm run db:migrate creates and applies a migration (production applies them with prisma migrate deploy via scripts/update.sh). Contributor rules and the design docs are in CLAUDE.md and doc/DESIGN.md.
Regenerate a token with claude setup-token and update CLAUDE_CODE_OAUTH_TOKEN in .env or Settings.
Reset the database (this deletes all sessions and messages):
rm -rf data
pnpm prisma migrate deployMIT