@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
package/README.md CHANGED
@@ -1,397 +1,689 @@
1
1
  # @nakedev/go-scaffold
2
2
 
3
- A CLI that scaffolds a Gin + GORM + PostgreSQL Go backend, then keeps
4
- generating consistent domain modules into that project as it grows — the Go
5
- counterpart to nest-scaffold.
3
+ @nakedev/go-scaffold is an npm CLI for creating Gin + GORM + PostgreSQL Go
4
+ backend projects and extending them with consistent domain modules, endpoints,
5
+ migrations, authentication, background jobs, RBAC, and observability.
6
6
 
7
- You don't hand-wire a new domain into `cmd/api/wiring.go`, write the
8
- handler/service/repository boilerplate, or decide error-handling conventions
9
- each time — the CLI does that, and every module it generates follows the
10
- same shape as the last one.
7
+ The normal workflow is simple: install the CLI with npm, run the wizard, then
8
+ run the same CLI from the generated project whenever the backend grows.
11
9
 
12
- ## Install
10
+ ## Install with npm
13
11
 
14
- ```bash
15
- npm install -g @nakedev/go-scaffold
16
- ```
12
+ Install the CLI globally when you expect to use it repeatedly:
17
13
 
18
- Or run it without installing:
14
+ ~~~bash
15
+ npm install --global @nakedev/go-scaffold
19
16
 
20
- ```bash
17
+ go-scaffold --version
18
+ go-scaffold --help
19
+ ~~~
20
+
21
+ npx is also supported when you do not want a global installation:
22
+
23
+ ~~~bash
21
24
  npx @nakedev/go-scaffold create my-api
22
- ```
25
+ npx @nakedev/go-scaffold --help
26
+ ~~~
27
+
28
+ ### Requirements
23
29
 
24
- Working on the CLI itself (not just using it)? Clone the repo, then:
30
+ - Node.js >=22.13 to run the CLI. npm and npx are included with Node.js.
31
+ - Go >=1.25 to build and run the generated project.
32
+ - PostgreSQL to run the application. Docker is optional; create can generate
33
+ a Docker Compose PostgreSQL service for local development.
34
+
35
+ The CLI itself only needs Node.js. Go and PostgreSQL are needed after a project
36
+ has been generated.
37
+
38
+ ## Developing the CLI
39
+
40
+ This repository is the source for the CLI and its generated-project contract.
41
+ Keep command logic in `src/`, emitted files in `templates/`, and behavior
42
+ covered by `tests/`. Generated-project `AGENTS.md`, Claude skill guidance, and
43
+ `docs/architect/` files are template outputs; update the template and a
44
+ regression test when their contract changes.
25
45
 
26
46
  ```bash
27
- pnpm install
47
+ pnpm install --frozen-lockfile
28
48
  pnpm run build
49
+ pnpm run verify
29
50
  node bin/go-scaffold.js create my-api --defaults
30
51
  ```
31
52
 
32
- `npm link` / `pnpm link --global` may not put the binary on your `PATH`
33
- depending on your machine's npm/pnpm global-bin config — running
34
- `node bin/go-scaffold.js ...` directly sidesteps that.
53
+ `pnpm run verify` is the required local gate: it builds the TypeScript CLI,
54
+ runs unit and integration tests, and runs the generated-project smoke suite.
55
+ The smoke suite needs the Go toolchain, PostgreSQL/migrate, and golangci-lint;
56
+ do not treat an unavailable external check as a passing generated-project
57
+ check. See [`AGENTS.md`](AGENTS.md) for the source/template/test workflow.
35
58
 
36
- ## Quick start
59
+ ### Release contract
60
+
61
+ `package.json` is the version source. Releases are published by
62
+ [`.github/workflows/release.yml`](.github/workflows/release.yml) only from a
63
+ clean, matching annotated `vX.Y.Z` tag. Before pushing a release tag, verify
64
+ the exact commit with:
37
65
 
38
66
  ```bash
67
+ pnpm run release:check -- v0.5.0
68
+ ```
69
+
70
+ The release workflow runs the full verification gate before npm publish and
71
+ GitHub Release creation.
72
+
73
+ ## Quick start
74
+
75
+ ~~~bash
76
+ npm install --global @nakedev/go-scaffold
39
77
  go-scaffold create my-api
78
+
40
79
  cd my-api
41
- make docker-up # if you kept Docker + PostgreSQL
42
- make db-create # create the database itself (safe to re-run)
80
+ cp .env.example .env # optional: edit local settings
81
+ make docker-up # only when create included Docker + PostgreSQL
82
+ make db-create
43
83
  go mod tidy
44
84
  make run
45
- ```
85
+ ~~~
46
86
 
47
- Then grow the project without leaving the CLI:
87
+ The default create wizard includes Docker and OpenAPI files. If you choose
88
+ --no-docker, start PostgreSQL separately before running make db-create.
48
89
 
49
- ```bash
90
+ Add a first domain from the generated project directory:
91
+
92
+ ~~~bash
50
93
  go-scaffold generate module orders
51
94
  go-scaffold generate method orders approve --type patch
