@de-otio/trellis 0.6.0 → 0.7.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 (339) hide show
  1. package/dist/env.d.ts +21 -0
  2. package/dist/env.d.ts.map +1 -1
  3. package/dist/env.js +12 -0
  4. package/dist/env.js.map +1 -1
  5. package/dist/lambda/nightly-cron.d.ts.map +1 -1
  6. package/dist/lambda/nightly-cron.js +5 -2
  7. package/dist/lambda/nightly-cron.js.map +1 -1
  8. package/dist/lambda/post-confirmation.d.ts +30 -0
  9. package/dist/lambda/post-confirmation.d.ts.map +1 -1
  10. package/dist/lambda/post-confirmation.js +333 -29
  11. package/dist/lambda/post-confirmation.js.map +1 -1
  12. package/dist/lambda/pre-token-generation.d.ts +20 -0
  13. package/dist/lambda/pre-token-generation.d.ts.map +1 -1
  14. package/dist/lambda/pre-token-generation.js +233 -48
  15. package/dist/lambda/pre-token-generation.js.map +1 -1
  16. package/dist/lib/activitypub/activity-processor.d.ts.map +1 -1
  17. package/dist/lib/activitypub/activity-processor.js +2 -1
  18. package/dist/lib/activitypub/activity-processor.js.map +1 -1
  19. package/dist/lib/activitypub/group-service.d.ts +2 -2
  20. package/dist/lib/activitypub/group-service.d.ts.map +1 -1
  21. package/dist/lib/activitypub/group-service.js +5 -2
  22. package/dist/lib/activitypub/group-service.js.map +1 -1
  23. package/dist/lib/age-tier-transition.d.ts.map +1 -1
  24. package/dist/lib/age-tier-transition.js +19 -10
  25. package/dist/lib/age-tier-transition.js.map +1 -1
  26. package/dist/lib/audit/csv-export.d.ts +25 -0
  27. package/dist/lib/audit/csv-export.d.ts.map +1 -0
  28. package/dist/lib/audit/csv-export.js +54 -0
  29. package/dist/lib/audit/csv-export.js.map +1 -0
  30. package/dist/lib/audit/emit.d.ts +56 -0
  31. package/dist/lib/audit/emit.d.ts.map +1 -0
  32. package/dist/lib/audit/emit.js +124 -0
  33. package/dist/lib/audit/emit.js.map +1 -0
  34. package/dist/lib/audit/event-types.d.ts +36 -0
  35. package/dist/lib/audit/event-types.d.ts.map +1 -0
  36. package/dist/lib/audit/event-types.js +69 -0
  37. package/dist/lib/audit/event-types.js.map +1 -0
  38. package/dist/lib/audit/pii-filter.d.ts +22 -0
  39. package/dist/lib/audit/pii-filter.d.ts.map +1 -0
  40. package/dist/lib/audit/pii-filter.js +51 -0
  41. package/dist/lib/audit/pii-filter.js.map +1 -0
  42. package/dist/lib/audit-logger.js +1 -1
  43. package/dist/lib/audit-logger.js.map +1 -1
  44. package/dist/lib/auth/auth-context.d.ts +34 -0
  45. package/dist/lib/auth/auth-context.d.ts.map +1 -0
  46. package/dist/lib/auth/auth-context.js +10 -0
  47. package/dist/lib/auth/auth-context.js.map +1 -0
  48. package/dist/lib/auth/auth-middleware.d.ts +50 -0
  49. package/dist/lib/auth/auth-middleware.d.ts.map +1 -0
  50. package/dist/lib/auth/auth-middleware.js +153 -0
  51. package/dist/lib/auth/auth-middleware.js.map +1 -0
  52. package/dist/lib/auth/capabilities.d.ts +40 -0
  53. package/dist/lib/auth/capabilities.d.ts.map +1 -0
  54. package/dist/lib/auth/capabilities.js +44 -0
  55. package/dist/lib/auth/capabilities.js.map +1 -0
  56. package/dist/lib/auth/claims-cache.d.ts +70 -0
  57. package/dist/lib/auth/claims-cache.d.ts.map +1 -0
  58. package/dist/lib/auth/claims-cache.js +139 -0
  59. package/dist/lib/auth/claims-cache.js.map +1 -0
  60. package/dist/lib/auth/cognito-jwt.d.ts +6 -0
  61. package/dist/lib/auth/cognito-jwt.d.ts.map +1 -1
  62. package/dist/lib/auth/cognito-jwt.js.map +1 -1
  63. package/dist/lib/auth/idp-redirect-builder.d.ts +43 -0
  64. package/dist/lib/auth/idp-redirect-builder.d.ts.map +1 -0
  65. package/dist/lib/auth/idp-redirect-builder.js +48 -0
  66. package/dist/lib/auth/idp-redirect-builder.js.map +1 -0
  67. package/dist/lib/auth/require.d.ts +51 -0
  68. package/dist/lib/auth/require.d.ts.map +1 -0
  69. package/dist/lib/auth/require.js +99 -0
  70. package/dist/lib/auth/require.js.map +1 -0
  71. package/dist/lib/auth/role-grants.d.ts +18 -0
  72. package/dist/lib/auth/role-grants.d.ts.map +1 -0
  73. package/dist/lib/auth/role-grants.js +62 -0
  74. package/dist/lib/auth/role-grants.js.map +1 -0
  75. package/dist/lib/cognito/idp-sdk.d.ts +80 -0
  76. package/dist/lib/cognito/idp-sdk.d.ts.map +1 -0
  77. package/dist/lib/cognito/idp-sdk.js +186 -0
  78. package/dist/lib/cognito/idp-sdk.js.map +1 -0
  79. package/dist/lib/cognito/issuer-probe.d.ts +47 -0
  80. package/dist/lib/cognito/issuer-probe.d.ts.map +1 -0
  81. package/dist/lib/cognito/issuer-probe.js +319 -0
  82. package/dist/lib/cognito/issuer-probe.js.map +1 -0
  83. package/dist/lib/comment-handler.d.ts +7 -7
  84. package/dist/lib/comment-handler.d.ts.map +1 -1
  85. package/dist/lib/comment-handler.js +23 -20
  86. package/dist/lib/comment-handler.js.map +1 -1
  87. package/dist/lib/compliance/baseline.d.ts +15 -0
  88. package/dist/lib/compliance/baseline.d.ts.map +1 -0
  89. package/dist/lib/compliance/baseline.js +205 -0
  90. package/dist/lib/compliance/baseline.js.map +1 -0
  91. package/dist/lib/compliance/tenant-merge.d.ts +35 -0
  92. package/dist/lib/compliance/tenant-merge.d.ts.map +1 -0
  93. package/dist/lib/compliance/tenant-merge.js +80 -0
  94. package/dist/lib/compliance/tenant-merge.js.map +1 -0
  95. package/dist/lib/compliance/types.d.ts +135 -0
  96. package/dist/lib/compliance/types.d.ts.map +1 -0
  97. package/dist/lib/compliance/types.js +9 -0
  98. package/dist/lib/compliance/types.js.map +1 -0
  99. package/dist/lib/connection-code-handler.d.ts +4 -4
  100. package/dist/lib/connection-code-handler.d.ts.map +1 -1
  101. package/dist/lib/connection-code-handler.js +21 -11
  102. package/dist/lib/connection-code-handler.js.map +1 -1
  103. package/dist/lib/feed-handler.d.ts +2 -2
  104. package/dist/lib/feed-handler.d.ts.map +1 -1
  105. package/dist/lib/feed-handler.js +5 -9
  106. package/dist/lib/feed-handler.js.map +1 -1
  107. package/dist/lib/middleware/idempotency-store.d.ts +86 -0
  108. package/dist/lib/middleware/idempotency-store.d.ts.map +1 -0
  109. package/dist/lib/middleware/idempotency-store.js +109 -0
  110. package/dist/lib/middleware/idempotency-store.js.map +1 -0
  111. package/dist/lib/middleware/idempotency.d.ts +37 -0
  112. package/dist/lib/middleware/idempotency.d.ts.map +1 -0
  113. package/dist/lib/middleware/idempotency.js +358 -0
  114. package/dist/lib/middleware/idempotency.js.map +1 -0
  115. package/dist/lib/net/trusted-client-ip.d.ts +39 -0
  116. package/dist/lib/net/trusted-client-ip.d.ts.map +1 -0
  117. package/dist/lib/net/trusted-client-ip.js +100 -0
  118. package/dist/lib/net/trusted-client-ip.js.map +1 -0
  119. package/dist/lib/notification-handler.d.ts +5 -5
  120. package/dist/lib/notification-handler.d.ts.map +1 -1
  121. package/dist/lib/notification-handler.js +11 -9
  122. package/dist/lib/notification-handler.js.map +1 -1
  123. package/dist/lib/oauth/cognito-issuer.d.ts +34 -0
  124. package/dist/lib/oauth/cognito-issuer.d.ts.map +1 -0
  125. package/dist/lib/oauth/cognito-issuer.js +53 -0
  126. package/dist/lib/oauth/cognito-issuer.js.map +1 -0
  127. package/dist/lib/oauth/device-authorization.d.ts +145 -0
  128. package/dist/lib/oauth/device-authorization.d.ts.map +1 -0
  129. package/dist/lib/oauth/device-authorization.js +312 -0
  130. package/dist/lib/oauth/device-authorization.js.map +1 -0
  131. package/dist/lib/oauth/envelope-crypto.d.ts +101 -0
  132. package/dist/lib/oauth/envelope-crypto.d.ts.map +1 -0
  133. package/dist/lib/oauth/envelope-crypto.js +223 -0
  134. package/dist/lib/oauth/envelope-crypto.js.map +1 -0
  135. package/dist/lib/oauth/refresh-detection.d.ts +126 -0
  136. package/dist/lib/oauth/refresh-detection.d.ts.map +1 -0
  137. package/dist/lib/oauth/refresh-detection.js +248 -0
  138. package/dist/lib/oauth/refresh-detection.js.map +1 -0
  139. package/dist/lib/openapi/generator.d.ts +78 -0
  140. package/dist/lib/openapi/generator.d.ts.map +1 -0
  141. package/dist/lib/openapi/generator.js +201 -0
  142. package/dist/lib/openapi/generator.js.map +1 -0
  143. package/dist/lib/post-handler.d.ts +1 -1
  144. package/dist/lib/post-handler.d.ts.map +1 -1
  145. package/dist/lib/post-handler.js +4 -15
  146. package/dist/lib/post-handler.js.map +1 -1
  147. package/dist/lib/rate-limit.d.ts.map +1 -1
  148. package/dist/lib/rate-limit.js +11 -3
  149. package/dist/lib/rate-limit.js.map +1 -1
  150. package/dist/lib/routes/agent-authorize.d.ts +32 -0
  151. package/dist/lib/routes/agent-authorize.d.ts.map +1 -0
  152. package/dist/lib/routes/agent-authorize.js +479 -0
  153. package/dist/lib/routes/agent-authorize.js.map +1 -0
  154. package/dist/lib/routes/agent-sessions.d.ts +20 -0
  155. package/dist/lib/routes/agent-sessions.d.ts.map +1 -0
  156. package/dist/lib/routes/agent-sessions.js +124 -0
  157. package/dist/lib/routes/agent-sessions.js.map +1 -0
  158. package/dist/lib/routes/agent-surface.d.ts +37 -0
  159. package/dist/lib/routes/agent-surface.d.ts.map +1 -0
  160. package/dist/lib/routes/agent-surface.js +208 -0
  161. package/dist/lib/routes/agent-surface.js.map +1 -0
  162. package/dist/lib/routes/auth-discover.d.ts +18 -0
  163. package/dist/lib/routes/auth-discover.d.ts.map +1 -0
  164. package/dist/lib/routes/auth-discover.js +177 -0
  165. package/dist/lib/routes/auth-discover.js.map +1 -0
  166. package/dist/lib/routes/comments.d.ts.map +1 -1
  167. package/dist/lib/routes/comments.js +36 -7
  168. package/dist/lib/routes/comments.js.map +1 -1
  169. package/dist/lib/routes/connection-codes.d.ts.map +1 -1
  170. package/dist/lib/routes/connection-codes.js +21 -4
  171. package/dist/lib/routes/connection-codes.js.map +1 -1
  172. package/dist/lib/routes/content-discovery.d.ts.map +1 -1
  173. package/dist/lib/routes/content-discovery.js +18 -13
  174. package/dist/lib/routes/content-discovery.js.map +1 -1
  175. package/dist/lib/routes/dashboard.js +1 -1
  176. package/dist/lib/routes/dashboard.js.map +1 -1
  177. package/dist/lib/routes/employees.d.ts.map +1 -1
  178. package/dist/lib/routes/employees.js +57 -15
  179. package/dist/lib/routes/employees.js.map +1 -1
  180. package/dist/lib/routes/entities.d.ts.map +1 -1
  181. package/dist/lib/routes/entities.js +35 -19
  182. package/dist/lib/routes/entities.js.map +1 -1
  183. package/dist/lib/routes/errors.d.ts +34 -0
  184. package/dist/lib/routes/errors.d.ts.map +1 -0
  185. package/dist/lib/routes/errors.js +57 -0
  186. package/dist/lib/routes/errors.js.map +1 -0
  187. package/dist/lib/routes/feeds.d.ts.map +1 -1
  188. package/dist/lib/routes/feeds.js +12 -2
  189. package/dist/lib/routes/feeds.js.map +1 -1
  190. package/dist/lib/routes/index.d.ts.map +1 -1
  191. package/dist/lib/routes/index.js +50 -0
  192. package/dist/lib/routes/index.js.map +1 -1
  193. package/dist/lib/routes/mfa.d.ts.map +1 -1
  194. package/dist/lib/routes/mfa.js +1 -0
  195. package/dist/lib/routes/mfa.js.map +1 -1
  196. package/dist/lib/routes/notifications.d.ts.map +1 -1
  197. package/dist/lib/routes/notifications.js +21 -4
  198. package/dist/lib/routes/notifications.js.map +1 -1
  199. package/dist/lib/routes/oauth.d.ts +15 -0
  200. package/dist/lib/routes/oauth.d.ts.map +1 -0
  201. package/dist/lib/routes/oauth.js +139 -0
  202. package/dist/lib/routes/oauth.js.map +1 -0
  203. package/dist/lib/routes/posts.d.ts.map +1 -1
  204. package/dist/lib/routes/posts.js +30 -19
  205. package/dist/lib/routes/posts.js.map +1 -1
  206. package/dist/lib/routes/products.d.ts.map +1 -1
  207. package/dist/lib/routes/products.js +19 -22
  208. package/dist/lib/routes/products.js.map +1 -1
  209. package/dist/lib/routes/setup-status.d.ts +34 -0
  210. package/dist/lib/routes/setup-status.d.ts.map +1 -0
  211. package/dist/lib/routes/setup-status.js +87 -0
  212. package/dist/lib/routes/setup-status.js.map +1 -0
  213. package/dist/lib/routes/taxonomy-analytics.d.ts.map +1 -1
  214. package/dist/lib/routes/taxonomy-analytics.js +15 -14
  215. package/dist/lib/routes/taxonomy-analytics.js.map +1 -1
  216. package/dist/lib/routes/taxonomy.d.ts.map +1 -1
  217. package/dist/lib/routes/taxonomy.js +19 -16
  218. package/dist/lib/routes/taxonomy.js.map +1 -1
  219. package/dist/lib/routes/tenant-audit.d.ts +19 -0
  220. package/dist/lib/routes/tenant-audit.d.ts.map +1 -0
  221. package/dist/lib/routes/tenant-audit.js +244 -0
  222. package/dist/lib/routes/tenant-audit.js.map +1 -0
  223. package/dist/lib/routes/tenant-compliance.d.ts +21 -0
  224. package/dist/lib/routes/tenant-compliance.d.ts.map +1 -0
  225. package/dist/lib/routes/tenant-compliance.js +122 -0
  226. package/dist/lib/routes/tenant-compliance.js.map +1 -0
  227. package/dist/lib/routes/tenant-domains.d.ts +11 -0
  228. package/dist/lib/routes/tenant-domains.d.ts.map +1 -0
  229. package/dist/lib/routes/tenant-domains.js +95 -0
  230. package/dist/lib/routes/tenant-domains.js.map +1 -0
  231. package/dist/lib/routes/tenant-idp.d.ts +3 -0
  232. package/dist/lib/routes/tenant-idp.d.ts.map +1 -0
  233. package/dist/lib/routes/tenant-idp.js +89 -0
  234. package/dist/lib/routes/tenant-idp.js.map +1 -0
  235. package/dist/lib/routes/tenant-members.d.ts +13 -0
  236. package/dist/lib/routes/tenant-members.d.ts.map +1 -0
  237. package/dist/lib/routes/tenant-members.js +75 -0
  238. package/dist/lib/routes/tenant-members.js.map +1 -0
  239. package/dist/lib/routes/tenant-role-mappings.d.ts +11 -0
  240. package/dist/lib/routes/tenant-role-mappings.d.ts.map +1 -0
  241. package/dist/lib/routes/tenant-role-mappings.js +90 -0
  242. package/dist/lib/routes/tenant-role-mappings.js.map +1 -0
  243. package/dist/lib/routes/tenants.d.ts +13 -0
  244. package/dist/lib/routes/tenants.d.ts.map +1 -0
  245. package/dist/lib/routes/tenants.js +121 -0
  246. package/dist/lib/routes/tenants.js.map +1 -0
  247. package/dist/lib/routes/types.d.ts +9 -0
  248. package/dist/lib/routes/types.d.ts.map +1 -1
  249. package/dist/lib/schemas.d.ts +2 -2
  250. package/dist/lib/secrets/idp-secrets.d.ts +51 -0
  251. package/dist/lib/secrets/idp-secrets.d.ts.map +1 -0
  252. package/dist/lib/secrets/idp-secrets.js +111 -0
  253. package/dist/lib/secrets/idp-secrets.js.map +1 -0
  254. package/dist/lib/security-monitor.d.ts.map +1 -1
  255. package/dist/lib/security-monitor.js +6 -1
  256. package/dist/lib/security-monitor.js.map +1 -1
  257. package/dist/lib/session-manager.d.ts +1 -0
  258. package/dist/lib/session-manager.d.ts.map +1 -1
  259. package/dist/lib/session-manager.js.map +1 -1
  260. package/dist/lib/taxonomy-handler-factory.d.ts +4 -2
  261. package/dist/lib/taxonomy-handler-factory.d.ts.map +1 -1
  262. package/dist/lib/taxonomy-handler-factory.js +8 -7
  263. package/dist/lib/taxonomy-handler-factory.js.map +1 -1
  264. package/dist/lib/tenant/audit-emit.d.ts +18 -0
  265. package/dist/lib/tenant/audit-emit.d.ts.map +1 -0
  266. package/dist/lib/tenant/audit-emit.js +16 -0
  267. package/dist/lib/tenant/audit-emit.js.map +1 -0
  268. package/dist/lib/tenant/derive-domain.d.ts +19 -0
  269. package/dist/lib/tenant/derive-domain.d.ts.map +1 -0
  270. package/dist/lib/tenant/derive-domain.js +38 -0
  271. package/dist/lib/tenant/derive-domain.js.map +1 -0
  272. package/dist/lib/tenant/domain-handler.d.ts +42 -0
  273. package/dist/lib/tenant/domain-handler.d.ts.map +1 -0
  274. package/dist/lib/tenant/domain-handler.js +344 -0
  275. package/dist/lib/tenant/domain-handler.js.map +1 -0
  276. package/dist/lib/tenant/domain-validator.d.ts +28 -0
  277. package/dist/lib/tenant/domain-validator.d.ts.map +1 -0
  278. package/dist/lib/tenant/domain-validator.js +145 -0
  279. package/dist/lib/tenant/domain-validator.js.map +1 -0
  280. package/dist/lib/tenant/domain-verifier.d.ts +30 -0
  281. package/dist/lib/tenant/domain-verifier.d.ts.map +1 -0
  282. package/dist/lib/tenant/domain-verifier.js +53 -0
  283. package/dist/lib/tenant/domain-verifier.js.map +1 -0
  284. package/dist/lib/tenant/idp-handler.d.ts +29 -0
  285. package/dist/lib/tenant/idp-handler.d.ts.map +1 -0
  286. package/dist/lib/tenant/idp-handler.js +693 -0
  287. package/dist/lib/tenant/idp-handler.js.map +1 -0
  288. package/dist/lib/tenant/idp-name.d.ts +2 -0
  289. package/dist/lib/tenant/idp-name.d.ts.map +1 -0
  290. package/dist/lib/tenant/idp-name.js +20 -0
  291. package/dist/lib/tenant/idp-name.js.map +1 -0
  292. package/dist/lib/tenant/member-handler.d.ts +31 -0
  293. package/dist/lib/tenant/member-handler.d.ts.map +1 -0
  294. package/dist/lib/tenant/member-handler.js +343 -0
  295. package/dist/lib/tenant/member-handler.js.map +1 -0
  296. package/dist/lib/tenant/reserved-slugs.d.ts +37 -0
  297. package/dist/lib/tenant/reserved-slugs.d.ts.map +1 -0
  298. package/dist/lib/tenant/reserved-slugs.js +116 -0
  299. package/dist/lib/tenant/reserved-slugs.js.map +1 -0
  300. package/dist/lib/tenant/resolve-role.d.ts +39 -0
  301. package/dist/lib/tenant/resolve-role.d.ts.map +1 -0
  302. package/dist/lib/tenant/resolve-role.js +60 -0
  303. package/dist/lib/tenant/resolve-role.js.map +1 -0
  304. package/dist/lib/tenant/role-mapping-handler.d.ts +26 -0
  305. package/dist/lib/tenant/role-mapping-handler.d.ts.map +1 -0
  306. package/dist/lib/tenant/role-mapping-handler.js +260 -0
  307. package/dist/lib/tenant/role-mapping-handler.js.map +1 -0
  308. package/dist/lib/tenant/setup-status.d.ts +83 -0
  309. package/dist/lib/tenant/setup-status.d.ts.map +1 -0
  310. package/dist/lib/tenant/setup-status.js +201 -0
  311. package/dist/lib/tenant/setup-status.js.map +1 -0
  312. package/dist/lib/tenant/slug-validator.d.ts +31 -0
  313. package/dist/lib/tenant/slug-validator.d.ts.map +1 -0
  314. package/dist/lib/tenant/slug-validator.js +42 -0
  315. package/dist/lib/tenant/slug-validator.js.map +1 -0
  316. package/dist/lib/tenant/tenant-handler.d.ts +49 -0
  317. package/dist/lib/tenant/tenant-handler.d.ts.map +1 -0
  318. package/dist/lib/tenant/tenant-handler.js +377 -0
  319. package/dist/lib/tenant/tenant-handler.js.map +1 -0
  320. package/dist/lib/tenant/transfer-ownership.d.ts +39 -0
  321. package/dist/lib/tenant/transfer-ownership.d.ts.map +1 -0
  322. package/dist/lib/tenant/transfer-ownership.js +66 -0
  323. package/dist/lib/tenant/transfer-ownership.js.map +1 -0
  324. package/dist/lib/user/derive-handle.d.ts +29 -0
  325. package/dist/lib/user/derive-handle.d.ts.map +1 -0
  326. package/dist/lib/user/derive-handle.js +65 -0
  327. package/dist/lib/user/derive-handle.js.map +1 -0
  328. package/dist/lib/user-deprovisioning.d.ts +11 -1
  329. package/dist/lib/user-deprovisioning.d.ts.map +1 -1
  330. package/dist/lib/user-deprovisioning.js +46 -2
  331. package/dist/lib/user-deprovisioning.js.map +1 -1
  332. package/dist/lib/validation/feature-toggle-schemas.d.ts +10 -10
  333. package/package.json +6 -3
  334. package/prisma/migrations/20260502094501_add_tenancy_model/migration.sql +334 -0
  335. package/prisma/migrations/20260503000000_add_tenant_region/migration.sql +4 -0
  336. package/prisma/schema.prisma +324 -74
  337. package/src/lambda/nightly-cron.ts +4 -1
  338. package/src/lambda/post-confirmation.ts +405 -29
  339. package/src/lambda/pre-token-generation.ts +300 -59
