Every GitHub Release Smeldr has ever cut has been source-tag-only. gh release view v1.66.1 --json assets returns "assets":[] — the tag exists, the CHANGELOG entry exists, but nothing you could actually download and run. If you wanted to self-host, your only path was cloning the repo and building it yourself, which meant knowing which package to build (core has no main — it's a library), which flags to pass, and which environment variables it reads. None of that was written down anywhere.
What ships now
A new .github/workflows/release.yml triggers on every v* tag push, builds example/server — the same generic server this project dogfoods as process.smeldr.dev — for Linux amd64, and attaches the binary to the release that tag already produced:
- name: Build example/server (linux/amd64, static)
working-directory: example/server
env:
GOWORK: 'off'
CGO_ENABLED: '0'
GOOS: 'linux'
GOARCH: 'amd64'
run: go build -o smeldr-server-linux-amd64 .
- name: Confirm the release exists before attaching to it
run: gh release view ${{ github.ref_name }} --json tagName >/dev/null
- name: Attach binary to the release
run: gh release upload ${{ github.ref_name }} example/server/smeldr-server-linux-amd64
That middle step exists for a reason worth naming. This project's own tagging discipline pushes a tag, then creates the GitHub Release as a second, separate step. A workflow that triggers on the tag push could in principle run before that second step ever happens — and gh release upload's behaviour against a tag with no release yet isn't something we could pin down with confidence from the CLI's own --help text alone. Rather than ship on a guess about undocumented behaviour, the workflow checks first and fails loudly if the release isn't there yet. A failed CI run once in a while is a far better outcome than a mystery draft release showing up on some future tag with no one around to explain why.
Linux amd64 only, for now — the one platform this project's own instance has ever run on, and every manual build done for it this cycle used exactly that target. No goreleaser, either: three build flags and one upload command already do the job, and this project has a standing preference for not adopting a dependency to solve a problem three lines of YAML already solve. Both are easy to widen later if a real request shows up; neither was worth building speculatively.
The other half: knowing what to do with it
A binary with no instructions is barely better than no binary. New docs/SELF_HOSTING.md covers the parts that aren't obvious from reading the source: which environment variables exist and what they do (pulled directly from example/server/main.go's own doc comment, not re-derived — if the two ever disagree, the source wins), a minimal systemd unit, and — the part most self-hosting docs skip — what a restore actually requires.
That last part matters more than it sounds. Smeldr's data lives in a single SQLite file, opened in WAL mode. Next to it, you'll find a -wal and a -shm file — both are part of the same live database, not incidental cache files. A backup that copies the .db file alone and skips those two isn't a backup of anything consistent. The doc says so plainly, because the alternative is finding out the hard way during an actual restore.