{"id":50699643,"url":"https://github.com/ralscha/base-go-backend","last_synced_at":"2026-06-09T08:32:52.349Z","repository":{"id":361623867,"uuid":"1180283974","full_name":"ralscha/base-go-backend","owner":"ralscha","description":"Production-ready Go backend template","archived":false,"fork":false,"pushed_at":"2026-05-31T13:51:48.000Z","size":371,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T15:22:30.935Z","etag":null,"topics":["go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ralscha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-12T22:18:27.000Z","updated_at":"2026-05-31T13:51:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ralscha/base-go-backend","commit_stats":null,"previous_names":["ralscha/base-go-backend"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ralscha/base-go-backend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ralscha%2Fbase-go-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ralscha%2Fbase-go-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ralscha%2Fbase-go-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ralscha%2Fbase-go-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ralscha","download_url":"https://codeload.github.com/ralscha/base-go-backend/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ralscha%2Fbase-go-backend/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34098932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang"],"created_at":"2026-06-09T08:32:50.825Z","updated_at":"2026-06-09T08:32:52.344Z","avatar_url":"https://github.com/ralscha.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# base - Go Backend Template\n\nA production-ready Go backend template with authentication, authorization, OAuth2, WebAuthn (passkeys), TOTP, email verification, and background job processing.\n\n## Features\n\n- **Authentication**: Password-based login with Argon2id hashing, email verification, password reset, and account recovery\n- **Two-Factor Authentication**: TOTP (Time-based One-Time Password) support\n- **Passkeys (WebAuthn)**: Passwordless authentication with discoverable credentials\n- **OAuth2/OIDC**: Pluggable OAuth providers with PKCE flow\n- **Role-Based Access Control**: Admin and user roles with pluggable permission middleware\n- **Session Management**: PostgreSQL-backed sessions with configurable lifetimes\n- **Email Outbox**: Asynchronous email delivery with retry and failure tracking\n- **Rate Limiting**: Login rate limiting with PostgreSQL-backed token buckets\n- **Background Jobs**: River-based job processing (email delivery, cleanup, inactivity checks)\n- **Database Migrations**: Goose-based schema migrations with embedded SQL files\n- **Configuration**: YAML config with environment variable overrides (via Koanf)\n- **Structured Logging**: JSON logging via `log/slog`\n- **Test Infrastructure**: Testcontainers-based PostgreSQL for integration tests\n\n## Quick Start\n\n### Prerequisites\n\n- Go 1.26+\n- Docker (for PostgreSQL, Inbucket, sqlc codegen, and tests)\n- [Task](https://taskfile.dev/) (optional, for convenience commands)\n\n### Development Setup\n\n```bash\n# Start PostgreSQL and Inbucket (mail catcher)\ndocker compose up -d\n\n# Run database migrations and start the server\ngo run ./cmd/app\n\n# Or use Task\ntask run\n```\n\nThe API is available at `http://localhost:8080`.\n\n### Running Tests\n\n```bash\n# Run all tests (requires Docker for Testcontainers)\ntask test\n\n# Generate coverage report\ntask coverage\n\n# Run linter\ntask lint\n```\n\n## Project Structure\n\n```\n.\n|-- cmd/\n|   |-- app/                # Application entry point\n|   `-- coveragefilter/     # Coverage report filter utility\n|-- config/\n|   `-- config.yaml         # Default configuration\n|-- db/\n|   |-- migrations/         # Goose SQL migrations (embedded)\n|   `-- queries/            # sqlc query definitions\n|-- internal/\n|   |-- app/                # Application bootstrap and lifecycle\n|   |-- auth/               # Authentication and authorization logic\n|   |-- cache/              # Generic in-memory cache with TTL\n|   |-- config/             # Configuration loading and validation\n|   |-- database/           # Database connection and migration runner\n|   |-- httpapi/            # HTTP API layer\n|   |   |-- handlers/       # Request handlers\n|   |   |-- jsonio/         # JSON request/response helpers\n|   |   `-- middleware/     # HTTP middleware\n|   |-- mailer/             # SMTP mailer\n|   |-- river/              # River background job client and workers\n|   |   `-- jobs/           # Job implementations\n|   |-- store/\n|   |   |-- dbtype/         # Custom database types (JSONB)\n|   |   `-- sqlc/           # Generated sqlc code\n|   |-- testutil/           # Test helpers (Testcontainers PostgreSQL)\n|   `-- validation/         # Request validation framework\n|-- sqlc/                   # sqlc Docker build files\n|-- docker-compose.yml      # Development services\n|-- Taskfile.yml            # Task runner commands\n`-- GOING_PROD.md           # Production deployment checklist\n```\n\n## Configuration\n\nConfiguration is loaded from `config/config.yaml` and overridden by environment variables prefixed with `BASE_`. Environment variable names join config path segments with `_` while preserving snake_case field names, so `BASE_DATABASE_MAX_OPEN_CONNS` overrides `database.max_open_conns` and `BASE_OAUTH_PROVIDERS_GOOGLE_CLIENT_ID` overrides `oauth.providers.google.client_id`. String slices accept comma-separated values such as `BASE_SECURITY_ALLOWED_ORIGINS=https://app.example.com,https://admin.example.com`.\n\n| Config Key | Env Var | Description |\n|---|---|---|\n| `app.env` | `BASE_APP_ENV` | Environment: `development`, `test`, or `production` |\n| `app.log_level` | `BASE_APP_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error` |\n| `http.address` | `BASE_HTTP_ADDRESS` | Listen address (e.g., `:8080`, `127.0.0.1:8080`) |\n| `http.trusted_proxies` | `BASE_HTTP_TRUSTED_PROXIES` | Trusted proxy IPs/CIDRs for `X-Forwarded-For` and `X-Real-IP` handling (comma-separated) |\n| `database.url` | `BASE_DATABASE_URL` | PostgreSQL connection string |\n| `database.max_open_conns` | `BASE_DATABASE_MAX_OPEN_CONNS` | Maximum open database connections |\n| `session.secure` | `BASE_SESSION_SECURE` | Set to `true` in production (HTTPS) |\n| `security.allowed_origins` | `BASE_SECURITY_ALLOWED_ORIGINS` | Allowed CORS origins for browser clients (comma-separated) |\n| `security.encryption_key` | `BASE_SECURITY_ENCRYPTION_KEY` | 32+ char secret for DB encryption |\n| `river.enabled` | `BASE_RIVER_ENABLED` | Enable background job processing |\n| `mailer.enabled` | `BASE_MAILER_ENABLED` | Enable email sending |\n\nSee `config/config.yaml` for all options. See [GOING_PROD.md](GOING_PROD.md) for production configuration guidance.\n\n## API Endpoints\n\n### Public\n| Method | Path | Description |\n|---|---|---|\n| `POST` | `/api/v1/auth/register` | Register a new user |\n| `POST` | `/api/v1/auth/login` | Login with email/password (+ optional TOTP) |\n| `GET` | `/api/v1/auth/verify-email` | Verify email with token |\n| `POST` | `/api/v1/auth/password-reset/request` | Request password reset email |\n| `POST` | `/api/v1/auth/password-reset/confirm` | Confirm password reset |\n| `POST` | `/api/v1/auth/account-recovery/request` | Request account recovery |\n| `POST` | `/api/v1/auth/account-recovery/confirm` | Confirm account recovery |\n| `GET` | `/api/v1/auth/oauth/{provider}/start` | Start OAuth flow |\n| `GET` | `/api/v1/auth/oauth/{provider}/callback` | OAuth callback |\n| `POST` | `/api/v1/auth/passkeys/login/start` | Begin passkey login |\n| `POST` | `/api/v1/auth/passkeys/login/finish` | Complete passkey login |\n\n### Authenticated\n| Method | Path | Description |\n|---|---|---|\n| `POST` | `/api/v1/auth/logout` | Destroy session |\n| `GET` | `/api/v1/auth/me` | Get current user |\n| `POST` | `/api/v1/auth/passkeys/register/start` | Begin passkey registration |\n| `POST` | `/api/v1/auth/passkeys/register/finish` | Complete passkey registration |\n| `POST` | `/api/v1/auth/totp/setup` | Generate TOTP secret |\n| `POST` | `/api/v1/auth/totp/enable` | Enable TOTP |\n| `POST` | `/api/v1/auth/totp/disable` | Disable TOTP |\n\n### Admin\n| Method | Path | Description |\n|---|---|---|\n| `GET` | `/api/v1/admin/access` | Admin-only endpoint |\n\n### Health\n| Method | Path | Description |\n|---|---|---|\n| `GET` | `/health` | Liveness check |\n| `GET` | `/readiness` | Readiness check (includes DB ping) |\n\n## Libraries\n\n### Core Dependencies\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | v5.3.0 | HTTP router with middleware support |\n| [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | v5.10.0 | PostgreSQL driver and connection pool |\n| [github.com/alexedwards/scs/v2](https://github.com/alexedwards/scs) | v2.9.0 | HTTP session management |\n| [github.com/alexedwards/scs/pgxstore](https://github.com/alexedwards/scs) | v0.0.0-20251002162104-209de6e426de | PostgreSQL session store for SCS |\n| [github.com/knadh/koanf/v2](https://github.com/knadh/koanf) | v2.3.5 | Configuration management (YAML + env) |\n\n### Authentication \u0026 Security\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/alexedwards/argon2id](https://github.com/alexedwards/argon2id) | v1.0.0 | Argon2id password hashing |\n| [github.com/pquerna/otp](https://github.com/pquerna/otp) | v1.5.0 | TOTP (two-factor) generation and validation |\n| [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) | v0.17.4 | WebAuthn / Passkey authentication |\n| [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) | v5.3.1 | JWT parsing (indirect, via WebAuthn) |\n| [github.com/google/uuid](https://github.com/google/uuid) | v1.6.0 | UUID generation (passkey AAGUID) |\n\n### Background Jobs \u0026 Messaging\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/riverqueue/river](https://github.com/riverqueue/river) | v0.39.0 | PostgreSQL-backed job queue (email outbox, cleanup, inactivity checks) |\n\n### Database \u0026 Migrations\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/pressly/goose/v3](https://github.com/pressly/goose) | v3.27.1 | Database schema migrations |\n| [github.com/lib/pq](https://github.com/lib/pq) | v1.12.3 | PostgreSQL driver (used by test utilities) |\n\n### Rate Limiting\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/ralscha/ratelimiter-pg](https://github.com/ralscha/ratelimiter-pg) | v0.0.0-20260531135312-9a7504910818 | PostgreSQL-backed token bucket rate limiter |\n\n### Testing\n\n| Library | Version | Purpose |\n|---|---|---|\n| [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) | v0.42.0 | Docker-based integration testing with real PostgreSQL |\n| [github.com/stretchr/testify](https://github.com/stretchr/testify) | v1.11.1 | Test assertions (indirect) |\n\n### Development Tools\n\n| Tool | Purpose |\n|---|---|\n| [sqlc](https://sqlc.dev/) (v1.30.0) | Type-safe SQL code generation (run via Docker) |\n| [golangci-lint](https://golangci-lint.run/) (v2.12.2) | Go linter (run via Docker) |\n| [Docker Compose](https://docs.docker.com/compose/) | Local PostgreSQL + Inbucket for development |\n| [Task](https://taskfile.dev/) | Build/test automation |\n\n## Code Generation\n\nSQL queries are defined in `db/queries/` and Go code is generated with [sqlc](https://sqlc.dev/):\n\n```bash\ntask db:build-sqlc    # Build the sqlc Docker image\ntask db:run-sqlc      # Generate Go code from SQL queries\n```\n\n## Architecture Notes\n\n- **Dual DB handles**: The app maintains both a `database/sql` handle (`*sql.DB`) and a `pgxpool` handle (`*pgxpool.Pool`). The `sql.DB` is used for sqlc-generated queries and migrations; `pgxpool` is used by River, SCS sessions, and the rate limiter.\n- **River outbox safety**: The periodic email outbox job is inserted uniquely so only one outbox sweep can be queued or running at a time, which avoids overlapping email sends.\n- **Proxy trust boundary**: Forwarded client IP headers are only honored when the immediate peer matches `http.trusted_proxies`. Leave that list empty unless the app is behind a proxy you control.\n- **Encryption at rest**: TOTP secrets and OAuth tokens stored in the database are encrypted with AES-256-GCM using the configured `security.encryption_key`.\n- **Credential masking**: Login failures intentionally return a generic \"Invalid email or password\" message regardless of whether the email exists, the account is locked, disabled, or unverified.\n- **Test isolation**: Each test gets its own PostgreSQL database created from a shared Testcontainers container, ensuring full isolation without per-test container overhead.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fralscha%2Fbase-go-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fralscha%2Fbase-go-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fralscha%2Fbase-go-backend/lists"}