open-loam 0.1.1 → 0.3.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 (287) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +148 -8
  3. data/LICENSE +1 -1
  4. data/README.md +27 -25
  5. data/app/jobs/{loam → open_loam}/custom_field_reindex_job.rb +6 -6
  6. data/app/jobs/{loam → open_loam}/event_delivery_job.rb +8 -8
  7. data/app/jobs/open_loam/event_log_prune_job.rb +15 -0
  8. data/app/jobs/{loam → open_loam}/event_redelivery_sweep_job.rb +4 -4
  9. data/app/jobs/{loam → open_loam}/webhook_delivery_job.rb +17 -8
  10. data/app/models/open_loam/api_token.rb +63 -0
  11. data/app/models/{loam → open_loam}/audit_record.rb +3 -3
  12. data/app/models/{loam → open_loam}/auth_attempt.rb +5 -5
  13. data/app/models/{loam → open_loam}/business_rule.rb +10 -10
  14. data/app/models/{loam → open_loam}/business_rule_run.rb +5 -5
  15. data/app/models/{loam → open_loam}/comment.rb +9 -9
  16. data/app/models/{loam → open_loam}/config.rb +7 -7
  17. data/app/models/{loam → open_loam}/custom_field_value.rb +5 -5
  18. data/app/models/{loam → open_loam}/dashboard_widget.rb +5 -5
  19. data/app/models/open_loam/dictionary.rb +20 -0
  20. data/app/models/{loam → open_loam}/dictionary_entry.rb +7 -7
  21. data/app/models/{loam → open_loam}/event_delivery.rb +4 -4
  22. data/app/models/open_loam/event_record.rb +42 -0
  23. data/app/models/{loam → open_loam}/field_definition.rb +8 -8
  24. data/app/models/{loam → open_loam}/inbound_webhook_delivery.rb +4 -4
  25. data/app/models/{loam → open_loam}/inbound_webhook_source.rb +13 -13
  26. data/app/models/{loam → open_loam}/membership.rb +6 -6
  27. data/app/models/{loam → open_loam}/mfa_credential.rb +8 -8
  28. data/app/models/{loam → open_loam}/notification.rb +10 -10
  29. data/app/models/{loam → open_loam}/pending_action.rb +70 -26
  30. data/app/models/{loam → open_loam}/perspective.rb +5 -5
  31. data/app/models/{loam → open_loam}/progress_job.rb +7 -7
  32. data/app/models/{loam → open_loam}/record_lock.rb +5 -5
  33. data/app/models/{loam → open_loam}/scheduled_job.rb +7 -7
  34. data/app/models/open_loam/search_token.rb +9 -0
  35. data/app/models/{loam → open_loam}/sso_identity.rb +4 -4
  36. data/app/models/open_loam/sso_provider.rb +66 -0
  37. data/app/models/open_loam/tenant.rb +20 -0
  38. data/app/models/{loam → open_loam}/translation.rb +5 -5
  39. data/app/models/open_loam/webhook_endpoint.rb +39 -0
  40. data/app/views/{loam → open_loam}/attachments/_attachments.html.erb +1 -1
  41. data/app/views/{loam → open_loam}/comments/_comments.html.erb +1 -1
  42. data/app/views/{loam → open_loam}/custom_fields/_fields.html.erb +6 -2
  43. data/lib/generators/{loam → open_loam}/entity/entity_generator.rb +9 -9
  44. data/lib/generators/{loam → open_loam}/entity/templates/api_controller.rb +2 -1
  45. data/lib/generators/{loam → open_loam}/entity/templates/controller.rb +32 -22
  46. data/lib/generators/{loam → open_loam}/entity/templates/entity_test.rb +12 -12
  47. data/lib/generators/{loam → open_loam}/entity/templates/migration.rb +5 -5
  48. data/lib/generators/{loam → open_loam}/entity/templates/model.rb +10 -10
  49. data/lib/generators/{loam → open_loam}/entity/templates/policy.rb +1 -1
  50. data/lib/generators/{loam → open_loam}/entity/templates/views/_form.html.erb +8 -5
  51. data/lib/generators/{loam → open_loam}/entity/templates/views/deleted.html.erb +7 -7
  52. data/lib/generators/open_loam/entity/templates/views/edit.html.erb +3 -0
  53. data/lib/generators/open_loam/entity/templates/views/index.html.erb +94 -0
  54. data/lib/generators/open_loam/entity/templates/views/new.html.erb +3 -0
  55. data/lib/generators/open_loam/entity/templates/views/show.html.erb +38 -0
  56. data/lib/generators/{loam → open_loam}/install/install_generator.rb +58 -47
  57. data/lib/generators/open_loam/install/templates/AGENTS.md +345 -0
  58. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_controller.rb +2 -2
  59. data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_index.html.erb +1 -1
  60. data/lib/generators/open_loam/install/templates/admin/api_tokens_controller.rb +48 -0
  61. data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_index.html.erb +23 -0
  62. data/lib/generators/{loam → open_loam}/install/templates/admin/base_controller.rb +101 -38
  63. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_controller.rb +7 -7
  64. data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_form.html.erb +2 -2
  65. data/lib/generators/{loam → open_loam}/install/templates/admin/comments_controller.rb +4 -4
  66. data/lib/generators/{loam → open_loam}/install/templates/admin/configs_controller.rb +9 -9
  67. data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +12 -0
  68. data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_controller.rb +5 -5
  69. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_controller.rb +5 -5
  70. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_form.html.erb +1 -1
  71. data/lib/generators/{loam → open_loam}/install/templates/admin/dictionary_entries_controller.rb +2 -2
  72. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_controller.rb +5 -5
  73. data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_index.html.erb +1 -1
  74. data/lib/generators/{loam → open_loam}/install/templates/admin/events_controller.rb +7 -5
  75. data/lib/generators/{loam → open_loam}/install/templates/admin/features_controller.rb +9 -9
  76. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_controller.rb +5 -5
  77. data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_new.html.erb +2 -2
  78. data/lib/generators/open_loam/install/templates/admin/history_controller.rb +64 -0
  79. data/lib/generators/{loam → open_loam}/install/templates/admin/history_index.html.erb +2 -2
  80. data/lib/generators/{loam → open_loam}/install/templates/admin/imports_controller.rb +25 -13
  81. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_controller.rb +7 -7
  82. data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_new.html.erb +2 -4
  83. data/lib/generators/open_loam/install/templates/admin/layout.html.erb +76 -0
  84. data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_controller.rb +11 -6
  85. data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_controller.rb +5 -3
  86. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_controller.rb +3 -3
  87. data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_index.html.erb +2 -2
  88. data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_controller.rb +3 -3
  89. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_controller.rb +13 -7
  90. data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_index.html.erb +1 -1
  91. data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +23 -0
  92. data/lib/generators/{loam → open_loam}/install/templates/admin/progress_jobs_index.html.erb +1 -1
  93. data/lib/generators/{loam → open_loam}/install/templates/admin/record_locks_controller.rb +4 -4
  94. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_controller.rb +7 -7
  95. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_form.html.erb +1 -1
  96. data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_index.html.erb +1 -1
  97. data/lib/generators/{loam → open_loam}/install/templates/admin/search_controller.rb +7 -5
  98. data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_controller.rb +33 -34
  99. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_controller.rb +6 -6
  100. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_form.html.erb +2 -2
  101. data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_index.html.erb +8 -1
  102. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_controller.rb +7 -5
  103. data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_new.html.erb +1 -1
  104. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_controller.rb +7 -7
  105. data/lib/generators/{loam → open_loam}/install/templates/admin/translations_index.html.erb +3 -3
  106. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_controller.rb +4 -4
  107. data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_index.html.erb +1 -1
  108. data/lib/generators/{loam → open_loam}/install/templates/admin.css +1 -1
  109. data/lib/generators/open_loam/install/templates/api_base_controller.rb +127 -0
  110. data/lib/generators/{loam → open_loam}/install/templates/guardrails_test.rb +17 -17
  111. data/lib/generators/open_loam/install/templates/import_job.rb +38 -0
  112. data/lib/generators/{loam → open_loam}/install/templates/inbound_webhooks_controller.rb +4 -4
  113. data/lib/generators/{loam → open_loam}/install/templates/initializer.rb +87 -75
  114. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +15 -0
  115. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_audit_records.rb +14 -0
  116. data/lib/generators/{loam/install/templates/migrations/create_loam_auth_attempts.rb → open_loam/install/templates/migrations/create_open_loam_auth_attempts.rb} +3 -3
  117. data/lib/generators/{loam/install/templates/migrations/create_loam_business_rules.rb → open_loam/install/templates/migrations/create_open_loam_business_rules.rb} +10 -10
  118. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_comments.rb +13 -0
  119. data/lib/generators/{loam/install/templates/migrations/create_loam_configs.rb → open_loam/install/templates/migrations/create_open_loam_configs.rb} +7 -7
  120. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_custom_field_values.rb +19 -0
  121. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dashboard_widgets.rb +12 -0
  122. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb +11 -0
  123. data/lib/generators/{loam/install/templates/migrations/create_loam_dictionary_entries.rb → open_loam/install/templates/migrations/create_open_loam_dictionary_entries.rb} +6 -6
  124. data/lib/generators/{loam/install/templates/migrations/create_loam_event_deliveries.rb → open_loam/install/templates/migrations/create_open_loam_event_deliveries.rb} +5 -5
  125. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_event_records.rb +13 -0
  126. data/lib/generators/{loam/install/templates/migrations/create_loam_field_definitions.rb → open_loam/install/templates/migrations/create_open_loam_field_definitions.rb} +4 -4
  127. data/lib/generators/{loam/install/templates/migrations/create_loam_inbound_webhooks.rb → open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb} +9 -10
  128. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb +11 -0
  129. data/lib/generators/{loam/install/templates/migrations/create_loam_mfa_credentials.rb → open_loam/install/templates/migrations/create_open_loam_mfa_credentials.rb} +4 -4
  130. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_notifications.rb +15 -0
  131. data/lib/generators/{loam/install/templates/migrations/create_loam_pending_actions.rb → open_loam/install/templates/migrations/create_open_loam_pending_actions.rb} +9 -9
  132. data/lib/generators/{loam/install/templates/migrations/create_loam_perspectives.rb → open_loam/install/templates/migrations/create_open_loam_perspectives.rb} +5 -5
  133. data/lib/generators/{loam/install/templates/migrations/create_loam_progress_jobs.rb → open_loam/install/templates/migrations/create_open_loam_progress_jobs.rb} +5 -5
  134. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_record_locks.rb +16 -0
  135. data/lib/generators/{loam/install/templates/migrations/create_loam_scheduled_jobs.rb → open_loam/install/templates/migrations/create_open_loam_scheduled_jobs.rb} +5 -5
  136. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_search_tokens.rb +18 -0
  137. data/lib/generators/{loam/install/templates/migrations/create_loam_sso_providers.rb → open_loam/install/templates/migrations/create_open_loam_sso_providers.rb} +15 -12
  138. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_tenants.rb +9 -0
  139. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_translations.rb +15 -0
  140. data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb +13 -0
  141. data/lib/generators/{loam → open_loam}/install/templates/migrations/create_users.rb +1 -1
  142. data/lib/generators/{loam → open_loam}/install/templates/user.rb +5 -5
  143. data/lib/generators/{loam → open_loam}/primary_key_options.rb +23 -23
  144. data/lib/open-loam.rb +2 -2
  145. data/lib/{loam → open_loam}/attachable.rb +4 -4
  146. data/lib/{loam → open_loam}/auditable.rb +23 -23
  147. data/lib/{loam → open_loam}/auth_throttle.rb +20 -12
  148. data/lib/{loam → open_loam}/base32.rb +1 -1
  149. data/lib/{loam → open_loam}/bulk.rb +7 -7
  150. data/lib/{loam → open_loam}/business_rules/actions.rb +7 -7
  151. data/lib/{loam → open_loam}/business_rules/condition.rb +5 -5
  152. data/lib/{loam → open_loam}/business_rules.rb +19 -19
  153. data/lib/{loam → open_loam}/commentable.rb +6 -6
  154. data/lib/{loam → open_loam}/configs.rb +19 -19
  155. data/lib/{loam → open_loam}/cron.rb +2 -2
  156. data/lib/{loam → open_loam}/csv.rb +2 -2
  157. data/lib/{loam → open_loam}/current.rb +5 -5
  158. data/lib/{loam → open_loam}/custom_field_index.rb +33 -30
  159. data/lib/{loam → open_loam}/custom_fields.rb +17 -17
  160. data/lib/{loam → open_loam}/dashboard.rb +5 -5
  161. data/lib/{loam → open_loam}/dictionaries.rb +12 -12
  162. data/lib/{loam → open_loam}/durable_events.rb +19 -19
  163. data/lib/{loam → open_loam}/encryptable.rb +39 -34
  164. data/lib/{loam → open_loam}/encryption/cipher.rb +3 -3
  165. data/lib/{loam → open_loam}/encryption/key_provider.rb +19 -3
  166. data/lib/{loam → open_loam}/encryption.rb +58 -15
  167. data/lib/open_loam/engine.rb +65 -0
  168. data/lib/{loam → open_loam}/enrichers.rb +5 -5
  169. data/lib/{loam → open_loam}/errors.rb +20 -10
  170. data/lib/{loam → open_loam}/eval.rb +2 -2
  171. data/lib/open_loam/event_log.rb +77 -0
  172. data/lib/{loam → open_loam}/event_stream.rb +14 -14
  173. data/lib/{loam → open_loam}/eventful.rb +12 -12
  174. data/lib/{loam → open_loam}/events.rb +5 -5
  175. data/lib/{loam → open_loam}/export.rb +12 -12
  176. data/lib/{loam → open_loam}/features.rb +15 -15
  177. data/lib/{loam → open_loam}/generated_key.rb +6 -6
  178. data/lib/{loam → open_loam}/import.rb +12 -15
  179. data/lib/{loam → open_loam}/inbound_webhooks.rb +19 -18
  180. data/lib/{loam → open_loam}/lifecycle.rb +54 -25
  181. data/lib/{loam/locales/loam.en.yml → open_loam/locales/open_loam.en.yml} +5 -5
  182. data/lib/{loam → open_loam}/mcp/server.rb +9 -9
  183. data/lib/{loam → open_loam}/mcp.rb +31 -22
  184. data/lib/{loam → open_loam}/notifications.rb +5 -5
  185. data/lib/{loam → open_loam}/open_api.rb +16 -16
  186. data/lib/open_loam/outbound_url.rb +100 -0
  187. data/lib/{loam → open_loam}/overrides.rb +10 -10
  188. data/lib/{loam → open_loam}/pending_actions.rb +9 -9
  189. data/lib/{loam → open_loam}/permissions.rb +8 -8
  190. data/lib/{loam → open_loam}/perspectives.rb +9 -9
  191. data/lib/{loam → open_loam}/policy.rb +21 -8
  192. data/lib/{loam → open_loam}/progress.rb +6 -6
  193. data/lib/{loam → open_loam}/record_locks.rb +9 -9
  194. data/lib/{loam → open_loam}/scheduler.rb +20 -20
  195. data/lib/{loam → open_loam}/search/driver.rb +3 -3
  196. data/lib/{loam → open_loam}/search/like_driver.rb +5 -5
  197. data/lib/{loam → open_loam}/search/token_driver.rb +16 -16
  198. data/lib/{loam → open_loam}/search.rb +5 -5
  199. data/lib/{loam → open_loam}/searchable.rb +22 -22
  200. data/lib/{loam → open_loam}/soft_deletable.rb +10 -10
  201. data/lib/{loam → open_loam}/sso/claims.rb +1 -1
  202. data/lib/{loam → open_loam}/sso/fake_provider.rb +1 -1
  203. data/lib/open_loam/sso/http_client.rb +51 -0
  204. data/lib/{loam → open_loam}/sso/oidc_provider.rb +2 -2
  205. data/lib/{loam → open_loam}/sso.rb +39 -18
  206. data/lib/{loam → open_loam}/telemetry.rb +6 -6
  207. data/lib/{loam → open_loam}/tenant_record.rb +10 -10
  208. data/lib/{loam → open_loam}/test_helpers.rb +4 -4
  209. data/lib/{loam → open_loam}/totp.rb +3 -3
  210. data/lib/{loam → open_loam}/translatable.rb +24 -24
  211. data/lib/{loam → open_loam}/undo.rb +11 -11
  212. data/lib/open_loam/version.rb +3 -0
  213. data/lib/{loam → open_loam}/webhooks.rb +4 -4
  214. data/lib/{loam → open_loam}/widgets.rb +9 -9
  215. data/lib/{loam → open_loam}/workflow.rb +45 -45
  216. data/lib/open_loam.rb +124 -0
  217. data/lib/tasks/open_loam.rake +203 -0
  218. data/lib/tasks/{loam_eval.rake → open_loam_eval.rake} +7 -7
  219. data/lib/tasks/open_loam_mcp.rake +20 -0
  220. metadata +247 -242
  221. data/app/models/loam/api_token.rb +0 -40
  222. data/app/models/loam/dictionary.rb +0 -20
  223. data/app/models/loam/search_token.rb +0 -9
  224. data/app/models/loam/sso_provider.rb +0 -35
  225. data/app/models/loam/tenant.rb +0 -20
  226. data/app/models/loam/webhook_endpoint.rb +0 -26
  227. data/lib/generators/loam/entity/templates/views/edit.html.erb +0 -3
  228. data/lib/generators/loam/entity/templates/views/index.html.erb +0 -90
  229. data/lib/generators/loam/entity/templates/views/new.html.erb +0 -3
  230. data/lib/generators/loam/entity/templates/views/show.html.erb +0 -36
  231. data/lib/generators/loam/install/templates/AGENTS.md +0 -341
  232. data/lib/generators/loam/install/templates/admin/api_tokens_controller.rb +0 -30
  233. data/lib/generators/loam/install/templates/admin/dashboard_controller.rb +0 -10
  234. data/lib/generators/loam/install/templates/admin/history_controller.rb +0 -38
  235. data/lib/generators/loam/install/templates/admin/layout.html.erb +0 -76
  236. data/lib/generators/loam/install/templates/admin/progress_jobs_controller.rb +0 -19
  237. data/lib/generators/loam/install/templates/api_base_controller.rb +0 -90
  238. data/lib/generators/loam/install/templates/import_job.rb +0 -25
  239. data/lib/generators/loam/install/templates/migrations/create_loam_api_tokens.rb +0 -15
  240. data/lib/generators/loam/install/templates/migrations/create_loam_audit_records.rb +0 -14
  241. data/lib/generators/loam/install/templates/migrations/create_loam_comments.rb +0 -13
  242. data/lib/generators/loam/install/templates/migrations/create_loam_custom_field_values.rb +0 -19
  243. data/lib/generators/loam/install/templates/migrations/create_loam_dashboard_widgets.rb +0 -12
  244. data/lib/generators/loam/install/templates/migrations/create_loam_dictionaries.rb +0 -11
  245. data/lib/generators/loam/install/templates/migrations/create_loam_memberships.rb +0 -11
  246. data/lib/generators/loam/install/templates/migrations/create_loam_notifications.rb +0 -15
  247. data/lib/generators/loam/install/templates/migrations/create_loam_record_locks.rb +0 -16
  248. data/lib/generators/loam/install/templates/migrations/create_loam_search_tokens.rb +0 -18
  249. data/lib/generators/loam/install/templates/migrations/create_loam_tenants.rb +0 -9
  250. data/lib/generators/loam/install/templates/migrations/create_loam_translations.rb +0 -15
  251. data/lib/generators/loam/install/templates/migrations/create_loam_webhook_endpoints.rb +0 -13
  252. data/lib/loam/engine.rb +0 -57
  253. data/lib/loam/sso/http_client.rb +0 -42
  254. data/lib/loam/version.rb +0 -3
  255. data/lib/loam.rb +0 -122
  256. data/lib/tasks/loam.rake +0 -171
  257. data/lib/tasks/loam_mcp.rake +0 -20
  258. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_edit.html.erb +0 -0
  259. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_index.html.erb +0 -0
  260. /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_new.html.erb +0 -0
  261. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_edit.html.erb +0 -0
  262. /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_index.html.erb +0 -0
  263. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_index.html.erb +0 -0
  264. /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_index.html.erb +0 -0
  265. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_edit.html.erb +0 -0
  266. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_index.html.erb +0 -0
  267. /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_new.html.erb +0 -0
  268. /data/lib/generators/{loam → open_loam}/install/templates/admin/features_index.html.erb +0 -0
  269. /data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_index.html.erb +0 -0
  270. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_new.html.erb +0 -0
  271. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_preview.html.erb +0 -0
  272. /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_summary.html.erb +0 -0
  273. /data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_index.html.erb +0 -0
  274. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_activated.html.erb +0 -0
  275. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_new.html.erb +0 -0
  276. /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_show.html.erb +0 -0
  277. /data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_index.html.erb +0 -0
  278. /data/lib/generators/{loam → open_loam}/install/templates/admin/pagination.rb +0 -0
  279. /data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_index.html.erb +0 -0
  280. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_edit.html.erb +0 -0
  281. /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_new.html.erb +0 -0
  282. /data/lib/generators/{loam → open_loam}/install/templates/admin/search_index.html.erb +0 -0
  283. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_mfa_challenge.html.erb +0 -0
  284. /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_new.html.erb +0 -0
  285. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_edit.html.erb +0 -0
  286. /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_new.html.erb +0 -0
  287. /data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_new.html.erb +0 -0
