A background agent platform for building software. Sign in with GitHub, chat with AI coding agents (Claude Code, OpenCode, Codex) via the Agent Client Protocol (ACP) in sandboxed environments. The agent writes code, runs tests, and deploys while you focus on other things.
Core value: The agent works autonomously in the background on real coding tasks — you come back to working code, not just suggestions.
Inspired by Ramp's Inspect background coding agent. Built by @dylsteck.
Screen.Recording.2026-03-20.at.10.38.18.PM.mov
- Node.js 20+ and pnpm 9+
- Cloudflare account (free tier)
- GitHub account (for OAuth)
- E2B account (for sandboxes) — e2b.dev
- Anthropic API key (for Claude Code agent) or OpenAI API key (for Codex agent); OpenCode has no required key
git clone <your-repo-url>
cd ship
pnpm installWeb app (apps/web):
cd apps/web
cp .env.example .env.localEdit .env.local:
| Variable | Description |
|---|---|
GITHUB_CLIENT_ID |
From GitHub OAuth App |
GITHUB_CLIENT_SECRET |
From same OAuth App |
SESSION_SECRET |
openssl rand -hex 32 |
API_BASE_URL |
http://localhost:8787 (local) |
NEXT_PUBLIC_API_URL |
Same as API_BASE_URL |
NEXT_PUBLIC_APP_URL |
http://localhost:3000 |
API (apps/api):
cd apps/api
cp .dev.vars.example .dev.varsEdit .dev.vars:
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
console.anthropic.com — for Claude Code agent |
E2B_API_KEY |
e2b.dev/dashboard → Settings → API Keys |
API_SECRET |
openssl rand -hex 32 (must match web app expectations) |
SESSION_SECRET |
Same as web app; for JWT verification |
ALLOWED_ORIGINS |
http://localhost:3000 |
OPENAI_API_KEY |
(optional) platform.openai.com — for Codex agent |
LOGIN_RESTRICTED_TO_SINGLE_USER |
(optional) true to restrict login to one user |
ALLOWED_USER_ID |
(optional) Your user ID from users table (required when restricted) |
Tip: For private instances, set
LOGIN_RESTRICTED_TO_SINGLE_USER=trueandALLOWED_USER_IDto your user ID (fromSELECT id FROM users) so only you can sign in.
cd apps/api
npx wrangler d1 create ship-dbCopy the database_id from the output into wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "ship-db"
database_id = "your-database-id-here"Apply schema:
npx wrangler d1 execute ship-db --local --file=src/db/schema.sql- github.com/settings/developers → New OAuth App
- Homepage URL:
http://localhost:3000 - Authorization callback URL:
http://localhost:3000/api/auth/github/callback - Copy Client ID and Client Secret into
apps/web/.env.local
pnpm dev- Web: http://localhost:3000
- API: http://localhost:8787
| Command | Description |
|---|---|
pnpm dev |
Start web + API |
pnpm build |
Build all apps |
pnpm lint |
Lint |
pnpm type-check |
TypeScript check |
pnpm deploy |
Deploy preview (web + API) |
pnpm deploy:prod |
Deploy production (web + API) |
| Command | Description |
|---|---|
npx wrangler dev |
Run Worker locally |
npx wrangler d1 execute ship-db --local --file=<sql> |
Run migration |
npx wrangler d1 execute ship-db --local --command="SELECT * FROM users" |
Query DB |
npx wrangler tail ship-api-production |
Stream prod logs |
- TypeScript strict mode
- pnpm (not npm/yarn)
- Named exports preferred
- Keep components < ~300 lines, functions < ~100 lines
- Use conventional commits:
feat:,fix:,chore:, etc. - One concern per PR
- Describe changes and what you tested
- Sign in with GitHub OAuth
- Create a session linked to a GitHub repo
- Chat with the AI agent (Claude Code, OpenCode, or Codex)
- sandbox-agent runs in an E2B sandbox (custom template for fast startup), hosts the ACP agent — writes code, runs tests, creates PRs
- Watch progress via SSE (tool calls, reasoning, file changes)
- Review & deploy via MCP (Vercel, GitHub, docs)
For a detailed architecture overview, see ARCHITECTURE.md.
graph TD
A[Next.js Web] -->|SSE| B[Cloudflare Worker]
B -->|Durable Objects| C[Session State]
B -->|HTTP| D[sandbox-agent]
D -->|ACP stdio| E[AI Agent]
E -->|E2B Sandbox| F[Code Execution]
E -->|GitHub API| G[PRs]
| Layer | Tech |
|---|---|
| Monorepo | Turborepo, pnpm workspaces |
| Frontend | Next.js 16, React 19, Tailwind v4, Base UI |
| Backend | Cloudflare Workers (Hono), Durable Objects |
| Database | Cloudflare D1 (SQLite) |
| Auth | GitHub OAuth (Arctic), JWT (jose) |
| Sandboxes | E2B (custom template with sandbox-agent pre-baked) |
| Agents | sandbox-agent + ACP (Claude Code, OpenCode, Codex) |
| MCP | Grep, DeepWiki, Exa |
| Real-time | SSE, WebSockets |
ship/
├── apps/
│ ├── web/ # Next.js app
│ │ ├── app/ # Routes, dashboard, auth
│ │ ├── components/
│ │ └── lib/ # API client, SSE, DAL
│ └── api/ # Cloudflare Worker
│ ├── src/
│ │ ├── index.ts
│ │ ├── routes/ # Hono routes
│ │ ├── durable-objects/
│ │ └── lib/ # E2B, sandbox-agent, event-translator
│ ├── migrations/
│ └── wrangler.toml
├── e2b/ # Custom E2B template
│ └── Dockerfile # Extends e2bdev/desktop with sandbox-agent + agents
├── e2b.toml # E2B template config
└── packages/
└── ui/ # Shared UI (@ship/ui)
API (Cloudflare Worker) from the repo root:
pnpm deploy # default / preview Worker
pnpm deploy:prod # production Worker (see apps/api/wrangler.toml)Web (Next.js) is meant to run in Docker (see apps/web/Dockerfile), for example on Coolify. Build context must be the repository root so packages/* workspace deps resolve.
Point Coolify at this repo and set:
| Field | Value |
|---|---|
| Build Pack | Dockerfile |
| Base Directory | / |
| Dockerfile Location | /apps/web/Dockerfile |
| Ports Exposes | 3000 |
Anything else (Install/Build/Start commands, Custom Docker Options) can stay empty — the Dockerfile handles it. If you leave Build Pack on Nixpacks, the build will fail with Unsupported URL Type "workspace:" because Nixpacks runs npm i and npm doesn't speak pnpm workspaces.
cd apps/api
# First time: create prod DB, run schema, set secrets
npx wrangler d1 create ship-db-production
# Add database_id to wrangler.toml [env.production.d1_databases]
npx wrangler d1 execute ship-db-production --file=src/db/schema.sql --env production
npx wrangler secret put ANTHROPIC_API_KEY --env production
npx wrangler secret put API_SECRET --env production
npx wrangler secret put E2B_API_KEY --env production
npx wrangler secret put SESSION_SECRET --env production # Must match web app
# Optional, for Codex agent:
# npx wrangler secret put OPENAI_API_KEY --env production
# Deploy
npx wrangler deploy --env production- API Worker deployed with prod D1 + secrets
- Web app deployed (Docker/Coolify) with env vars from
apps/web/.env.example -
ALLOWED_ORIGINSon the API includes your web app URL - Production GitHub OAuth App (callback = prod web URL)
- Test: sign in, create session, chat with agent
MIT