VayuPress Development Guide
Goal
A new developer should be able to run VayuPress locally in under ten minutes and make their first change.
Prerequisites
- Go — the version in
go.mod'stoolchainline (go1.27.2 today). WithGOTOOLCHAIN=auto, an older Go downloads it for you. - A C compiler (
build-essential): SQLite is linked through CGO. - Chromium and Node, only for the browser tests (
tests/e2e).
Run it
git clone https://github.com/johalputt/vayupress.git
cd vayupress
export API_KEY=dev-secret-key # required; the server exits without it
export DOMAIN=localhost
export PORT=8080
export DB_PATH=/tmp/vp/vayupress.db
export CACHE_DIR=/tmp/vp/cache
export MEDIA_DIR=/tmp/vp/media
export TMP_DIR=/tmp/vp/tmp
export STATIC_DIR="$PWD/static" # the binary writes its assets here at boot
export VAYU_DOCS_DIR="$PWD/docs"
mkdir -p /tmp/vp/cache /tmp/vp/media /tmp/vp/tmp
make dev # or: go run ./cmd/vayupress
The site is at http://localhost:8080 and the console at
http://localhost:8080/os. A fresh database creates admin@localhost with a
random password, written to a file beside the database and logged once; the
console asks for a new one at first sign-in.
Seed a post through the API:
curl -X POST http://localhost:8080/api/v1/articles -H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Hello","slug":"hello","content":"<p>First post.</p>","tags":["welcome"]}'
VayuShield challenges a client that does not look like a browser; test pages with a browser, or with the API key.
The source
One Go module: the server is cmd/vayupress (its handlers, the console's pages
in admin_os_*.go, routes in routes.go), and everything else is under
internal/, one package per concern — the map is in
ARCHITECTURE.md. The tools under tools/ (importers,
vayu-compat, vayu-backup) are separate modules.
Static assets (static/) and the docs are compiled into the binary and written
to STATIC_DIR at boot, so a release replaces them with the binary.
Checks to run before you commit
gofmt -l . # must print nothing
go vet ./...
go build ./...
go test ./... # unit tests
go test -race ./...
go test -tags integration ./... # integration
staticcheck ./...
golangci-lint run # zero issues
gosec -severity high -confidence high ./...
govulncheck ./...
make check-size # binary under 45 MB gzipped
Browser tests, against a running install:
cd tests/e2e && npm ci && npx playwright test
QUALITY-GATES.md lists every gate CI runs and what each does not prove.
Making a change
- Branch:
git checkout -b feat/my-change. - Make the change, with a test that fails without it — run the test once against the code with the change undone and watch it fail at its own assertion.
- Run the checks above.
- Add a line under
## [Unreleased]inCHANGELOG.md, in the reader's words. - Update the docs the change makes untrue.
- Sign off:
git commit -s(Developer Certificate of Origin). - Open a pull request.
Key constraints:
- SQLite only: every feature works with the one database — no external service.
- No framework, no build step: server-rendered Go; htmx and Alpine's CSP build where a page needs behaviour.
- No new dependency without a reason recorded in an ADR.
- No breaking API change without a major version (compatibility/api-contracts.md).
Debugging
curl http://localhost:8080/health/dependencies
curl -H "X-API-Key: $API_KEY" http://localhost:8080/api/v1/queue
curl http://localhost:8080/metrics
curl -H "X-API-Key: $API_KEY" "http://localhost:8080/debug/pprof/goroutine?debug=1"
Detail health, /metrics and pprof answer requests from the machine itself or
carrying the API key.
Common Issues
See TROUBLESHOOTING.md.