@@ -1,9 +1,9 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # One row per change: who (actor), what (auditable + action + changeset),
3
3
  # when (created_at), in which tenant (tenant_id, via TenantRecord).
4
4
  # Append-only by convention; not itself audited or evented.
5
- class AuditRecord < Loam::TenantRecord
6
- self.table_name = "loam_audit_records"
5
+ class AuditRecord < OpenLoam::TenantRecord
6
+ self.table_name = "open_loam_audit_records"
7
7
 
8
8
  belongs_to :actor, class_name: "User", optional: true
9
9
 
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A single failed authentication attempt, for rate-limiting/lockout
3
- # (Loam::AuthThrottle). Deliberately NOT tenant-scoped — authentication happens
3
+ # (OpenLoam::AuthThrottle). Deliberately NOT tenant-scoped — authentication happens
4
4
  # BEFORE a tenant is chosen (login), so this is a global auth-layer table keyed
5
- # by the submitted identifier (the same reason Loam::MfaCredential is global).
5
+ # by the submitted identifier (the same reason OpenLoam::MfaCredential is global).
6
6
  # Allowlisted in the guardrails tenancy lint.
7
7
  class AuthAttempt < ApplicationRecord
8
- include Loam::GeneratedKey
9
- self.table_name = "loam_auth_attempts"
8
+ include OpenLoam::GeneratedKey
9
+ self.table_name = "open_loam_auth_attempts"
10
10
 
