# custard — operating manual Day-to-day use of a custard forge over [Soft Serve](https://github.com/charmbracelet/soft-serve). Replace `your-host` with your forge domain and `you/tap` with your tap namespace throughout. ## The pieces - **Web forge:** `https://your-host` — custard, behind Caddy (auto-TLS) on your server. - **Git server:** Soft Serve on the server. An ssh `soft` alias → `your-host:23231`; remotes are `soft:`. - **Homebrew tap:** a `homebrew-tap` repo → `brew install` straight from your host. A handy `~/.ssh/config` alias: ``` Host soft HostName your-host Port 23231 IdentityFile ~/.ssh/id_ed25519 ``` ## Everyday git ```sh # new repo ssh soft repo create thing -d "what it is" cd thing && git init && git add -A && git commit -m "init" && git branch -M main git remote add origin soft:thing && git push -u origin main # clone git clone soft:thing # ssh git clone https://your-host/git/thing.git # public, read-only # admin (run `ssh soft help` for everything) ssh soft repo list | info | description "…" | private true|false | webhook … ``` ## Privacy ```sh ssh soft repo private thing true # hide from the public forge ssh soft repo private thing false # publish ``` With `--soft-serve-db` set, only **public** repos appear on the forge; private/hidden → 404. ## Versioning (git tags) ```sh git tag -a v0.2.0 -m "release name" git push origin v0.2.0 ``` - Shows as a **version pill** in the header + index; the annotated **message is labelled on the refs page**. - Highest semver wins. Bump anytime — tags are independent of commits, no code change needed. ## Issues `backlog/tasks/*.md` ([Backlog.md](https://backlog.md)) render at `/r//issues`. Groups order in progress → paused → backlog → done. Manage with the `backlog` CLI. ## Deploy a web app — the custard CLI `.custard.yaml`: ```yaml ci: - npm test deploy: # any command that prints a deployment URL (netlify/fly/rsync work too). # Vercel needs its project pinned — see "What's in the build" below. preview: VERCEL_ORG_ID=… VERCEL_PROJECT_ID=… vercel deploy --yes promote: VERCEL_ORG_ID=… VERCEL_PROJECT_ID=… vercel promote {{url}} --yes ``` ```sh custard check # run ci on the working tree (fast loop) custard preview # build pushed commit → run ci → deploy preview → URL custard promote # promote the last preview → production ``` ### What's in the build custard deploys the **newest non-empty, pushed commit** — a clean `git archive` of it, **not** your working tree. Two consequences: - **Pushed only.** custard never runs `git push`; it deploys what's on the remote, so deployed == what's in the repo. Push first, then `preview`/`promote`. (Under **jj**, the working copy is an empty commit that normally sits on top after a push — custard skips it and deploys the described commit beneath, so you never ship an empty tree.) - **Gitignored + uncommitted files are ABSENT.** The archive contains only committed, tracked files. So: - If `ci` uses **`npm ci`**, the `package-lock.json` **must be committed** (a gitignored lockfile → `npm ci` fails in the export). `node_modules`, `.env`, and build artifacts are likewise gone — your `ci` must regenerate whatever it needs. - **Vercel:** `.vercel/project.json` (gitignored) isn't present, so a bare `vercel deploy` creates a stray new project. Pin the target with `VERCEL_ORG_ID=… VERCEL_PROJECT_ID=… vercel deploy --yes` (IDs aren't secrets; auth still comes from your local `vercel login`). custard warns on an unpinned Vercel deploy, and surfaces the real `*.vercel.app` URL even though Vercel also prints API/inspect links. > **Preview privacy:** Vercel previews are often behind **Deployment Protection** — the preview URL > redirects to an SSO login and is viewable only by your team. Production (your custom domain) is public. > Toggle it in Project → Settings → Deployment Protection. ## Release a CLI to Homebrew `.custard.yaml`: ```yaml brew: { enabled: true, package: "." } # or ./cmd/ ``` One-time per repo, register the webhook (secret = your `WEBHOOK_SECRET`): ```sh ssh soft repo webhook create https://your-host/hooks/release \ -e branch_tag_create -c json -s "$WEBHOOK_SECRET" ``` Release + install: ```sh custard release v0.2.0 # checks → tag → push → tap publishes brew tap you/tap https://your-host/git/homebrew-tap.git brew trust you/tap # newer Homebrew gates third-party taps brew install ; brew upgrade ``` ## Updating the forge ```sh cp deploy/deploy.env.example deploy/deploy.env # first time: fill REMOTE, DOMAIN, secrets… deploy/deploy.sh # build static binary, ship, restart ``` ## Automatic niceties - README/markdown images with relative paths render; gifs sit embossed; click any image for a lightbox. - Deploy badge (✓ in production / 👁 preview / ⚠ unverified) shows only for repos with a `deploy:` in `.custard.yaml`. - README is its own tab; `code` is the file tree. See [MIGRATION.md](MIGRATION.md) to move a repo off GitHub.