@nakedev/go-scaffold 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +598 -306
  2. package/dist/commands/auth.js +65 -23
  3. package/dist/commands/check.js +281 -0
  4. package/dist/commands/config.js +50 -0
  5. package/dist/commands/create.js +33 -2
  6. package/dist/commands/generate.js +29 -3
  7. package/dist/commands/method.js +74 -63
  8. package/dist/commands/migration.js +2 -2
  9. package/dist/commands/observability.js +4 -53
  10. package/dist/commands/rbac.js +21 -10
  11. package/dist/commands/undo.js +11 -3
  12. package/dist/commands/worker.js +15 -5
  13. package/dist/index.js +198 -59
  14. package/dist/prompts/auth-wizard.js +40 -6
  15. package/dist/prompts/create-wizard.js +42 -1
  16. package/dist/prompts/generate-wizard.js +89 -9
  17. package/dist/templates/auth-manifest.js +50 -19
  18. package/dist/templates/create-manifest.js +8 -0
  19. package/dist/templates/module-manifest.js +84 -26
  20. package/dist/templates/rbac-manifest.js +16 -11
  21. package/dist/templates/worker-manifest.js +4 -1
  22. package/dist/types.js +8 -0
  23. package/dist/utils/auth-patcher.js +124 -33
  24. package/dist/utils/config.js +167 -4
  25. package/dist/utils/docs-patcher.js +68 -0
  26. package/dist/utils/hexagonal-method-patcher.js +334 -0
  27. package/dist/utils/main-patcher.js +32 -30
  28. package/dist/utils/marker-patch.js +7 -1
  29. package/dist/utils/module-location.js +17 -11
  30. package/dist/utils/module-profile.js +32 -0
  31. package/dist/utils/platform-patcher.js +56 -7
  32. package/dist/utils/rbac-patcher.js +89 -210
  33. package/package.json +7 -2
  34. package/templates/add/auth/cmd/seed/main.go.hbs +15 -3
  35. package/templates/add/auth/docs/login.yaml.hbs +11 -1
  36. package/templates/add/auth/docs/mfa-verify.yaml.hbs +19 -0
  37. package/templates/add/auth/docs/provider-exchange.yaml.hbs +40 -0
  38. package/templates/add/auth/docs/provider-login.yaml.hbs +31 -0
  39. package/templates/add/auth/docs/refresh.yaml.hbs +7 -0
  40. package/templates/add/auth/docs/register.yaml.hbs +7 -0
  41. package/templates/add/auth/docs/reset-password.yaml.hbs +1 -1
  42. package/templates/add/auth/docs/schemas.yaml.hbs +59 -1
  43. package/templates/add/auth/docs/users-me-mfa-confirm.yaml.hbs +19 -0
  44. package/templates/add/auth/docs/users-me-mfa-disable.yaml.hbs +15 -0
  45. package/templates/add/auth/docs/users-me-mfa-setup.yaml.hbs +14 -0
  46. package/templates/add/auth/docs/users-me-mfa.yaml.hbs +12 -0
  47. package/templates/add/auth/internal/app/user/adapters/inbound/http/browser_policy.go.hbs +98 -0
  48. package/templates/add/auth/internal/app/user/adapters/inbound/http/dto.go.hbs +159 -0
  49. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler.go.hbs +228 -0
  50. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_local.go.hbs +76 -0
  51. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_mfa.go.hbs +83 -0
  52. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_oauth.go.hbs +70 -0
  53. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_recovery.go.hbs +49 -0
  54. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_test.go.hbs +311 -0
  55. package/templates/add/auth/internal/app/user/adapters/inbound/http/handler_user.go.hbs +41 -0
  56. package/templates/add/auth/internal/app/user/adapters/inbound/http/session_cookie.go.hbs +35 -0
  57. package/templates/add/auth/internal/app/user/adapters/outbound/password/bcrypt.go.hbs +35 -0
  58. package/templates/add/auth/internal/app/user/adapters/outbound/password/bcrypt_test.go.hbs +20 -0
  59. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/mfa_store.go.hbs +129 -0
  60. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/mfa_store_test.go.hbs +174 -0
  61. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/model.go.hbs +84 -0
  62. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/repository.go.hbs +211 -0
  63. package/templates/add/auth/internal/app/user/{repository_test.go.hbs → adapters/outbound/postgres/repository_test.go.hbs} +18 -19
  64. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/tokenstore_pg.go.hbs +213 -0
  65. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/tokenstore_pg_test.go.hbs +103 -0
  66. package/templates/add/auth/internal/app/user/adapters/outbound/postgres/tokenstore_recovery.go.hbs +84 -0
  67. package/templates/add/auth/internal/app/user/adapters/outbound/redis/tokenstore.go.hbs +228 -0
  68. package/templates/add/auth/internal/app/user/adapters/outbound/redis/tokenstore_test.go.hbs +196 -0
  69. package/templates/add/auth/internal/app/user/application/contracts.go.hbs +52 -0
  70. package/templates/add/auth/internal/app/user/application/dto.go.hbs +75 -0
  71. package/templates/add/auth/internal/app/user/application/errors.go.hbs +62 -0
  72. package/templates/add/auth/internal/app/user/application/external_login.go.hbs +198 -0
  73. package/templates/add/auth/internal/app/user/application/jwt.go.hbs +58 -0
  74. package/templates/add/auth/internal/app/user/application/local_auth.go.hbs +96 -0
  75. package/templates/add/auth/internal/app/user/application/mfa_service.go.hbs +449 -0
  76. package/templates/add/auth/internal/app/user/application/mfa_service_test.go.hbs +200 -0
  77. package/templates/add/auth/internal/app/user/application/oauth.go.hbs +132 -0
  78. package/templates/add/auth/internal/app/user/application/provider_test.go.hbs +285 -0
  79. package/templates/add/auth/internal/app/user/application/recovery.go.hbs +82 -0
  80. package/templates/add/auth/internal/app/user/application/recovery_service.go.hbs +112 -0
  81. package/templates/add/auth/internal/app/user/application/service.go.hbs +145 -0
  82. package/templates/add/auth/internal/app/user/application/service_test.go.hbs +891 -0
  83. package/templates/add/auth/internal/app/user/application/sessions.go.hbs +99 -0
  84. package/templates/add/auth/internal/app/user/application/tokenstore_ports.go.hbs +14 -0
  85. package/templates/add/auth/internal/app/user/application/user_query.go.hbs +65 -0
  86. package/templates/add/auth/internal/app/user/composition.go.hbs +168 -0
  87. package/templates/add/auth/internal/app/user/domain/entity.go.hbs +41 -0
  88. package/templates/add/auth/internal/app/user/domain/errors.go.hbs +32 -0
  89. package/templates/add/auth/internal/app/user/ports/password.go.hbs +9 -0
  90. package/templates/add/auth/internal/app/user/ports/repository.go.hbs +90 -0
  91. package/templates/add/auth/internal/platform/authprovider/google/google.go.hbs +389 -0
  92. package/templates/add/auth/internal/platform/authprovider/google/google_test.go.hbs +312 -0
  93. package/templates/add/auth/migrations/create_auth_tokens.up.sql.hbs +10 -5
  94. package/templates/add/auth/migrations/create_identities.up.sql.hbs +1 -1
  95. package/templates/add/auth/migrations/create_login_throttle.up.sql.hbs +1 -1
  96. package/templates/add/auth/migrations/create_mfa.down.sql.hbs +3 -0
  97. package/templates/add/auth/migrations/create_mfa.up.sql.hbs +29 -0
  98. package/templates/add/auth/migrations/create_users.up.sql.hbs +4 -3
  99. package/templates/add/rbac/internal/app/role/adapters/inbound/http/handler.go.hbs +142 -0
  100. package/templates/add/rbac/internal/app/role/adapters/inbound/http/handler_test.go.hbs +19 -0
  101. package/templates/add/rbac/internal/app/role/adapters/outbound/postgres/model.go.hbs +48 -0
  102. package/templates/add/rbac/internal/app/role/adapters/outbound/postgres/repository.go.hbs +127 -0
  103. package/templates/add/rbac/internal/app/role/{repository_test.go.hbs → adapters/outbound/postgres/repository_test.go.hbs} +8 -8
  104. package/templates/add/rbac/internal/app/role/application/dto.go.hbs +47 -0
  105. package/templates/add/rbac/internal/app/role/application/errors.go.hbs +19 -0
  106. package/templates/add/rbac/internal/app/role/application/service.go.hbs +157 -0
  107. package/templates/add/rbac/internal/app/role/{service_test.go.hbs → application/service_test.go.hbs} +26 -19
  108. package/templates/add/rbac/internal/app/role/composition.go.hbs +48 -0
  109. package/templates/add/rbac/internal/app/role/domain/entity.go.hbs +23 -0
  110. package/templates/add/rbac/internal/app/role/domain/errors.go.hbs +26 -0
  111. package/templates/add/rbac/internal/app/role/ports/repository.go.hbs +25 -0
  112. package/templates/add/rbac/migrations/add_roles.down.sql.hbs +3 -11
  113. package/templates/add/rbac/migrations/add_roles.up.sql.hbs +17 -6
  114. package/templates/add/worker/internal/platform/queue/river_test.go.hbs +84 -0
  115. package/templates/create/base/.claude/skills/go-scaffold/SKILL.md.hbs +358 -121
  116. package/templates/create/base/.env.example.hbs +0 -1
  117. package/templates/create/base/.golangci.yml.hbs +2 -2
  118. package/templates/create/base/AGENTS.md.hbs +279 -67
  119. package/templates/create/base/Makefile.hbs +2 -1
  120. package/templates/create/base/README.md.hbs +115 -32
  121. package/templates/create/base/cmd/api/wiring.go.hbs +13 -9
  122. package/templates/create/base/internal/composition/doc.go.hbs +7 -0
  123. package/templates/create/base/internal/platform/database/database.go.hbs +3 -3
  124. package/templates/create/base/internal/shared/apperror/apperror.go.hbs +15 -2
  125. package/templates/create/base/internal/shared/config/config.go.hbs +0 -8
  126. package/templates/create/base/internal/shared/middleware/cors_test.go.hbs +40 -0
  127. package/templates/create/base/internal/shared/middleware/error.go.hbs +15 -5
  128. package/templates/create/features/docs/architecture.md.hbs +92 -32
  129. package/templates/create/features/docs/patterns.md.hbs +137 -91
  130. package/templates/create/features/docs/techstack.md.hbs +18 -3
  131. package/templates/generate/module/hexagonal/adapters/inbound/http/dto.go.hbs +45 -0
  132. package/templates/generate/module/hexagonal/adapters/inbound/http/dto.minimal.go.hbs +28 -0
  133. package/templates/generate/module/hexagonal/adapters/inbound/http/handler.go.hbs +182 -0
  134. package/templates/generate/module/hexagonal/adapters/inbound/http/handler.minimal.go.hbs +83 -0
  135. package/templates/generate/module/hexagonal/adapters/inbound/http/handler_crud_test.go.hbs +18 -0
  136. package/templates/generate/module/hexagonal/adapters/inbound/http/handler_test.go.hbs +30 -0
  137. package/templates/generate/module/hexagonal/adapters/outbound/postgres/model.go.hbs +37 -0
  138. package/templates/generate/module/hexagonal/adapters/outbound/postgres/repository.go.hbs +95 -0
  139. package/templates/generate/module/{repository_test.go.hbs → hexagonal/adapters/outbound/postgres/repository_test.go.hbs} +8 -8
  140. package/templates/generate/module/hexagonal/application/commands.crud.go.hbs +54 -0
  141. package/templates/generate/module/hexagonal/application/commands.go.hbs +25 -0
  142. package/templates/generate/module/hexagonal/application/cqrs_test.go.hbs +66 -0
  143. package/templates/generate/module/hexagonal/application/dto.go.hbs +35 -0
  144. package/templates/generate/module/hexagonal/application/dto.minimal.go.hbs +25 -0
  145. package/templates/generate/module/hexagonal/application/queries.crud.go.hbs +33 -0
  146. package/templates/generate/module/hexagonal/application/queries.go.hbs +25 -0
  147. package/templates/generate/module/hexagonal/application/service.crud.go.hbs +73 -0
  148. package/templates/generate/module/hexagonal/application/service.go.hbs +29 -0
  149. package/templates/generate/module/hexagonal/application/service_test.go.hbs +62 -0
  150. package/templates/generate/module/hexagonal/composition.go.hbs +27 -0
  151. package/templates/generate/module/hexagonal/domain/entity.go.hbs +20 -0
  152. package/templates/generate/module/hexagonal/domain/errors.go.hbs +11 -0
  153. package/templates/generate/module/hexagonal/ports/repository.go.hbs +38 -0
  154. package/templates/generate/module/migration.up.sql.hbs +1 -1
  155. package/dist/utils/method-patcher.js +0 -357
  156. package/templates/add/auth/docs/google-callback.yaml.hbs +0 -22
  157. package/templates/add/auth/docs/google-login.yaml.hbs +0 -7
  158. package/templates/add/auth/internal/app/user/dto.go.hbs +0 -77
  159. package/templates/add/auth/internal/app/user/errors.go.hbs +0 -43
  160. package/templates/add/auth/internal/app/user/handler.go.hbs +0 -276
  161. package/templates/add/auth/internal/app/user/jwt.go.hbs +0 -108
  162. package/templates/add/auth/internal/app/user/model/authtoken.go.hbs +0 -39
  163. package/templates/add/auth/internal/app/user/model/identity.go.hbs +0 -31
  164. package/templates/add/auth/internal/app/user/model/loginthrottle.go.hbs +0 -26
  165. package/templates/add/auth/internal/app/user/model/user.go.hbs +0 -30
  166. package/templates/add/auth/internal/app/user/repository.go.hbs +0 -137
  167. package/templates/add/auth/internal/app/user/service.go.hbs +0 -531
  168. package/templates/add/auth/internal/app/user/service_test.go.hbs +0 -316
  169. package/templates/add/auth/internal/app/user/tokenstore.go.hbs +0 -30
  170. package/templates/add/auth/internal/app/user/tokenstore_pg.go.hbs +0 -144
  171. package/templates/add/auth/internal/app/user/tokenstore_redis.go.hbs +0 -147
  172. package/templates/add/rbac/internal/app/role/dto.go.hbs +0 -45
  173. package/templates/add/rbac/internal/app/role/errors.go.hbs +0 -39
  174. package/templates/add/rbac/internal/app/role/handler.go.hbs +0 -104
  175. package/templates/add/rbac/internal/app/role/model/permission.go.hbs +0 -12
  176. package/templates/add/rbac/internal/app/role/model/role.go.hbs +0 -22
  177. package/templates/add/rbac/internal/app/role/model/role_permission.go.hbs +0 -11
  178. package/templates/add/rbac/internal/app/role/repository.go.hbs +0 -97
  179. package/templates/add/rbac/internal/app/role/service.go.hbs +0 -217
  180. package/templates/generate/module/dto.go.hbs +0 -36
  181. package/templates/generate/module/errors.go.hbs +0 -33
  182. package/templates/generate/module/handler.go.hbs +0 -134
  183. package/templates/generate/module/handler_test.go.hbs +0 -174
  184. package/templates/generate/module/minimal/dto.go.hbs +0 -28
  185. package/templates/generate/module/minimal/handler.go.hbs +0 -48
  186. package/templates/generate/module/minimal/handler_test.go.hbs +0 -10
  187. package/templates/generate/module/minimal/service.go.hbs +0 -45
  188. package/templates/generate/module/minimal/service_test.go.hbs +0 -77
  189. package/templates/generate/module/model/model.go.hbs +0 -36
  190. package/templates/generate/module/repository.go.hbs +0 -103
  191. package/templates/generate/module/service.go.hbs +0 -108
  192. package/templates/generate/module/service_test.go.hbs +0 -161