11
11
  validates :identifier, :kind, presence: true
12
12
  end
@@ -1,20 +1,20 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A per-tenant, admin-configurable rule: WHEN a condition holds THEN run
3
3
  # actions. Declared as DATA (a trigger event pattern + a safe condition tree +
4
- # a typed action list), never as code — see Loam::BusinessRules for the engine
4
+ # a typed action list), never as code — see OpenLoam::BusinessRules for the engine
5
5
  # that evaluates it. Audited like any business record.
6
- class BusinessRule < Loam::TenantRecord
7
- self.table_name = "loam_business_rules"
6
+ class BusinessRule < OpenLoam::TenantRecord
7
+ self.table_name = "open_loam_business_rules"
8
8
 
9
- include Loam::Auditable
9
+ include OpenLoam::Auditable
10
10
 
11
- has_many :runs, class_name: "Loam::BusinessRuleRun", dependent: :delete_all
11
+ has_many :runs, class_name: "OpenLoam::BusinessRuleRun", dependent: :delete_all
12
12
 
13
13
  validates :name, :trigger, presence: true
14
14
  # A rule may only target a tenant-scoped model (or none, for an event-only
15
15
  # rule). Refusing a global class like `User` at SAVE time means a poisoned
16
16
  # rule can't even be persisted — kept in lockstep with
