custard — operating manual
Day-to-day use of a custard forge over 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
softalias →your-host:23231; remotes aresoft:<repo>. - Homebrew tap: a
homebrew-taprepo →brew installstraight from your host.
A handy ~/.ssh/config alias:
Host soft
HostName your-host
Port 23231
IdentityFile ~/.ssh/id_ed25519
Everyday git
# 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 <n> | description <n> "…" | private <n> true|false | webhook …
Privacy
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)
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) render at /r/<repo>/issues. Groups order
in progress → paused → backlog → done. Manage with the backlog CLI.
Deploy a web app — the custard CLI
.custard.yaml:
ci:
- npm test
deploy:
preview: vercel deploy # any command that prints a URL (netlify/fly/rsync work too)
promote: vercel promote {{url}} # {{url}} = the preview; default is vercel
custard check # run ci on the working tree (fast loop)
custard preview # build HEAD → run ci → deploy preview → URL
custard promote # promote the last preview → production
Deploys build the committed HEAD (uncommitted edits never ship); HEAD must be pushed. The CLI
never runs git push — you push however you like.
Release a CLI to Homebrew
.custard.yaml:
brew: { enabled: true, package: "." } # or ./cmd/<name>
One-time per repo, register the webhook (secret = your WEBHOOK_SECRET):
ssh soft repo webhook create <name> https://your-host/hooks/release \
-e branch_tag_create -c json -s "$WEBHOOK_SECRET"
Release + install:
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 <name> ; brew upgrade <name>
Updating the forge
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;
codeis the file tree.
See MIGRATION.md to move a repo off GitHub.