The build
Everything builds with Bazel 9.2 (bzlmod only), which brings its own Go, Node, pnpm, buf and Hugo:
| Ruleset | For |
|---|---|
| rules_go, Gazelle | The Go code; dependencies from go.mod; BUILD files written by bazel run //:gazelle |
| protobuf, rules_buf | The protos; buf lint and breaking-change checks |
| aspect_rules_js, rules_nodejs | The desktop page: Node 22, npm packages from its pnpm lockfile, Vite, vitest, tsc |
| rules_pkg | Release archives and the Linux packages |
| apple_support | Xcode toolchains, for the tray’s cgo on macOS |
| rules_hugo | This site |
Plain go build doesn’t work: the generated proto package exists only in the build. Editors reach Bazel’s view through GOPACKAGESDRIVER=tools/gopackagesdriver.sh.
| Directory | |
|---|---|
apps/, pkg/, proto/ |
The code |
build/ |
How things are built: code generators, nogo and its analyzers, macOS signing, version stamping |
tools/ |
Runnable checks and upkeep: dependency rules, formatting, license headers, third-party notices, specs, tidy |
third_party/ |
Patches to dependencies |
release/ |
The CLI release archives |
docs/ |
This site |
- Go protos: one package with the messages and Connect’s handlers, generated by
protoc-gen-goandprotoc-gen-connect-go. - TypeScript:
protoc-gen-eswrites the desktop page’s client (//apps/desktop/web:api_ts). - The page: Vite builds it; the Electron app carries the result, and the tray embeds it with
//go:embed. - License texts:
pkg/legalembeds the repository’sLICENSE,NOTICEandTHIRD_PARTY_NOTICES, copied in at build time, so every program can show them.
Nothing generated is committed or written into the source tree.
nogo runs in every compile of the repository’s own Go code: Go vet’s analyzers, staticcheck’s default checks, and doccomment, Blitz’s own analyzer, which requires a doc comment on every package and exported declaration. A finding fails the build. The page has tsc and a doc-comment lint of its own, and buf checks the protos’ comments.
The CLI and the service build with CGO_ENABLED=0: static binaries with no runtime dependencies, cross-compiled from either macOS or Linux to every platform. Only the tray uses cgo, on macOS (its Dock icon); the desktop app is Electron, from its prebuilt archives (spec_electron_038), with the same pure-Go programs inside.
The release archives come out byte for byte the same whether built on macOS or Linux. CI builds them on both and compares the checksums. What makes that work:
- Output directories are named after the target platform, not the host, since generated files’ paths end up in the binaries.
- rules_go links with a fixed build ID, and
--config=releasestamps only the version, fromgit describe. - Archives have fixed owners and modification times.
Tagging v* runs the release workflow: the archives, an SPDX SBOM for each, a checksums.txt signed keylessly with cosign through GitHub’s OIDC, and the desktop packages (a notarized macOS disk image for each Mac, a Windows zip, and the Linux packages), each with its own signature. Getting started shows how to verify them.
.github/workflows/ci.yml runs on macOS and Linux:
- Every test, Go with the race detector; the Linux job installs bubblewrap and a pinned gVisor, and fails if the sandbox or gVisor tests skip.
- Formatting (gofmt, Gazelle’s BUILD files), license headers, the dependency rules, third-party notices, and the specs’ index and paths.
- govulncheck on the built binaries; buf lint, format and breaking-change checks.
- The release archives, compared across the two platforms.
.github/workflows/docs.yml builds this site with Bazel and publishes it to GitHub Pages from main.