@starci/hfs 5.1.0 → 5.2.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 (199) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +10 -1
  3. package/db/postgres.mjs +9 -1
  4. package/package.json +1 -1
  5. package/runtime/knowledge/hfs/canon-pins.yaml +30 -9
  6. package/runtime/knowledge/hfs/fe-stack.yaml +7 -7
  7. package/runtime/knowledge/hfs/fork-images.yaml +7 -0
  8. package/runtime/knowledge/hfs/rules.yaml +55 -7
  9. package/runtime/knowledge/hfs/slots.yaml +75 -24
  10. package/runtime/modules/kernel/failure-codes.yaml +62 -0
  11. package/runtime/modules/schemas/application-stacks.schema.yaml +6 -0
  12. package/runtime/scripts/hfs/architecture/identity.mjs +18 -83
  13. package/runtime/scripts/hfs/architecture/transport-owner.d.mts +1 -2
  14. package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -6
  15. package/runtime/scripts/hfs/manifest-shape.d.mts +1 -0
  16. package/runtime/scripts/hfs/manifest-shape.mjs +3 -1
  17. package/runtime/scripts/hfs/rules/a15-findings.mjs +5 -0
  18. package/runtime/scripts/hfs/rules/stacks-local.d.mts +7 -0
  19. package/runtime/scripts/hfs/rules/stacks-local.mjs +71 -0
  20. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +6 -1
  21. package/runtime/scripts/hfs/rules/stacks-service-images.d.mts +12 -0
  22. package/runtime/scripts/hfs/rules/stacks-service-images.mjs +120 -0
  23. package/runtime/scripts/hfs/secret-dev-init.d.mts +11 -0
  24. package/runtime/scripts/hfs/secret-dev-init.mjs +79 -0
  25. package/runtime/scripts/hfs/secret.mjs +10 -7
  26. package/runtime/scripts/hfs/slots.mjs +1 -1
  27. package/runtime/scripts/hfs/stack-local-config.d.mts +101 -0
  28. package/runtime/scripts/hfs/stack-local-config.mjs +320 -0
  29. package/runtime/scripts/hfs/stacks-gate.mjs +1 -1
  30. package/runtime/scripts/lib/swarm-stack.d.mts +2 -1
  31. package/runtime/scripts/lib/swarm-stack.mjs +2 -2
  32. package/scaffold/add-module.d.mts +16 -0
  33. package/scaffold/add-module.mjs +232 -0
  34. package/scaffold/add-queue-lite.mjs +49 -0
  35. package/scaffold/add.mjs +5 -0
  36. package/scaffold/app.mjs +7 -7
  37. package/scaffold/module-registry.d.mts +19 -0
  38. package/scaffold/module-registry.mjs +84 -0
  39. package/src/main.mjs +23 -2
  40. package/stack/cli.mjs +6 -1
  41. package/stack/context.d.mts +15 -0
  42. package/stack/context.mjs +1 -0
  43. package/stack/local-cli.d.mts +25 -0
  44. package/stack/local-cli.mjs +89 -0
  45. package/stack/local-config.d.mts +17 -0
  46. package/stack/local-config.mjs +37 -0
  47. package/stack/local-custody.d.mts +24 -0
  48. package/stack/local-custody.mjs +109 -0
  49. package/stack/local-doctor.d.mts +6 -0
  50. package/stack/local-doctor.mjs +49 -0
  51. package/stack/local-down.d.mts +8 -0
  52. package/stack/local-down.mjs +20 -0
  53. package/stack/local-host.d.mts +42 -0
  54. package/stack/local-host.mjs +163 -0
  55. package/stack/local-logs.d.mts +8 -0
  56. package/stack/local-logs.mjs +32 -0
  57. package/stack/local-ports.d.mts +7 -0
  58. package/stack/local-ports.mjs +39 -0
  59. package/stack/local-prepare.d.mts +22 -0
  60. package/stack/local-prepare.mjs +62 -0
  61. package/stack/local-run.d.mts +71 -0
  62. package/stack/local-run.mjs +200 -0
  63. package/stack/local-state.d.mts +35 -0
  64. package/stack/local-state.mjs +29 -0
  65. package/stack/local-status.d.mts +6 -0
  66. package/stack/local-status.mjs +36 -0
  67. package/stack/local-up.d.mts +11 -0
  68. package/stack/local-up.mjs +166 -0
  69. package/stack/local-verify.d.mts +6 -0
  70. package/stack/local-verify.mjs +21 -0
  71. package/sync/index.d.mts +13 -12
  72. package/sync/index.mjs +83 -28
  73. package/templates/app/module/jest.config.js.tpl +8 -0
  74. package/templates/app/module/src/index.ts.tpl +2 -0
  75. package/templates/app/module/src/manifest/declaration.spec.ts.tpl +11 -0
  76. package/templates/app/module/src/manifest/declaration.ts.tpl +13 -0
  77. package/templates/app/module/src/manifest/index.ts.tpl +1 -0
  78. package/templates/app/module/src/public/index.ts.tpl +2 -0
  79. package/templates/app/module/src/template/__name__.template.spec.ts.tpl +13 -0
  80. package/templates/app/module/src/template/__name__.template.ts.tpl +41 -0
  81. package/templates/app/module/src/template/index.ts.tpl +1 -0
  82. package/templates/app/module/src/workbench/components/__pascal__WorkbenchView.tsx.tpl +16 -0
  83. package/templates/app/module/src/workbench/index.ts.tpl +2 -0
  84. package/templates/app/package-scripts/package.json +1 -1
  85. package/templates/app/package-scripts-lite/package.json +1 -1
  86. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +76 -70
  87. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +23 -13
  88. package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +25 -0
  89. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -26
  90. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +1 -0
  91. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +2 -1
  92. package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +57 -14
  93. package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +11 -0
  94. package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.handler.ts +23 -0
  95. package/templates/be/auth-lite/src/features/api/auth/application/get-jwks.query.ts +12 -0
  96. package/templates/be/auth-lite/src/features/api/auth/application/google-begin.command.ts +11 -0
  97. package/templates/be/auth-lite/src/features/api/auth/application/google-begin.handler.ts +22 -0
  98. package/templates/be/auth-lite/src/features/api/auth/application/google-complete.command.ts +12 -0
  99. package/templates/be/auth-lite/src/features/api/auth/application/google-complete.handler.ts +23 -0
  100. package/templates/be/auth-lite/src/features/api/auth/application/login.command.ts +12 -0
  101. package/templates/be/auth-lite/src/features/api/auth/application/login.handler.ts +23 -0
  102. package/templates/be/auth-lite/src/features/api/auth/application/logout.command.ts +11 -0
  103. package/templates/be/auth-lite/src/features/api/auth/application/logout.handler.ts +22 -0
  104. package/templates/be/auth-lite/src/features/api/auth/application/refresh.command.ts +12 -0
  105. package/templates/be/auth-lite/src/features/api/auth/application/refresh.handler.ts +23 -0
  106. package/templates/be/auth-lite/src/features/api/auth/application/session.command.ts +12 -0
  107. package/templates/be/auth-lite/src/features/api/auth/application/session.handler.ts +23 -0
  108. package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +13 -0
  109. package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +16 -0
  110. package/templates/be/auth-lite/src/features/api/auth/auth.options.ts +3 -0
  111. package/templates/be/auth-lite/src/features/api/auth/index.ts +2 -0
  112. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +13 -0
  113. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module.ts +9 -0
  114. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.options.ts +3 -0
  115. package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +90 -0
  116. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/google-start.request.ts +17 -0
  117. package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/login.request.ts +16 -0
  118. package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +46 -0
  119. package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +28 -0
  120. package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +145 -0
  121. package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +95 -0
  122. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-identity.entity.ts +38 -0
  123. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-user.entity.ts +54 -0
  124. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-attempt.entity.ts +18 -0
  125. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +34 -0
  126. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-session.entity.ts +30 -0
  127. package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +17 -0
  128. package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.spec.ts +86 -0
  129. package/templates/be/auth-lite/src/modules/domain/identity/rate-counter.service.ts +45 -0
  130. package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +97 -0
  131. package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +81 -0
  132. package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +78 -0
  133. package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +87 -0
  134. package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +44 -0
  135. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +6 -5
  136. package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +3 -2
  137. package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +10 -5
  138. package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +34 -0
  139. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +22 -1
  140. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +47 -2
  141. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -0
  142. package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +24 -0
  143. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +3 -3
  144. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +3 -1
  145. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +8 -4
  146. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +12 -0
  147. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module.ts +29 -0
  148. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +6 -31
  149. package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +18 -9
  150. package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +35 -11
  151. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +44 -8
  152. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +40 -18
  153. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +18 -6
  154. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.spec.ts +73 -0
  155. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.kit.ts +26 -0
  156. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +24 -53
  157. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.registration.ts +31 -0
  158. package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +1 -1
  159. package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +2 -1
  160. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
  161. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +11 -4
  162. package/templates/be/test-world/e2e/serves-once-migrated.e2e-spec.ts.tpl +9 -9
  163. package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +4 -4
  164. package/templates/fe/auth-lite/apps/web/src/modules/auth/client.ts +26 -0
  165. package/templates/fe/common/site-shell.tsx.partial +3 -3
  166. package/templates/fe/skeleton/apps/__app__/src/components/providers/AppProviders/index.tsx +33 -10
  167. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/index.ts +1 -0
  168. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useViewerId.ts +11 -0
  169. package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +15 -12
  170. package/templates/fe/skeleton/apps/__app__/src/modules/viewer-cache/index.ts +5 -0
  171. package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +1 -1
  172. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -1
  173. package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +4 -7
  174. package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/index.tsx +2 -3
  175. package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +4 -7
  176. package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/index.tsx +1 -2
  177. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +0 -16
  178. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +0 -11
  179. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +0 -7
  180. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +0 -6
  181. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +0 -8
  182. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +0 -6
  183. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +0 -186
  184. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +0 -22
  185. package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +0 -25
  186. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +0 -104
  187. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +0 -34
  188. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.spec.ts +0 -28
  189. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +0 -11
  190. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +0 -88
  191. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +0 -69
  192. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +0 -41
  193. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +0 -40
  194. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +0 -63
  195. package/templates/fe/skeleton/packages/__project__-ui/src/frames/SiteShell/classNames.ts +0 -4
  196. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +0 -30
  197. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +0 -50
  198. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +0 -31
  199. package/templates/fe/skeleton-lite/apps/web/src/components/frames/SiteShell/classNames.ts +0 -4
