Architecture Manual
This document describes the architecture of Snackbox: its component structure and deployment topology. The product requirements that drive these decisions are defined in REQUIREMENTS.md.
Snackbox aims to be fully compliant with the 12-factor app methodology. See 12FACTOR.md for the current compliance evaluation.
Components
Component View
graph TD
Client([Client])
subgraph server["server · :8080 (Go net/http)"]
MW["Middleware\nCORS → Logging → Max Body"]
Router["Router\n(Auth · Rate Limit applied per route)"]
AuthH["Auth Handler"]
HealthH["Health Handler"]
ResourceH["Resource Handlers"]
Repos["Repository\nTagRepo · PageRepo · PostRepo · UserRepo · MediaRepo"]
end
subgraph metricsMux["metricsMux · :9091 (Go net/http)"]
MetricsH["Metrics Handler\n(Prometheus registry)"]
end
subgraph storage["Storage"]
SQLite[("SQLite (file)")]
Disk[("Storage (disk)")]
end
Client --> MW
MW --> Router
Router --> AuthH
Router --> HealthH
Router --> ResourceH
HealthH --> SQLite
ResourceH --> Repos
Repos --> SQLite
ResourceH --> Disk
The binary is structured in strict layers. Each layer only calls the layer directly below it. This enforces the requirement that business logic must not appear in the router or middleware:
| Layer | Packages | Responsibility |
|---|---|---|
| Entry point | cmd/server | Wires all components, starts HTTP servers, handles OS signals |
| Middleware | internal/middleware | CORS, auth (JWT), logging, rate-limit, max-body |
| Router | internal/router | Registers all routes, applies middleware chain |
| Handlers | internal/handlers | Parses requests, calls repositories, writes responses |
| Repositories | internal/repository | SQL queries against SQLite |
| Storage | SQLite file + filesystem | Persistent state |
A second HTTP server runs on METRICS_ADDR (default :9091) and exposes only GET /metrics. It is wired directly in cmd/server/main.go and shares no middleware with the main API. This satisfies the requirement that the metrics endpoint must not be exposed on the public API port.
Bootstrap
On every startup internal/bootstrap runs pending SQL migrations from MIGRATIONS_PATH and, if no admin user exists, creates one from the ADMIN_NAME / ADMIN_EMAIL / ADMIN_PASSWORD environment variables. This satisfies the requirement that migrations are applied automatically on startup.
Configuration
All runtime configuration is loaded by internal/config from environment variables, with an optional .env file as a fallback. See the Administrator Manual for the full reference.
Deployment
Deployment View
flowchart TD
Client([Client])
Prometheus([Prometheus])
RP["Reverse Proxy\nnginx · Caddy · …"]
subgraph host["Container / Host"]
API["Snackbox · :8080 · API\n/usr/local/bin/snackbox"]
Metrics["Snackbox · :9091 · Metrics\n/usr/local/bin/snackbox"]
DB[("Database\n$DATABASE_PATH")]
Media[("Media Storage\n$STORAGE_PATH")]
end
Client -->|"80/TCP"| RP
RP -->|"8080/TCP"| API
Prometheus -->|"9091/TCP"| Metrics
API --> DB
API --> Media
Snackbox is deployed as a single process behind a reverse proxy. The reverse proxy (nginx, Caddy, or similar) handles TLS termination and forwards requests to the API port. The metrics port is kept on loopback or an internal monitoring network. See Deployment Constraints for platform and resource requirements.
| Port | Default | Exposure |
|---|---|---|
LISTEN_ADDR | :8080 | Via reverse proxy (public) |
METRICS_ADDR | :9091 | Internal / loopback only |
Persistent state lives in two locations:
| Path | Content |
|---|---|
DATABASE_PATH | SQLite database file |
STORAGE_PATH | Uploaded media files |
Both paths should reside on the same volume so that a single backup covers all state. See the Administrator Manual for deployment guides (systemd and Docker).
Tools and Frameworks
| Tool / Library | Purpose |
|---|---|
| Go 1.26 | Primary language |
net/http | HTTP server (standard library, no framework) |
mattn/go-sqlite3 | SQLite driver (CGO) |
golang-jwt/jwt | JWT signing and validation |
prometheus/client_golang | Prometheus metrics |
golang.org/x/time/rate | Token-bucket rate limiter |
golangci-lint | Static analysis |
hurl | End-to-end API integration tests |
Dependencies and Licensing
Snackbox is released under the BSD-3-Clause License. All direct and transitive Go module dependencies use MIT, BSD-3-Clause, or Apache-2.0 — all of which are compatible with BSD-3-Clause outbound licensing and impose no copyleft or viral obligations.
Notable: mattn/go-sqlite3 bundles the SQLite C amalgamation, which is public domain and carries no license obligations.
Software Bill of Materials (SBOM)
| Package | Version | License |
|---|---|---|
github.com/golang-jwt/jwt/v5 | v5.3.1 | MIT |
github.com/mattn/go-sqlite3 | v1.14.34 | MIT |
golang.org/x/crypto | v0.48.0 | BSD-3-Clause |
golang.org/x/time | v0.15.0 | BSD-3-Clause |
golang.org/x/sys | v0.41.0 | BSD-3-Clause |
github.com/beorn7/perks | v1.0.1 | MIT |
github.com/cespare/xxhash/v2 | v2.3.0 | MIT |
github.com/munnerz/goautoneg | v0.0.0-20191010083416-a7dc8b61c822 | BSD-3-Clause |
github.com/prometheus/client_golang | v1.23.2 | Apache-2.0 |
github.com/prometheus/client_model | v0.6.2 | Apache-2.0 |
github.com/prometheus/common | v0.66.1 | Apache-2.0 |
go.yaml.in/yaml/v2 | v2.4.2 | Apache-2.0 |
google.golang.org/protobuf | v1.36.8 | BSD-3-Clause |
github.com/kr/text | v0.2.0 | MIT |
Regenerating the Report
Install go-licenses and run it from the repository root:
go install github.com/google/go-licenses@latest
$(go env GOPATH)/bin/go-licenses report ./...
This prints one line per dependency with its name, license URL, and SPDX identifier. Update the table above whenever go.mod changes.