52
- ```
95
+ ~~~
53
96
 
54
- ## Commands
97
+ The generated project starts with shared infrastructure and no business
98
+ domains. generate module adds the domain; generate method adds endpoints one
99
+ at a time.
55
100
 
56
- Every `add` command shows what it's about to do and asks before writing;
57
- `-y/--yes` skips that (and `--defaults` implies it) for CI and scripts.
58
- Running `go-scaffold` with no arguments picks the command from a menu.
101
+ ## How the wizard works
59
102
 
103
+ You can use the CLI interactively or provide answers as arguments and flags.
60
104
 
61
- ### `create <name>` scaffold a new project
105
+ ### Start with the top-level wizard
62
106
 
63
- ```bash
64
- go-scaffold create my-api # interactive wizard
65
- go-scaffold create my-api --defaults # no prompts, CI-friendly
66
- go-scaffold create my-api --defaults --no-docker --api-prefix beta
67
- ```
107
+ Running the CLI without a command opens a menu:
108
+
109
+ ~~~bash
110
+ go-scaffold
111
+ ~~~
112
+
113
+ From a generated project directory, the menu offers:
68
114
 
69
- Produces a **bare skeleton only** — `cmd/api`, the shared platform packages
70
- (config/apperror/dberr/httpx/id/middleware/pagination/tx), Docker+Postgres,
71
- migrations folder, and the standards docs (`docs/architect/`, `AGENTS.md`,
72
- `CLAUDE.md`, `.claude/skills/go-scaffold/`). No domain modules add those
73
- with `generate module`.
115
+ 1. Create a new project
116
+ 2. Generate a module, method, or migration
117
+ 3. Configure defaults for future modules
118
+ 4. Add auth, worker, RBAC, or observability
119
+ 5. Undo a generated module
120
+
121
+ When run outside a generated project, only create can run, so the CLI goes
122
+ straight to the project-creation flow.
123
+
124
+ You can also open a more focused wizard:
125
+
126
+ ~~~bash
127
+ go-scaffold generate # choose module, method, or migration
128
+ go-scaffold add # choose worker, auth, RBAC, or observability
129
+ go-scaffold undo # choose a generated module and confirm
130
+ ~~~
131
+
132
+ ### Answer only what is missing
133
+
134
+ The command is the first choice, followed by only the questions that still need
135
+ an answer. A value passed as a flag is not asked again.
136
+
137
+ For example:
138
+
139
+ ~~~text
140
+ go-scaffold create my-api
141
+ 1. Docker + PostgreSQL?
142
+ 2. OpenAPI files?
143
+ 3. Metrics + tracing?
144
+ 4. API route prefix?
145
+ 5. Default module profile: Lean / CRUD / CQRS / Advanced?
146
+ 6. Create project with these settings?
147
+
148
+ cd my-api
149
+ go-scaffold generate module orders
150
+ 1. Module profile: Lean / CRUD / CQRS / Advanced?
151
+ 2. Require an access token? # only when auth is installed
152
+ 3. Permission code? # only when auth + RBAC are installed
153
+ ~~~
154
+
155
+ Advanced is the wizard option that exposes the two lower-level module choices
156
+ separately: module surface and application boundary.
157
+
158
+ ### Non-interactive usage
159
+
160
+ Use flags for scripts and CI. --defaults skips the optional wizard questions
161
+ where the command supports it. A project name is still required for scripted
162
+ create usage.
163
+
164
+ ~~~bash
165
+ go-scaffold create my-api --defaults
166
+ go-scaffold generate module orders --defaults
167
+ go-scaffold add worker --defaults
168
+ go-scaffold add auth --defaults
169
+ ~~~
170
+
171
+ Important behavior:
172
+
173
+ - In a terminal, omitted values open the relevant wizard.
174
+ - Without an interactive terminal, omitted values cause an error before files
175
+ are written. Pass the missing flags or use --defaults.
176
+ - --yes / -y skips an add or undo confirmation. It does not necessarily answer
177
+ every other wizard question; pass the choice flags too.
178
+ - Every add command shows a summary and asks for confirmation unless --yes or
179
+ --defaults is used.
180
+ - Run any command with --help to see its current options.
181
+
182
+ ## Command overview
183
+
184
+ | Command | Purpose | Alias |
185
+ |---|---|---|
186
+ | create [name] | Create a new Go backend project | c |
187
+ | check | Validate hexagonal layout, layer dependencies, and service/CQRS contract | — |
188
+ | generate | Open the module/method/migration wizard | g |
189
+ | generate module [name] | Add a domain module | g m |
190
+ | generate method [module] [name] | Add one endpoint to an existing module | g me |
191
+ | generate migration [name] | Create a timestamped SQL migration pair | g mig |
192
+ | config | Set defaults for future modules | — |
193
+ | config show | Print the resolved project configuration | — |
194
+ | config validate | Validate configuration without changing files | — |
195
+ | add | Open the infrastructure-feature wizard | — |
196
+ | add worker | Add background jobs, mail, and cmd/worker | — |
197
+ | add auth | Add email/password and provider authentication | — |
198
+ | add rbac | Add roles, permissions, and authorization middleware | — |
199
+ | add observability | Add Prometheus metrics and OpenTelemetry tracing | — |
200
+ | undo module [name] | Remove a generated module and its wiring | undo m |
201
+
202
+ All commands except create are intended to run from the generated project
203
+ directory.
204
+
205
+ ## create [name] — create a project
206
+
207
+ ~~~bash
208
+ go-scaffold create my-api # interactive wizard
209
+ go-scaffold create my-api --defaults # use documented defaults
210
+ go-scaffold create my-api --defaults --no-docker
211
+ go-scaffold create my-api --api-prefix v1
212
+ go-scaffold c my-api
213
+ ~~~
214
+
215
+ If [name] is omitted, the wizard asks for it. With a simple name, it becomes
216
+ both the project directory and the Go module path in the generated go.mod. A
217
+ full Go module path is also accepted; in that case the project directory uses
218
+ the path's final segment.
219
+
220
+ ### Creation options
74
221
 
75
222
  | Option | Effect |
76
223
  |---|---|
77
- | `--defaults` | Skip the wizard, use defaults (Docker on, OpenAPI docs on, no route prefix) |
78
- | `--no-docker` | Skip `docker-compose.yml` |
79
- | `--no-openapi-docs` | Skip `docs/openapi.yaml` |
80
- | `--observability` | Prometheus `/metrics` + OpenTelemetry tracing (off by default — `add observability` does the same later) |
81
- | `--api-prefix <prefix>` | URL prefix every route is grouped under opt in with e.g. `v1` or `api/v1`; omit it for none |
82
-
83
- Without `--defaults`, an interactive wizard asks the same four questions
84
- skipping any a flag already answered, so `create my-api --no-docker` never asks
85
- about Docker and never scaffolds it.
86
- The prefix is a single project-wide choice made once at `create` time —
87
- there's no per-domain versioning (a domain that needs a real breaking change
88
- gets a new domain package or a new DTO field, not a duplicated model pointed
89
- at the same table under a different URL — see "Why no per-domain versioning"
90
- below).
91
-
92
- **Config file** — every `create` writes `go-scaffold.config.json` to the
93
- project root; `generate` reads it back (or auto-detects from `go.mod` /
94
- directory layout if missing).
95
-
96
- ### `generate module <name>` (alias `m`) — add a domain module
224
+ | --defaults | Skip settings prompts; use Docker and OpenAPI, no API prefix, and Lean defaults for future modules |
225
+ | --no-docker | Do not create docker-compose.yml or a local PostgreSQL service |
226
+ | --no-openapi-docs | Do not create docs/openapi.yaml or OpenAPI files for generated features/modules |
227
+ | --observability | Include Prometheus /metrics and OpenTelemetry tracing at creation time |
228
+ | --api-prefix <prefix> | Put every API route under a prefix such as v1 or api/v1 |
229
+ | --module-profile <lean\|crud\|cqrs> | Set the default profile for future generate module commands |
230
+ | --module-surface <minimal\|crud> | Legacy way to set the future module surface; prefer --module-profile |
231
+ | --application-style <service\|cqrs> | Legacy way to set the future application boundary; prefer --module-profile |
97
232
 
98
- ```bash
99
- go-scaffold generate module orders # asks for the shape (and auth, if installed)
100
- go-scaffold generate module orders --full # opt-in CRUD skeleton, no prompt
101
- go-scaffold generate module orders --defaults # safe minimal module, no prompt (CI/scripting)
102
- ```
233
+ The route prefix is a project-wide choice. For example, --api-prefix v1 puts a
234
+ module route under /v1/orders.
103
235
 
104
- Anything you don't pass as a flag is asked for; `--defaults` takes the
105
- documented defaults (minimal, no auth) and asks nothing.
236
+ ### What create generates
106
237
 
107
- `--full` scaffolds:
238
+ create produces a runnable base skeleton, not a business domain. It includes
239
+ the API entrypoint, shared packages, database connection, migrations folder,
240
+ development commands, and architecture documentation. Add domains later with
241
+ generate module.
108
242
 
109
- ```text
110
- internal/app/order/
111
- ├── model/model.go # domain model + GORM table (id/created_at/updated_at — add real fields yourself; a folder so multi-table domains can add more files)
112
- ├── dto.go # request/response structs (empty stubs — add real fields yourself)
113
- ├── errors.go # ORDER_NOT_FOUND / ORDER_CONFLICT / ORDER_HAS_REFERENCES / ORDER_STALE
114
- ├── repository.go # GORM data access
115
- ├── service.go # business logic + repository interface (mockable)
116
- ├── handler.go # Gin routes, registered under the project's API prefix
117
- ├── service_test.go # unit test, function-backed repository stub
118
- ├── handler_test.go # HTTP unit test, service stub, no DB
119
- └── repository_test.go # Postgres integration test against migrated schema
120
- ```
243
+ Every project also gets go-scaffold.config.json. It stores the project defaults,
244
+ installed features, API prefix, and the resolved profile of each module so later
245
+ CLI commands can continue from the same choices.
121
246
 
122
- The default minimal mode scaffolds the same `model`/`errors`/`repository` (so `generate
123
- method` always has a full data-access surface to call), but `dto`/`service`/
124
- `handler` start empty — no default CRUD, no routes, just the plumbing
125
- (`Register()`, the `repository` interface, `wrapFindErr`) that `generate
126
- method` patches into. Use it when a domain doesn't need the full REST
127
- surface, or you'd rather add endpoints one at a time.
247
+ ## Module profiles
128
248
 
129
- Both modes also:
249
+ The module wizard presents a useful profile before exposing lower-level
250
+ architecture choices:
130
251
 
131
- - Register the module in `cmd/api/wiring.go` (via marker comments — see
132
- `// go-scaffold:*` in that file) — full wires an actual route, minimal
133
- wires an empty route group
134
- - Create the module's own Postgres schema (`<module>_svc`, e.g. `order_svc`)
135
- and add the model to the `AutoMigrate(...)` call
136
- - Append `migrations/<timestamp>_create_<plural>.{up,down}.sql`, which creates that
137
- same schema for `AUTO_MIGRATE=false`/production
252
+ | Profile | Generated shape | Use it when |
253
+ |---|---|---|
254
+ | lean | Minimal surface + one service | The domain should start small and gain endpoints as requirements become real |
255
+ | crud | CRUD surface + one service | The domain needs list/get/create/update/delete starter endpoints |
256
+ | cqrs | Minimal surface + command/query handlers | Reads and writes have different application concerns |
257
+ | Advanced | Choose surface and application style separately | You need a custom combination, such as CRUD + CQRS |
138
258
 