@@ -42,78 +42,84 @@ components:
42
42
  environments:
43
43
  dev:
44
44
  status: supported
45
- runtime: docker-swarm
46
- composeFiles: [infra/swarm/stack.yaml]
45
+ runtime: docker-compose
46
+ composeFiles: [infra/compose/compose.yaml]
47
47
  components:
48
- api: {replicas: 1, port: 3001}
49
- web: {replicas: 1, port: 3000}
50
- db: {replicas: 1, port: 5432}
51
- ingress: {replicas: 1, port: 80}
48
+ db: {port: 54322}
49
+ api: {port: 3001}
50
+ web: {port: 3000}
52
51
  runbook: README.md
53
- ingress:
54
- component: ingress
55
- hosts: ["dev.{{project}}.example.invalid"]
56
- dns:
57
- - host: "dev.{{project}}.example.invalid"
58
- owner: Development operator
59
- receipt: .starcistacks/dev/infra/swarm/owner-actions.md#dns
60
- tls:
61
- owner: Development operator
62
- receipt: .starcistacks/dev/infra/swarm/owner-actions.md#tls
63
- release:
64
- imagesLock: .starcistacks/dev/infra/swarm/releases/images.lock.yaml
65
- pointer: .starcistacks/dev/infra/swarm/releases/current.yaml
66
- previous: .starcistacks/dev/infra/swarm/releases/previous.yaml
67
- healthAssertions: [API /health/live, API /health/ready, Web /health/live]
68
- backup:
69
- - component: ingress
70
- target: Owner-provisioned encrypted off-host storage for both ingress volumes
71
- retention: Seven daily snapshots
72
- owner: Development operator
73
- restoreProof: .starcistacks/dev/infra/swarm/recovery/restore-proof.md
74
- - component: db
75
- target: Owner-provisioned encrypted off-host storage for starci app db dump archives
76
- retention: Seven daily exports
77
- owner: Development operator
78
- restoreProof: .starcistacks/dev/infra/swarm/recovery/restore-proof.md
79
- # Scaffolding seals generated placeholders to this app's own recipient; set real values before use.
80
- secrets:
81
- - name: primary-db-url
82
- runtimeName: "{{project}}-dev-primary-db-url-v1"
83
- version: 1
84
- where: secrets/primary-db-url
85
- recipientPolicy: This app's own age recipient configured in .sops.yaml
86
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
87
- - name: db-password
88
- runtimeName: "{{project}}-dev-db-password-v1"
89
- version: 1
90
- where: secrets/db-password
91
- recipientPolicy: This app's own age recipient configured in .sops.yaml
92
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
93
- - name: google-client-secret
94
- runtimeName: "{{project}}-dev-google-client-secret-v1"
95
- version: 1
96
- where: secrets/google-client-secret
97
- recipientPolicy: This app's own age recipient configured in .sops.yaml
98
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
99
- - name: auth-jwt-private-key
100
- runtimeName: "{{project}}-dev-auth-jwt-private-key-v1"
101
- version: 1
102
- where: secrets/auth-jwt-private-key
103
- recipientPolicy: This app's own age recipient configured in .sops.yaml
104
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
105
- - name: storage-access-key-id
106
- runtimeName: "{{project}}-dev-storage-access-key-id-v1"
107
- version: 1
108
- where: secrets/storage-access-key-id
109
- recipientPolicy: This app's own age recipient configured in .sops.yaml
110
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
111
- - name: storage-secret-access-key
112
- runtimeName: "{{project}}-dev-storage-secret-access-key-v1"
113
- version: 1
114
- where: secrets/storage-secret-access-key
115
- recipientPolicy: This app's own age recipient configured in .sops.yaml
116
- keyCustody: SOPS_AGE_KEY in the host's untracked .claude/secret.env
52
+ # Dev custody is generated once, dev-only, by the owner-armed `starci app secret init --env dev --execute`, which also declares each key here.
53
+ secrets: []
54
+ # `starci app stack up --env dev --execute` reads this block (PROJECT-STACKS-6): host prerequisites, dev custody, setup, the compose services,
55
+ # the environment's own database role, migrations, native api and web processes, readiness, origins.
56
+ local:
57
+ ports:
58
+ db: 54322
59
+ api: 3001
60
+ web: 3000
61
+ services:
62
+ - {name: db, wait: healthy}
63
+ database:
64
+ service: db
65
+ port: db
66
+ urlEnv: PRIMARY_DB_URL
67
+ appRole: app_be
68
+ password: {member: db, key: DEV_DB_OWNER_PASSWORD}
69
+ adminPassword: {member: db, key: POSTGRES_PASSWORD}
70
+ appPassword: {member: db, key: DEV_DB_APP_PASSWORD}
71
+ grants:
72
+ - {name: db, keys: [POSTGRES_PASSWORD]}
73
+ custody:
74
+ - {member: db, key: POSTGRES_PASSWORD, kind: password}
75
+ - {member: db, key: DEV_DB_OWNER_PASSWORD, kind: password}
76
+ - {member: db, key: DEV_DB_APP_PASSWORD, kind: password}
77
+ - {member: auth, key: AUTH_JWT_PRIVATE_KEY, kind: ed25519-pem}
78
+ - {member: google, key: GOOGLE_CLIENT_SECRET, kind: hex}
79
+ - {member: storage, key: STORAGE_ACCESS_KEY_ID, kind: hex, bytes: 12}
80
+ - {member: storage, key: STORAGE_SECRET_ACCESS_KEY, kind: hex}
81
+ setup:
82
+ - {name: install, argv: [npm, ci], heavy: install, skipIfExists: node_modules}
83
+ - {name: codegen, argv: [npm, run, codegen, --silent], heavy: build}
84
+ processes:
85
+ - name: api
86
+ argv: [npx, ts-node-dev, --respawn, -r, tsconfig-paths/register, apps/api/src/main.ts]
87
+ cwd: be
88
+ port: api
89
+ files: [GOOGLE_CLIENT_SECRET, AUTH_JWT_PRIVATE_KEY, STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY]
90
+ env:
91
+ PORT: "${port.api}"
92
+ HTTP_SECURITY_ALLOWED_ORIGINS: "http://localhost:${port.web}"
93
+ PRIMARY_DB_SCHEMA: public
94
+ GOOGLE_CLIENT_ID: dev-local-client-id
95
+ GOOGLE_CALLBACK_URL: "http://localhost:${port.api}/auth/google/callback"
96
+ AUTH_JWT_ISSUER: "http://localhost:${port.api}"
97
+ AUTH_WEB_ORIGIN: "http://localhost:${port.web}"
98
+ AUTH_JWT_AUDIENCE: {{project}}
99
+ AUTH_JWT_KEY_ID: dev-1
100
+ STORAGE_ENDPOINT: "http://127.0.0.1:9000"
101
+ STORAGE_REGION: us-east-1
102
+ STORAGE_BUCKET: {{project}}-dev
103
+ health: {url: "http://localhost:${port.api}/health/live"}
104
+ - name: web
105
+ argv: [npm, run, dev:fe]
106
+ port: web
107
+ env:
108
+ NEXT_PUBLIC_SITE_URL: "http://localhost:${port.web}"
109
+ NEXT_PUBLIC_API_URL: "http://localhost:${port.api}"
110
+ NEXT_PUBLIC_GRAPHQL_URL: "http://localhost:${port.api}/graphql"
111
+ health: {url: "http://localhost:${port.web}/health/live"}
112
+ ready:
113
+ - {name: api-live, url: "http://localhost:${port.api}/health/live"}
114
+ - {name: api-ready, url: "http://localhost:${port.api}/health/ready"}
115
+ - {name: web-live, url: "http://localhost:${port.web}/health/live"}
116
+ verify:
117
+ - {name: api-live, url: "http://localhost:${port.api}/health/live"}
118
+ - {name: api-ready, url: "http://localhost:${port.api}/health/ready"}
119
+ - {name: web-live, url: "http://localhost:${port.web}/health/live"}
120
+ origins:
121
+ - {name: web, url: "http://localhost:${port.web}"}
122
+ - {name: api, url: "http://localhost:${port.api}"}
117
123
  production:
118
124
  status: supported
119
125
  runtime: docker-swarm
@@ -1,20 +1,30 @@
1
1
  # Development stack
2
2
 
3
- Every development dependency runs in Docker Linux containers. The `db` service (the official postgres image plus pgvector, built from `.starcistacks/images/db/Dockerfile`) starts with `npm run db:start` and takes the migrations with `npm run db:push`; storage is the S3-compatible MinIO the environment names. Every other declared dependency, such as Redis, starts through its managed `<dep>:create`, `<dep>:start` and `<dep>:stop` scripts or the declared container model; Compose is an implementation detail. The app services may run in the local Docker Swarm model or through a declared native development profile while dependencies stay in Docker.
4
- The application image references contain illustrative all-zero digests. Replace them with verified published release digests in the manifest, model and `.starcistacks/dev/infra/swarm/releases/images.lock.yaml` before starting the container model. Seal the local database connection and the integration credentials; a container cannot use the host's loopback address without owner-configured networking. Local source development remains available through the app scripts.
3
+ The dev environment is one command: `starci app stack up --env dev --execute`. It checks the machine, opens the dev custody, runs the setup
4
+ commands, starts the dependencies in Docker Linux containers (the `db` service: the official postgres image plus pgvector, built from
5
+ `.starcistacks/images/db/Dockerfile`), gives the environment its own database role, migrates, runs the seed, starts the api and web
6
+ processes natively and waits for readiness. What it runs is the `local` block of `environments.dev` in `.starcistacks/application-stacks.yaml`
7
+ (PROJECT-STACKS-6 in contexts/project/stacks.md); this runbook never carries a docker command, an agent never runs docker by hand.
5
8
 