17
- # Loam::BusinessRules.subject_for, which refuses the same at run time.
17
+ # OpenLoam::BusinessRules.subject_for, which refuses the same at run time.
18
18
  validate :entity_type_targets_a_tenant_record
19
19
 
20
20
  scope :active, -> { where(active: true) }
@@ -22,7 +22,7 @@ module Loam
22
22
  scope :for_entity, ->(entity_type) { where(entity_type: entity_type.to_s) }
23
23
 
24
24
  def matches_trigger?(event_name)
25
- Loam::Events.pattern_matches?(trigger, event_name)
25
+ OpenLoam::Events.pattern_matches?(trigger, event_name)
26
26
  end
27
27
 
28
28
  # The stored json, defensively — a hand-created row could hold a non-hash /
@@ -41,8 +41,8 @@ module Loam
41
41
  return if entity_type.blank? # event-only rule, no subject to load
42
42
 
43
43
  klass = entity_type.safe_constantize
44
- unless klass.is_a?(Class) && klass < Loam::TenantRecord
45
- errors.add(:entity_type, "must name a Loam tenant-scoped model (not #{entity_type.inspect})")
44
+ unless klass.is_a?(Class) && klass < OpenLoam::TenantRecord
45
+ errors.add(:entity_type, "must name a OpenLoam tenant-scoped model (not #{entity_type.inspect})")
46
46
  end
