Reusable Go API foundation for That Software Company. The generated application name is {{APP_NAME}}.
Requirements
Go 1.26.0 or newer. CI and Docker currently validate Go 1.26.7.
PostgreSQL 16 or newer for database-backed execution.
Docker and Docker Compose for the container workflow.
Local execution
PostgreSQL is enabled by default and DATABASE_URL is required in that mode. Copy .env.example to a local .env outside version control, set the database values, and export them before starting:
set -a
source .env
set +a
go run ./cmd/api
To run the API without PostgreSQL:
DATABASE_ENABLED=false go run ./cmd/api
The application validates all environment variables at startup. APP_ENV must be development, test, or production.
Docker Compose
Set the local-only POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB variables in your shell or a local .env file, then run:
docker compose up --build
Compose starts PostgreSQL with a healthcheck, runs development migrations on API startup, and stores PostgreSQL data in a named local volume. The production image is built separately:
docker build --target production -t example-api:local .
The production container runs as a non-root user. Production migrations must be executed explicitly with the migrate binary or go run ./cmd/migrate; automatic startup migrations are rejected in production.
Migrations
go run ./cmd/migrate -command up
go run ./cmd/migrate -command down -steps 1
go run ./cmd/migrate -command version
The migration directory uses normal .up.sql and .down.sql files. The current foundation creates error_events.
Operational endpoints
GET /__ping checks only that the process is alive. It never queries PostgreSQL.
GET /api/v1/health checks readiness. It reports database: "disabled", "up", or "down"; the latter returns HTTP 503 without exposing failure details.
Every response includes a validated X-Correlation-ID. Error responses include the same value in the JSON error object.
Architecture
This is a modular monolith with a simple MVC flow:
routes -> controller -> service -> repository/client
internal/modules/health owns health transport and readiness rules.
internal/modules/errors prepares the future internal error use case.
internal/app/routes.go is the application-owned extension point for registering product modules.
OpenAPI files live in docs/openapi/ and are separate from controllers.
The template owns the operational composition in cmd/api, including /__ping and /api/v1/health. A generated project must not add product routes to those files or to internal/modules/health. Add product modules under internal/modules/<business-module>/ and register them from internal/app/routes.go; the template updater preserves that extension point.
Security
The foundation emits structured JSON logs through log/slog, security headers, an explicit CORS allowlist, and correlation IDs. It never logs request bodies, authorization headers, cookies, passwords, tokens, or secrets. Persisted HTTP 5xx events contain only the safe fields documented by the error_events migration.
In production, frontend and backend should be served under the same public origin. CORS credentials are enabled only for explicitly allowed origins; * is never accepted.
The internal error listing route is intentionally not registered until authentication and authorization exist.
Tests and quality checks
go mod tidy
go mod verify
test -z "$(gofmt -l .)"
go vet ./...
go test ./...
go test -race ./...
go build ./cmd/api
go build ./cmd/migrate
go build ./cmd/template
# Template lifecycle and shell checks
bash -n scripts/*.sh
./scripts/test-template-lifecycle.sh
Integration tests require PostgreSQL and use the integration build tag:
TEST_DATABASE_URL='postgres://USER:PASSWORD@localhost:5432/DB?sslmode=disable' go test -tags=integration ./...
The CI workflow runs the integration suite against PostgreSQL 16 and also performs Docker build and smoke checks. The hardening release measures critical behavior and scenarios rather than requiring an arbitrary 100% line coverage threshold.
Setup script
The Bash setup script safely configures a generated project without arbitrary overwrites:
The setup script resolves the source commit for the published template_version tag and records it in template_commit; --template-commit can provide an explicit override. It never uses the generated repository's own commit as template provenance. It records generated_from from --generated-from, defaulting to the generated module path, and preserves both values on subsequent idempotent runs.
Validate the template manifest and required files with:
./scripts/validate-template.sh
Template metadata and updates
.template/manifest.json records the source repository, template version, template commit, generated origin, compatibility, dependencies, and update policy. The update automation detects new template versions, opens PRs in derived repositories, enforces compatibility, and leaves breaking-change records and application-specific conflicts for manual review.
The generated repository also includes a scheduled and manually dispatchable template-update workflow. It looks for vMAJOR.MINOR.PATCH tags, applies a three-way patch from the recorded template_commit, normalizes the canonical Go module path to the generated repository's module path, checks Go and PostgreSQL compatibility, records new provenance, and opens a pull request. It never merges automatically. The repository owner must allow GitHub Actions to create pull requests and review generated changes manually.
If an older generated project recorded its own repository commit instead of the template commit, the workflow resolves provenance from the matching release tag and opens a small repair pull request automatically.
Required derived-repository onboarding
Complete these steps immediately after generating a repository from this template and before running the template-update workflow:
Create a dedicated fine-grained personal access token or GitHub App installation token. Scope it to the generated repository only and grant:
Contents: Read and write
Workflows: Read and write
Pull requests: Read and write
Add it to the generated repository under Settings -> Secrets and variables -> Actions as the repository secret TEMPLATE_UPDATE_TOKEN.
In Settings -> Actions -> General, allow read and write workflow permissions and allow GitHub Actions to create pull requests when the organization policy exposes that option.
Run Template update through Actions -> Template update -> Run workflow once and verify that it can create its update branch and pull request.
GitHub's built-in GITHUB_TOKEN is retained as a fallback for updates that do not modify workflow files, but it is not sufficient for the full template lifecycle. Without TEMPLATE_UPDATE_TOKEN, a workflow update can fail with refusing to allow a GitHub App to create or update workflow ... without workflows permission. Never commit the token or place it in .env files. If the organization requires approval for fine-grained tokens, the token must be approved before it can write to the generated repository.
If an update reports a conflict in .github/workflows/template-update.yml, preserve the template's latest provenance and update logic together with the TEMPLATE_UPDATE_TOKEN checkout and GH_TOKEN configuration. Run the generated repository tests before committing the manually resolved update.
The template maintainer must publish version tags such as v0.1.0 before derived repositories can detect releases. The initial release tag should point to the merged template commit.
Planned phases
The current foundation release is 0.2.6, which includes the 0.2.5 hardening work plus lifecycle module-normalization and legacy bridge fixes. The next planned releases are:
0.3.0: administrated login, Argon2id, Ed25519/EdDSA JWTs, approximately 15-minute access tokens, 30-day rotating/revocable refresh tokens, HttpOnly cookies, environment-specific Secure and SameSite policies, CSRF protection, authentication/authorization middleware, and authorized access to /api/v1/internal/errors?endpoint=<path>.
0.5.0: provider-agnostic same-origin deployment contract and trusted reverse-proxy configuration.
1.0.0: final validation from a clean testing-templatev2 repository.
Google OAuth, public registration, password recovery, and frontend implementation are not part of the current backend foundation.
After review and merge, the backend and frontend repositories must be marked as GitHub Template Repositories from Settings -> General -> Template repository. This is a post-merge checklist item, not an automated repository mutation.