6
- The runtime template tracks no encrypted members. Scaffolding generates the six declared members with placeholder values encrypted to this app's own recipient in `.sops.yaml`. Replace those values with `starci app secret set --env dev` using the matching host age identity in untracked local custody. Existing generated envelopes are decryptable with that app identity and can be updated in place. Each Docker secret contains the corresponding key's real value, and the API receives it through its declared mounted file.
9
+ **A new machine.** Install what `starci app stack doctor --env dev` reports missing (it prints the exact winget, brew or apt step per tool; Node
10
+ from `.nvmrc`, npm, git, Docker with Compose v2 running Linux containers, WSL2 on Windows, sops and age), then run `up`. A fresh clone needs
11
+ nothing else: `up` runs `npm ci` and the codegen itself.
12
+
13
+ **Custody.** Dev values are sealed like any secret and generated once, dev-only: the owner arms `starci app secret init --env dev --execute`, which
14
+ creates every value `local.custody` declares, declares its key in the manifest and never replaces a value that is already sealed. A machine that
15
+ lacks the age identity of an existing dev member is refused with the owner step to obtain it. The `db` service takes `POSTGRES_PASSWORD`, the
16
+ migrations run as the environment's own owner role and the api connects as the `app_be` role with its own password, so no role is shared with
17
+ another environment.
7
18
 
8
19
  | Command | Operation |
9
20
  | --- | --- |
