Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ packages:
github.com/meigma/template-go-api/internal/todo:
interfaces:
Repository:
github.com/meigma/template-go-api/internal/authz:
interfaces:
Authenticator:
EntityResolver:
github.com/meigma/template-go-api/internal/authz/apikey:
interfaces:
APIKeyStore:
34 changes: 29 additions & 5 deletions DELETE_ME.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ It is only here to orient the initial project owner.

- A runnable hexagonal HTTP API server (chi + Huma) at `github.com/meigma/template-go-api`, with a `todo` example resource, RFC 9457 errors, `/healthz`, `/readyz`, and `/metrics`, runtime API docs at `/docs`, and an `openapi` spec-export command.
- A PostgreSQL persistence adapter (pgx + sqlc typed queries + goose migrations) behind the domain's `todo.Repository` port: a `migrate` subcommand, a committed-and-drift-guarded sqlc layer, a real `/readyz` check, and container-backed integration tests. The port is the seam — implement it to back the template with a different datastore.
- An authorization tier (Cedar via `cedar-go`) with a deny-by-default Huma middleware, a modular per-resource authz slice pattern, and authentication deferred to the integrator: a placeholder API-key authenticator (`X-API-Key`/Bearer, backed by an `api_keys` table) and dev-only mock keys seeded for the Compose demo. Replace the authenticator with real authn — see step 6.
- A Cobra/Viper entrypoint under `cmd/template-go-api` and `internal/cli` exposing `serve` (default), `version`, `openapi`, and `migrate`.
- Moon tasks for `format`, `lint`, `build`, `test`, and `check`, plus `sqlc` / `sqlc-check` (regenerate and drift-guard the typed query layer), `mockery` / `mockery-check` (regenerate and drift-guard the testify mocks), `migrate` (run database migrations), and `test-integration` (container-backed adapter tests).
- `golangci-lint`, `sqlc`, `goose`, and `mockery` wired through Proto and Moon.
Expand Down Expand Up @@ -85,6 +86,7 @@ The nominal generated-project path is an HTTP service with both a downloadable b
- Add a domain package `internal/<resource>` (entity, `Repository` port, and `Service`), mirroring `internal/todo`. Each resource owns its adapters nested beneath it (`internal/<resource>/{httpapi,postgres}`).
- Implement the port in a nested adapter: mirror `internal/todo/postgres` (pgx + sqlc, on the shared pool/migrations in `internal/adapter/postgres`). The README's [Persistence](README.md#persistence) section covers the migration and sqlc-regeneration workflow.
- Add a transport adapter `internal/<resource>/httpapi` (DTOs, domain mapping, error translation, and a `Register` function), mirroring `internal/todo/httpapi`.
- Add an authz slice `internal/<resource>/authz` (policies, actions, fact resolver), tag the `httpapi` operations with `authz.Require`/`authz.Public`, and merge its `Contribution` into `authz.New` in `internal/app/app.go`, mirroring `internal/todo/authz`. Authorization is deny-by-default, so an untagged operation is rejected (see the README's [Authorization](README.md#authorization) section). If you are dropping authorization, see step 6.
- Add one `Register` call in `registerResources` in `internal/app/app.go`.
- When you wire a real datastore, add a readiness check to the `Readiness` slice in `internal/app/app.go` so `/readyz` reflects it (the PostgreSQL adapter shows the pattern with its `Ping` check).
- Put cross-package integration tests in `internal/integration` (package `integration`, `//go:build integration`), run via `moon run test-integration`; keep fast unit tests beside the code they cover. Repository doubles come from the mockery-generated mocks in `internal/<resource>/mocks` (register the new port in `.mockery.yaml`); a stateful in-memory fake for end-to-end tests lives in `internal/todo/todotest`. The README's [Testing](README.md#testing) section covers the split.
Expand All @@ -94,13 +96,35 @@ The nominal generated-project path is an HTTP service with both a downloadable b

Keep the generic transport in `internal/adapter/http` (router, middleware, `/healthz`/`/readyz`/`/metrics`, RFC 9457 fallbacks, the `Registrar` seam), `internal/config`, and `internal/observability` as-is unless you have a reason to change them.

6. Refresh module metadata:
6. Wire real authentication (and prune or keep the authorization tier).

The template ships an authorization tier (Cedar via `cedar-go`, deny-by-default Huma middleware) with authentication **deferred to you**. The shipped API-key authenticator is a placeholder, not a security mechanism. Address it before the first real deployment:

- **Replace the shipped API-key authenticator (first priority).** It is a placeholder: it stores keys verbatim and is meant only to demonstrate the flow. Implement `authz.Authenticator` with a real verifier (JWT via `lestrrat-go/jwx`, OIDC via `coreos/go-oidc`, sessions, etc.) and inject it with `app.WithAuthenticator` in `internal/app/app.go`. Map the verified claims (subject, roles/groups) into the `authz.Principal`. If you keep the API-key store instead of replacing it, at minimum store a **hash** of each key and compare in constant time (`crypto/subtle.ConstantTimeCompare`) — see the SECURITY note in `internal/authz/apikey/store.go` — and never seed the mock keys.
- **Delete the dev mock-keys seed regardless:** remove `hack/sql/0002_seed_api_keys.sql` (insecure public credentials, dev-only). Real deployments insert their own `api_keys` rows out of band and never apply `hack/sql/`.

To remove the authorization tier **entirely** (surgical, the slice pattern keeps it self-contained):

- Delete the base engine `internal/authz` (this also removes `internal/authz/apikey`, the shipped authenticator + PostgreSQL `APIKeyStore`, and the base mockery doubles under `internal/authz/mocks` and `internal/authz/apikey/mocks`).
- Delete the todo authz slice `internal/todo/authz` (its `policy.cedar`, actions, and fact resolver).
- Delete the `api_keys` goose migration `internal/adapter/postgres/migrations/00002_create_api_keys.sql` and the dev seed `hack/sql/0002_seed_api_keys.sql`.
- Untag the `httpapi` routes: remove the `Metadata: authz.Require(...)` lines and the `authz`/`todoauthz` imports from `internal/todo/httpapi/handler.go`.
- Remove the authz wiring from the composition root `internal/app/app.go` (`authzInstaller`/`resolveAuthenticator`, the `WithAuthenticator` option, the `InstallAuthz`/`FinalizeAuthz` hooks, and the `DocumentSecurity` call in the spec exporter).
- Remove the `--authz-enabled` and `--authz-policy-dir` flags (and the `AuthzEnabled`/`AuthzPolicyDir` fields) from `internal/config/config.go`.
- Remove the new authz ports from `.mockery.yaml`.
- Run `go mod tidy` to drop `github.com/cedar-policy/cedar-go`.
- **No `sqlc` regen is needed:** `omit_unused_structs: true` in `sqlc.yaml` already keeps the todo sqlc package todo-only, so the `api_keys` table never produced any todo-sqlc output and dropping the migration changes nothing there.
- Drop the authz integration/e2e coverage in `internal/integration` (the container-backed `APIKeyStore` test and the functional authz tests).

As a quick alternative for incremental adoption, set `--authz-enabled=false` (env `TEMPLATE_GO_API_AUTHZ_ENABLED=false`) to bypass the middleware entirely without deleting anything.

7. Refresh module metadata:

```sh
go mod tidy
```

7. Configure releases for the chosen shape.
8. Configure releases for the chosen shape.

For the nominal binary plus container case:

Expand Down Expand Up @@ -135,19 +159,19 @@ The nominal generated-project path is an HTTP service with both a downloadable b

In every release-bearing project, configure the release app credentials, protected-tag bypass, and repository package permissions before the first release. Run the release dry-run workflow after these edits and before merging the first release PR.

8. Run the full local check:
9. Run the full local check:

```sh
moon run root:check
```

9. Update project-facing docs:
10. Update project-facing docs:

- Rewrite `README.md` for the actual project.
- Review `CONTRIBUTING.md` and `SECURITY.md`.
- Add a real license before publishing the repository.

10. Delete this file:
11. Delete this file:

```sh
rm DELETE_ME.md
Expand Down
Loading
Loading