@@ -1,80 +1,292 @@
1
1
  # Agent Guidance: {{projectName}}
2
2
 
3
- This project was scaffolded by `@nakedev/go-scaffold` and is maintained using
4
- its `generate` subcommands new domain modules and new endpoints are not
5
- hand-written.
3
+ This project was scaffolded by `@nakedev/go-scaffold`. It is a modular
4
+ monolith: each business capability lives in its own feature package, while
5
+ `cmd/api/wiring.go` is the process composition root. This file is the working
6
+ contract for AI coding agents and humans; keep generated structure and the
7
+ runtime safety rules below intact.
6
8
 
7
- ## Hard rule: use the CLI to add a new domain module or a new endpoint
9
+ ## Start every task here
8
10
 
9
- If you are about to create a **new** domain (a new `internal/app/<name>/`
10
- package with its own model/handler/service/repository), or add a **new**
11
- endpoint to an existing one stop and run the CLI instead:
11
+ 1. Read the relevant code, `docs/architect/`, tests, and this file before
12
+ editing. Search for an existing module or port before creating one.
13
+ 2. Check `git status --short --branch`. Work on a dedicated, non-protected
14
+ branch and preserve unrelated changes. Never edit a protected branch just
15
+ to make a quick fix.
16
+ 3. Classify the request: generated surface, business behavior, schema, auth /
17
+ security, infrastructure, or documentation. Ask the owner before changing
18
+ schema/migrations, authentication or security behavior, architecture,
19
+ dependencies, or deployment configuration when the project workflow
20
+ requires approval.
21
+ 4. Make the smallest complete change, add a focused test, and report exact
22
+ commands and output. Do not claim a check passed if it was skipped or
23
+ failed.
24
+
25
+ ## Architecture contract
26
+
27
+ This scaffold combines modular boundaries with Hexagonal Architecture,
28
+ domain-oriented design, and an optional CQRS style. They are compatible:
29
+ apply them inside each feature instead of creating a second architecture or
30
+ an empty layer for every noun.
31
+
32
+ - **Inbound adapter:** `adapters/inbound/http/dto.go` owns JSON/binding request
33
+ DTOs, response DTOs, and mapping at the HTTP boundary; `handler.go` parses
34
+ HTTP and calls the relevant application service or command/query port.
35
+ - **Application boundary:** `application/service.go` (or
36
+ `application/commands.go`/`application/queries.go` when
37
+ the module uses `--cqrs`) orchestrates use cases. It owns
38
+ workflow decisions and depends on narrow consumer-owned ports.
39
+ - **Domain:** entities, value rules, and invariants stay independent of Gin,
40
+ GORM, Redis, HTTP status codes, and environment variables.
41
+ - **Outbound adapter:** `adapters/outbound/postgres/repository.go`, token
42
+ stores, mail/queue clients,
43
+ and other platform integrations translate a port into persistence or I/O.
44
+ - **Composition:** the feature-local `composition.go` constructs its
45
+ repository, application boundary, and handler. `internal/composition/` owns
46
+ process-level cross-feature adapters. `cmd/api/wiring.go` selects shared
47
+ infrastructure, supplies those adapters and security dependencies, and
48
+ registers feature routes; it does not contain cross-feature adapter logic.
49
+
50
+ The project defaults new modules to `{{defaultApplicationStyle}}` application
51
+ handling and a `{{defaultModuleSurface}}` surface. The `generate module` wizard
52
+ starts there through the named Lean/CRUD/CQRS profiles; `Advanced` exposes the
53
+ two axes for unusual combinations. `--profile` is the clear per-module
54
+ override, while `--cqrs`/`--full` remain supported for existing scripts, and
55
+ `go-scaffold config` changes defaults for future modules only.
56
+
57
+ Use CQRS when a feature has genuinely different command and query models,
58
+ consistency needs, or scaling/read-shape pressure: commands mutate state and
59
+ queries read state. For simple CRUD, one application service and one port are
60
+ clearer. Do not introduce a broker, a second database, or a command/query
61
+ folder only to satisfy a label.
62
+
63
+ Keep feature boundaries real. Do not import another feature's private model,
64
+ repository, or handler. Expose a small public application port, call a
65
+ documented feature API, or use an explicit event when cross-feature behavior
66
+ is required. Put synchronous adapters in `internal/composition/`, import only
67
+ the other feature's public package root, and keep one-way dependencies. Keep
68
+ dependency direction flowing inward:
69
+
70
+ ```text
71
+ handler -> application/use case -> domain + ports -> adapters
72
+ ^ |
73
+ +--- composition -+
74
+ ```
75
+
76
+ The generated CRUD shape is an intentionally incremental starter. It gives
77
+ each module a testable application/inbound seam and a feature-local
78
+ composition root; it does not decide the final aggregate, fields, invariants,
79
+ or split between commands and queries for the product.
80
+
81
+ For a CQRS module, `application/commands.go` and
82
+ `application/queries.go` are the primary application boundary and
83
+ `composition.go` passes separate command and query handlers to the inbound
84
+ adapter's `NewHandler`. A CQRS module must not also grow an
85
+ `application/service.go` facade. Service and CQRS may coexist across modules;
86
+ they are mutually exclusive inside one module.
87
+
88
+ Auth and RBAC are security-sensitive modules, but they use the same canonical
89
+ split boundary as every other feature. Auth owns user application use cases
90
+ and ports; RBAC owns role policy and authorization adapters. Their public
91
+ module roots compose those pieces, and one canonical implementation tree is
92
+ generated.
93
+
94
+ ## Hard rule: use the CLI for generated surface
95
+
96
+ If you are creating a **new domain** (`internal/app/<name>/` with its own
97
+ domain/application/ports/adapters boundary), or adding a **new endpoint** to
98
+ an existing domain, stop and run the CLI:
12
99
 