10
- | prepare | Run `npm run db:start` at the app root; then `npm run db:push` to apply the migrations; export `DATABASE_URL` for the database verbs. For a declared Redis (its `redis:create`, `redis:start` and `redis:stop` scripts arrive in package.json with the first queue or job), run the create script once, then the start script when using the native app profile. An owner who chooses local Swarm initializes it, labels its node `{{project}}=dev` and provisions the ingress volumes; the model starts its declared Redis with the app. Replace the reserved development host consistently in the manifest, Caddyfile and public configuration; resolve it to the node and record this in `.starcistacks/dev/infra/swarm/owner-actions.md`. Create the external development secrets named in the model from real sealed members. |
11
- | doctor | From this directory, run `docker stack config -c infra/swarm/stack.yaml`. Confirm image, secret and local database availability. |
12
- | up | Run `docker stack deploy -c infra/swarm/stack.yaml {{project}}-dev` from this directory after preparation. |
13
- | status | Run `docker stack services {{project}}-dev`. |
14
- | logs | Run `docker service logs {{project}}-dev_api`; never print mounted credentials. |
15
- | down | Run `docker stack rm {{project}}-dev`. |
16
- | verification | Confirm the API `/health/live` and `/health/ready` endpoints and web `/health/live`, then run `npm run lint` from the app root. Retain the last accepted release in `.starcistacks/dev/infra/swarm/releases/previous.yaml` before recording the verified release in `.starcistacks/dev/infra/swarm/releases/current.yaml`. |
17
-
18
- Keep real sealed members under `.starcistacks/dev/secrets/*.enc` at the declared paths and inventory their key names in `.starcistacks/dev/runtime/env/KEYS.md`. Rotation creates a new immutable `-v<N>` external secret and updates its version and runtime name together in the manifest and model. Only the declared ingress publishes port 80; the local route uses HTTP with automatic TLS disabled.
21
+ | prepare | `starci app stack prepare --env dev --execute`: checks the machine, opens the custody, runs the setup commands and builds the images. |
22
+ | doctor | `starci app stack doctor --env dev`: read-only table of host prerequisites with the owner-run install step per OS, custody, compose render and ports. |
23
+ | up | `starci app stack up --env dev --execute`: prepare, then the services, the database role, migrations with a receipt, the seed, the processes and readiness; prints the origins. |
24
+ | status | `starci app stack status --env dev`: desired against running, the migration receipt and every readiness probe. |
25
+ | logs | `starci app stack logs --env dev --service api` (a process or a compose service, bounded and redacted). |
26
+ | down | `starci app stack down --env dev --execute`: ends the processes and stops the compose project; volumes and sealed values stay. |
27
+ | verification | `starci app stack verify --env dev`: the declared liveness and readiness probes after a real start, then `npm run lint` from the app root. |
19
28
 
20
- The manifest declares seven daily encrypted off-host database exports and ingress-volume snapshots owned by the development operator. `.starcistacks/dev/infra/swarm/recovery/restore-proof.md` is a pending recovery record. Complete an isolated restore and attach a dated receipt before relying on these backups. Release, DNS and restore records remain explicitly pending until an operator supplies real evidence.
29
+ The database volume `db-data` is rebuildable development data: `starci app db migrate` recreates its schema from `db/bootstrap.sql` and
30
+ `db/migrations`. Key names of the environment are in `.starcistacks/dev/runtime/env/KEYS.md`.
@@ -0,0 +1,25 @@
1
+ # The dev environment's dependencies, run by `starci app stack up --env dev --execute` (PROJECT-STACKS-3, PROJECT-STACKS-6).
2
+ # Host ports come from environments.dev.local.ports through STACK_PORT_<NAME>; the superuser password is read from a file through the least
3
+ # grant STACK_GRANT_DB (a directory holding only POSTGRES_PASSWORD), never from the environment. Every port publishes on loopback only. The api and web run natively against these containers.
4
+ name: {{project}}-dev
5
+ services:
6
+ db:
7
+ build:
8
+ context: ../../../images/db
9
+ image: {{project}}-db:local
10
+ environment:
11
+ POSTGRES_PASSWORD_FILE: /run/secrets/POSTGRES_PASSWORD
12
+ ports:
13
+ - "127.0.0.1:${STACK_PORT_DB}:5432"
14
+ volumes:
15
+ - db-data:/var/lib/postgresql/data
16
+ - ${STACK_GRANT_DB}:/run/secrets:ro
17
+ healthcheck:
18
+ test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
19
+ interval: 5s
20
+ timeout: 5s
21
+ retries: 30
22
+ start_period: 10s
23
+ restart: unless-stopped
24
+ volumes:
25
+ db-data: {}
@@ -1,30 +1,22 @@
1
- # Environment key roster
1
+ # Environment key roster (dev)
2
2
 
3
- This roster declares names only. Scaffolding generates the six declared encrypted members with placeholder values encrypted to this app's own age recipient. Replace their values through `starci app secret set --env dev` before creating external secrets. The runtime template tracks no ciphertext; its specs generate members with ephemeral identities in disposable copies. Store product credential values in sealed custody and materialize private KEY files outside the repository.
3
+ Names only. Dev values are generated once by the owner-armed `starci app secret init --env dev --execute`, sealed in
4
+ `.starcistacks/dev/secrets/<member>.enc.yaml` and opened by `starci app stack up --env dev --execute` into private files outside the repository
5
+ (`<KEY>_FILE`, which the back end reads first). The public front-end configuration is supplied by the manifest's `local.processes` environment.
4
6
 
5
7
  | Key | Consumer |
6
8
  | --- | --- |