47
47
  end
48
48
  end
@@ -1,13 +1,13 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # The log of why a business rule did (or did not) act — so the admin can see
3
- # WHY something happened. Plumbing, like Loam::RecordLock: NOT audited (a log
3
+ # WHY something happened. Plumbing, like OpenLoam::RecordLock: NOT audited (a log
4
4
  # of a log is noise). Capped per rule so the table stays lean.
5
- class BusinessRuleRun < Loam::TenantRecord
6
- self.table_name = "loam_business_rule_runs"
5
+ class BusinessRuleRun < OpenLoam::TenantRecord
6
+ self.table_name = "open_loam_business_rule_runs"
7
7
 
8
8
  KEEP_PER_RULE = 50
9
9
 
10
- belongs_to :business_rule, class_name: "Loam::BusinessRule"
10
+ belongs_to :business_rule, class_name: "OpenLoam::BusinessRule"
11
11
 
12
12
  scope :recent, -> { order(created_at: :desc) }
13
13
 
@@ -1,22 +1,22 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A note by one person, on one record, in one tenant. Polymorphic, so any
3
- # entity that `include Loam::Commentable` gets a discussion for free.
3
+ # entity that `include OpenLoam::Commentable` gets a discussion for free.
4
4
  #
5
5
  # Not audited on purpose: the comment IS the activity record. A
6
- # Loam::AuditRecord saying "someone created a comment" beside a comment
6
+ # OpenLoam::AuditRecord saying "someone created a comment" beside a comment
7
7
  # saying what they wrote is the same fact stored twice.
8
8
  #
9
- # Evented on purpose, in Loam's own domain ("loam.comment.created"), because
9
+ # Evented on purpose, in OpenLoam's own domain ("open_loam.comment.created"), because
10
10
  # a new comment is exactly the kind of thing an app wants to react to —
11
11
  # notify the watchers, ping a webhook — without every app inventing its own
12
12
  # event name for it.
13
- class Comment < Loam::TenantRecord
14
- include Loam::Eventful
13
+ class Comment < OpenLoam::TenantRecord
14
+ include OpenLoam::Eventful
15
15
 
16
- self.table_name = "loam_comments"
16
+ self.table_name = "open_loam_comments"
17
17
 
18
- event_domain :loam
19
- event_entity :comment # not "loam_comment", which the namespace would give
18
+ event_domain :open_loam
19
+ event_entity :comment # not "open_loam_comment", which the namespace would give
20
20
 
21
21
  belongs_to :commentable, polymorphic: true
22
22
  belongs_to :author, class_name: "User"
@@ -1,22 +1,22 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A single configuration setting: one namespaced key, one JSON-able value.
3
3
  #
4
- # Deliberately NOT a Loam::TenantRecord. A setting exists at two levels — a
4
+ # Deliberately NOT a OpenLoam::TenantRecord. A setting exists at two levels — a
5
5
  # GLOBAL default (tenant_id NULL) that every tenant sees, and a per-tenant
6
6
  # OVERRIDE (tenant_id set) that wins for that one tenant — so tenancy is a
7
7
  # nullable column here, not a default_scope. Resolving a key means reading
8
8
  # across both levels at once, which is why the lookup lives in vetted gem code
9
- # (Loam::Configs), never in app code (see the tenancy allowlist in
10
- # test/loam_guardrails_test.rb).
9
+ # (OpenLoam::Configs), never in app code (see the tenancy allowlist in
10
+ # test/open_loam_guardrails_test.rb).
11
11
  #
12
12
  # The value goes in a json column, so a bool, number, string, array, or hash
13
13
  # all round-trip under the same key. Row existence — not the value — is what
14
14
  # marks a key as set, so the value may legitimately be JSON null.
15
15
  class Config < ApplicationRecord
16
- include Loam::GeneratedKey
17
- self.table_name = "loam_configs"
16
+ include OpenLoam::GeneratedKey
17
+ self.table_name = "open_loam_configs"
18
18
 
19
- belongs_to :tenant, class_name: "Loam::Tenant", optional: true
19
+ belongs_to :tenant, class_name: "OpenLoam::Tenant", optional: true
20
20
 
21
21
  validates :key, presence: true
22
22
  # One global per key, one override per key per tenant. AR renders the nil
@@ -1,16 +1,16 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A read-model projection of ONE custom field of ONE record into typed,
3
3
  # INDEXED columns — so filtering/sorting/searching on a custom field is
4
4
  # index-backed instead of a per-row JSON extraction over the `custom_fields`
5
5
  # column (which can't be indexed and full-scans at scale). One row per
6
- # (record, custom field); maintained by Loam::CustomFieldIndex from the
7
- # Loam::CustomFields save/destroy hooks. Tenant-scoped.
6
+ # (record, custom field); maintained by OpenLoam::CustomFieldIndex from the
7
+ # OpenLoam::CustomFields save/destroy hooks. Tenant-scoped.
8
8
  #
9
9
  # The value is written into the column matching the field's declared type
10
10
  # (value_number / value_boolean / value_datetime) AND always into value_text
11
11
  # (its string form) for text ops (contains/present) and canonical equality.
12
- class CustomFieldValue < Loam::TenantRecord
13
- self.table_name = "loam_custom_field_values"
12
+ class CustomFieldValue < OpenLoam::TenantRecord
13
+ self.table_name = "open_loam_custom_field_values"
14
14
 
15
15
  validates :indexable_type, :indexable_id, :field_key, presence: true
16
16
  validates :field_key, uniqueness: { scope: %i[indexable_type indexable_id] }
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A tenant's dashboard layout: which registered widgets appear, in what order.
3
3
  # A manager arranges these on the Dashboard settings screen; the dashboard
4
- # renders the active ones by position (see Loam::Dashboard). Tenant-scoped and
4
+ # renders the active ones by position (see OpenLoam::Dashboard). Tenant-scoped and
5
5
  # audited.