139
- What it does **not** do: invent your fields or wire foreign keys between
140
- domains see `docs/architect/patterns.md` in the generated project for the
141
- conventions to follow by hand.
259
+ minimal means no default CRUD endpoints are invented. CQRS separates the
260
+ command and query application paths inside the same modular monolith; it does
261
+ not add a second database, broker, or event-sourcing system.
142
262
 
143
- ### `generate method <module> <name>` (alias `me`) add one endpoint
263
+ The generated code is DDD-shaped: each domain has a package boundary,
264
+ repository port, application boundary, delivery adapter, and shared error
265
+ conventions. The CLI does not invent your aggregates, fields, value objects,
266
+ events, or business invariants.
144
267
 
145
- ```bash
268
+ ## generate module [name] — add a domain
269
+
270
+ ~~~bash
271
+ go-scaffold generate module orders # profile wizard
272
+ go-scaffold generate module orders --profile lean
273
+ go-scaffold generate module orders --profile crud
274
+ go-scaffold generate module orders --profile cqrs
275
+ go-scaffold generate module orders --defaults # use project defaults
276
+ go-scaffold g m orders --profile crud
277
+ ~~~
278
+
279
+ When no name is supplied, the wizard asks for a singular module name such as
280
+ order or product. The package name is normalised for Go, while routes and
281
+ tables use plural names such as orders and order_items.
282
+
283
+ ### Module options
284
+
285
+ | Option | Effect |
286
+ |---|---|
287
+ | --profile <lean\|crud\|cqrs> | Select a named profile without opening the profile question |
288
+ | --full | Legacy alias for the CRUD surface |
289
+ | --cqrs | Legacy axis flag for command/query handlers; combine with --full for CRUD + CQRS |
290
+ | --auth | Require a valid access token for this module's routes; needs add auth |
291
+ | --permission <code> | Also require an RBAC permission such as orders:manage; needs add rbac and --auth |
292
+ | --defaults | Use the project's recorded module defaults, skip prompts, and keep the module public |
293
+
294
+ --profile cannot be combined with the legacy --full or --cqrs flags. With
295
+ --defaults, a fresh project generates the Lean shape. If the project has auth
296
+ installed, the module remains public unless --auth is explicitly passed.
297
+
298
+ ### Module output
299
+
300
+ For orders, the module package is internal/app/order/:
301
+
302
+ ~~~text
303
+ internal/app/order/
304
+ ├── domain/
305
+ │ ├── entity.go # business state and invariants
306
+ │ └── errors.go # domain error sentinels
307
+ ├── ports/
308
+ │ └── repository.go # consumer-owned persistence ports
309
+ ├── application/
310
+ │ ├── dto.go # use-case inputs and response mapping
311
+ │ ├── service.go # service-style application boundary
312
+ │ └── service_test.go # application unit tests
313
+ ├── adapters/
314
+ │ ├── inbound/http/
315
+ │ │ ├── handler.go # Gin delivery adapter
316
+ │ │ └── handler_test.go # HTTP adapter tests
317
+ │ └── outbound/postgres/
318
+ │ ├── model.go # persistence model + mapping
319
+ │ ├── repository.go # GORM adapter
320
+ │ └── repository_test.go # PostgreSQL integration tests
321
+ └── composition.go # feature-local object graph
322
+ ~~~
323
+
324
+ CRUD modules contain the starter list/get/create/update/delete methods. Lean
325
+ modules keep the endpoint surface small so it can be extended with
326
+ generate method.
327
+
328
+ CQRS modules additionally contain:
329
+
330
+ ~~~text
331
+ internal/app/order/
332
+ └── application/
333
+ ├── commands.go # command port + state-changing handlers
334
+ ├── queries.go # query port + read-only handlers
335
+ └── cqrs_test.go # command/query boundary tests
336
+ ~~~
337
+
338
+ `application/service.go` OR `application/commands.go` plus
339
+ `application/queries.go` is mutually exclusive inside one module. Service and
340
+ CQRS can coexist across modules in the same modular monolith. The root package
341
+ contains only `composition.go`; inbound and outbound adapters are the
342
+ framework/database edges.
343
+
344
+ Both shapes register the module in cmd/api/wiring.go, create a module-owned
345
+ PostgreSQL schema such as order_svc, append a timestamped create migration, and
346
+ record the resolved module profile in go-scaffold.config.json.
347
+
348
+ The CLI creates the structural TODOs, not your domain rules. Add real model and
349
+ DTO fields, implement business behavior, and review the generated migration
350
+ before applying it.
351
+
352
+ ## generate method [module] [name] — add one endpoint
353
+
354
+ ~~~bash
146
355
  go-scaffold generate method orders approve --type patch
147
356
  go-scaffold generate method orders findByStatus --type get --get-mode one --field status
148
357
  go-scaffold g me orders findOverdue --type get --get-mode all
149
- ```
358
+ ~~~
359
+
360
+ If the module, method name, or endpoint details are omitted, the wizard asks
361
+ for them. The module selector lists modules that exist on disk, so the command
362
+ does not require memorising the normalised Go package name.
150
363
 