7
- | `PORT` | API listener |
8
- | `HTTP_SECURITY_ALLOWED_ORIGINS` | API origin allowlist |
9
- | `PRIMARY_DB_URL` | API least-privilege database connection; `.starcistacks/dev/secrets/primary-db-url.enc` maps to external `{{project}}-dev-primary-db-url-v1` and mounted `/run/secrets/primary-db-url` |
10
- | `PRIMARY_DB_SCHEMA` | API primary database schema |
11
- | `POSTGRES_PASSWORD` | The db service's superuser password; `.starcistacks/dev/secrets/db-password.enc` maps to external `{{project}}-dev-db-password-v1` and mounted `/run/secrets/db-password` |
12
- | `GOOGLE_CLIENT_ID` | API Google OAuth client |
13
- | `GOOGLE_CLIENT_SECRET` | API Google OAuth client; `.starcistacks/dev/secrets/google-client-secret.enc` maps to external `{{project}}-dev-google-client-secret-v1` and mounted `/run/secrets/google-client-secret` |
14
- | `GOOGLE_CALLBACK_URL` | API Google OAuth callback door |
15
- | `AUTH_JWT_ISSUER` | API access-token issuer |
16
- | `AUTH_JWT_AUDIENCE` | API access-token audience |
17
- | `AUTH_JWT_KEY_ID` | API signing key id, published in the JWKS |
18
- | `AUTH_COOKIE_DOMAIN` | API cookie `Domain` (a bare domain such as `example.com`, no scheme or port); optional while the web and the API share one host, required when they are served on different hosts, because a host-only refresh cookie never reaches the web server's forwarded `Cookie` header |
19
- | `AUTH_JWT_PRIVATE_KEY` | API ES256 signing key; `.starcistacks/dev/secrets/auth-jwt-private-key.enc` maps to external `{{project}}-dev-auth-jwt-private-key-v1` and mounted `/run/secrets/auth-jwt-private-key` |
20
- | `STORAGE_ENDPOINT` | API S3-compatible storage endpoint |
21
- | `STORAGE_REGION` | API storage signing region |
22
- | `STORAGE_BUCKET` | API storage bucket |
23
- | `STORAGE_ACCESS_KEY_ID` | API storage credential; `.starcistacks/dev/secrets/storage-access-key-id.enc` maps to external `{{project}}-dev-storage-access-key-id-v1` and mounted `/run/secrets/storage-access-key-id` |
24
- | `STORAGE_SECRET_ACCESS_KEY` | API storage credential; `.starcistacks/dev/secrets/storage-secret-access-key.enc` maps to external `{{project}}-dev-storage-secret-access-key-v1` and mounted `/run/secrets/storage-secret-access-key` |
25
- | `DATABASE_URL` | `starci app db` verbs (migrate, dump) and `contract:emit`, from the operator's environment |
26
- | `NEXT_PUBLIC_SITE_URL` | Front-end site URL |
27
- | `NEXT_PUBLIC_API_URL` | Front-end auth doors of the API |
28
- | `NEXT_PUBLIC_GRAPHQL_URL` | Front-end GraphQL transport |
29
-
30
- Public front-end configuration is supplied at build time and stays outside sealed custody. The database verbs read `DATABASE_URL` from the operator's environment; the GitHub secret store carries only the age identity.
9
+ | `PORT`, `HTTP_SECURITY_ALLOWED_ORIGINS`, `PRIMARY_DB_SCHEMA` | API listener, origin allowlist, primary schema (manifest `local.processes.api.env`) |
10
+ | `PRIMARY_DB_URL` | API least-privilege connection as `app_be` with this environment's own password; composed by the stack verb, read through `PRIMARY_DB_URL_FILE` |
11
+ | `POSTGRES_PASSWORD` | The db service's superuser password (member `db`) |
12
+ | `DEV_DB_OWNER_PASSWORD` | The environment's own owner role, which runs the migrations (member `db`) |
13
+ | `DEV_DB_APP_PASSWORD` | The password of the `app_be` role in this environment's database (member `db`) |
14
+ | `GOOGLE_CLIENT_ID`, `GOOGLE_CALLBACK_URL` | API Google OAuth client and callback (local placeholders in the manifest) |
15
+ | `GOOGLE_CLIENT_SECRET` | API Google OAuth client secret (member `google`) |
16
+ | `AUTH_JWT_ISSUER`, `AUTH_JWT_AUDIENCE`, `AUTH_JWT_KEY_ID` | API access-token claims and key id (the key id is published in the JWKS) |
17
+ | `AUTH_WEB_ORIGIN` | API origin of the web app: where the Google callback lands and the only origin a return address may name |
18
+ | `AUTH_JWT_PRIVATE_KEY` | API Ed25519 (EdDSA) signing key (member `auth`) |
19
+ | `STORAGE_ENDPOINT`, `STORAGE_REGION`, `STORAGE_BUCKET` | API storage address |
20
+ | `STORAGE_ACCESS_KEY_ID`, `STORAGE_SECRET_ACCESS_KEY` | API storage credentials (member `storage`) |
21
+ | `NEXT_PUBLIC_SITE_URL`, `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_GRAPHQL_URL` | Front-end site, auth doors and GraphQL transport |
22
+ | `DATABASE_URL` | `starci app db` verbs; the stack verb composes it in memory for the migration |
@@ -12,6 +12,7 @@ services:
12
12
  GOOGLE_CLIENT_SECRET_FILE: /run/secrets/google-client-secret
13
13
  GOOGLE_CALLBACK_URL: https://api.{{project}}.example.invalid/auth/google/callback
14
14
  AUTH_JWT_ISSUER: https://api.{{project}}.example.invalid
15
+ AUTH_WEB_ORIGIN: https://{{project}}.example.invalid
15
16
  AUTH_JWT_AUDIENCE: {{project}}
16
17
  AUTH_JWT_KEY_ID: production-1
17
18
  AUTH_COOKIE_DOMAIN: {{project}}.example.invalid