6
- class DashboardWidget < Loam::TenantRecord
7
- self.table_name = "loam_dashboard_widgets"
6
+ class DashboardWidget < OpenLoam::TenantRecord
7
+ self.table_name = "open_loam_dashboard_widgets"
8
8
 
9
- include Loam::Auditable
9
+ include OpenLoam::Auditable
10
10
 
11
11
  validates :widget_key, presence: true, uniqueness: { scope: :tenant_id }
12
12
 
@@ -0,0 +1,20 @@
1
+ module OpenLoam
2
+ # A per-tenant managed lookup list — a named set of entries (e.g.
3
+ # "damage_severity" → minor/major/critical). Admins curate the values without a
4
+ # code deploy, and a OpenLoam::FieldDefinition of type "dictionary" can point a
5
+ # custom field at one. Tenant-scoped and audited like every OpenLoam entity; see
6
+ # OpenLoam::Dictionaries for the read API.
7
+ class Dictionary < OpenLoam::TenantRecord
8
+ self.table_name = "open_loam_dictionaries"
9
+
10
+ include OpenLoam::Auditable
11
+
12
+ has_many :entries, class_name: "OpenLoam::DictionaryEntry",
13
+ foreign_key: :dictionary_id, dependent: :delete_all
14
+
15
+ validates :key, presence: true, uniqueness: { scope: :tenant_id }
16
+ validates :name, presence: true
17
+
18
+ normalizes :key, with: ->(key) { key.to_s.strip.presence }
19
+ end
20
+ end
@@ -1,15 +1,15 @@
1
- module Loam
2
- # One option in a Loam::Dictionary: a stored `value` (the code that lands in a
1
+ module OpenLoam
2
+ # One option in a OpenLoam::Dictionary: a stored `value` (the code that lands in a
3
3
  # record) plus display metadata (label, color, icon), an ordering `position`,
4
4
  # a `is_default` flag, and an `active` switch (a retired option stops being
5
5
  # offered without deleting the historical values already stored). Tenant-scoped
6
6
  # and audited.
7
- class DictionaryEntry < Loam::TenantRecord
8
- self.table_name = "loam_dictionary_entries"
7
+ class DictionaryEntry < OpenLoam::TenantRecord
8
+ self.table_name = "open_loam_dictionary_entries"
9
9
 
10
- include Loam::Auditable
10
+ include OpenLoam::Auditable
11
11
 
12
- belongs_to :dictionary, class_name: "Loam::Dictionary"
12
+ belongs_to :dictionary, class_name: "OpenLoam::Dictionary"
13
13
 
14
14
  validates :value, presence: true, uniqueness: { scope: :dictionary_id }
15
15
  validates :label, presence: true
@@ -25,7 +25,7 @@ module Loam
25
25
  # A within-request edit must not read a stale cached list (the cache also
26
26
  # resets per request, so this only matters when a write and a read share one).
27
27
  def clear_dictionary_cache
28
- Loam::Dictionaries.clear_cache
28
+ OpenLoam::Dictionaries.clear_cache
29
29
  end
30
30
  end
31
31
  end
@@ -1,14 +1,14 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # One durable delivery of one event to one persistent subscriber
3
- # (see Loam::DurableEvents). The ROW is the durable record of intent: it is
3
+ # (see OpenLoam::DurableEvents). The ROW is the durable record of intent: it is
4
4
  # committed in the event's tenant at publish time, and the background job is
5
5
  # only an accelerator. If the job is lost, the sweep re-enqueues from this row.
6
6
  #
7
7
  # Lifecycle: pending -> delivered (handler ran) | dead (handler removed, or
8
8
  # MAX_ATTEMPTS exhausted). A pending row with next_attempt_at in the future is
9
9
  # simply waiting out its backoff.
10
- class EventDelivery < Loam::TenantRecord
11
- self.table_name = "loam_event_deliveries"
10
+ class EventDelivery < OpenLoam::TenantRecord
11
+ self.table_name = "open_loam_event_deliveries"
12
12
 
13
13
  STATUSES = %w[pending delivered dead].freeze
14
14
 
@@ -0,0 +1,42 @@
1
+ module OpenLoam
2
+ # One captured domain event (see OpenLoam::EventLog). Where
3
+ # OpenLoam::EventDelivery records that a durable subscriber was told, this
4
+ # records that the thing happened at all.
5
+ #
6
+ # Append-only: a persisted row is readonly, so retention must delete_all
7
+ # rather than destroy.
8
+ class EventRecord < OpenLoam::TenantRecord
9
+ self.table_name = "open_loam_event_records"
10
+
11
+ validates :name, :occurred_at, presence: true
12
+
13
+ scope :chronological, -> { order(:occurred_at, :id) }
14
+
15
+ # ESCAPE is load-bearing: sanitize_sql_like backslash-escapes `_`, and SQLite
16
+ # has no default escape character, so "damage_report." would search for a
17
+ # literal backslash and silently match nothing.
18
+ scope :matching, ->(name_or_prefix) {
19
+ pattern = name_or_prefix.to_s
20
+ if pattern.end_with?(".")
21
+ where("name LIKE ? ESCAPE ?", "#{sanitize_sql_like(pattern)}%", "\\")
22
+ else
23
+ where(name: pattern)
24
+ end
25
+ }
26
+
27
+ def readonly?
28
+ persisted?
29
+ end
30
+
31
+ # The stored payload as a Hash regardless of adapter (Postgres jsonb returns
32
+ # a Hash; a text/json column may hand back a String).
33
+ def payload_hash
34
+ value = self[:payload]
35
+ return value if value.is_a?(Hash)
36
+
37
+ JSON.parse(value.to_s.presence || "{}")
38
+ rescue JSON::ParserError
39
+ {}
40
+ end
41
+ end
42
+ end
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Runtime declaration of a migration-free field on a tenant-scoped entity —
3
3
  # "entity_type Equipment gets a field called serial_number of type string,
4
4
  # writable by managers only." Tenant-scoped like everything else: a field
5
5
  # defined in one tenant is invisible to another's records of the same
6
6
  # entity_type, and definitions are the ONLY way values in an entity's
7
- # `custom_fields` json column get read/written (see Loam::CustomFields).
8
- class FieldDefinition < Loam::TenantRecord
9
- self.table_name = "loam_field_definitions"
7
+ # `custom_fields` json column get read/written (see OpenLoam::CustomFields).
8
+ class FieldDefinition < OpenLoam::TenantRecord
9
+ self.table_name = "open_loam_field_definitions"
10
10
 
