Building PatchMon locally
The three build paths (server, agent, Docker image), what each one produces, and how to run the stack for development.
PatchMon is a Go server with an embedded React frontend, plus a separate Go agent. This page explains what the build paths are and what each one produces, so you know which one you want before you start.
CONTRIBUTING.md in the repository is the authority on the exact commands and the full target list. It lives alongside the code, so it moves whenever the build does. This page is the map, not the reference.
What you need
- Go (the version is pinned in each
go.mod, so let your toolchain read it rather than installing a specific release by hand) - Node.js (the version is pinned in the root
package.json) - Docker, only if you want to build the container image
- A clone with tags and history. Not a shallow clone. The version comes from the nearest git tag, and working it out means walking back through history to find one, so
git clone --depth 1will not do. If you have a shallow clone, rungit fetch --unshallow --tags.
The three build paths
Server
cd server-source-code
make build # build the binary
make run # build and run
make check # fmt, vet, golangci-lint, tests. Run before you pushThe server serves the REST API and also serves the React frontend, embedded into the binary with Go's embed. That means a plain make build embeds whatever is currently in the frontend build output directory, not a freshly built frontend. For day to day frontend work you want the dev server instead, see below.
Agent
cd agent-source-code
make build # native binary for your machine
make build-all # the platforms that ship in a release
make check # fmt, vet, golangci-lint, testsmake build-all is Linux (amd64, 386, arm64, arm), FreeBSD (amd64, 386, arm64, arm) and Windows (amd64, arm64), copied into the agents directories. Those are the platforms attached to a release.
macOS is not in that set and is not published in releases. make build gives you a native macOS binary if you are working on the agent from a Mac, which is the only way it is built.
Docker image
./docker/build.shUse this rather than calling docker build yourself. It builds the agent binaries the image bundles, builds the frontend, and passes the version in as a build argument. That last part matters more than it sounds: see How versioning works.
Useful flags:
Flag | Effect |
|---|---|
| Image tag, default |
| Report a specific version instead of deriving one |
| Reuse the agent binaries already staged, for a faster rebuild |
Working on the frontend
The frontend has its own dev server with hot reload, which is what you want for UI work rather than rebuilding the Go binary each time.
cd frontend
npm run devIt listens on port 3000 and proxies /api through to the server on port 3001. Note that 3000 is the frontend, not the server, which catches people out.
Before you push:
npx biome check --write src/
npx biome check src/The second command must come back clean. A pre-commit hook runs Biome and will block the commit otherwise.
Running the tests
cd server-source-code && make check # Go server
cd agent-source-code && make check # Go agent
cd frontend && npm run test:runmake check is the gate for Go work. It runs formatting checks, go vet, golangci-lint and the tests together, and it is what CI runs. If it passes locally it should pass in CI.
One difference to know about: CI runs the Go tests with -race. A data race will fail the build there even if the tests pass for you. If you are touching anything concurrent, run go test -race ./... before you push.
Where to go next
- How versioning works, which is the least obvious part of the build and worth reading before you wonder why your dev instance reports the version it does
- What happens when you open a pull request
- How a release happens
- How work gets planned and shipped
- Contributing code for the pull request process, the rule for feature PRs, and the AI disclosure requirement
Was this helpful?
Your feedback shapes what we write next.