@@ -13,10 +13,11 @@ This inventory declares names, consumers and mounted file paths. Scaffolding gen
13
13
  | `GOOGLE_CLIENT_SECRET` | API Google OAuth client; reads `GOOGLE_CLIENT_SECRET_FILE` at `/run/secrets/google-client-secret` |
14
14
  | `GOOGLE_CALLBACK_URL` | API Google OAuth callback door |
15
15
  | `AUTH_JWT_ISSUER` | API access-token issuer |
16
+ | `AUTH_WEB_ORIGIN` | API origin of the web app: where the Google callback lands and the only origin a return address may name |
16
17
  | `AUTH_JWT_AUDIENCE` | API access-token audience |
17
18
  | `AUTH_JWT_KEY_ID` | API signing key id, published in the JWKS |
18
19
  | `AUTH_COOKIE_DOMAIN` | API cookie `Domain` (a bare domain such as `example.com`, no scheme or port); optional while the web and the API share one host, required when they are served on different hosts, because a host-only refresh cookie never reaches the web server's forwarded `Cookie` header |
19
- | `AUTH_JWT_PRIVATE_KEY` | API ES256 signing key; reads `AUTH_JWT_PRIVATE_KEY_FILE` at `/run/secrets/auth-jwt-private-key` |
20
+ | `AUTH_JWT_PRIVATE_KEY` | API Ed25519 (EdDSA) signing key; reads `AUTH_JWT_PRIVATE_KEY_FILE` at `/run/secrets/auth-jwt-private-key` |
20
21
  | `STORAGE_ENDPOINT` | API S3-compatible storage endpoint |
21
22
  | `STORAGE_REGION` | API storage signing region |
22
23
  | `STORAGE_BUCKET` | API storage bucket |
@@ -24,25 +24,68 @@ begin
24
24
  end;
25
25
  $$;
26
26
 