13
100
  ```bash
14
101
  go-scaffold generate module <name> --defaults
15
102
  go-scaffold generate method <module> <name> --type <get|post|put|patch|delete>
16
103
  ```
17
104
 
18
- **Pass every value as a flag.** Run interactively, these commands ask for
19
- anything you left out which has no answer in a non-interactive shell, so
20
- they exit 1 having written nothing. `--defaults` on `generate module` means
21
- "minimal, no auth, ask nothing"; add `--full`/`--auth`/`--permission` to it
22
- when you want those. Same rule elsewhere: `--get-mode` is required with
23
- `--type get`, and `undo module` needs `-y`. Every `add` command confirms
24
- before writing — `-y` skips that, but `add auth`/`add worker` also ask which
25
- backing store to use, so those two want `--defaults` (or `--store`/`--queue`
26
- plus `-y`).
27
-
28
- This applies **even when the request doesn't say "module"/"method" or name
29
- the CLI at all**. Recognize indirect asks as generation work, for example:
30
-
31
- - "add a products feature" → a new domain (`generate module products --defaults`)
32
- - "let admins approve orders" → a new method on an existing module
33
- (`generate method orders approve --type patch`)
34
- - "we need an endpoint that lists overdue invoices" a new `get` method
35
- (`generate method invoices findOverdue --type get --get-mode all`)
36
- - "add a way to look up a user by email"a new `get` lookup
37
- (`generate method users findByEmail --type get --get-mode one --field email`)
38
-
39
- Run `go-scaffold generate module --help` / `generate method --help` before
40
- hand-rolling anything that looks like scaffolding.
41
-
42
- ## What the CLI does NOT do — these are still done by hand, as normal
43
-
44
- - Business logic inside a generated method (the CLI leaves a TODO-marked
45
- stub filling in real behavior is your job)
46
- - Adding fields to a generated model/DTO beyond `id`/`created_at`/`updated_at`
47
- - Foreign keys / relations between domains add the column, the migration
48
- `REFERENCES ... ON DELETE ...` clause, and the `dberr.IsForeignKey` mapping
49
- yourself; see `docs/architect/patterns.md` for the 3 rules domains with a
50
- relation must follow
51
- - Bug fixes, refactors, or edits to existing logic
52
- - Anything in a project that does **not** have `go-scaffold.config.json` at
53
- its root — that means the project wasn't scaffolded by this CLI, and this
54
- rule does not apply
105
+ Pass every value as a flag in CI. In an interactive shell, omitted values use
106
+ the project defaults as the initial wizard choices. In a non-interactive
107
+ shell, omitted values prompt and the command exits without writing.
108
+ `--defaults` uses the recorded project defaults and no auth; use `--profile`
109
+ for a named per-module preset, or add `--full`, `--cqrs`, `--auth`, and
110
+ `--permission <code>` deliberately.
111
+ `--get-mode`
112
+ is required for `--type get`, and `undo module` needs `-y`. For `add auth`,
113
+ choose the backing store and browser topology with `--defaults` or explicit
114
+ flags (`--store`, `--browser-topology`) as well as the confirmation flag.
115
+ For `add worker`, choose the backing queue with `--defaults` or an explicit
116
+ flag.
117
+
118
+ Recognize indirect requests as generation work:
119
+
120
+ - “add a products feature” → `generate module products --defaults`
121
+ - “let admins approve orders” `generate method orders approve --type patch`
122
+ - “list overdue invoices” → `generate method invoices findOverdue --type get --get-mode all`
123
+ - look up a user by email`generate method users findByEmail --type get --get-mode one --field email`
124
+
125
+ Run `go-scaffold generate module --help` or `generate method --help` when the
126
+ desired shape is unclear. Do not hand-edit generated route markers to add a
127
+ new endpoint; the CLI keeps the inbound adapter, application port, test seam,
128
+ and OpenAPI/migration patching in sync.
129
+
130
+ ## What the CLI does not decide
131
+
132
+ These remain deliberate engineering work:
133
+
134
+ - business behavior, invariants, authorization policy, and real DTO/model
135
+ fields;
136
+ - validation beyond the transport shape and all domain-specific rules;
137
+ - foreign keys and relations between domains, including their reviewed SQL
138
+ and `dberr.IsForeignKey` mapping;
139
+ - the business-specific command/query contracts and the public port used for
140
+ cross-feature collaboration;
141
+ - bug fixes, refactors, and changes to an existing method;
142
+ - any work in a repository that lacks `go-scaffold.config.json`.
143
+
144
+ Generated methods are explicit stubs. `generate method` GET-all reuses
145
+ `FindAll` until real filtering is added; POST and DELETE return an internal
146
+ error until implemented; PUT/PATCH intentionally return `501 Not Implemented`
147
+ and must not read or write a record before their behavior is designed. Treat
148
+ the generated OpenAPI TODO contract as incomplete, not as a promise that the
149
+ endpoint is production-ready.
150
+
151
+ ## Runtime and schema safety
152
+
153
+ - `APP_ENV` is the single environment gate and accepts only `development` or
154
+ `production`. Do not add a parallel flag such as `AUTO_MIGRATE`.
155
+ - Development may use the convenience `db.AutoMigrate(...)` path for the
156
+ models registered by the generator. Production must never bootstrap or
157
+ mutate schema at API startup: apply versioned SQL with
158
+ `make migrate-up`, then let the startup migration-version check fail fast if
159
+ the database is behind or dirty.
160
+ - Any schema change is a reviewed pair of versioned files in `migrations/`.
161
+ Use `go-scaffold generate migration <name>` for changes outside a new
162
+ module, write both directions, and test them against a disposable database.
163
+ - Keep `*sql.DB`, Redis, queue, mail, and other external resources owned by
164
+ their composition root. Close them on every exit path and preserve the
165
+ signal-driven graceful shutdown; do not call `os.Exit` from a goroutine or
166
+ hide a startup error.
167
+ - Keep liveness and readiness semantics intact. Readiness may check external
168
+ dependencies; liveness must not turn a transient dependency failure into a
169
+ process restart.
170
+
171
+ ## Auth and error boundaries
172
+
173
+ Treat auth and security changes as high-risk and get the required owner
174
+ approval before changing their contract.
175
+
176
+ - Never log passwords, raw access/refresh/reset/verification tokens, JWT
177
+ secrets, OAuth client secrets, or authorization headers.
178
+ - Refresh rotation is an atomic consume: Postgres uses one SQL unit and Redis
179
+ uses one Lua operation, including active-session cleanup and the reuse
180
+ tombstone. Its inactivity expiry may move on rotation, but its persisted
181
+ absolute expiry never moves. Never replace it with a read-then-delete
182
+ sequence.
183
+ - Password reset and email verification consume a one-time token in the same
184
+ retry-safe transaction as the user/identity update. A failed post-commit
185
+ session revocation must not make a successful reset impossible to retry.
186
+ - The generated auth implementation is canonical: use cases live in
187
+ `application/`, domain rules in `domain/`, contracts in `ports/`, and
188
+ provider/HTTP/Postgres details in their adapters. Do not import Gin, provider
189
+ SDKs, or persistence models into new application use cases; keep the
190
+ token/OAuth/MFA tests as the security gate.
191
+ - Browser provider exchange consumes a one-time server-side transaction bound
192
+ to provider, state, S256 PKCE challenge, and OIDC nonce. Google ID tokens
193
+ must be signature, issuer, audience/azp, time-claim, nonce, and subject
194
+ validated before identity resolution. Token responses use
195
+ `Cache-Control: no-store` and `Pragma: no-cache`; SameSite=None also needs an
196
+ exact allowed Origin guard in addition to CORS.
197
+
198
+ MFA has an operator capability switch and a separate per-user enrollment:
199
+
200
+ - `AUTH_MFA_ENABLED=false` is the default. When enabled, the composition root
201
+ must validate `MFA_ENCRYPTION_KEY` as a base64-encoded 32-byte key before
202
+ serving traffic. Keep the issuer, challenge TTL, TOTP window, and recovery
203
+ code count explicit and within their safety limits.
204
+ - Authenticated users manage enrollment through `GET /users/me/mfa`,
205
+ `POST /users/me/mfa/setup`, `POST /users/me/mfa/confirm`, and
206
+ `POST /users/me/mfa/disable` (the last requires the current TOTP code).
207
+ Setup material is pending-only; recovery codes are returned once.
208
+ - Password login and provider exchange may return a pre-session
209
+ `{ "mfa_required": true, "challenge": "..." }`. Do not issue an access
210
+ token or refresh cookie until `POST /auth/mfa/verify` succeeds. Challenges
211
+ are hashed, short-lived, and one-use; failed verification consumes the
212
+ challenge. Recovery codes are hashed, atomic one-use login factors and are
213
+ not accepted to disable MFA.
214
+ - MFA data is durable in Postgres for both refresh-token store choices. The
215
+ `create_mfa` migration must be applied before production and covered by
216
+ real-store/concurrency tests.
217
+
218
+ Keep auth construction explicit: `NewService(deps Dependencies, cfg
219
+ AuthConfig)`. `Dependencies` owns narrow ports such as `MFA`, `Providers`,
220
+ `RefreshTokens`, `RecoveryTokens`, `Mailer`, `Roles`, and `Clock`; `AuthConfig`
221
+ owns JWT, OAuth, recovery, and `MFASettings` policy. Do not pass the generated
222
+ shared config package into the application service or replace these contracts
223
+ with a positional constructor.
224
+ - Return a stable, generic client error for unexpected failures. Wrap causes
225
+ with `%w` or pass them to `apperror.NewInternal(cause)` so middleware can
226
+ log the server-side cause with `request_id`; production responses must not
227
+ expose that cause or internal field details.
228
+
229
+ ## Verification workflow
230
+
231
+ After changing Go templates or generated Go code, run the checks that match
232
+ the change (from the generated project root):
233
+
234
+ ```bash
235
+ gofmt -w <changed-go-files>
236
+ go test ./...
237
+ go test -race ./... # required for concurrency/auth/token changes
238
+ go vet ./...
239
+ golangci-lint run
240
+ ```
241
+
242
+ For schema/repository/auth adapter work, run the real-store tests with a
243
+ migrated disposable PostgreSQL/Redis instance and `REQUIRE_TEST_DB=true` or
244
+ `REQUIRE_TEST_REDIS=true`; a skipped integration test is not evidence of
245
+ correctness. MFA state still requires PostgreSQL even when refresh tokens use
246
+ Redis. When routes or OpenAPI templates change, inspect the updated
247
+ `docs/openapi.yaml` and run the configured OpenAPI linter.
248
+
249
+ When a River worker is installed, run `make river-migrate-test` in addition to
250
+ `make migrate-up-test`. The queue integration test must enqueue a real job and
251
+ observe the worker handler; a worker process that merely boots is not enough.
252
+
253
+ Only in the `go-scaffold` source repository, run the generator project's
254
+ `pnpm run verify` for CLI/template changes, then generate fresh sample projects
255
+ to inspect the output. A generated Go project does not contain the generator's
256
+ `package.json` or pnpm scripts; from that project run the Go checks above and
257
+ use the installed/local CLI for generation checks.
258
+
259
+ At each checkpoint record: branch and base, files changed, generated output
260
+ shape, tests and their exact result, and any limitation. Before handoff,
261
+ review `git diff --check`, `git diff`, and `git status --short`; leave unrelated
262
+ work untouched.
55
263
 