151
- Patches an *existing* module's `handler.go`/`service.go` in place via the
152
- same marker-comment approach as `main.go` — never a whole new module. Never
153
- overwrites a method with the same name; picks a different one or errors.
364
+ ### Method options
154
365
 
155
366
  | Option | Effect |
156
367
  |---|---|
157
- | `--type <get\|post\|put\|patch\|delete>` | HTTP verb |
158
- | `--get-mode <all\|one>` | For `get` only list-style vs. single-record lookup |
159
- | `--field <name>` | For `get --get-mode one` the lookup field (e.g. `email`, `status`); can't be `id` |
368
+ | --type <get\|post\|put\|patch\|delete> | HTTP verb |
369
+ | --get-mode <all\|one> | For GET only: list endpoint or single-record lookup |
370
+ | --field <name> | For get --get-mode one: lookup column such as email, status, or slug; id is reserved |
371
+
372
+ The generated route and code depend on the method type:
160
373
 
161
- | `--type` | Route | What's generated |
374
+ | Input | Route shape | Result |
162
375
  |---|---|---|
163
- | `get --get-mode all` | `GET /<plural>/<kebab-name>` | reuses `FindAll` TODO to add real filtering |
164
- | `get --get-mode one --field <f>` | `GET /<plural>/<f>/:<f>` | a real `FindBy<F>` query added to the repository (+ its interface + function-backed repository test stub) |
165
- | `post` | `POST /<plural>/<kebab-name>` | adds a body DTO; service is a TODO stub |
166
- | `put` / `patch` | `<VERB> /<plural>/:id/<kebab-name>` | finds by id, TODO before saving (safe no-op until implemented) |
167
- | `delete` | `DELETE /<plural>/:id/<kebab-name>` | TODO stub |
376
+ | get --get-mode all | GET /<plural>/<method> | Uses the module's list query; add real filtering yourself |
377
+ | get --get-mode one --field <field> | GET /<plural>/<field>/:<field> | Adds a FindBy<Field> query and a column/index migration |
378
+ | post | POST /<plural>/<method> | Adds a request body DTO and a TODO service method |
379
+ | put / patch | <VERB> /<plural>/:id/<method> | Loads by ID and leaves the update behavior as a TODO |
380
+ | delete | DELETE /<plural>/:id/<method> | Adds a delete endpoint stub |
168
381
 
