Skip to content

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:

  1. Model — add a struct to internal/models/
  2. Migration — add a numbered .sql file to storage/migrations/
  3. Repository — implement CRUD in internal/repository/, with a corresponding _test.go
  4. Handler — implement HTTP handlers in internal/handlers/, with a corresponding _test.go
  5. Router — register the new routes in internal/router/router.go
  6. OpenAPI — document the new endpoints in api/openapi.yaml
  7. API test — add coverage in the appropriate test/*.hurl file

Development Workflow

  1. Open an issue describing the feature or fix, or comment on an existing one to claim it
  2. Fork the repository or create a branch
  3. Implement your changes
  4. Run make fmt and make test-race before opening a PR — CI runs both
  5. Use conventional commits and sign them properly
  6. 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.