11
11
  FIELD_TYPES = %w[string text integer decimal boolean date datetime dictionary].freeze
12
12
 
@@ -17,15 +17,15 @@ module Loam
17
17
 
18
18
  # Field-level READ gate for the runtime field, mirroring writable_roles: an
19
19
  # empty readable_roles means any member may read it; otherwise only the listed
20
- # roles. Used by Loam::CustomFieldIndex (so a filter can't be an oracle on a
21
- # restricted field) and Loam::Policy#custom_field_readable?.
20
+ # roles. Used by OpenLoam::CustomFieldIndex (so a filter can't be an oracle on a
21
+ # restricted field) and OpenLoam::Policy#custom_field_readable?.
22
22
  def readable_by?(role)
23
23
  roles = readable_roles
24
24
  roles.blank? || (role.present? && roles.map(&:to_sym).include?(role.to_sym))
25
25
  end
26
26
 
27
27
  # The `config` json holds type-specific settings. For a "dictionary" field it
28
- # carries the key of the Loam::Dictionary whose entries populate the select.
28
+ # carries the key of the OpenLoam::Dictionary whose entries populate the select.
29
29
  def dictionary_key
30
30
  config.is_a?(Hash) ? config["dictionary"] : nil
31
31
  end
@@ -40,7 +40,7 @@ module Loam
40
40
  def dictionary_key_resolves
41
41
  if dictionary_key.blank?
42
42
  errors.add(:dictionary_key, "is required for a dictionary field")
43
- elsif Loam::Dictionaries.get(dictionary_key).nil?
43
+ elsif OpenLoam::Dictionaries.get(dictionary_key).nil?
44
44
  errors.add(:dictionary_key, "must name an existing dictionary in this tenant")
45
45
  end
46
46
  end
@@ -1,12 +1,12 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # One received inbound webhook. Two jobs: an idempotency ledger (a unique
3
3
  # (source_id, external_id) index turns a replayed delivery into a no-op) and an
4
4
  # audit/payload store — the raw body is kept here so an event subscriber reads
5
5
  # it, keeping the published event payload scalar-clean (ids only).
6
- class InboundWebhookDelivery < Loam::TenantRecord
7
- self.table_name = "loam_inbound_webhook_deliveries"
6
+ class InboundWebhookDelivery < OpenLoam::TenantRecord
7
+ self.table_name = "open_loam_inbound_webhook_deliveries"
8
8
 
9
- belongs_to :source, class_name: "Loam::InboundWebhookSource", inverse_of: :deliveries
9
+ belongs_to :source, class_name: "OpenLoam::InboundWebhookSource", inverse_of: :deliveries
10
10
 
11
11
  validates :external_id, presence: true
12
12
  # (source_id, external_id) uniqueness is enforced by a DB index — the ledger
@@ -1,27 +1,27 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # A registered external system allowed to POST webhooks INTO this tenant — the
3
- # inbound sibling of Loam::WebhookEndpoint (which delivers events OUT). The
3
+ # inbound sibling of OpenLoam::WebhookEndpoint (which delivers events OUT). The
4
4
  # `token` is the unguessable URL id (`/webhooks/:token`) that identifies the
5
5
  # source; the `secret` is the HMAC key that AUTHENTICATES each call. Identity is
6
6
  # not authority: rotating either is supported from the admin.
7
7
  #
8
- # On a verified delivery, Loam publishes `event_name` onto the domain event bus
9
- # with a reference to the stored Loam::InboundWebhookDelivery, so durable
10
- # subscribers (Loam::DurableEvents) react — the payload itself lives on the row.
11
- class InboundWebhookSource < Loam::TenantRecord
12
- self.table_name = "loam_inbound_webhook_sources"
8
+ # On a verified delivery, OpenLoam publishes `event_name` onto the domain event bus
9
+ # with a reference to the stored OpenLoam::InboundWebhookDelivery, so durable
10
+ # subscribers (OpenLoam::DurableEvents) react — the payload itself lives on the row.
11
+ class InboundWebhookSource < OpenLoam::TenantRecord
12
+ self.table_name = "open_loam_inbound_webhook_sources"
13
13
 
14
- DEFAULT_SIGNATURE_HEADER = "X-Loam-Signature".freeze
14
+ DEFAULT_SIGNATURE_HEADER = "X-OpenLoam-Signature".freeze
15
15
  DEFAULT_TOLERANCE = 300 # seconds
16
16
 
17
- has_many :deliveries, class_name: "Loam::InboundWebhookDelivery",
17
+ has_many :deliveries, class_name: "OpenLoam::InboundWebhookDelivery",
18
18
  foreign_key: :source_id, dependent: :destroy, inverse_of: :source
19
19
 
20
20
  validates :name, presence: true
21
21
  validates :token, presence: true, uniqueness: true
22
22
  validates :secret, presence: true
23
23
  validates :event_name, presence: true,
24
- format: { with: Loam::Events::NAME_FORMAT, message: "must follow domain.thing.happened" }
24
+ format: { with: OpenLoam::Events::NAME_FORMAT, message: "must follow domain.thing.happened" }
25
25
 
26
26
  scope :active, -> { where(active: true) }
27
27
 
@@ -32,9 +32,9 @@ module Loam
32
32
  self.active = true if active.nil?
33
33
  end
34
34
 
35
- # THE blessed cross-tenant lookup (see Loam::ApiToken.authenticate): a public
35
+ # THE blessed cross-tenant lookup (see OpenLoam::ApiToken.authenticate): a public
36
36
  # inbound request arrives with no tenant context — the token in the URL is how
37
- # it discovers its tenant. Establishes Loam::Current.tenant (never an actor:
37
+ # it discovers its tenant. Establishes OpenLoam::Current.tenant (never an actor:
38
38
  # the sender is a machine, not a user) and returns the ACTIVE source, or nil.
39
39
  def self.resolve(raw_token)
40
40
  return nil if raw_token.blank?
@@ -42,7 +42,7 @@ module Loam
42
42
  source = unscoped.find_by(token: raw_token)
43
43
  return nil unless source&.active?
44
44
 
45
- Loam::Current.tenant = source.tenant
45
+ OpenLoam::Current.tenant = source.tenant
46
46
  source
47
47
  end
48
48
 
@@ -1,27 +1,27 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # Connects an actor (the host app's User) to a tenant with a role. Roles are
3
3
  # plain strings ("manager", "employee", ...) — policies interpret them.