169
- Business logic is always left as a `TODO`-marked stub that compiles and
170
- returns a clean `500` rather than inventing behavior — see
171
- `docs/architect/patterns.md` in the generated project.
382
+ For CQRS modules, GET methods are added to application/queries.go; other
383
+ methods are added to application/commands.go. Service modules use
384
+ application/service.go. Existing method names are never overwritten.
172
385
 
173
- When OpenAPI docs are enabled, `generate method` also creates a valid TODO stub
174
- under `docs/<plural>/methods/` and wires the route into `docs/openapi.yaml`.
175
- Replace its placeholder request/response schemas while implementing the TODO.
386
+ The generated business logic is deliberately a compiling TODO and returns a
387
+ clean not-implemented response until you implement it. If OpenAPI files were
388
+ enabled, a method document is also added under docs/<plural>/methods/ and
389
+ linked from docs/openapi.yaml.
176
390
 
177
- **Drift check** — `generate` type-checks the project (`go vet ./...`) before and
178
- after it writes. If the project was fine beforehand and the generated code
179
- doesn't compile, it stops with the compiler output instead of leaving you to
180
- find it later:
391
+ ## generate migration [name] reserve a migration pair
181
392
 
182
- ```text
183
- the generated code doesn't compile, but this project was fine a moment ago.
393
+ ~~~bash
394
+ go-scaffold generate migration add_status_to_orders
395
+ go-scaffold g mig add_status_to_orders
396
+ ~~~
184
397
 
185
- The most likely cause is drift: this project's internal/shared layer has been edited
186
- since it was scaffolded, so the templates this CLI emits no longer match it.
398
+ This creates:
187
399
 
188
- scaffolded with: go-scaffold 0.1.2
189
- this CLI: go-scaffold 0.3.0
190
- ```
400
+ ~~~text
401
+ migrations/<timestamp>_add_status_to_orders.up.sql
402
+ migrations/<timestamp>_add_status_to_orders.down.sql
403
+ ~~~
191
404
 
192
- That happens because `generate`'s templates are written against the `shared/`
193
- layer `create` emits editing that layer is normal work, but it moves the
194
- project away from what this CLI's templates expect. `create` records its own
195
- version in `go-scaffold.config.json` so the message can name both sides. A
196
- project that was *already* broken (mid-refactor, or `go mod tidy` not run yet)
197
- is left alone — only a passed-before/broken-after transition is reported. No Go
198
- on `PATH` means the check is skipped.
405
+ Both files contain TODO comments. The CLI reserves the timestamp and filename;
406
+ you write the SQL and then apply it with:
199
407
 
200
- ### `generate migration <name>` (alias `mig`) — reserve a SQL migration pair
408
+ ~~~bash
409
+ make migrate-up
410
+ ~~~
201
411
 
202
- ```bash
203
- go-scaffold generate migration add_status_to_orders
204
- ```
412
+ ## config — configure future module defaults
205
413
 
206
- Creates timestamped `migrations/<version>_<name>.up.sql` and `.down.sql` TODO
207
- stubs. The CLI reserves the names; you own the SQL and should apply it with
208
- `make migrate-up` (or `migrate -path migrations -database "$DB_DSN" up`).
414
+ Run this from the generated project directory:
209
415
 
210
- ### `add worker` — add background job processing
416
+ ~~~bash
417
+ go-scaffold config # profile wizard
418
+ go-scaffold config show # print JSON; no wizard
419
+ go-scaffold config validate # validate; no file changes
420
+ ~~~
211
421
 
212
- ```bash
213
- go-scaffold add worker # asks where jobs should live
214
- go-scaffold add worker --queue postgres # River (default)
215
- go-scaffold add worker --queue redis # Asynq
216
- go-scaffold add worker --defaults # no prompt, Postgres
217
- ```
422
+ config changes defaults for future modules only. Existing modules keep their
423
+ recorded surface and application style.
218
424
 
219
- Adds `internal/platform/queue` (a backend-neutral contract plus one adapter),
220
- async email delivery, and `cmd/worker`.
425
+ ## add add optional project features
221
426
 
222
- | | `--queue postgres` (River) | `--queue redis` (Asynq) |
223
- |---|---|---|
224
- | Extra service to run | none | Redis |
225
- | Needed by `add auth` | no | no — `add auth --store` decides that separately |
226
- | Enqueue joins your DB transaction | yes | **no** — needs an outbox |
227
- | Throughput | thousands/sec | tens of thousands/sec |
228
- | Inspect pending jobs | plain SQL | asynqmon |
229
-
230
- The default is Postgres because a job enqueued inside `tx.Do` is then only
231
- delivered if that transaction commits — no more welcome emails for signups
232
- that rolled back. Run `make river-migrate` once per database to create
233
- River's tables, then `make worker`.
234
-
235
- Application code only ever sees `queue.Job`, `queue.Enqueuer` and
236
- `queue.Handler` — no backend package appears outside its own adapter file, so
237
- switching later means writing one adapter, not touching every module that
238
- enqueues something.
239
-
240
- ```go
241
- type WelcomeEmail struct{ To string `json:"to"` }
242
- func (WelcomeEmail) Kind() string { return "email:welcome" }
243
-
244
- // cmd/api — the job is discarded with the transaction if this fails
245
- tx.Do(ctx, db, func(ctx context.Context) error {
246
- if err := repo.Create(ctx, u); err != nil { return err }
247
- return jobs.Enqueue(ctx, WelcomeEmail{To: u.Email}, nil)
248
- })
249
- ```
427
+ Running go-scaffold add opens a feature wizard. It shows features already
428
+ installed as unavailable and explains that RBAC requires auth. Direct feature
429
+ commands are useful for scripts and explicit usage.
250
430
 
251
- ### `add auth` add email/password authentication
431
+ After an incremental feature is added, the CLI refreshes the generated
432
+ README and architect docs to reflect the resolved configuration. It only does
433
+ so when each file still matches the version it would have generated; edited
434
+ docs are preserved and reported for manual maintenance.
252
435
 