27
- -- The rotating refresh sessions of integrations/passport (auth.users comes from db/bootstrap.sql).
28
- -- One row per issued refresh token, stored by its sha256 only. A family is one sign-in; rotation revokes the presented row
29
- -- and inserts its successor, and presenting a revoked row revokes the whole family (reuse detection).
30
- create table auth.refresh_sessions (
31
- id uuid primary key default gen_random_uuid(),
32
- family_id uuid not null,
27
+ grant usage on schema auth to app_be;
28
+ grant select, insert, update on table auth.users to app_be;
29
+ grant select, insert on table auth.identities to app_be;
30
+
31
+ -- The device sessions and rotating refresh tokens of @starci/auth. A token is stored by its sha256 only; a rotated token stays,
32
+ -- marked used, so a replay is detected (a replay inside the grace window is a racing tab, an older one ends the session).
33
+ -- identity_attempts counts the sign-in attempts of one address in a window every replica shares (the budget of failures).
34
+ -- The app owns auth.users (db/bootstrap.sql) and the role app_be.
35
+
36
+ create table public.identity_sessions (
37
+ id uuid primary key,
33
38
  user_id uuid not null references auth.users (id) on delete cascade,
34
- token_hash char(64) not null unique,
39
+ created_at timestamptz not null default now(),
40
+ refreshed_at timestamptz not null default now(),
41
+ user_agent text,
42
+ ip text
43
+ );
44
+
45
+ create index identity_sessions_user_idx on public.identity_sessions (user_id);
46
+
47
+ create table public.identity_refresh_tokens (
48
+ id uuid primary key default gen_random_uuid(),
49
+ session_id uuid not null references public.identity_sessions (id) on delete cascade,
50
+ user_id uuid not null,
51
+ token_hash text not null,
52
+ created_at timestamptz not null default now(),
35
53
  expires_at timestamptz not null,
36
- revoked_at timestamptz,
37
- created_at timestamptz not null default now()
54
+ used_at timestamptz,
55
+ constraint identity_refresh_tokens_hash_key unique (token_hash)
38
56
  );
39
57
 
40
- create index refresh_sessions_family_idx on auth.refresh_sessions (family_id);
58
+ create index identity_refresh_tokens_session_idx on public.identity_refresh_tokens (session_id);
59
+ create index identity_refresh_tokens_expiry_idx on public.identity_refresh_tokens (user_id, expires_at);
41
60
 
42
- grant usage on schema auth to app_be;
43
- grant select, insert, update on table auth.users to app_be;
44
- grant select, insert on table auth.identities to app_be;
45
- grant select, insert, update on table auth.refresh_sessions to app_be;
61
+ create table public.identity_attempts (
62
+ key varchar(128) primary key,
63
+ hits integer not null,
64
+ reset_at timestamptz not null
65
+ );
66
+
67
+ create index identity_attempts_reset_idx on public.identity_attempts (reset_at);
68
+
69
+ alter table public.identity_sessions enable row level security;
70
+ alter table public.identity_sessions force row level security;
71
+ alter table public.identity_refresh_tokens enable row level security;
72
+ alter table public.identity_refresh_tokens force row level security;
73
+ alter table public.identity_attempts enable row level security;
74
+ alter table public.identity_attempts force row level security;
75
+
76
+ revoke all on table public.identity_sessions from public;
77
+ revoke all on table public.identity_refresh_tokens from public;
78
+ revoke all on table public.identity_attempts from public;
79
+ grant select, insert, update, delete on table public.identity_sessions to app_be;
80
+ grant select, insert, update, delete on table public.identity_refresh_tokens to app_be;
81
+ grant select, insert, update, delete on table public.identity_attempts to app_be;
82
+
83
+ create policy identity_sessions_app_be_all on public.identity_sessions
84
+ for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
85
+ create policy identity_refresh_tokens_app_be_all on public.identity_refresh_tokens
86
+ for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
87
+ create policy identity_attempts_app_be_all on public.identity_attempts
88
+ for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
46
89
 
47
90
  create table public.profiles (
48
91
  id uuid primary key references auth.users (id) on delete cascade,
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import type { HttpExchange } from "@modules/integrations/passport"
3
+
4
+ /** One cookie door of sign-in: the request and response the strategy or the refresh cookie work on. */
5
+ export interface ExchangeRequest {
6
+ /** The request and response. */
7
+ readonly exchange: HttpExchange
8
+ }
9
+
10
+ /** A request with nothing in it. */
11
+ export type EmptyRequest = Record<string, never>
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { QueryHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { JwksDocument } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { GetJwksQuery } from "./get-jwks.query"
9
+
10
+ @QueryHandler(GetJwksQuery)
11
+ /** Answers the JWKS. */
12
+ export class GetJwksHandler extends ICQRSHandler<GetJwksQuery, JwksDocument> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(): Promise<JwksDocument> {
21
+ return this.signIn.jwks()
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Query } from "@nestjs/cqrs"
3
+ import type { JwksDocument } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { EmptyRequest } from "./auth.contracts"
6
+
7
+ /** Reads the public key document access tokens verify against. */
8
+ export class GetJwksQuery extends Query<JwksDocument> {
9
+ constructor(readonly params: PublicExecuteParams<EmptyRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { ExchangeRequest } from "./auth.contracts"
5
+
6
+ /** Starts the Google round trip: the strategy writes the redirect and the signed state cookie. */
7
+ export class GoogleBeginCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { GoogleBeginCommand } from "./google-begin.command"
8
+
9
+ @CommandHandler(GoogleBeginCommand)
10
+ /** Starts the Google round trip: the strategy writes the redirect and the signed state cookie. */
11
+ export class GoogleBeginHandler extends ICQRSHandler<GoogleBeginCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly signIn: IdentitySignInService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: GoogleBeginCommand): Promise<void> {
20
+ return this.signIn.beginGoogle(command.params.request.exchange)
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { GoogleRedirect } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Finishes the Google round trip. */
8
+ export class GoogleCompleteCommand extends Command<GoogleRedirect> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { GoogleRedirect } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { GoogleCompleteCommand } from "./google-complete.command"
9
+
10
+ @CommandHandler(GoogleCompleteCommand)
11
+ /** Finishes the Google round trip. */
12
+ export class GoogleCompleteHandler extends ICQRSHandler<GoogleCompleteCommand, GoogleRedirect> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: GoogleCompleteCommand): Promise<GoogleRedirect> {
21
+ return this.signIn.completeGoogle(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { AccessGrant } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Signs in with email and password through the local strategy and opens a session. */
8
+ export class LoginCommand extends Command<AccessGrant> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }
@@ -0,0 +1,23 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import type { AccessGrant } from "@modules/integrations/passport"
5
+ import { ICQRSHandler } from "@modules/platform/cqrs"
6
+ import { InjectLogger } from "@modules/platform/logging"
7
+ import type { Logger } from "@modules/platform/logging"
8
+ import { LoginCommand } from "./login.command"
9
+
10
+ @CommandHandler(LoginCommand)
11
+ /** Signs in with email and password through the local strategy and opens a session. */
12
+ export class LoginHandler extends ICQRSHandler<LoginCommand, AccessGrant> {
13
+ constructor(
14
+ @InjectLogger() logger: Logger,
15
+ private readonly signIn: IdentitySignInService,
16
+ ) {
17
+ super(logger)
18
+ }
19
+
20
+ protected override process(command: LoginCommand): Promise<AccessGrant> {
21
+ return this.signIn.login(command.params.request.exchange)
22
+ }
23
+ }
@@ -0,0 +1,11 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
4
+ import type { ExchangeRequest } from "./auth.contracts"
5
+
6
+ /** Ends the session of the refresh cookie and clears it. */
7
+ export class LogoutCommand extends Command<void> {
8
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
9
+ super()
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { CommandHandler } from "@nestjs/cqrs"
3
+ import { IdentitySignInService } from "@modules/domain/identity"
4
+ import { ICQRSHandler } from "@modules/platform/cqrs"
5
+ import { InjectLogger } from "@modules/platform/logging"
6
+ import type { Logger } from "@modules/platform/logging"
7
+ import { LogoutCommand } from "./logout.command"
8
+
9
+ @CommandHandler(LogoutCommand)
10
+ /** Ends the session of the refresh cookie and clears it. */
11
+ export class LogoutHandler extends ICQRSHandler<LogoutCommand, void> {
12
+ constructor(
13
+ @InjectLogger() logger: Logger,
14
+ private readonly signIn: IdentitySignInService,
15
+ ) {
16
+ super(logger)
17
+ }
18
+
19
+ protected override process(command: LogoutCommand): Promise<void> {
20
+ return this.signIn.logout(command.params.request.exchange)
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ // Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
2
+ import { Command } from "@nestjs/cqrs"
3
+ import type { AccessGrant } from "@modules/integrations/passport"
4
+ import type { PublicExecuteParams } from "@modules/platform/cqrs"
5
+ import type { ExchangeRequest } from "./auth.contracts"
6
+
7
+ /** Rotates the refresh cookie and answers a new access token; a bad cookie is cleared. */
8
+ export class RefreshCommand extends Command<AccessGrant> {
9
+ constructor(readonly params: PublicExecuteParams<ExchangeRequest>) {
10
+ super()
11
+ }
12
+ }