4
4
  # Tenant-scoped like everything else: asking for someone's role always means
5
5
  # "their role in the CURRENT tenant".
6
- class Membership < Loam::TenantRecord
7
- self.table_name = "loam_memberships"
6
+ class Membership < OpenLoam::TenantRecord
7
+ self.table_name = "open_loam_memberships"
8
8
 
9
9
  belongs_to :user
10
10
 
11
11
  validates :role, presence: true
12
12
  validates :user_id, uniqueness: { scope: :tenant_id }
13
13
 
14
- # The other blessed cross-tenant lookup (see Loam::ApiToken.authenticate).
14
+ # The other blessed cross-tenant lookup (see OpenLoam::ApiToken.authenticate).
15
15
  # "Which tenants may this person enter?" is asked at login, before any
16
16
  # tenant is chosen, so it cannot be answered from inside one — which is why
17
17
  # it lives in the gem rather than in host app code, where reaching across
18
18
  # tenants is a guardrail failure.
19
19
  #
20
- # Returns a Loam::Tenant relation, so callers can order/filter it further.
20
+ # Returns a OpenLoam::Tenant relation, so callers can order/filter it further.
21
21
  def self.tenants_for(user)
22
22
  user_id = user.respond_to?(:id) ? user.id : user
23
23
 
24
- Loam::Tenant.where(id: unscoped.where(user_id: user_id).select(:tenant_id)).order(:name)
24
+ OpenLoam::Tenant.where(id: unscoped.where(user_id: user_id).select(:tenant_id)).order(:name)
25
25
  end
26
26
  end
27
27
  end
@@ -1,21 +1,21 @@
1
1
  require "bcrypt"
2
2
 
3
- module Loam
3
+ module OpenLoam
4
4
  # A user's multi-factor credential: a TOTP secret plus single-use recovery
5
5
  # codes. Deliberately NOT tenant-scoped — MFA belongs to the PERSON, who spans
6
6
  # tenants, and the second-factor challenge runs at login BEFORE any tenant is
7
- # chosen. So the secret is encrypted under a USER-scoped key (Loam::Encryptable
7
+ # chosen. So the secret is encrypted under a USER-scoped key (OpenLoam::Encryptable
8
8
  # `scope:`), which decrypts in any tenant and with no tenant at all — the whole
9
9
  # reason a per-tenant key would be a lockout bug here.
10
10
  class MfaCredential < ApplicationRecord
11
- include Loam::GeneratedKey
12
- self.table_name = "loam_mfa_credentials"
11
+ include OpenLoam::GeneratedKey
12
+ self.table_name = "open_loam_mfa_credentials"
13
13
 
14
14
  RECOVERY_CODE_COUNT = 10
15
15
 
16
16
  belongs_to :user
17
17
 
18
- include Loam::Encryptable
18
+ include OpenLoam::Encryptable
19
19
  encrypts :totp_secret, scope: ->(credential) { "user/#{credential.user_id}" }
20
20
 
21
21
  # Recovery codes are stored HASHED (BCrypt), never in the clear: [{ "digest",
@@ -46,7 +46,7 @@ module Loam
46
46
  # if the code is wrong. The old secret stays valid until this succeeds, so a
47
47
  # half-finished re-enrollment never downgrades an active credential.
48
48
  def activate_with!(candidate_secret, code)
49
- step = Loam::Totp.matching_step(candidate_secret, code)
49
+ step = OpenLoam::Totp.matching_step(candidate_secret, code)
50
50
  return nil unless step
51
51
 
52
52
  self.totp_secret = candidate_secret
@@ -66,7 +66,7 @@ module Loam
66
66
  return false unless activated?
67
67
 
68
68
  with_lock do
69
- step = Loam::Totp.matching_step(totp_secret, code)
69
+ step = OpenLoam::Totp.matching_step(totp_secret, code)
70
70
  if step && (last_totp_step.nil? || step > last_totp_step)
71
71
  update!(last_totp_step: step)
72
72
  true
@@ -100,7 +100,7 @@ module Loam
100
100
  end
101
101
 
102
102
  def provisioning_uri(issuer:)
103
- Loam::Totp.provisioning_uri(totp_secret, account: user.email, issuer: issuer)
103
+ OpenLoam::Totp.provisioning_uri(totp_secret, account: user.email, issuer: issuer)
104
104
  end
105
105
 
106
106
  private
@@ -1,15 +1,15 @@
1
- module Loam
1
+ module OpenLoam
2
2
  # One in-app message for one recipient (`user`) in one tenant. The same
3
3
  # person in two tenants has two separate inboxes — like everything else in
4
- # Loam, "your notifications" always means "in the current tenant".
4
+ # OpenLoam, "your notifications" always means "in the current tenant".
5
5
  #
6
- # Plumbing, not business data: not audited, the same way Loam::AuditRecord
7
- # isn't. It IS evented, though — creating one publishes "loam.notification.created"
8
- # (carrying the recipient's user_id) so Loam::EventStream can push it to that
6
+ # Plumbing, not business data: not audited, the same way OpenLoam::AuditRecord
7
+ # isn't. It IS evented, though — creating one publishes "open_loam.notification.created"
8
+ # (carrying the recipient's user_id) so OpenLoam::EventStream can push it to that
9
9
  # user's browser and the bell updates live. Created through
10
- # Loam::Notifications.notify, read through the admin bell.
11
- class Notification < Loam::TenantRecord
12
- self.table_name = "loam_notifications"
10
+ # OpenLoam::Notifications.notify, read through the admin bell.
11
+ class Notification < OpenLoam::TenantRecord
12
+ self.table_name = "open_loam_notifications"
13
13
 
14
14
  belongs_to :user
15
15
 
@@ -18,9 +18,9 @@ module Loam
18
18
  scope :unread, -> { where(read_at: nil) }
19
19
 
20
20
  # The one signal the real-time bell listens for. Only id + recipient ride the
21
- # event (Loam::Events also stamps tenant_id); no message content is broadcast.
21
+ # event (OpenLoam::Events also stamps tenant_id); no message content is broadcast.
22
22
  after_create_commit do
23
- Loam::Events.publish("loam.notification.created", id: id, user_id: user_id)
23
+ OpenLoam::Events.publish("open_loam.notification.created", id: id, user_id: user_id)
24
24
  end
25
25
 
26
26
  # What the notification is about, if anything (source_type/source_id) —