# Ship Skills — Reimagining Ship as Claude Skills ## The Idea Rather than a monolithic CLI that bakes in rigid assumptions, ship becomes a family of narrow, composable Claude skills. Each skill knows how to do one thing well. Claude provides the reasoning and orchestration. The server is the source of truth. Skills are completely generic — no hostnames, app names, or passwords baked in. The same skills work for anyone. Share them with a friend, point them at a different VPS, they just work. ## Shared Configuration A single static file at `~/.config/ship/config.json` holds the VPS host (and little else). All skills read from this file. No vault dependency — works for anyone. ```json { "host": "ubuntu@1.2.3.4", "domain": "example.com" } ``` The server itself is the source of truth for everything else — what services are running, what ports are allocated, what Caddy configs exist. No local state file that can go stale. ## The Skills ### `ship-setup` One-time setup. Asks for VPS host if not configured, saves to `~/.config/ship/config.json`, SSHes in and installs server dependencies (Caddy, directory structure, etc). All other skills depend on this having been run once. ### `ship-status` Derives current state entirely from the server at runtime: - Running apps → `systemctl list-units --type=service` - Ports → `/etc/ship/ports/` or env files - Domains → parse Caddy configs in `sites-enabled/` - Static sites → list `/var/www/` No state file needed. Always accurate. Replaces the need for any local tracking. ### `ship-env` Read and write env vars with merge semantics. Never overwrites — reads existing file first, merges new values on top, writes result. Old vars survive redeployments. ### `ship-caddy` Manage per-app Caddyfile config. Knows Caddy syntax. Diffs before writing. Validates before reloading. Never regenerates from scratch — only touches what needs changing. ### `ship-service` Systemd management. Handles the difference between a new service (enable + start) and an existing one (restart). Status, logs, restart, stop — all covered. ### `ship-binary` Upload and install a pre-built binary. SCP to `/tmp`, move to `/usr/local/bin/`, chmod +x, set up work directory and service user. Calls `ship-service` and `ship-env` to complete the deployment. ### `ship-static` Rsync a local dist folder to `/var/www/{name}` on the server. Calls `ship-caddy` to configure serving. ### `ship-deploy` A runbook skill that orchestrates the others in the right order for a full deployment. Not imperative code — just a checklist of steps with enough context for Claude to reason about what to do. Adapts based on what the user tells it (binary vs static, what env vars are needed, etc). ## What the Server Knows All persistent state lives on the server in conventional locations: ``` /etc/caddy/sites-enabled/{name}.caddy # per-app Caddy config /etc/ship/env/{name}.env # environment variables /etc/ship/ports/{name} # allocated port number /etc/systemd/system/{name}.service # systemd unit /var/www/{name}/ # static site files /var/lib/{name}/ # app work directory (binary, data) /usr/local/bin/{name} # binary executable ``` ## Why This Is Better Than the CLI - **Transparent** — Claude tells you what it's about to do before doing it - **Flexible** — no rigid assumptions, Claude reasons about edge cases - **Mergeable** — env files, Caddy configs never blindly overwritten - **Debuggable** — if something goes wrong, just ask Claude to fix it - **Shareable** — no app-specific knowledge baked in, works for anyone - **No stale state** — server is always the source of truth ## Per-App Notes (Optional) The server can't know things like "this app needs FOODTRACKER_PASSWORD on redeploy" or "this app has SQLite at /var/lib/foodtracker/data/". That's documentation, not state. Users can keep these as plain notes in whatever system they prefer — a vault, a README, a comment in a script. The skills don't depend on it. ## SQLite Backup Before swapping a binary, `ship-binary` checks `/var/lib/{name}/` for any `.db` files and backs them up to `/var/lib/{name}/backups/{timestamp}.db` before proceeding. Silent and automatic — you never lose data from a bad deploy. ## Multi-Host Support Config supports multiple named hosts. One is marked as default. All skills use the default unless told otherwise. ```json { "default": "prod", "hosts": { "prod": { "host": "ubuntu@1.2.3.4", "domain": "example.com" }, "staging": { "host": "ubuntu@5.6.7.8", "domain": "staging.example.com" } } } ``` Usage is natural — "deploy foodtracker to staging" and Claude picks the right host. `ship-setup` can be run multiple times to add new hosts. The default can be changed at any time. ## Out of Scope (For Now) - Health checks — skipping initially, can add later if needed