# Ship v2 **Ship is a deployment tool built for AI agents.** Agents write code. Ship puts it on the internet. That's it. ## The Problem AI agents can write code, but deploying it is a mess: - Vercel/Railway/Fly require accounts, API tokens, platform-specific config - Docker/K8s are overkill for "make this code accessible via URL" - Most tools assume a human is reading the output Agents need: code → URL. Nothing else. ## Ship's Approach ```bash ship ./myproject # {"status":"ok","url":"https://abc123.example.com","took_ms":4200} ``` **That's the entire interface.** Point ship at code, get a URL back as JSON. ### Core Principles 1. **JSON in, JSON out** — No human-formatted output. No spinners. No emoji. Agents parse JSON. 2. **One command** — No workflows. No "init then configure then deploy." One command does everything. 3. **Verify or fail** — Every deploy is health-checked. If the app isn't responding, ship returns an error. No silent failures. 4. **Self-cleaning** — Deploys can auto-expire. Agents create lots of previews; they shouldn't pile up forever. 5. **Zero config** — Point at a directory. Ship figures out if it's static, Docker, or a binary. No config files required. 6. **SSH-only** — No accounts. No API tokens. No vendor lock-in. Just SSH access to a VPS. ## Interface ### Deploy ```bash ship ./myproject ship ./myproject --name myapp ship ./myproject --name preview --ttl 24h ship ./site --static ship ./api --health /healthz ``` Output: ```json { "status": "ok", "name": "myapp", "url": "https://myapp.example.com", "type": "docker", "took_ms": 4200, "health": {"status": 200, "latency_ms": 45} } ``` ### Error ```json { "status": "error", "code": "HEALTH_CHECK_FAILED", "message": "GET /healthz returned 503 after 30s", "name": "myapp", "url": "https://myapp.example.com" } ``` ### List ```bash ship list ``` ```json { "status": "ok", "deploys": [ {"name": "api", "url": "https://api.example.com", "type": "docker", "running": true}, {"name": "preview-x7k", "url": "https://preview-x7k.example.com", "type": "static", "expires": "2024-02-16T18:00:00Z"} ] } ``` ### Status / Logs / Remove ```bash ship status myapp ship logs myapp ship logs myapp --lines 100 ship remove myapp ``` All return JSON with `{"status": "ok", ...}` or `{"status": "error", "code": "...", ...}`. ## Error Codes Machine-readable. No guessing. | Code | Meaning | |------|---------| | `SSH_FAILED` | Can't connect to VPS | | `UPLOAD_FAILED` | File transfer failed | | `BUILD_FAILED` | Docker build or compile failed | | `DEPLOY_FAILED` | systemd/Caddy setup failed | | `HEALTH_CHECK_FAILED` | App not responding | | `NOT_FOUND` | App doesn't exist | | `CONFLICT` | Name already taken | ## Auto-Detection Ship looks at the directory and figures out what to do: | Directory contains | Deploy type | |-------------------|-------------| | `Dockerfile` | Docker build → systemd service | | `index.html` | Static site → Caddy file_server | | Single executable | Binary → systemd service | | `go.mod` | Go build → systemd service | | `package.json` + no Dockerfile | Error: "Add a Dockerfile" | No config files. No `ship.json`. No `ship init`. ## TTL (Time-To-Live) Agents create previews. Previews should auto-delete. ```bash ship ./site --name pr-123 --ttl 1h ship ./site --name pr-123 --ttl 7d ``` After TTL expires, the deploy is removed automatically. The `expires` field in JSON tells you when. ## Health Checks Every deploy is verified. Ship waits for the app to respond before returning success. - Static sites: `GET /` returns 2xx - Apps: `GET /` by default, or specify `--health /healthz` - Timeout: 30s - If health check fails: `{"status": "error", "code": "HEALTH_CHECK_FAILED", ...}` ## Name Generation No name? Ship generates one. ```bash ship ./site # {"name": "ship-a1b2c3", "url": "https://ship-a1b2c3.example.com", ...} ``` Provide a name to get a stable URL: ```bash ship ./site --name docs # {"name": "docs", "url": "https://docs.example.com", ...} ``` ## Host Setup One-time setup for a VPS: ```bash ship host init user@my-vps.com --domain example.com ``` ```json { "status": "ok", "host": "my-vps.com", "domain": "example.com", "installed": ["caddy", "docker", "systemd"] } ``` After this, the host is ready. Ship remembers it. ## Human Output Humans are an afterthought, but they can use ship too: ```bash ship ./site --pretty ``` ``` ✓ Deployed to https://ship-a1b2c3.example.com (4.2s) ``` Or set globally: ```bash export SHIP_PRETTY=1 ``` ## Implementation Phases ### Phase 1: JSON Everything - [ ] JSON output on all commands - [ ] Structured error codes - [ ] Exit codes match error states ### Phase 2: Smart Deploys - [ ] Auto-detect project type - [ ] Health checks on every deploy - [ ] `--ttl` with server-side cleanup ### Phase 3: Zero Friction - [ ] `ship ./dir` with no flags (auto name, auto detect) - [ ] `ship host init` fully automated - [ ] One binary, zero dependencies on client ## Non-Goals - **Pretty output** — That's what `--pretty` is for - **Interactive prompts** — Never. Agents can't answer prompts. - **Config files** — Zero config. Detect everything. - **Plugin system** — Keep it simple. - **Multi-cloud orchestration** — One VPS at a time. ## Success Criteria Ship is done when an agent can: 1. Build code 2. Run `ship ./code` 3. Parse the JSON response 4. Use the URL No docs. No setup. No tokens. No accounts. Just `ship ./code`. --- *Built for agents. Tolerated by humans.*