253
- ```bash
254
- go-scaffold add auth # asks where tokens should live, then confirms
255
- go-scaffold add auth --store postgres # tokens in Postgres, no extra service
256
- go-scaffold add auth --store redis # tokens in Redis, exact across replicas
257
- go-scaffold add auth --defaults # Postgres, no prompt at all (CI/scripting)
258
- ```
436
+ ### add worker — background jobs and mail
259
437
 
260
- Adds JWT access tokens, refresh-token rotation with reuse detection,
261
- registration/login/logout, password reset, email verification, failed-login
262
- lockout, and Google OAuth routes. Apply the generated migrations;
263
- `AUTO_MIGRATE=true` is convenient in development, while production should use
264
- `migrate up`.
438
+ ~~~bash
439
+ go-scaffold add worker # queue wizard + confirmation
440
+ go-scaffold add worker --queue postgres # River in PostgreSQL
441
+ go-scaffold add worker --queue redis # Asynq in Redis
442
+ go-scaffold add worker --defaults # PostgreSQL/River, no prompts
443
+ go-scaffold add worker --queue redis --yes # skip confirmation
444
+ ~~~
265
445
 
266
- No prerequisites. On a project with no worker the verification and reset mail
267
- is sent inline, and `add worker` later moves it onto the queue for you — the
268
- two endpoints that send mail block on SMTP until you do.
446
+ The command adds internal/platform/queue, mail delivery, and cmd/worker.
269
447
 
270
- | `--store` | Tokens and rate-limit counters | Extra service |
448
+ | Queue option | Storage | Operational note |
271
449
  |---|---|---|
272
- | `postgres` (default) | `user_svc.auth_tokens`, counters in-process | none |
273
- | `redis` | Redis | Redis |
450
+ | postgres (River) | Project PostgreSQL database | No extra service; enqueueing can join the database transaction |
451
+ | redis (Asynq) | Redis | Run Redis separately; enqueueing cannot join a PostgreSQL transaction |
274
452
 
275
- The rate limiter follows the store rather than being chosen separately, because
276
- "I want this exact across replicas" is one decision. With `postgres` the per-IP
277
- budget is per-replica; the failed-login lockout is in Postgres either way, since
278
- that one can't be approximate.
453
+ For River:
279
454
 
280
- ### `add rbac` — add roles and permissions
455
+ ~~~bash
456
+ make river-migrate
457
+ make river-migrate-test # before required real-store tests
458
+ make worker
459
+ ~~~
281
460
 
282
- ```bash
283
- go-scaffold add rbac
284
- go-scaffold generate module secrets --auth --permission secret:manage
285
- ```
461
+ For Redis, start Redis first and then run make worker. make dev runs the API and
462
+ worker together after worker support has been added.
286
463
 
287
- Requires `add auth`. Adds role/permission administration, cached authorization
288
- middleware, and role assignment. Its migration seeds the default roles and
289
- permissions, so apply it with `migrate up`: AutoMigrate creates tables but does
290
- not run SQL seed statements.
464
+ ### add auth email/password and provider auth
291
465
 
292
- ### `add observability` — add metrics + tracing
466
+ ~~~bash
467
+ go-scaffold add auth # store + browser topology wizard
468
+ go-scaffold add auth --store postgres # no extra service
469
+ go-scaffold add auth --store redis # Redis for shared refresh state
470
+ go-scaffold add auth --browser-topology same-origin
471
+ go-scaffold add auth --browser-topology same-site
472
+ go-scaffold add auth --browser-topology cross-site
473
+ go-scaffold add auth --defaults # Postgres + same-site defaults
474
+ ~~~
293
475
 
294
- ```bash
295
- go-scaffold add observability # on an existing project
296
- go-scaffold create my-api --defaults --observability # or at creation time
297
- ```
476
+ Auth adds:
298
477
 
299
- Adds Prometheus metrics at `/metrics` and OpenTelemetry tracing for Gin +
300
- GORM, patched into `cmd/api/wiring.go` and `internal/platform/database` the same
301
- way `add worker`/`add auth`/`add rbac` patch an existing project. Tracing is
302
- disabled until `OTEL_EXPORTER_OTLP_ENDPOINT` is configured; `/metrics` works
303
- either way. `create --observability` is exactly this command run right after
304
- scaffolding the two produce the same project.
478
+ - JWT access tokens and refresh-token rotation with reuse detection
479
+ - registration, login, logout, refresh, password reset, and email verification
480
+ - generic provider OAuth routes, with Google as the first adapter
481
+ - failed-login lockout and user-session management
482
+ - MFA endpoints and configuration hooks
483
+ - internal/app/user, auth middleware, cmd/seed, migrations, and OpenAPI
484
+ documents when OpenAPI is enabled
305
485
 
306
- ### `undo module <name>` (alias `undo m`) take back a `generate module`
486
+ The --store choice controls refresh-token storage and rate-limit counters:
307
487
 
308
- ```bash
309
- go-scaffold undo module orders # confirms first
310
- go-scaffold undo m orders --yes # skip the confirm
311
- ```
488
+ | --store | Refresh/recovery token storage | Extra service |
489
+ |---|---|---|
490
+ | postgres (default) | PostgreSQL; rate-limit counters are in-process | None |
491
+ | redis | Refresh state and rate-limit counters in Redis; recovery remains in PostgreSQL | Redis |
312
492
 
