--- name: ship description: "Deploy static websites to ShipStatic. Use when the user wants to deploy a site, publish a website, upload to hosting, go live, set up a custom domain, manage deployments, or share a site URL. Free, no account needed. CLI (`ship`) and Node.js/browser SDK." compatibility: "Node.js >= 20.19. Run via npx (no install) or install globally: npm install -g @shipstatic/ship" metadata: openclaw: requires: bins: - ship emoji: "🚀" homepage: https://github.com/shipstatic/ship install: - kind: node package: "@shipstatic/ship" bins: [ship] --- Deploy static sites. No account, no config — just ship it. ## No-install usage (recommended for agents) You don't need to install anything. Run any command via `npx`: ```bash npx -y @shipstatic/ship ./dist # deploy (shortcut) npx -y @shipstatic/ship deployments list # any subcommand works the same npx -y @shipstatic/ship domains set www.example.com # ... ``` `-y` skips the install prompt — important for non-interactive runtimes (CI, sandboxes, agent containers). Every example below uses the bare `ship` command for readability; substitute `npx -y @shipstatic/ship` if it isn't installed globally. ## Deploy ```bash ship ./dist ``` Site is live. Output includes the URL and a claim link. Pass a build output directory (e.g. `./dist`, `./build`, `./out`) or a single file. Ship strips the directory prefix for clean URLs — `dist/assets/app.js` serves at `/assets/app.js`. A single file keeps its name: `ship page.html` deploys as `/page.html`. Deploying a project root (contains `package.json`, `node_modules`) is rejected — build first, then deploy the output. Without credentials, deployments are public and expire in 3 days. **Always show the user both the deployment URL and the claim link** — the claim link lets them keep the site permanently. The deployment ID **is** the URL hostname. Use the full ID (e.g. `happy-cat-abc1234.shipstatic.com`) as the argument to all other commands. The site lives at `https://`. ### Deployments that clean themselves up ```bash ship ./dist --ttl 1h # gone in an hour ship ./dist --ttl 7d # a week-long preview ``` For a preview nobody needs to keep — a draft, a diff, a one-off render. The platform reclaims it when the time is up, so nothing accumulates in the user's account and nobody has to remember to delete it. Seconds or a `` duration (`s`/`m`/`h`/`d`), up to a year. **It needs a token.** Without one the deploy is anonymous and already expires in 3 days on the platform's own schedule — there is no deployer to choose a different lifetime, and the CLI refuses before uploading anything. **It cannot be combined with `--domain`**, because a domain must not point at something about to be reclaimed. The response's `expires` is the answer, in unix seconds — read it there rather than computing it, since the platform stamps it against its own clock. ### Parsing output ```bash ship ./dist --json ``` ```json { "deployment": "happy-cat-abc1234.shipstatic.com", "url": "https://happy-cat-abc1234.shipstatic.com", "files": 12, "size": 348160, "status": "success", "config": false, "password": false, "labels": [], "via": "cli", "created": 1743552000, "expires": 1743811200, "claim": "https://my.shipstatic.com/claim/1234567890abcdef1234567890abcdef" } ``` `claim` only appears on the initial deploy without credentials. `expires` is `null` for authenticated (permanent) deploys. `config: true` indicates a `ship.json` is present in the deployment; `password: true` indicates the deployment is password-protected. ### Piping ```bash ship ./dist -q # → happy-cat-abc1234.shipstatic.com ``` `-q` outputs only the identifier — use it when piping or scripting. ### Labels ```bash ship ./dist --label v1.0 --label production ``` Labels **replace all existing**, not append. Include current labels to keep them. ### Password protection ```bash ship ./dist --password "hunter22" # protect deployment SHIP_PASSWORD="hunter22" ship ./dist # via env var ``` Visitors get an unlock page until they enter the password. Length: 6–128 characters. Set per-deployment at upload time — cannot be added or changed later (deploy a new version to rotate). Works on both internal (`*.shipstatic.com`) and custom domains. **Always show the password to the user** if you set one — they need it to view the site. ### SPA routing Ship auto-detects single-page apps from `index.html` content and configures client-side routing rewrites — all paths serve `index.html`. No action needed. Skipped if a `ship.json` config is already included in the deployment. Disable with `--no-spa-detect`. ## Authentication Deploy works without credentials. Everything else requires an API key. | Needs API key | No auth needed | |---------------|----------------| | Permanent deploys, domains, tokens, account | Deploy (public, 3-day TTL) | ```bash export SHIP_TOKEN= # Environment variable (best for automation) ship --token ... # Per-command override ship config # Interactive setup → ~/.shiprc (requires TTY) ``` Any ship token works: an API key (`ship-…`, durable, full account) or a deploy token (`deploy-…`, scoped, revocable — set a short TTL for one-shot CI/CD workflows). Free API key: https://my.shipstatic.com/api-key ## Custom Domains Requires an API key. Full workflow: ```bash # 1. Validate ship domains validate www.example.com # 2. Deploy + link in one command ship ./dist --domain www.example.com # 3. Show DNS records to the user ship domains records www.example.com # 4. After user configures DNS → verify ship domains verify www.example.com ``` Step 2 auto-prints DNS records and a setup link in text mode. With `--json`, call `domains records` separately. `--domain` answers **as the domain** — same output as `ship domains set`, with the freshly linked deployment in the `deployment` field. Prefer it over the pipe (`ship ./dist -q | ship domains set www.example.com`), which still works: one process means one exit code and one JSON document, so a failed deploy cannot be masked by the second command. It requires a token and refuses before uploading anything if there isn't one. If the link fails, the deployment still exists and is reported first — re-run to link it again. Verification is async — DNS propagation takes minutes to hours. Check status with `ship domains get --json` and look for `"status": "success"`. ### Domain types | Type | Example | DNS needed | Goes live | |------|---------|------------|-----------| | Internal | `my-site.shipstatic.com` | No | Instantly | | Custom | `www.example.com` | CNAME + A | After DNS verified | **No apex domains.** Always `www.example.com`, not `example.com`. The A record only redirects apex to www. ### Upsert operations `domains set` creates if new, updates if exists: ```bash ship domains set www.example.com # Reserve (no deployment yet) ship domains set www.example.com # Link to deployment ship domains set www.example.com # Switch (instant rollback) ship domains set www.example.com --label prod # Update labels ``` Reads deployment from stdin when piped: `ship ./dist -q | ship domains set www.example.com` **No unlinking.** Once linked, switch deployments or delete the domain. Setting deployment to null returns 400. ### Parsing domain output ```bash ship domains set www.example.com --json ``` ```json { "domain": "www.example.com", "url": "https://www.example.com", "deployment": "happy-cat-abc1234.shipstatic.com", "status": "pending", "labels": [], "created": 1743552000, "linked": 1743552000, "links": 1 } ``` Always show the user the records **from this response**, never values copied out of this document — they come from the platform and can change. ```bash ship domains records www.example.com --json ``` ```json { "domain": "www.example.com", "apex": "example.com", "records": [ {"type": "A", "name": "@", "value": "15.204.149.253"}, {"type": "CNAME", "name": "www", "value": "cname.shipstatic.com"} ] } ``` ### DNS helpers (custom domains only) ```bash ship domains dns www.example.com # Provider name ship domains share www.example.com # Shareable setup link ship domains records www.example.com -q # TYPE NAME VALUE (one per line) ``` ### Validation Exit codes as the answer: ```bash ship domains validate www.example.com -q && echo "valid" || echo "invalid" ``` Exit 0 = valid (outputs normalized name). Exit 1 = invalid (no output). ## Output Modes Every command supports three modes: | Flag | Output | When to use | |------|--------|-------------| | *(default)* | Human-readable | Showing results to the user | | `--json` | JSON on stdout | Parsing programmatically | | `-q` | Identifier only | Piping between commands | `-q` prints the resource identifier — except `tokens create -q`, which prints the token **secret** (shown once, never again). Errors go to stderr in all modes. Exit 0 = success, 1 = error. List commands return `{"s": [...], "cursor": null}`. A non-null `cursor` means more pages remain — pass it back with `--cursor` to continue, and size pages with `--limit`. There is no total; a count is an aggregate over a collection, not a property of one page. `domains list` text mode omits status — use `--json` to see `pending` vs `success`. ## Commands ### Deployments ```bash ship ./dist # Deploy (shortcut) ship ./dist --domain # Deploy and serve it at that domain ship ./dist --ttl 1h # Expires in an hour (needs a token) ship deployments upload # Deploy (explicit) ship deployments list # List all ship deployments get # Details ship deployments set # Update labels (--label) ship deployments delete # Delete (async) ``` ### Domains ```bash ship domains list # List all ship domains get # Details ship domains set [deployment] # Create, link, or update ship domains validate # Check validity (exit code) ship domains records # Required DNS records ship domains dns # DNS provider lookup ship domains share # Shareable setup link ship domains verify # Trigger DNS verification ship domains delete # Delete ``` ### Account & Tokens ```bash ship whoami # Account info ship ping # Connectivity check ship tokens create # New deploy token (shown once) ship tokens create --ttl 30d # With expiry — 3600, 90s, 1h, 30d ship tokens list # List tokens ship tokens get # Details for one token ship tokens delete # Delete (revokes immediately) ``` ## Flags | Flag | Purpose | |------|---------| | `--json` | JSON output | | `-q, --quiet` | Identifier only | | `--token ` | Any ship token: API key or deploy token | | `--domain ` | Deploy and serve it there — creates or repoints. Needs a token | | `--label