56
264
  ## Command quick reference
57
265
 
58
- - `go-scaffold generate module <name> --defaults` — safe minimal model + errors +
59
- repository + service/handler plumbing, wired into `cmd/api/wiring.go` and
60
- appended to `migrations/`. Add endpoints one at a time with `generate
61
- method`, or pass `--full` to opt into a CRUD skeleton with TODO DTO fields
62
- - `go-scaffold generate method <module> <name> --type <get|post|put|patch|delete> [--get-mode all|one] [--field <name>]` —
63
- patches an *existing* module's handler + service (and repository, for a
64
- `get --get-mode one --field` lookup) in place; never overwrites a method
65
- with the same name pick a different one if it collides. With OpenAPI
66
- enabled it also writes a valid TODO path document and wires the index;
67
- replace placeholder schemas while implementing the method
68
- - `go-scaffold undo module <name> -y` takes back a `generate module` that
69
- shouldn't have happened (typo'd name, domain decided against): deletes the
70
- package, un-wires main.go/OpenAPI, and deletes the module's migration files.
71
- It refuses if those migrations are committed to git or already applied to
72
- your database, since then they may exist somewhere this can't reach. Not for
73
- retiring a domain that has shipped — that's `generate migration drop_<table>`
74
- plus a reviewed data removal; the table is never dropped either way
75
- - Every route in this project is grouped under{{#if apiPrefix}} `/{{apiPrefix}}`{{else}} no prefix{{/if}}
76
- (set once at `create` time via `--api-prefix`)there is no per-domain
77
- versioning; a breaking API change gets a new domain package or a new DTO
78
- field, not a duplicated model
79
- - Full architecture rationale for this project's configuration: see
80
- `docs/architect/`
266
+ - `go-scaffold generate module <name> --defaults` — minimal domain entity,
267
+ errors, ports, application boundary, inbound/outbound adapters,
268
+ feature-local `composition.go`, root route registration, and versioned module
269
+ migrations. Add endpoints with
270
+ `generate method`, use `--profile crud` for a CRUD skeleton, or
271
+ `--profile cqrs` for separate command/query application handlers.
272
+ - `go-scaffold generate method <module> <name> --type <get|post|put|patch|delete> [--get-mode all|one] [--field <name>]` patches an existing
273
+ module's inbound handler, application service or command/query handler and,
274
+ for a GET-one lookup, its repository port/adapter plus test seam. It never
275
+ overwrites a same-named method.
276
+ - `go-scaffold generate migration <name>`reserves a timestamped SQL
277
+ migration pair for a reviewed schema change.
278
+ - `go-scaffold config` interactively change future module defaults;
279
+ `config show` prints the resolved manifest and `config validate` checks it
280
+ without writing.
281
+ - `go-scaffold check` validates every module's split hexagonal layout,
282
+ forbidden layer dependencies, sibling imports, and the exclusive
283
+ service-versus-CQRS contract.
284
+ - `go-scaffold undo module <name> -y` — removes an unshipped generated module,
285
+ its migration, and its wiring. It refuses once those migrations are
286
+ committed or applied; use a reviewed drop migration for a shipped domain.
287
+ - All routes share the project-wide prefix chosen at `create` time:
288
+ `{{#if apiPrefix}}/{{apiPrefix}}{{else}}(no prefix){{/if}}`. There is no
289
+ per-domain versioning.
290
+
291
+ Read `docs/architect/architecture.md`, `docs/architect/patterns.md`, and
292
+ `docs/architect/techstack.md` for the rationale behind these constraints.
@@ -68,7 +68,8 @@ tools:
68
68
  tidy:
69
69
  go mod tidy
70
70
 
71
- # creates the database itself (not the schema — AUTO_MIGRATE or migrate-up does that).
71
+ # creates the database itself (not the schema — the app's development path or
72
+ # migrate-up does that).
72
73
  # connects to the always-present "postgres" maintenance DB to run CREATE DATABASE,
73
74
  # since the target DB may not exist yet. Safe to re-run — skips if it already exists.
74
75
  # \gexec only works read from stdin, not through -c, hence the pipe.
@@ -2,38 +2,75 @@
2
2
 
3
3
  Gin + GORM backend, scaffolded by [go-scaffold](https://github.com/nakedev/go-scaffold). Organized by feature (domain), Postgres-backed, schema managed with [golang-migrate](https://github.com/golang-migrate/migrate).
4
4
 
5
- This project starts as a bare skeleton. Add a safe minimal domain with:
5
+ This project starts as a bare skeleton. Its defaults for future modules resolve
6
+ to `{{defaultModuleSurface}}` surface + `{{defaultApplicationStyle}}` application
7
+ style. The `generate module` wizard presents this as a named Lean, CRUD, CQRS,
8
+ or Advanced profile so you can choose by intent instead of memorising two
9
+ implementation axes. Add a domain with:
6
10
 
7
11
  ```bash
8
12
  go-scaffold generate module orders
9
13
  ```
10
14
 
11
- Add endpoints one at a time with `generate method`, or pass `--full` to opt
12
- into a CRUD skeleton whose DTO fields and business rules remain explicit TODOs.
15
+ The `generate module` wizard starts from those defaults. Add endpoints one at a
16
+ time with `generate method`, pass `--profile crud` for a CRUD skeleton, or use
17
+ `--profile cqrs` when commands and queries deserve separate application
18
+ handlers. DTO fields and business rules remain explicit TODOs. Run
19
+ `go-scaffold config` later to change defaults for future modules; existing
20
+ modules are unchanged. The older `--full` and `--cqrs` flags remain supported
21
+ for scripts.
13
22
 
14
23
  See `docs/architect/` for the conventions every generated module follows, and `AGENTS.md`/`CLAUDE.md` if you're working with an AI coding agent in this repo.
15
24
 
25
+ ## Installed optional features
26
+
27
+ This project records its generated capabilities in `go-scaffold.config.json`:
28
+
29
+ - authentication: {{#if auth}}enabled{{else}}not installed — use `go-scaffold add auth` when needed{{/if}}
30
+ - background jobs: {{#if worker}}enabled{{else}}not installed — use `go-scaffold add worker` when needed{{/if}}
31
+ - RBAC: {{#if rbac}}enabled{{else}}not installed — use `go-scaffold add rbac` after auth when needed{{/if}}
32
+ - metrics and tracing: {{#if observability}}enabled{{else}}not installed — use `go-scaffold add observability` when needed{{/if}}
33
+
34
+ The `add` commands refresh untouched generated architecture docs and this
35
+ section. If a maintainer has edited a document, the command leaves it alone
36
+ and reports that it needs a manual update.
37
+
16
38
  ## Layout
17
39
 
18
40
  ```
19
41
  cmd/
20
- └── api/
21
- └── main.go # config, slog, graceful shutdown, wires domains — the only entry point for now
42
+ ├── api/
43
+ │ ├── main.go # process entry point, config, slog, graceful shutdown
44
+ │ └── wiring.go # process composition root and route registration
22
45
  internal/
23
- ├── platform/ # talks to real external systems (DB, later: cache, queue, mail, ...)
24
- │ └── database/ # opens the connection + pool (GORM)
46
+ ├── composition/ # cross-feature adapters and process-level wiring
47
+ ├── platform/ # adapters for external systems
48
+ │ └── database/ # opens the PostgreSQL connection + pool
25
49
  ├── shared/ # pure logic/framework glue, no I/O
26
50
  │ ├── config/ # loads config from env
27
- │ ├── apperror/ # central error type (status + payload)
28
- │ ├── dberr/ # maps DB errors to constraint kind (IsDuplicate, IsForeignKey) — shared by every domain
29
- │ ├── httpx/ # HTTP helpers shared by every domain (ParseID, BindErr)
30
- │ ├── id/ # UUID v7 generation (id.New) — app-side, not a DB default
31
- │ ├── middleware/ # RequestID, Logger (slog), Error, CORS
32
- │ ├── pagination/ # parses ?limit=&offset=, response envelope
33
- │ └── tx/ # carries a transaction on the ctx (tx.Do / tx.From) so two repositories commit together
34
- └── app/ # domain packages empty until you `generate module`
51
+ │ ├── apperror/ # central error type (status + payload)
52
+ │ ├── dberr/ # maps DB errors to constraint kind — shared by every domain
53
+ │ ├── httpx/ # HTTP helpers shared by every domain
54
+ │ ├── id/ # UUID v7 generation (id.New)
55
+ │ ├── middleware/ # RequestID, Logger, Error, CORS{{#if auth}}, auth{{/if}}{{#if rbac}}, RBAC{{/if}}{{#if observability}}, metrics, tracing{{/if}}
56
+ │ ├── pagination/ # parses ?limit=&offset=, response envelope
57
+ │ └── tx/ # carries a transaction on the context
58
+ └── app/ # one package per domain, added with `generate module`
35
59
  ```
36
60
 
61
+ The tree shows the baseline. Installed optional features add the process and
62
+ platform packages listed in the section above without changing these module
63
+ boundaries.
64
+
65
+ Cross-feature adapters belong in `internal/composition/`, not in `cmd/api/`.
66
+ The API wiring file selects infrastructure, invokes those adapters, and
67
+ registers routes. Keep one-way dependencies and expose only narrow public
68
+ capabilities from each feature.
69
+
70
+ Optional features stay in the same architecture: auth owns user application
71
+ use cases and provider ports, RBAC owns role policy, worker owns queue/mail
72
+ adapters, and observability owns metrics/tracing setup.
73
+
37
74
  ## Run
38
75
 
39
76
  ```bash
@@ -42,7 +79,10 @@ make docker-up # local Postgres (postgres:5432)
42
79
  {{/if}}
43
80
  make db-create # create the {{dbName}} database itself (once — safe to re-run)
44
81
  go mod tidy
45
- make run # AUTO_MIGRATE=true creates the schema automatically in dev
82
+ {{#if worker}}{{#if (eq queue "river")}}make river-migrate # create River's job tables before queued mail
83
+ {{else}}# start Redis and set REDIS_URL before running the worker
84
+ {{/if}}{{/if}}
85
+ make run # APP_ENV=development enables the convenience schema bootstrap
46
86
  ```
47
87
 
48
88
  `make run`/`make test`/`make migrate-up`/`make migrate-down` load `.env` if
@@ -65,17 +105,27 @@ falls back to `docker exec` into whichever container is publishing
65
105
  other Postgres container{{else}}any Postgres container{{/if}} you already have running.
66
106
 
67
107
  Server listens on `:8080` (override with `PORT`). Ctrl+C = graceful shutdown.
108
+ {{#if worker}}`make dev` starts both the HTTP API and the worker; `make run` starts only the API.
109
+ {{/if}}
68
110
 
69
111
  ## Makefile
70
112
 
71
113
  ```bash
72
114
  make run # go run ./cmd/api
73
- make build # go build -o bin/api ./cmd/api
115
+ {{#if worker}}make dev # run cmd/api and cmd/worker together
116
+ make worker # run cmd/worker separately
117
+ {{#if (eq queue "river")}}make river-migrate # create/update River's Postgres job tables
118
+ make river-migrate-test # same River tables in TEST_DB_DSN
119
+ {{else}}# start Redis before running make worker
120
+ {{/if}}
121
+ {{/if}}{{#if auth}}make seed # run one-shot cmd/seed
122
+ {{/if}}make build # go build -o bin/api ./cmd/api
74
123
  make test # go test ./...
75
124
  make fmt # gofmt -w .
76
125
  make vet # go vet ./...
77
126
  make lint # golangci-lint run (see .golangci.yml)
78
127
  make tidy # go mod tidy
128
+ make tools # install pinned migrate and golangci-lint tools
79
129
  make db-create # create the database itself (safe to re-run)
80
130
  make db-drop # drop the database
81
131
  make migrate-up # apply migrations (reads DB_DSN from ENV_FILE)
@@ -103,10 +153,11 @@ migrate -path migrations -database "$DB_DSN" down 1
103
153
  migrate create -ext sql -dir migrations -seq=false add_something # same as `go-scaffold generate migration`, if you'd rather not use the CLI
104
154
  ```
105
155
 
106
- **dev:** leave `AUTO_MIGRATE=true` (default) so GORM's AutoMigrate creates the schema quickly.
107
- **prod:** set `AUTO_MIGRATE=false` and run `migrate up` as a separate deploy step — versioned, has rollback (`down`), doesn't lock the table the way AutoMigrate does once there's real data.
156
+ **dev:** `APP_ENV=development` enables the convenience table bootstrap.
157
+ **prod:** `APP_ENV=production` never bootstraps or mutates the schema; run
158
+ `migrate up` as a separate deploy step — versioned and rollback-capable.
108
159
 
109
- With `AUTO_MIGRATE=false`, the app checks the DB's applied migration version
160
+ In production, the app checks the DB's applied migration version
110
161
  against the migration files baked into the binary (embedded at build time)
111
162
  before it starts serving traffic — a stale or half-applied schema fails fast
112
163
  at boot with a clear message, instead of failing later on whatever query
@@ -128,12 +179,25 @@ make migrate-verify # up -> down-to-zero -> up, catches a broken down.sql befo
128
179
  | `PORT` | `8080` | |
129
180
  | `DB_DSN` | `postgres://postgres:postgres@localhost:5432/{{dbName}}?sslmode=disable` | used by both GORM and the `migrate` CLI |
130
181
  | `LOG_LEVEL` | `info` | debug/info/warn/error |
131
- | `AUTO_MIGRATE` | `true` | disable in prod and use `migrate up` (see Migrations) |
132
182
  | `DB_MAX_OPEN_CONNS` | `10` | |
133
183
  | `DB_MAX_IDLE_CONNS` | `10` | |
134
184
  | `DB_CONN_MAX_LIFETIME_MIN` | `5` | minutes |
135
185
  | `CORS_ALLOWED_ORIGINS` | `http://localhost:3000` | comma-separated frontend origins allowed to call this API with credentials (cookies) |
136
186
 
187
+ After `go-scaffold add auth`, `.env.example` also contains
188
+ `GOOGLE_OAUTH_REDIRECT_URI` (the exact browser callback URI registered with
189
+ Google), `JWT_REFRESH_MAX_TTL_MIN`, `OAUTH_STATE_TTL_MIN`, and
190
+ `AUTH_BROWSER_TOPOLOGY`. Refresh rotation uses the configured inactivity TTL
191
+ but never moves a token past its absolute lifetime. The backend stores a
192
+ one-time OAuth transaction binding provider, state, S256 challenge, and OIDC
193
+ nonce; the frontend still owns the callback route and sends the code to the
194
+ exchange endpoint. The provider redirect URI is separate from
195
+ `CORS_ALLOWED_ORIGINS`; the latter remains an exact-origin credentialed API
196
+ policy. Token responses use `Cache-Control: no-store` and `Pragma: no-cache`.
197
+ For `cross-site`, configure HTTPS origins, `COOKIE_SAMESITE=none`,
198
+ `COOKIE_SECURE=true`, and an exact allowed Origin; the API applies an Origin
199
+ guard independently of CORS.
200
+
137
201
  ## Tests
138
202
 
139
203
  Handler and service tests are fast unit tests: handlers depend on a narrow
@@ -152,9 +216,17 @@ make docker-up # local postgres first
152
216
  {{/if}}
153
217
  make db-create DB_NAME={{dbName}}_test
154
218
  make migrate-up-test
219
+ {{#if (eq queue "river")}}make river-migrate-test
220
+ {{/if}}
155
221
  TEST_DB_DSN=postgres://postgres:postgres@localhost:5432/{{dbName}}_test?sslmode=disable REQUIRE_TEST_DB=true go test ./...
156
222
  ```
157
223
 
224
+ {{#if (eq queue "river")}}The River integration test also starts a real worker,
225
+ enqueues a JSON job, and waits for its backend-neutral handler. River's own
226
+ schema history is separate from this project's application migrations, and the
227
+ `river-migrate` targets use the pinned River version from `go.mod`.
228
+ {{/if}}
229
+
158
230
  ## Error payload
159
231
 
160
232
  ```json
@@ -183,40 +255,51 @@ Then open `docs/openapi.bundled.yaml` — it needs no network and no running ser
183
255
  go-scaffold generate module orders
184
256
  ```
185
257
 
186
- Scaffolds the safe minimal `internal/app/order/` module, wires its empty route
187
- group/model into `cmd/api/wiring.go`, and appends a migration file. Add endpoints
188
- with `generate method`; use `--full` only when a CRUD skeleton is intentional.
189
- See `docs/architect/patterns.md` for the module shape and foreign-key rules.
258
+ Scaffolds an `internal/app/order/` module using the project defaults
259
+ (`{{defaultModuleSurface}}` + `{{defaultApplicationStyle}}`), wires its route
260
+ into `cmd/api/wiring.go`, and appends a migration file. Add endpoints
261
+ with `generate method`; use `--profile crud` for a CRUD skeleton or
262
+ `--profile cqrs` for separate command/query handlers. See
263
+ `docs/architect/patterns.md` for the module shape and foreign-key rules.
190
264
 
191
265
  ## Deploying
192
266
 
267
+ {{#if worker}}
193
268
  Two processes, one image, one target each:
194
269
 
195
270
  ```bash
196
271
  docker build --target api -t {{projectName}}-api .
197
- docker build --target worker -t {{projectName}}-worker . # once `add worker` exists
272
+ docker build --target worker -t {{projectName}}-worker .
198
273
  ```
199
274
 
200
275
  `api` is the last stage, so a plain `docker build .` builds it — the worker
201
- stage has to be asked for by name, and only resolves once `add worker` has
202
- created `cmd/worker`.
276
+ stage has to be asked for by name.{{#if auth}} `cmd/seed` is a one-shot
277
+ operational binary run through `make seed`, not a long-running target.{{/if}}
278
+ {{else}}
279
+ The base project has one long-running `cmd/api` image. A worker target becomes
280
+ available after `go-scaffold add worker`; do not deploy a worker that has not
281
+ been generated.
282
+ {{/if}}
203
283
 
204
284
  Both stages are distroless and run as `nonroot`, so there is no shell in either
205
285
  image. Debug against the `build` stage instead:
206
286
  `docker run --rm -it --entrypoint sh $(docker build -q --target build .)`.
207
287
 
208
- `cmd/api` serves HTTP. `cmd/worker` consumes the queue and is a separate
209
- deployment with **no port and no health endpoint** — scale it independently,
210
- and remember that not running it means queued mail is never sent, silently.
288
+ `cmd/api` serves HTTP.{{#if worker}} `cmd/worker` consumes the queue and is a
289
+ separate deployment with **no port and no health endpoint** — scale it
290
+ independently, and remember that not running it means queued mail is never
291
+ sent, silently.{{/if}}
211
292
 
212
293
  Before the first deploy of a release:
213
294
 
214
295
  | | |
215
296
  |---|---|
216
- | `AUTO_MIGRATE` | leave unset it defaults off when `APP_ENV=production`, and the app checks the applied migration version instead |
297
+ | `APP_ENV` | `production` | production disables schema bootstrap and requires the applied migration version |
217
298
  | migrations | `migrate -path migrations -database "$DB_DSN" up` as its own step, before the new binaries roll |
218
299
  | `TRUSTED_PROXIES` | the CIDRs of your ingress, or the auth rate limiter keys on a header anyone can send |
219
300
  | `COOKIE_SECURE` | `true` · `COOKIE_SAMESITE=none` as well if your frontend is on a different site |
301
+ | `JWT_REFRESH_MAX_TTL_MIN` | `43200` | absolute refresh-token lifetime; rotation cannot extend beyond it |
302
+ | `OAUTH_STATE_TTL_MIN` | `10` | lifetime of the one-time server-side OAuth state transaction |
220
303
  | `/metrics` | reachable in-cluster for Prometheus, blocked at the ingress |
221
304
  | `JWT_SECRET`, `SMTP_HOST` | the app refuses to start in production without real values |
222
305