Developer Manual
This guide covers the project structure and workflow for contributors and developers working on Snackbox. Before diving in, it is worth reading the Requirements and Architecture Manual to understand the design constraints and component structure.
Prerequisites
The following tools are required for local development:
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.26+ | Build and test |
golangci-lint | latest | Linting (make lint) |
hurl | latest | API integration tests (make api) |
Go
Install Go 1.26 or later from go.dev/dl. make vet runs go mod tidy and go vet ./... automatically, so no separate tidy step is needed during normal development.
golangci-lint
Install via the official installer. The linter config is embedded in the project; run make lint to execute it.
hurl
Install via hurl.dev. Only required if you want to run the API integration tests locally (make api or make test).
Project Structure
cmd/server/ # main package — wires everything together and starts the servers
internal/
bootstrap/ # runs migrations and creates the initial admin user on first start
config/ # loads configuration from environment variables / .env file
database/ # opens the SQLite connection and runs migrations
handlers/ # HTTP handlers for each resource (auth, health, pages, posts, tags, users, media)
metrics/ # Prometheus metric definitions and helpers
middleware/ # HTTP middleware (auth, CORS, logging, rate limit, max body)
models/ # plain Go structs for domain types (no DB tags)
repository/ # SQLite data access for each resource
router/ # registers all routes and chains the middleware
testutil/ # shared test helpers (in-memory DB setup, etc.)
validation/ # shared input validation helpers
api/
openapi.yaml # full OpenAPI 3 spec
configs/
.env.example # reference configuration with all variables and defaults
deployments/
docker/ # docker-compose.yml for container deployments
systemd/ # snackbox.service for systemd deployments
storage/
migrations/ # numbered SQL migration files (applied in order at startup)
test/
*.hurl # end-to-end API integration tests (run via `make api`)
Building and Testing
# list all available targets with descriptions
make help
# format source files
make fmt
# build the binary
make build # output: ./build/snackbox
# run the server locally (uses built-in smoke-test credentials)
make run
# run unit tests
make unit # go test -v ./...
# run unit tests with race detector
make test-race # go test -race ./...
# individual checks
make vet # go mod tidy && go vet ./...
make lint # golangci-lint run
# full CI equivalent (vet + lint + unit + api)
make test
# API integration tests only (builds binary, starts server, runs hurl, tears down)
make api
All environment variables used by make run and make api are set automatically by the Makefile. For a persistent local setup, copy configs/.env.example to .env and adjust values as needed.
Exploring the API
The full API is documented in api/openapi.yaml. You can also browse it interactively via godoc:
go install golang.org/x/tools/cmd/godoc@latest
godoc -http=:6060
Open http://localhost:6060 in your browser to navigate package documentation with live cross-references and type information.
Adding a New Resource
The pattern is consistent across all resources:
- Model — add a struct to
internal/models/ - Migration — add a numbered
.sqlfile tostorage/migrations/ - Repository — implement CRUD in
internal/repository/, with a corresponding_test.go - Handler — implement HTTP handlers in
internal/handlers/, with a corresponding_test.go - Router — register the new routes in
internal/router/router.go - OpenAPI — document the new endpoints in
api/openapi.yaml - API test — add coverage in the appropriate
test/*.hurlfile
Development Workflow
- Open an issue describing the feature or fix, or comment on an existing one to claim it
- Fork the repository or create a branch
- Implement your changes
- Run
make fmtandmake test-racebefore opening a PR — CI runs both - Use conventional commits and sign them properly
- Open a merge request and check that the pipeline passes
For any questions, ping via the issue.
Documentation
Snackbox documentation is written in Markdown and lives in the docs/ directory. It is published as a static site via MkDocs Material on GitLab Pages whenever a new release tag is pushed.
Structure
| File | Purpose |
|---|---|
docs/README.md | Home page of the docs site |
docs/REQUIREMENTS.md | Product requirements (functional, quality, constraints) |
docs/ARCHITECTURE.md | Component structure and deployment topology |
docs/12FACTOR.md | 12-factor compliance evaluation |
docs/ADMINISTRATOR.md | Installation and operations guide |
docs/USER.md | REST API usage guide |
docs/DEVELOPER.md | This file |
docs/TAGS.md | Tag index (auto-populated by the tags plugin) |
docs/TODO.md | Known issues and planned improvements |
docs/assets/ | Images and diagrams referenced by docs pages |
mkdocs.yml | MkDocs site configuration |
Writing Docs
Each Markdown file can optionally include a YAML frontmatter block with tags:
---
tags:
- "development"
- "administration"
---
Tags are picked up automatically by the MkDocs tags plugin and rendered on the Tags page — no manual maintenance required.
Testing Locally
Use the same Docker image as CI to preview the site with live reload:
docker run --rm -it \
-p 8000:8000 \
-v "$PWD":/docs \
squidfunk/mkdocs-material serve --dev-addr 0.0.0.0:8000
Open http://localhost:8000 in your browser. The page reloads automatically on every file save.
To run the same strict build that CI executes (fails on warnings):
docker run --rm \
-v "$PWD":/docs \
squidfunk/mkdocs-material build --strict --site-dir public
A non-zero exit code means CI will also fail — verify this locally before pushing.