@@ -15,6 +15,8 @@ datasource db {
15
15
  // NO tenantId - schema-per-tenant provides isolation
16
16
  model Entity {
17
17
  id String @id @default(cuid())
18
+ // Multi-tenancy (v0.7) — every entity belongs to exactly one tenant.
19
+ tenantId String @map("tenant_id")
18
20
  name String
19
21
  entityType String? @map("entity_type") // 'pet', 'product', 'event', etc. (defaults to 'dog' for MVP)
20
22
  metadata Json? // Flexible schema for entity-specific fields (breed, bio, birthdate, breedSize, etc.)
@@ -41,7 +43,7 @@ model Entity {
41
43
  updatedAt DateTime @updatedAt @map("updated_at")
42
44
 
43
45
  // Co-ownership (replaces single ownerId)
44
- owners EntityOwnership[]
46
+ owners EntityOwnership[]
45
47
  // Relationships and follower counts live in the graph DB (AuraDB), not Prisma
46
48
 
47
49
  subjectPosts PostSubject[]
@@ -50,6 +52,10 @@ model Entity {
50
52
 
51
53
  connectionCodes ConnectionCode[]
52
54
 
55
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
56
+
57
+ @@index([tenantId])
58
+ @@index([tenantId, entityType, status])
53
59
  @@index([status])
54
60
  @@index([entityType, status])
55
61
  @@map("entities")
@@ -133,8 +139,10 @@ model User {
133
139
  suspended Boolean @default(false)
134
140
  suspendedAt DateTime? @map("suspended_at")
135
141
  suspendedReason String? @map("suspended_reason")
136
- partnerId String?
137
- partner Partner? @relation(fields: [partnerId], references: [id])
142
+
143
+ // Multi-tenancy (v0.7) user's auto-created personal tenant.
144
+ // Nullable to allow brief moments during sign-up before personal tenant exists.
145
+ personalTenantId String? @unique @map("personal_tenant_id")
138
146
 
139
147
  // Account Deletion Grace Period
140
148
  deletionRequestedAt DateTime? @map("deletion_requested_at") // When user requested deletion
@@ -267,8 +275,8 @@ model User {
267
275
  guardianLinks ParentalLink[] @relation("GuardianParentalLinks")
268
276
 
269
277
  // Safer Social Design: Quiet hours (Phase 2)
270
- quietHoursStart Int? @map("quiet_hours_start") // minutes from midnight (e.g., 1320 = 10PM)
271
- quietHoursEnd Int? @map("quiet_hours_end") // minutes from midnight (e.g., 420 = 7AM)
278
+ quietHoursStart Int? @map("quiet_hours_start") // minutes from midnight (e.g., 1320 = 10PM)
279
+ quietHoursEnd Int? @map("quiet_hours_end") // minutes from midnight (e.g., 420 = 7AM)
272
280
  quietHoursEnabled Boolean @default(false) @map("quiet_hours_enabled")
273
281
 
274
282
  // Safer Social Design: Profile visibility and DM access (Phase 5)
@@ -283,11 +291,17 @@ model User {
283
291
  createdConnectionCodes ConnectionCode[] @relation("ConnectionCodeCreator")
284
292
  redeemedConnectionCodes ConnectionCodeRedemption[]
285
293
 
294
+ // Multi-tenancy relations (v0.7)
295
+ personalTenant Tenant? @relation("PersonalTenantOwner")
296
+ tenantMemberships TenantMember[] @relation("TenantMemberships")
297
+ invitedTenantMembers TenantMember[] @relation("TenantInvitedBy")
298
+ sentInvitations TenantInvitation[] @relation("TenantInvitationInviter")
299
+ acceptedInvitations TenantInvitation[] @relation("TenantInvitationAcceptedBy")
300
+
286
301
  @@index([role])
287
302
  @@index([region])
288
303
  @@index([dataRegion])
289
304
  @@index([suspended])
290
- @@index([partnerId])
291
305
  @@index([username])
292
306
  @@index([emailVerified])
293
307
  @@index([identityVerified])
@@ -298,6 +312,7 @@ model User {
298
312
  @@index([emailHash]) // Index for email hash lookups
299
313
  @@index([anonymousId]) // Index for anonymous ID lookups
300
314
  @@index([cognitoSub]) // Index for Cognito sub lookups
315
+ @@index([personalTenantId])
301
316
  @@map("users")
302
317
  }
303
318
 
@@ -373,9 +388,9 @@ model CrossRegionConsent {
373
388
  // Posting radius — how far content radiates on the author's social graph
374
389
  enum PostRadius {
375
390
  WHISPER // Inner circle only (tier 0)
376
- NORMAL // Close friends + inner circle (tiers 0-1)
377
- LOUD // Community and closer (tiers 0-2)
378
- SHOUT // Everyone (all tiers)
391
+ NORMAL // Close friends + inner circle (tiers 0-1)
392
+ LOUD // Community and closer (tiers 0-2)
393
+ SHOUT // Everyone (all tiers)
379
394
  }
380
395
 
381
396
  // Privacy levels for follow relationships
@@ -387,30 +402,33 @@ enum Privacy {
387
402
 
388
403
  // Post model (unified for all visibility levels)
389
404
  model Post {
390
- id String @id @default(cuid())
391
- authorId String @map("author_id")
392
- text String @db.Text
393
- radius PostRadius @default(NORMAL)
394
- geoData Json? @map("geo_data")
405
+ id String @id @default(cuid())
406
+ // Multi-tenancy (v0.7) — set to author's active tenant at write time.
407
+ tenantId String @map("tenant_id")
408
+ authorId String @map("author_id")
409
+ text String @db.Text
410
+ radius PostRadius @default(NORMAL)
411
+ geoData Json? @map("geo_data")
395
412
  uri String? // ActivityPub URI (for public posts)
396
- contentWarnings String[] @map("content_warnings")
397
- deletedAt DateTime? @map("deleted_at")
398
- hiddenByAuthor Boolean @default(false) @map("hidden_by_author")
399
- createdAt DateTime @default(now()) @map("created_at")
400
- updatedAt DateTime @updatedAt @map("updated_at")
401
- editedAt DateTime? @map("edited_at") // Timestamp of last user edit (null if never edited)
402
-
403
- author User @relation(fields: [authorId], references: [id])
404
- group Group? @relation(fields: [groupId], references: [id])
405
- groupId String? @map("group_id") // Group this post belongs to
406
- media PostMedia[]
407
- sentiments PostSentiment[]
408
- comments PostComment[]
413
+ contentWarnings String[] @map("content_warnings")
414
+ deletedAt DateTime? @map("deleted_at")
415
+ hiddenByAuthor Boolean @default(false) @map("hidden_by_author")
416
+ createdAt DateTime @default(now()) @map("created_at")
417
+ updatedAt DateTime @updatedAt @map("updated_at")
418
+ editedAt DateTime? @map("edited_at") // Timestamp of last user edit (null if never edited)
419
+
420
+ author User @relation(fields: [authorId], references: [id])
421
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
422
+ group Group? @relation(fields: [groupId], references: [id])
423
+ groupId String? @map("group_id") // Group this post belongs to
424
+ media PostMedia[]
425
+ sentiments PostSentiment[]
426
+ comments PostComment[]
409
427
  subjectEntities PostSubject[]
410
- primaryEntityId String? @map("primary_entity_id")
411
- primaryEntity Entity? @relation("PrimaryEntityPosts", fields: [primaryEntityId], references: [id])
412
- taxonomyTags PostTaxonomyTag[]
413
- linkChecks LinkCheck[]
428
+ primaryEntityId String? @map("primary_entity_id")
429
+ primaryEntity Entity? @relation("PrimaryEntityPosts", fields: [primaryEntityId], references: [id])
430
+ taxonomyTags PostTaxonomyTag[]
431
+ linkChecks LinkCheck[]
414
432
 
415
433
  // PREPARATORY CHANGE: Region tracking for China expansion
416
434
  // dataRegion: Where post data is stored (for compliance)
@@ -447,6 +465,7 @@ model Post {
447
465
  // Content category (optional classification)
448
466
  contentCategory String? @map("content_category")
449
467
 
468
+ @@index([tenantId, createdAt])
450
469
  @@index([authorId, createdAt])
451
470
  @@index([authorId, radius, createdAt])
452
471
  @@index([primaryEntityId])
@@ -596,6 +615,8 @@ model PostSentiment {
596
615
  // Post comments
597
616
  model PostComment {
598
617
  id String @id @default(cuid())
618
+ // Multi-tenancy (v0.7) — denormalized from the parent post; same scope.
619
+ tenantId String @map("tenant_id")
599
620
  postId String @map("post_id")
600
621
  postUri String? @map("post_uri")
601
622
  authorId String @map("author_id")
@@ -607,7 +628,7 @@ model PostComment {
607
628
 
608
629
  // Edit tracking
609
630
  editedAt DateTime? @map("edited_at") // When last edited
610
- originalText String? @db.Text @map("original_text") // Preserve original on first edit
631
+ originalText String? @map("original_text") @db.Text // Preserve original on first edit
611
632
 
612
633
  // Soft delete tracking
613
634
  deletedAt DateTime? @map("deleted_at") // When deleted (soft delete)
@@ -633,10 +654,12 @@ model PostComment {
633
654
  contentCategory String? @map("content_category")
634
655
 
635
656
  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
657
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
636
658
  media PostCommentMedia[]
637
659
  sentiments CommentSentiment[]
638
660
  linkChecks LinkCheck[]
639
661
 
662
+ @@index([tenantId])
640
663
  @@index([postId, createdAt])
641
664
  @@index([postUri, createdAt])
642
665
  @@index([authorId])
@@ -709,13 +732,16 @@ model PostSubject {
709
732
  @@map("post_subjects")
710
733
  }
711
734
 
712
- // Security Events for SSO monitoring and compliance
735
+ // Security Events for SSO monitoring and compliance.
736
+ // In v0.7, this also serves as the audit-log substrate (T7 wires admin actions here).
713
737
  model SecurityEvent {
714
738
  id String @id @default(cuid())
715
- type String // sso_login, sso_failed, sso_config_error, rate_limit_exceeded, suspicious_activity, unauthorized_access
739
+ type String // sso_login, sso_failed, sso_config_error, rate_limit_exceeded, suspicious_activity, unauthorized_access, tenant_*, member_*, idp_*, etc.
716
740
  severity String // low, medium, high, critical
717
741
  userId String?
718
- partnerId String?
742
+ // Tenancy scope (v0.7) — replaces the old `partnerId` field.
743
+ // Nullable because some events (system-level) have no tenant context.
744
+ tenantId String? @map("tenant_id")
719
745
  ipAddress String? @map("ip_address")
720
746
  userAgent String? @map("user_agent")
721
747
  details String // JSON string
@@ -727,31 +753,18 @@ model SecurityEvent {
727
753
  // Retention periods: critical=365 days, high=90 days, medium=30 days, low=7 days
728
754
  retentionUntil DateTime? @map("retention_until")
729
755
 
730
- user User? @relation(fields: [userId], references: [id])
731
- partner Partner? @relation(fields: [partnerId], references: [id])
756
+ user User? @relation(fields: [userId], references: [id])
732
757
 
733
758
  @@index([type])
734
759
  @@index([severity])
735
760
  @@index([timestamp])
736
761
  @@index([userId])
737
- @@index([partnerId])
762
+ @@index([tenantId])
738
763
  @@index([ipAddress])
739
764
  @@index([retentionUntil]) // Index for efficient cleanup job queries
740
765
  @@map("security_events")
741
766
  }
742
767
 
743
- // Partner model for B2B SSO
744
- model Partner {
745
- id String @id @default(cuid())
746
- name String
747
- createdAt DateTime @default(now()) @map("created_at")
748
-
749
- users User[]
750
- securityEvents SecurityEvent[]
751
-
752
- @@map("partners")
753
- }
754
-
755
768
  // Feature Toggle model for global feature flags
756
769
  model FeatureToggle {
757
770
  id String @id @default(cuid())
@@ -942,10 +955,12 @@ model ProductTaxonomyTag {
942
955
 
943
956
  // Entity co-ownership — replaces single Entity.ownerId FK
944
957
  model EntityOwnership {
945
- id String @id @default(cuid())
946
- entityId String @map("entity_id")
947
- userId String @map("user_id")
948
- role OwnershipRole @default(CO_OWNER)
958
+ id String @id @default(cuid())
959
+ // Multi-tenancy (v0.7) — denormalized from entity; same scope.
960
+ tenantId String @map("tenant_id")
961
+ entityId String @map("entity_id")
962
+ userId String @map("user_id")
963
+ role OwnershipRole @default(CO_OWNER)
949
964
 
950
965
  addedByUserId String @map("added_by_user_id")
951
966
  addedAt DateTime @default(now()) @map("added_at")
@@ -954,9 +969,11 @@ model EntityOwnership {
954
969
 
955
970
  entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
956
971
  user User @relation("EntityOwners", fields: [userId], references: [id], onDelete: Cascade)
972
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
957
973
  addedBy User @relation("OwnershipAddedBy", fields: [addedByUserId], references: [id])
958
974
 
959
975
  @@unique([entityId, userId])
976
+ @@index([tenantId])
960
977
  @@index([entityId])
961
978
  @@index([userId])
962
979
  @@index([entityId, role])
@@ -965,8 +982,8 @@ model EntityOwnership {
965
982
 
966
983
  enum OwnershipRole {
967
984
  PRIMARY_OWNER // Full control, can transfer primary, can delete entity
968
- CO_OWNER // Post and manage, cannot delete entity
969
- CARETAKER // Can post about entity, cannot modify profile
985
+ CO_OWNER // Post and manage, cannot delete entity
986
+ CARETAKER // Can post about entity, cannot modify profile
970
987
  }
971
988
 
972
989
  enum OwnershipStatus {
@@ -985,13 +1002,13 @@ model CircleConfig {
985
1002
  userId String @unique @map("user_id")
986
1003
 
987
1004
  // Tier thresholds (score >= threshold = in this tier)
988
- innerThreshold Float @default(0.8) @map("inner_threshold") // tier 0
1005
+ innerThreshold Float @default(0.8) @map("inner_threshold") // tier 0
989
1006
  closeFriendThreshold Float @default(0.5) @map("close_friend_threshold") // tier 1
990
- communityThreshold Float @default(0.2) @map("community_threshold") // tier 2
1007
+ communityThreshold Float @default(0.2) @map("community_threshold") // tier 2
991
1008
  // Below communityThreshold = ambient (tier 3)
992
1009
 
993
1010
  // View preferences
994
- dailyDeckSize Int? @map("daily_deck_size") // null = no daily limit
1011
+ dailyDeckSize Int? @map("daily_deck_size") // null = no daily limit
995
1012
  glanceLimit Int @default(20) @map("glance_limit") // Max items in glance mode
996
1013
  depthWindowDays Int @default(7) @map("depth_window_days") // How far back depth mode goes
997
1014
 
@@ -1007,7 +1024,7 @@ model CircleConfig {
1007
1024
  model CircleReadState {
1008
1025
  id String @id @default(cuid())
1009
1026
  userId String @map("user_id")
1010
- tier Int // 0 = inner, 1 = close friends, 2 = community, 3 = ambient
1027
+ tier Int // 0 = inner, 1 = close friends, 2 = community, 3 = ambient
1011
1028
 
1012
1029
  lastReadAt DateTime @map("last_read_at")
1013
1030
  lastReadPostId String? @map("last_read_post_id") // Cursor for pagination
@@ -1029,6 +1046,8 @@ model CircleReadState {
1029
1046
  // Group model for ActivityPub private groups
1030
1047
  model Group {
1031
1048
  id String @id @default(cuid())
1049
+ // Multi-tenancy (v0.7) — group is owned by exactly one tenant.
1050
+ tenantId String @map("tenant_id")
1032
1051
  name String
1033
1052
  description String? @db.Text
1034
1053
  actorUri String @unique @map("actor_uri") // Group actor URI
@@ -1043,7 +1062,9 @@ model Group {
1043
1062
 
1044
1063
  members GroupMember[]
1045
1064
  posts Post[]
1065
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1046
1066
 
1067
+ @@index([tenantId])
1047
1068
  @@index([actorUri])
1048
1069
  @@index([privacy])
1049
1070
  @@map("groups")
@@ -1052,14 +1073,18 @@ model Group {
1052
1073
  // Group membership model
1053
1074
  model GroupMember {
1054
1075
  id String @id @default(cuid())
1076
+ // Multi-tenancy (v0.7) — denormalized from group.
1077
+ tenantId String @map("tenant_id")
1055
1078
  groupId String @map("group_id")
1056
1079
  actorUri String @map("actor_uri") // User's actor URI
1057
1080
  role GroupRole
1058
1081
  joinedAt DateTime @default(now()) @map("joined_at")
1059
1082
 
1060
- group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
1083
+ group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
1084
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1061
1085
 
1062
1086
  @@unique([groupId, actorUri])
1087
+ @@index([tenantId])
1063
1088
  @@index([groupId])
1064
1089
  @@index([actorUri])
1065
1090
  @@index([groupId, role])
@@ -1244,8 +1269,8 @@ model LinkReport {
1244
1269
  model EmailSuppression {
1245
1270
  id String @id @default(cuid())
1246
1271
  email String @unique
1247
- reason String // "bounce" | "complaint"
1248
- bounceType String? // "permanent" | "transient" for bounces
1272
+ reason String // "bounce" | "complaint"
1273
+ bounceType String? // "permanent" | "transient" for bounces
1249
1274
  suppressedAt DateTime @default(now())
1250
1275
  createdAt DateTime @default(now())
1251
1276
  updatedAt DateTime @updatedAt
@@ -1263,7 +1288,7 @@ model DeletionAuditLog {
1263
1288
  email String
1264
1289
  requestedAt DateTime @map("requested_at")
1265
1290
  confirmedAt DateTime? @map("confirmed_at")
1266
- completedAt DateTime @map("completed_at") @default(now())
1291
+ completedAt DateTime @default(now()) @map("completed_at")
1267
1292
  itemsDeleted Json @map("items_deleted")
1268
1293
  createdAt DateTime @default(now()) @map("created_at")
1269
1294
 
@@ -1278,22 +1303,22 @@ model DeletionAuditLog {
1278
1303
  // Age tiers for age-gated behavior
1279
1304
  enum AgeTier {
1280
1305
  CHILD // under 13
1281
- TEEN // 13-17
1306
+ TEEN // 13-17
1282
1307
  ADULT // 18+
1283
1308
  }
1284
1309
 
1285
1310
  // Profile visibility levels
1286
1311
  enum ProfileVisibility {
1287
- PUBLIC // discoverable in search, visible to all
1312
+ PUBLIC // discoverable in search, visible to all
1288
1313
  CONNECTIONS // visible only to mutual follows
1289
- PRIVATE // visible only to self and guardian
1314
+ PRIVATE // visible only to self and guardian
1290
1315
  }
1291
1316
 
1292
1317
  // DM access control
1293
1318
  enum DmAccess {
1294
- ANYONE // anyone can DM
1319
+ ANYONE // anyone can DM
1295
1320
  CONNECTIONS // only mutual follows
1296
- NOBODY // DMs disabled
1321
+ NOBODY // DMs disabled
1297
1322
  }
1298
1323
 
1299
1324
  // Parental link status
@@ -1339,6 +1364,8 @@ enum NotificationType {
1339
1364
  // Notification model (poll-based, never pushed)
1340
1365
  model Notification {
1341
1366
  id String @id @default(cuid())
1367
+ // Multi-tenancy (v0.7) — set to recipient's active tenant at write time.
1368
+ tenantId String @map("tenant_id")
1342
1369
  userId String @map("user_id")
1343
1370
  type NotificationType
1344
1371
  title String
@@ -1349,8 +1376,10 @@ model Notification {
1349
1376
  deliveredAt DateTime? @map("delivered_at")
1350
1377
  batchId String? @map("batch_id")
1351
1378
 
1352
- user User @relation(fields: [userId], references: [id])
1379
+ user User @relation(fields: [userId], references: [id])
1380
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1353
1381
 
1382
+ @@index([tenantId, userId, read, createdAt])
1354
1383
  @@index([userId, read, createdAt])
1355
1384
  @@index([userId, batchId])
1356
1385
  @@map("notifications")
@@ -1358,8 +1387,8 @@ model Notification {
1358
1387
 
1359
1388
  // Notification preferences (safety and parental always enabled, not configurable)
1360
1389
  model NotificationPreference {
1361
- id String @id @default(cuid())
1362
- userId String @unique @map("user_id")
1390
+ id String @id @default(cuid())
1391
+ userId String @unique @map("user_id")
1363
1392
  dmEnabled Boolean @default(true) @map("dm_enabled")
1364
1393
  followEnabled Boolean @default(true) @map("follow_enabled")
1365
1394
  digestEnabled Boolean @default(true) @map("digest_enabled")
@@ -1375,6 +1404,8 @@ model NotificationPreference {
1375
1404
  // a relationship in the graph DB. Entity-scoped codes can be created by entity owners.
1376
1405
  model ConnectionCode {
1377
1406
  id String @id @default(cuid())
1407
+ // Multi-tenancy (v0.7) — code's owning tenant (creator's active tenant at create time).
1408
+ tenantId String @map("tenant_id")
1378
1409
  code String @unique
1379
1410
  creatorId String @map("creator_id")
1380
1411
  entityId String? @map("entity_id")
@@ -1385,8 +1416,10 @@ model ConnectionCode {
1385
1416
 
1386
1417
  creator User @relation("ConnectionCodeCreator", fields: [creatorId], references: [id])
1387
1418
  entity Entity? @relation(fields: [entityId], references: [id])
1419
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1388
1420
  redemptions ConnectionCodeRedemption[]
1389
1421
 
1422
+ @@index([tenantId])
1390
1423
  @@index([creatorId, expiresAt])
1391
1424
  @@index([entityId])
1392
1425
  @@map("connection_codes")
@@ -1397,12 +1430,229 @@ model ConnectionCodeRedemption {
1397
1430
  id String @id @default(cuid())
1398
1431
  codeId String @map("code_id")
1399
1432
  userId String @map("user_id")
1433
+ tenantId String @map("tenant_id")
1400
1434
  createdAt DateTime @default(now()) @map("created_at")
1401
1435
 
1402
- code ConnectionCode @relation(fields: [codeId], references: [id])
1403
- user User @relation(fields: [userId], references: [id])
1436
+ code ConnectionCode @relation(fields: [codeId], references: [id])
1437
+ user User @relation(fields: [userId], references: [id])
1438
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1404
1439
 
1405
1440
  @@unique([codeId, userId])
1406
1441
  @@index([userId])
1442
+ @@index([tenantId])
1407
1443
  @@map("connection_code_redemptions")
1408
1444
  }
1445
+
1446
+ // ============================================================================
1447
+ // MULTI-TENANCY MODEL (added in v0.7 — see plans/mvp/10-trellis-stages/01-schema-migration.md)
1448
+ // ============================================================================
1449
+
1450
+ model Tenant {
1451
+ id String @id @default(cuid())
1452
+ slug String @unique
1453
+ displayName String @map("display_name")
1454
+ type TenantType
1455
+ status TenantStatus @default(ACTIVE)
1456
+
1457
+ // Data-residency region for the tenant. Surfaced in the per-tenant
1458
+ // compliance bundle (`/api/tenants/:id/compliance.json`). Defaults to
1459
+ // "EU" since the platform's primary region is Frankfurt; tenants can
1460
+ // elect a different region during onboarding.
1461
+ region String @default("EU")
1462
+
1463
+ // For PERSONAL tenants only — the single owner. Always equals the User.id who created it.
1464
+ personalOwnerUserId String? @unique @map("personal_owner_user_id")
1465
+
1466
+ createdAt DateTime @default(now()) @map("created_at")
1467
+ updatedAt DateTime @updatedAt @map("updated_at")
1468
+ suspendedAt DateTime? @map("suspended_at")
1469
+ suspendReason String? @map("suspend_reason")
1470
+
1471
+ members TenantMember[]
1472
+ domains TenantDomain[]
1473
+ identityProvider TenantIdentityProvider?
1474
+ roleMappings TenantRoleMapping[]
1475
+ invitations TenantInvitation[]
1476
+
1477
+ // Tenant-scoped resources
1478
+ entities Entity[]
1479
+ posts Post[]
1480
+ postComments PostComment[]
1481
+ groups Group[]
1482
+ groupMembers GroupMember[]
1483
+ entityOwnerships EntityOwnership[]
1484
+ connectionCodes ConnectionCode[]
1485
+ connectionCodeRedemptions ConnectionCodeRedemption[]
1486
+ notifications Notification[]
1487
+
1488
+ // Personal-tenant owner back-relation (uses @relation name to disambiguate)
1489
+ personalOwner User? @relation("PersonalTenantOwner", fields: [personalOwnerUserId], references: [id])
1490
+
1491
+ @@index([slug])
1492
+ @@index([type, status])
1493
+ @@map("tenants")
1494
+ }
1495
+
1496
+ enum TenantType {
1497
+ PERSONAL
1498
+ ORGANIZATION
1499
+ }
1500
+
1501
+ enum TenantStatus {
1502
+ ACTIVE
1503
+ SUSPENDED
1504
+ DELETING // Phase 3 — soft state during cascade-delete
1505
+ }
1506
+
1507
+ model TenantMember {
1508
+ id String @id @default(cuid())
1509
+ tenantId String @map("tenant_id")
1510
+ userId String @map("user_id")
1511
+ role TenantRole
1512
+ status TenantMemberStatus @default(ACTIVE)
1513
+
1514
+ isJitProvisioned Boolean @default(false) @map("is_jit_provisioned")
1515
+ invitedByUserId String? @map("invited_by_user_id")
1516
+ invitedAt DateTime? @map("invited_at")
1517
+ joinedAt DateTime? @map("joined_at")
1518
+ removedAt DateTime? @map("removed_at")
1519
+ lastActiveAt DateTime? @map("last_active_at")
1520
+
1521
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1522
+ user User @relation("TenantMemberships", fields: [userId], references: [id], onDelete: Cascade)
1523
+ invitedBy User? @relation("TenantInvitedBy", fields: [invitedByUserId], references: [id])
1524
+
1525
+ @@unique([tenantId, userId])
1526
+ @@index([userId])
1527
+ @@index([tenantId, status])
1528
+ @@index([tenantId, role])
1529
+ @@map("tenant_members")
1530
+ }
1531
+
1532
+ enum TenantRole {
1533
+ OWNER
1534
+ ADMIN
1535
+ MEMBER
1536
+ GUEST
1537
+ }
1538
+
1539
+ enum TenantMemberStatus {
1540
+ INVITED
1541
+ ACTIVE
1542
+ SUSPENDED
1543
+ REMOVED
1544
+ }
1545
+
1546
+ model TenantDomain {
1547
+ id String @id @default(cuid())
1548
+ tenantId String @map("tenant_id")
1549
+ domain String @unique // lowercased; no protocol
1550
+ verificationToken String @map("verification_token") // 32 hex chars
1551
+ // Token expires 7 days after creation; admin must re-claim if expired (sec finding #5)
1552
+ tokenExpiresAt DateTime @map("token_expires_at")
1553
+ verifiedAt DateTime? @map("verified_at")
1554
+ verifyAttemptedAt DateTime? @map("verify_attempted_at")
1555
+ verifyAttempts Int @default(0) @map("verify_attempts")
1556
+ createdAt DateTime @default(now()) @map("created_at")
1557
+
1558
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1559
+
1560
+ @@index([tenantId])
1561
+ @@index([verifiedAt])
1562
+ @@index([tokenExpiresAt])
1563
+ @@map("tenant_domains")
1564
+ }
1565
+
1566
+ model TenantIdentityProvider {
1567
+ id String @id @default(cuid())
1568
+ tenantId String @unique @map("tenant_id")
1569
+
1570
+ kind IdpKind
1571
+
1572
+ // Cognito's IdP record name. Max 32 chars (Cognito quota), unique within the user pool.
1573
+ cognitoIdpName String @unique @map("cognito_idp_name")
1574
+
1575
+ // SAML configuration
1576
+ metadataUrl String? @map("metadata_url")
1577
+ metadataXml String? @map("metadata_xml") @db.Text
1578
+
1579
+ // OIDC configuration
1580
+ issuerUrl String? @map("issuer_url")
1581
+ clientId String? @map("client_id")
1582
+ clientSecretArn String? @map("client_secret_arn") // never the raw secret — Secrets Manager ARN
1583
+ scopes String @default("openid email profile groups")
1584
+
1585
+ attributeMapping Json @default("{}") @map("attribute_mapping")
1586
+
1587
+ // Default role applied when no TenantRoleMapping matches the user's IdP groups.
1588
+ // null means "deny if no group match."
1589
+ defaultRole TenantRole? @map("default_role")
1590
+
1591
+ status IdpStatus @default(PENDING)
1592
+ enabledAt DateTime? @map("enabled_at")
1593
+ lastError String? @map("last_error") @db.Text
1594
+ lastErrorAt DateTime? @map("last_error_at")
1595
+
1596
+ createdAt DateTime @default(now()) @map("created_at")
1597
+ updatedAt DateTime @updatedAt @map("updated_at")
1598
+
1599
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1600
+
1601
+ @@index([cognitoIdpName])
1602
+ @@index([status])
1603
+ @@map("tenant_identity_providers")
1604
+ }
1605
+
1606
+ enum IdpKind {
1607
+ SAML
1608
+ OIDC
1609
+ }
1610
+
1611
+ enum IdpStatus {
1612
+ PENDING
1613
+ ACTIVE
1614
+ DISABLED
1615
+ ERROR
1616
+ }
1617
+
1618
+ model TenantRoleMapping {
1619
+ id String @id @default(cuid())
1620
+ tenantId String @map("tenant_id")
1621
+ idpGroupName String @map("idp_group_name")
1622
+ tenantRole TenantRole @map("tenant_role")
1623
+ // Lower priority = higher precedence (admin-grants beat member-grants).
1624
+ priority Int @default(100)
1625
+
1626
+ createdAt DateTime @default(now()) @map("created_at")
1627
+ updatedAt DateTime @updatedAt @map("updated_at")
1628
+
1629
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1630
+
1631
+ @@unique([tenantId, idpGroupName])
1632
+ @@index([tenantId, priority])
1633
+ @@map("tenant_role_mappings")
1634
+ }
1635
+
1636
+ model TenantInvitation {
1637
+ id String @id @default(cuid())
1638
+ tenantId String @map("tenant_id")
1639
+ email String
1640
+ role TenantRole
1641
+ token String @unique // signed JWT or random opaque token; one-shot
1642
+ expiresAt DateTime @map("expires_at")
1643
+ acceptedAt DateTime? @map("accepted_at")
1644
+ acceptedByUserId String? @map("accepted_by_user_id")
1645
+
1646
+ invitedByUserId String @map("invited_by_user_id")
1647
+ createdAt DateTime @default(now()) @map("created_at")
1648
+
1649
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
1650
+ invitedBy User @relation("TenantInvitationInviter", fields: [invitedByUserId], references: [id])
1651
+ acceptedBy User? @relation("TenantInvitationAcceptedBy", fields: [acceptedByUserId], references: [id])
1652
+
1653
+ @@unique([tenantId, email])
1654
+ @@index([token])
1655
+ @@index([email])
1656
+ @@index([expiresAt])
1657
+ @@map("tenant_invitations")
1658
+ }