313
- The inverse of `generate module`, for the case it's actually the inverse of:
314
- a module you didn't mean to generate — a typo'd name, a domain you decided
315
- against. It deletes `internal/app/<name>/`, the per-module docs folder, **and
316
- the module's migration files**, and reverses the import/AutoMigrate/route in
317
- `main.go` plus the paths/schemas in `docs/openapi.yaml`. Restores the
318
- `_ = api` placeholder if it was the last module, so the project still builds.
319
-
320
- Deleting the migrations is the point. `migrations/embed.go` is a `//go:embed
321
- *`, so a typo'd `create_oders` left behind once ran on every database created
322
- from then on. That's only safe while those files exist nowhere but your
323
- working tree, so `undo` proves it first and refuses loudly otherwise:
324
-
325
- - **any of them is tracked by git** — it may already have been pulled or
326
- deployed somewhere, so nothing is deleted. Retire that domain the explicit
327
- way instead: `go-scaffold generate migration drop_<name>`.
328
- - **your database is already at or past that version** (read via the `migrate`
329
- CLI when it's on `PATH` and a DSN is configured) deleting the files would
330
- strand `schema_migrations` at a version with no migration behind it. Run
331
- `migrate ... down` first, then try again.
332
-
333
- The table itself is never dropped either way — `undo` only reverses what the
334
- CLI wrote. Prefer it to hand-deleting the folder: it also un-wires `main.go`,
335
- `.golangci.yml` and the OpenAPI index, and it refuses when another domain
336
- still imports this one rather than leaving you an un-compilable project.
337
-
338
- ## Why no per-domain versioning
339
-
340
- Earlier versions of this CLI let a domain live in a `v1/`/`v2/` folder with
341
- its own route group and import alias, so the same domain name could exist
342
- twice with different behavior. It was cut: the migration (and usually the
343
- DB table) is shared between "versions" of the same domain, but each version
344
- got its own physically-copied `model.go` nothing stopped the two structs
345
- from drifting apart. Verified against a real Postgres instance:
346
- `AutoMigrate` silently accepted a column typed `int` in one version's model
347
- and `float64` in the other for the *same* column, converging it to
348
- `numeric` with no error — the two versions would then read/write the same
349
- data with different, silently incompatible interpretations.
350
-
351
- Instead, every route in a project is grouped under a single project-wide
352
- `--api-prefix` chosen once at `create` time — opt-in, no prefix unless you ask. A domain that
353
- needs a real breaking change gets a new domain package, or a new field on
354
- the existing DTOnot a duplicated model pointed at a table it can drift
355
- out of sync with.
356
-
357
- ## Project structure produced by `create`
358
-
359
- ```text
360
- cmd/api/wiring.go
361
- internal/
362
- ├── platform/database/
363
- ├── shared/{config,apperror,dberr,httpx,id,middleware,pagination,tx}/
364
- └── app/ # empty until you `generate module`
365
- docs/
366
- ├── architect/{architecture,patterns,techstack}.md
367
- └── openapi.yaml + common/ + health/ # if openapi docs enabled
368
- migrations/
369
- .github/workflows/ci.yml # build, vet, gofmt check, golangci-lint, go test (with a Postgres service)
370
- Makefile
371
- .env.example
372
- .gitignore
373
- .golangci.yml
374
- redocly.yaml # if openapi docs enabled
375
- docker-compose.yml # if Docker enabled
376
- .vscode/settings.json
377
- README.md
378
- AGENTS.md
379
- CLAUDE.md
380
- .claude/skills/go-scaffold/SKILL.md
381
- go-scaffold.config.json
382
- ```
493
+ --browser-topology describes how a browser frontend and API are deployed:
494
+
495
+ | Topology | Typical setup |
496
+ |---|---|
497
+ | same-origin | Same scheme, host, and port |
498
+ | same-site (default) | Different origin on the same site, such as localhost:3000 and localhost:8080 |
499
+ | cross-site | Different sites; use HTTPS, SameSite=None, secure cookies, and exact CORS origins |
500
+
501
+ After adding auth:
502
+
503
+ ~~~bash
504
+ go mod tidy
505
+ make migrate-up
506
+ SEED_ADMIN_EMAIL=admin@example.com SEED_ADMIN_PASSWORD='change-me' make seed
507
+ ~~~
508
+
509
+ Without add worker, verification and password-reset mail is sent inline. If a
510
+ worker is already installed, those jobs use the queue instead.
511
+
512
+ ### add rbac — roles and permissions
513
+
514
+ ~~~bash
515
+ go-scaffold add rbac
516
+ go-scaffold add rbac --yes
517
+ go-scaffold generate module secrets --profile lean --auth --permission secret:manage
518
+ ~~~
519
+
520
+ RBAC requires add auth first. It adds:
521
+
522
+ - role and permission administration
523
+ - cached authorization middleware
524
+ - role assignment endpoints
525
+ - the internal/app/role module and its migration
526
+
527
+ Apply the migration before using the seeded roles and permissions:
528
+
529
+ ~~~bash
530
+ make migrate-up
531
+ SEED_ADMIN_EMAIL=admin@example.com SEED_ADMIN_PASSWORD='change-me' make seed
532
+ ~~~
533
+
534
+ ### add observabilitymetrics and tracing
535
+
536
+ ~~~bash
537
+ go-scaffold add observability
538
+ go-scaffold add observability --yes
539
+ go-scaffold create my-api --defaults --observability
540
+ ~~~
541
+
542
+ This adds Prometheus GET /metrics and OpenTelemetry tracing for Gin and GORM.
543
+ Run go mod tidy after adding it. Metrics work immediately; set
544
+ OTEL_EXPORTER_OTLP_ENDPOINT when you want to export traces.
545
+
546
+ create --observability and create followed by add observability produce the same
547
+ feature configuration.
548
+
549
+ ## undo module [name] remove a generated module
550
+
551
+ ~~~bash
552
+ go-scaffold undo module orders # asks for confirmation
553
+ go-scaffold undo m orders --yes # skip confirmation
554
+ go-scaffold undo # choose the module in a wizard
555
+ ~~~
556
+
557
+ undo module removes the module package, its generated OpenAPI folder, owned
558
+ migrations, configuration entry, and wiring from cmd/api/wiring.go. It does not
559
+ drop a database table.
560
+
561
+ The command is intentionally conservative: it refuses when the module's
562
+ migrations may already have been shared or applied, or when another module
563
+ still depends on it. Use an explicit hand-written migration to retire a domain
564
+ that is already in use.
565
+
566
+ ## Generated project structure
567
+
568
+ After go-scaffold create my-api, the important runtime structure is:
569
+
570
+ ~~~text
571
+ my-api/
572
+ ├── cmd/
573
+ │ └── api/
574
+ │ ├── main.go # process entrypoint and shutdown
575
+ │ └── wiring.go # infrastructure and module composition
576
+ ├── internal/
577
+ │ ├── platform/
578
+ │ │ └── database/ # GORM connection and pool
579
+ │ ├── shared/
580
+ │ │ ├── config/ # environment configuration
581
+ │ │ ├── apperror/ # consistent application errors
582
+ │ │ ├── dberr/ # database error classification
583
+ │ │ ├── httpx/ # HTTP parsing and binding helpers
584
+ │ │ ├── id/ # UUID generation
585
+ │ │ ├── middleware/ # request ID, logging, errors, CORS
586
+ │ │ ├── pagination/ # pagination parsing and responses
587
+ │ │ └── tx/ # transaction context helpers
588
+ │ └── app/ # empty until generate module is used
589
+ ├── migrations/ # embedded, versioned SQL migrations
590
+ ├── docs/
591
+ │ ├── architect/ # architecture, patterns, tech stack
592
+ │ └── openapi.yaml # optional API index and referenced files
593
+ ├── go-scaffold.config.json # CLI defaults, features, and module choices
594
+ ├── go.mod
595
+ ├── Makefile
596
+ ├── .env.example
597
+ ├── Dockerfile
598
+ └── docker-compose.yml # optional, when Docker was selected
599
+ ~~~
600
+
601
+ Optional commands add these areas:
602
+
603
+ ~~~text
604
+ add worker -> internal/platform/{queue,mail}/ and cmd/worker/
605
+ add auth -> internal/app/user/, auth middleware, and cmd/seed/
606
+ add rbac -> internal/app/role/ and authorization middleware
607
+ add observability -> internal/platform/telemetry/ and metrics/tracing middleware
608
+ ~~~
609
+
610
+ After generate module orders, the domain sits behind its own package boundary:
611
+
612
+ ~~~text
613
+ internal/app/order/
614
+ ├── domain/ # entities, invariants, and domain errors
615
+ ├── ports/ # consumer-owned application dependencies
616
+ ├── application/ # service OR commands + queries, plus DTOs/tests
617
+ ├── adapters/inbound/http/ # Gin delivery adapter and tests
618
+ ├── adapters/outbound/postgres/ # persistence model, adapter, and tests
619
+ └── composition.go # module-local dependency wiring
620
+ ~~~
621
+
622
+ The generated service and CQRS styles are exclusive within a module: a
623
+ service module has `application/service.go`; a CQRS module has
624
+ `application/commands.go` and `application/queries.go` and no service facade.
625
+ Different modules may choose different styles. `go-scaffold check` enforces the
626
+ physical layout and dependency direction.
627
+
628
+ The module name orders produces the Go package order, REST collection /orders,
629
+ plural table names such as order_items, and a module-owned schema such as
630
+ order_svc.
383
631
 
384
- ## Supported stack
632
+ ## Useful commands after scaffolding
385
633
 
386
- Pinned in the generated `go.mod` — this table mirrors
387
- `templates/create/base/go.mod.hbs`, which is the source of truth.
634
+ Run these from the generated project directory:
388
635
 
389
- | Package | Version |
636
+ | Command | Purpose |
390
637
  |---|---|
391
- | Gin | v1.10.1 |
392
- | GORM + postgres driver | v1.31.2 / v1.6.2 |
393
- | validator/v10 | v10.30.3 |
394
- | google/uuid | v1.6.0 |
638
+ | make docker-up | Start the generated local PostgreSQL service |
639
+ | make docker-down | Stop the generated local services |
640
+ | make db-create | Create the project database; safe to run again |
641
+ | make run | Run cmd/api |
642
+ | make river-migrate | Apply River's own schema when the worker uses Postgres/River |
643
+ | make river-migrate-test | Apply River's own schema to TEST_DB_DSN for worker integration tests |
644
+ | make build | Build cmd/api into bin/api |
645
+ | make test | Run Go tests |
646
+ | make fmt | Format Go code |
647
+ | make vet | Run go vet ./... |
648
+ | make tidy | Run go mod tidy |
649
+ | make migrate-up | Apply migrations from migrations/ |
650
+ | make migrate-down | Roll back one migration |
651
+ | make migrate-verify | Check that migrations can roll forward and back |
652
+ | make openapi-bundle | Bundle OpenAPI $ref files when OpenAPI is enabled |
653
+
654
+ make run, make test, and migration targets load .env when it exists. Copy
655
+ .env.example to .env and adjust DB_DSN, PORT, CORS, mail, auth, or telemetry
656
+ settings as needed.
657
+
658
+ ## What to edit after generation
659
+
660
+ The CLI gives you a compiling structure and explicit TODOs. Your application
661
+ still owns:
662
+
663
+ - domain fields and invariants in `domain/entity.go`
664
+ - request/use-case and response fields in `application/dto.go`
665
+ - persistence fields and mapping in `adapters/outbound/postgres/model.go`
666
+ - business rules in `application/service.go` or command/query handlers
667
+ - SQL bodies in generated migration files
668
+ - OpenAPI schemas and method descriptions
669
+ - tests for the behavior you add
670
+
671
+ Use go-scaffold generate method for the repetitive endpoint shape, then fill in
672
+ the generated TODO before treating the endpoint as production behavior.
673
+
674
+ ## Architecture and migration note
675
+
676
+ Schema version 2 is the canonical hexagonal split layout. Every generated
677
+ feature, including auth and RBAC, uses `domain/`, `application/`, `ports/`,
678
+ `adapters/`, and a feature-local `composition.go`; one canonical implementation
679
+ tree is generated. Projects with the old schema 1 manifest are
680
+ rejected with a migration hint instead of silently generating a conflicting
681
+ architecture.
682
+
683
+ The generated tree has no `compat/` directory and no feature-level `model/`
684
+ directory. The word “compatibility” below refers only to CLI command aliases:
685
+ the hidden `remove module` alias remains supported for old scripts, while new
686
+ commands should use `go-scaffold undo module`.
395
687
 
396
688
  ## License
397
689