No description
- TypeScript 84.6%
- CSS 7.5%
- Shell 5.8%
- Dockerfile 1.4%
- HTML 0.4%
- Other 0.3%
| .forgejo/workflows | ||
| nexus | ||
| worker | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| DEPLOYMENT.md | ||
| docker-compose.yml | ||
| README.md | ||
Agent Nexus
Self-hosted webapp for spawning and managing dockerized Claude Code agents.
Connects to one or more git providers (Forgejo today, GitHub planned), lists your repositories, and spawns Nexus Worker containers — each running Claude Code under tmux with cloud Remote Control so you can attach from the Claude mobile/web app or docker exec from the host.
Quick start
# 1. Build images
docker build -t nexus-worker:latest ./worker
docker build -t agent-nexus:latest ./nexus
# 2. One-time: bootstrap the shared Claude session.
# Remote Control refuses inference-only setup-tokens, so workers share the
# full-scope OAuth state produced by `claude auth login`. The bootstrap
# script runs it interactively and saves the result to a docker volume.
docker run --rm -it -v claude-session:/session nexus-worker:latest \
/bootstrap-claude-auth.sh
# 3. Start Nexus + the docker-socket proxy
docker compose up -d
Then open http://localhost:3001/ and:
- Set a master passphrase (first run only — it encrypts provider tokens at rest)
- Unlock with that passphrase
- If the bootstrap step ran, the UI advances past the Bootstrap Claude session screen automatically. If not, you'll see instructions to run step 2 above.
- Add a Forgejo connection (base URL + PAT with
read:repository read:user read:organization) - Pick a repo, name a session, hit Launch
- The session shows up at claude.ai/code and in the Claude Android/iOS app a few seconds later
Architecture
┌────────────────────────────────┐
│ browser / Claude mobile app │
└──────────────┬─────────────────┘
│ HTTPS (nginx, optional)
┌──────────────▼─────────────────┐ ┌─────────────────────────────┐
│ agent-nexus (Fastify + SQLite)│ via TCP │ docker-socket-proxy │
│ - master-passphrase vault │────────►│ endpoint allowlist: │
│ - provider abstraction │ to:2375 │ containers, exec, volumes, │
│ - spawns workers │ │ images. Everything else 403.│
└──────────────┬─────────────────┘ └────────────┬─────────────────┘
│ creates / inspects │ mounts /var/run/docker.sock:ro
│ ▼
│ ┌─────────────────────────────┐
│ │ host Docker engine │
▼ └────────────┬─────────────────┘
┌───────────────────────────────────┐ │ spawns
│ nexus-worker (one per session) │◄─────────────────┘
│ - tmux PID 1, session "claude" │
│ - claude --remote-control NAME │ ┌─────────────────────────┐
│ --worktree NAME │──────►│ Anthropic cloud relay │
│ - /session ← claude-session vol │ └────────────┬────────────┘
│ - /workspace ← per-worker volume │ │
└───────────────────────────────────┘ │
▼
your Claude mobile / claude.ai/code
- One worker container = one Claude session = one git worktree. Spawn multiple workers on the same repo for collaborating agents; they share the same
/workspaceclone but each runs--worktree <name>so changes land on isolated branches. - Shared OAuth state: the
claude-sessiondocker volume holdscredentials.json+account.jsonproduced byclaude auth login. Workers mount it read-write so refresh-token rotations persist across the fleet. - Docker socket is fronted by a proxy (
tecnativa/docker-socket-proxy) on a default-deny internal network. Even if Nexus is compromised, the attacker cannot reachdocker run --privilegedor any non-allowlisted endpoint. - Per-spawn secrets (Forgejo token, authed clone URL) are injected via tmpfs after the worker starts. They do NOT appear in
docker inspector/proc/1/environ. - Worker discovery: containers carry
nexus-managed=true. The UI never lists unrelated host containers.
Repo layout
agent-nexus/
├── nexus/ # the webapp (Fastify + Vite + Kysely)
├── worker/ # the Claude runtime image
├── docker-compose.yml # nexus + docker-socket-proxy
├── DEPLOYMENT.md # threat model, nginx, backups
└── .forgejo/workflows/ci.yml
See DEPLOYMENT.md for production deployment, nginx reverse-proxy config, the threat model after the socket-proxy hardening, and backup snippets.