{"id":45321179,"url":"https://github.com/andresmorales07/hatchpod","last_synced_at":"2026-03-03T17:16:19.941Z","repository":{"id":338849099,"uuid":"1159422358","full_name":"andresmorales07/hatchpod","owner":"andresmorales07","description":"A persistent, self-hosted Claude Code environment you can access from any machine — via SSH, Mosh, web terminal, or Tailscale VPN.","archived":false,"fork":false,"pushed_at":"2026-02-20T04:12:06.000Z","size":297,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-20T05:26:29.439Z","etag":null,"topics":["ai-agent","claude","claude-code","devbox","developer-tools","docker","docker-compose","mosh","remote-development","self-hosted","ssh","sysbox","tailscale","ttyd"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andresmorales07.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-16T17:56:57.000Z","updated_at":"2026-02-20T04:12:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/andresmorales07/hatchpod","commit_stats":null,"previous_names":["andresmorales07/claude-box","andresmorales07/hatchpod"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/andresmorales07/hatchpod","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresmorales07%2Fhatchpod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresmorales07%2Fhatchpod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresmorales07%2Fhatchpod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresmorales07%2Fhatchpod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andresmorales07","download_url":"https://codeload.github.com/andresmorales07/hatchpod/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andresmorales07%2Fhatchpod/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29676987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T06:23:40.028Z","status":"ssl_error","status_checked_at":"2026-02-21T06:23:39.222Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","claude","claude-code","devbox","developer-tools","docker","docker-compose","mosh","remote-development","self-hosted","ssh","sysbox","tailscale","ttyd"],"created_at":"2026-02-21T08:02:44.401Z","updated_at":"2026-03-03T17:16:19.926Z","avatar_url":"https://github.com/andresmorales07.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003ch1\u003e📦 Hatchpod\u003c/h1\u003e\n\n\u003cp\u003e\u003cstrong\u003eA persistent, self-hosted Claude Code workstation you can access from anywhere.\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003e\n  \u003ca href=\"https://github.com/andresmorales07/hatchpod/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/andresmorales07/hatchpod/ci.yml?branch=main\u0026label=CI\u0026logo=github\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/andresmorales07/hatchpod/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/andresmorales07/hatchpod?logo=github\" alt=\"Release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/andresmorales07/hatchpod/pkgs/container/hatchpod\"\u003e\u003cimg src=\"https://img.shields.io/badge/ghcr.io-hatchpod-blue?logo=docker\" alt=\"Container\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/andresmorales07/hatchpod\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003c/div\u003e\n\nRun Claude Code on a server, VPS, or homelab — then connect via **SSH**, **web browser**, **Mosh**, or **Tailscale VPN** from wherever you are. Your files, credentials, MCP servers, dotfiles, and Docker images all persist across restarts.\n\nThink of it as your personal cloud dev machine with Claude Code built in.\n\n## Why Hatchpod?\n\nUnlike ephemeral sandboxes (like [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/claude-code/)) that spin up for a single task and disappear, Hatchpod is a **long-lived workstation**.\n\n| | Ephemeral Sandboxes | Hatchpod |\n|---|---|---|\n| **Lifecycle** | Task-scoped, disposable | Persistent — pick up where you left off |\n| **Access** | Local only | SSH, Mosh, web terminal, Tailscale VPN |\n| **Customization** | Pre-set image | Full Linux env with sudo, dotfiles, any tooling |\n| **Docker-in-Docker** | Limited or none | Full DinD via [Sysbox](https://github.com/nestybox/sysbox) |\n| **Requires** | Docker Desktop | Any Linux host with Docker Engine |\n\n## Quick Start\n\n```bash\n# 1. Clone and configure\ngit clone https://github.com/andresmorales07/hatchpod.git\ncd hatchpod\ncp .env.example .env            # edit .env to set your passwords\n\n# 2. Start (pulls the prebuilt image — no build step needed)\ndocker compose up -d\n\n# 3. Connect\nssh -p 2222 hatchpod@localhost    # password is SSH_PASSWORD from .env\n\n# 4. Authenticate Claude Code (first time only)\nclaude                          # follow the login link that appears\n```\n\n\u003e **No Sysbox?** The default `docker-compose.yml` sets `runtime: sysbox-runc` for Docker-in-Docker. If you don't have [Sysbox](https://github.com/nestybox/sysbox) installed, create a one-line override:\n\u003e\n\u003e ```bash\n\u003e echo 'services: { hatchpod: { runtime: runc } }' \u003e docker-compose.override.yml\n\u003e docker compose up -d\n\u003e ```\n\u003e\n\u003e Everything except `docker` commands inside the container will work without Sysbox.\n\n## What's Included\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003e\u003cstrong\u003eCategory\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003e\u003cstrong\u003eSoftware\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003e\u003cstrong\u003ePurpose\u003c/strong\u003e\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e🤖 \u003cstrong\u003eAI\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003eClaude Code\u003c/td\u003e\u003ctd\u003eAnthropic's CLI agent\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eWeb UI + API\u003c/td\u003e\u003ctd\u003eClaude Code web interface (Tailwind CSS v4 + shadcn/ui) and REST/WebSocket API (port 8080)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e⚡ \u003cstrong\u003eRuntimes\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003eNode.js 20 LTS\u003c/td\u003e\u003ctd\u003eMCP servers (npx)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003ePython 3 + venv\u003c/td\u003e\u003ctd\u003eMCP servers (uvx)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e📦 \u003cstrong\u003ePackage Mgrs\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003enpm\u003c/td\u003e\u003ctd\u003eNode packages (global prefix persisted)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euv / uvx\u003c/td\u003e\u003ctd\u003ePython packages and tool runner\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e🐳 \u003cstrong\u003eContainers\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003eDocker Engine + Compose\u003c/td\u003e\u003ctd\u003eDocker-in-Docker (requires Sysbox on host)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"4\"\u003e🌐 \u003cstrong\u003eAccess\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003eOpenSSH server\u003c/td\u003e\u003ctd\u003eRemote access (port 2222)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003ettyd\u003c/td\u003e\u003ctd\u003eWeb terminal (port 7681)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003emosh\u003c/td\u003e\u003ctd\u003eResilient mobile shell (UDP 60000-60003)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eTailscale\u003c/td\u003e\u003ctd\u003eVPN access (opt-in, set \u003ccode\u003eTS_AUTHKEY\u003c/code\u003e)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"4\"\u003e🔧 \u003cstrong\u003eDev Tools\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003egit\u003c/td\u003e\u003ctd\u003eVersion control\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eGitHub CLI (gh)\u003c/td\u003e\u003ctd\u003eGitHub operations\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003ecurl, jq\u003c/td\u003e\u003ctd\u003eHTTP requests and JSON processing\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003emake, g++\u003c/td\u003e\u003ctd\u003eC++ build tools for native Node.js addons (node-pty)\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd rowspan=\"2\"\u003e🖥️ \u003cstrong\u003eSystem\u003c/strong\u003e\u003c/td\u003e\u003ctd\u003es6-overlay v3\u003c/td\u003e\u003ctd\u003eProcess supervision\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003esudo (passwordless)\u003c/td\u003e\u003ctd\u003eRoot access for \u003ccode\u003ehatchpod\u003c/code\u003e user\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n## Access Methods\n\nConnect from any machine — all access methods work both locally and remotely.\n\n### SSH (port 2222)\n\n```bash\nssh -p 2222 hatchpod@localhost\n```\n\nUse your `SSH_PASSWORD` to authenticate, or add your public key:\n\n```bash\nssh-copy-id -p 2222 hatchpod@localhost\n```\n\n### Web Terminal (port 7681)\n\nOpen `http://localhost:7681` in your browser. Authenticate with `TTYD_USERNAME` / `TTYD_PASSWORD` from your `.env`.\n\n### Web UI + API (port 8080)\n\nMobile-friendly web interface for Claude Code. Works on phones, tablets, and desktops.\n\n```bash\n# Web UI\nopen http://localhost:8080\n\n# API docs (interactive, no auth required)\nopen http://localhost:8080/api/docs\n\n# REST API\ncurl -H \"Authorization: Bearer $API_PASSWORD\" http://localhost:8080/api/sessions\n\n# Create a session\ncurl -X POST -H \"Authorization: Bearer $API_PASSWORD\" \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"prompt\":\"What files are in the workspace?\"}' \\\n     http://localhost:8080/api/sessions\n\n# OpenAPI spec\ncurl http://localhost:8080/api/openapi.json\n```\n\nAPI endpoints: `GET /healthz`, `GET /api/openapi.json`, `GET /api/docs`, `POST /api/sessions`, `GET /api/sessions`, `GET /api/sessions/:id`, `DELETE /api/sessions/:id`, `GET /api/sessions/:id/history`, `GET /api/sessions/:id/messages`, `GET /api/browse`, `GET /api/config`, `GET /api/providers`. WebSocket streaming at `WS /api/sessions/:id/stream`.\n\n### Mosh (UDP 60000-60003)\n\nResilient connection that survives WiFi switches, VPN reconnects, and laptop sleep/wake:\n\n```bash\nmosh --ssh='ssh -p 2222' hatchpod@localhost\n```\n\n### Tailscale VPN (Optional)\n\nConnect from anywhere without exposing ports publicly. Set `TS_AUTHKEY` in your `.env`:\n\n1. Generate an auth key at [Tailscale Admin → Settings → Keys](https://login.tailscale.com/admin/settings/keys)\n2. Add to `.env`:\n   ```\n   TS_AUTHKEY=tskey-auth-xxxxx\n   ```\n3. Restart: `make down \u0026\u0026 make up`\n4. Connect via your Tailscale IP:\n   ```bash\n   ssh -p 2222 hatchpod@\u003ctailscale-ip\u003e\n   ```\n\n**Networking mode:** The container auto-detects TUN device availability at startup:\n- **Kernel TUN mode** (default with `docker-compose.yml`): Transparent routing — all apps can reach Tailscale peers without any proxy configuration. Requires `cap_add: NET_ADMIN` and `/dev/net/tun` device (both provided in `docker-compose.yml`).\n- **Userspace fallback** (no TUN device): Apps must use the SOCKS5 proxy at `localhost:1055` explicitly. A `TAILSCALE_PROXY` variable is written to `/etc/profile.d/tailscale-proxy.sh` for convenience, but is **not exported** to avoid breaking general internet connectivity.\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `SSH_PASSWORD` | SSH password for `hatchpod` user | `changeme` |\n| `TTYD_USERNAME` | Web terminal username | `hatchpod` |\n| `TTYD_PASSWORD` | Web terminal password | `changeme` |\n| `API_PASSWORD` | API server + Web UI password | `changeme` |\n| `TS_AUTHKEY` | Tailscale auth key (enables VPN) | _(disabled)_ |\n| `TS_HOSTNAME` | Tailscale node name | `hatchpod` |\n| `DOTFILES_REPO` | Git URL for dotfiles repo | _(disabled)_ |\n| `DOTFILES_BRANCH` | Branch to checkout | _(default)_ |\n\n### Authentication\n\nHatchpod uses the interactive login flow. Run `claude` inside the container and follow the login link. Credentials are stored in `~/.claude/` which is backed by the `home` Docker volume, so they persist across restarts.\n\n### MCP Servers\n\nMCP servers configured inside the container persist across restarts:\n\n```bash\nssh -p 2222 hatchpod@localhost\nclaude mcp add my-server -- npx some-mcp-server\n```\n\n### Dotfiles (Optional)\n\nSet `DOTFILES_REPO` in your `.env` to automatically clone and install dotfiles on first boot:\n\n```\nDOTFILES_REPO=https://github.com/youruser/dotfiles.git\n```\n\nOn first boot, the repo is cloned to `~/dotfiles`. If an install script (`install.sh`, `setup.sh`, or `bootstrap.sh`) is found, it runs automatically. Otherwise, if a `Makefile` is present, `make` is run.\n\n### Volumes\n\n| Volume | Container Path | Purpose |\n|--------|---------------|---------|\n| `home` | `/home/hatchpod` | Claude config, workspace, dotfiles, npm globals |\n| `docker-data` | `/var/lib/docker` | Docker images, containers, layers |\n\n## Docker-in-Docker\n\nHatchpod includes Docker Engine inside the container. With [Sysbox](https://github.com/nestybox/sysbox) installed on the host, agents can build and run Docker containers securely without `--privileged`.\n\n```bash\n# Verify DinD works\nmake docker-test\n\n# Use Docker inside the container\nmake shell\ndocker run --rm alpine echo \"Hello from nested container\"\ndocker build -t myapp .\n```\n\nThe `docker-data` volume persists pulled images and build cache across container restarts.\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────┐\n│            hatchpod container (sysbox-runc)               │\n│                                                          │\n│  ┌────────┐ ┌─────────┐ ┌───────┐ ┌──────┐ ┌──────────┐ │\n│  │  api   │ │  sshd   │ │ ttyd  │ │dockerd│ │tailscaled│ │\n│  │ :8080  │ │  :2222  │ │ :7681 │ │ DinD  │ │(opt-in)  │ │\n│  └────┬───┘ └────┬────┘ └───┬───┘ └───┬──┘ └────┬─────┘ │\n│       │          │          │         │          │       │\n│  ┌────┴──────────┴──────────┴─────────┴──────────┘       │\n│  │  Provider Abstraction Layer (NormalizedMessage)        │\n│  │  └─ ClaudeAdapter → Claude Code CLI                   │\n│  └───────────────────────────────────────────────────────┘│\n│       Node.js 20 · Python 3 · uv/uvx (MCP)              │\n│                                                          │\n│  Volumes:                                                │\n│   /home/hatchpod   → home vol                            │\n│   /var/lib/docker  → docker-data vol                     │\n└──────────────────────────────────────────────────────────┘\n```\n\nProcess supervision by [s6-overlay](https://github.com/just-containers/s6-overlay). Web terminal by [ttyd](https://github.com/tsl0922/ttyd).\n\n## Make Targets\n\n| Target | Description |\n|--------|-------------|\n| `make build` | Build the Docker image |\n| `make up` | Start the container |\n| `make down` | Stop the container |\n| `make logs` | Follow container logs |\n| `make shell` | Open a shell in the container |\n| `make ssh` | SSH into the container |\n| `make mosh` | Connect via mosh |\n| `make clean` | Stop container, remove volumes and image |\n| `make docker-test` | Run hello-world inside the container (DinD smoke test) |\n\n## Upgrading from claude-box\n\nIf you're upgrading from an earlier version named \"claude-box\", there are three breaking changes:\n\n**1. Volume name changed** (`claude-home` → `home`). Migrate your data before starting:\n\n```bash\n# Stop the old container\ndocker compose down\n\n# Create the new volume and copy data\ndocker volume create hatchpod_home\ndocker run --rm \\\n  -v claude-box_claude-home:/from \\\n  -v hatchpod_home:/to \\\n  alpine sh -c \"cp -a /from/. /to/\"\n```\n\n**2. Linux user changed** (`claude` → `hatchpod`). The migration above copies the files, but internal paths shift from `/home/claude/` to `/home/hatchpod/`. The container's init script automatically fixes ownership on boot.\n\n**3. Env var renamed** (`CLAUDE_USER_PASSWORD` → `SSH_PASSWORD`). Update your `.env` file. The old name still works temporarily but prints a deprecation warning.\n\n## Security Notes\n\n- Change all default passwords in `.env` before exposing to a network\n- The `.env` file is excluded from git via `.gitignore`\n- SSH root login is disabled\n- For remote access, use SSH tunneling or put behind a reverse proxy with TLS\n- The `hatchpod` user has passwordless sudo inside the container\n\n## Backup and Restore\n\n```bash\n# Backup\ndocker run --rm -v hatchpod_home:/data -v $(pwd):/backup alpine \\\n    tar czf /backup/home-backup.tar.gz -C /data .\n\n# Restore\ndocker run --rm -v hatchpod_home:/data -v $(pwd):/backup alpine \\\n    tar xzf /backup/home-backup.tar.gz -C /data\n```\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n\u003csub\u003eBuilt with \u003ca href=\"https://github.com/just-containers/s6-overlay\"\u003es6-overlay\u003c/a\u003e · \u003ca href=\"https://github.com/tsl0922/ttyd\"\u003ettyd\u003c/a\u003e · \u003ca href=\"https://github.com/nestybox/sysbox\"\u003eSysbox\u003c/a\u003e\u003c/sub\u003e\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandresmorales07%2Fhatchpod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandresmorales07%2Fhatchpod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandresmorales07%2Fhatchpod/lists"}