railwatch 0.9.4 → 1.0.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 (452) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +50 -0
  4. data/README.md +51 -60
  5. data/config/routes.rb +0 -59
  6. data/docs/ai-and-mcp.md +9 -8
  7. data/docs/configuration.md +35 -66
  8. data/docs/faq.md +17 -25
  9. data/docs/getting-started.md +105 -117
  10. data/docs/replacing-nightwatch.md +8 -7
  11. data/docs/replacing-sentry.md +12 -16
  12. data/docs/security.md +14 -20
  13. data/docs/self-hosting.md +24 -22
  14. data/docs/testing.md +6 -14
  15. data/docs/troubleshooting.md +12 -46
  16. data/lib/generators/railwatch/install/install_generator.rb +12 -240
  17. data/lib/generators/railwatch/install/templates/initializer.rb.tt +10 -40
  18. data/lib/generators/railwatch/install/templates/post-deploy +0 -7
  19. data/lib/railwatch/authentication.rb +6 -5
  20. data/lib/railwatch/configuration.rb +21 -151
  21. data/lib/railwatch/engine.rb +9 -118
  22. data/lib/railwatch/patches.rb +0 -11
  23. data/lib/railwatch/reporter.rb +3 -19
  24. data/lib/railwatch/subscribers/process_info.rb +1 -2
  25. data/lib/railwatch/version.rb +1 -1
  26. data/lib/railwatch.rb +4 -62
  27. data/lib/tasks/railwatch_tasks.rake +3 -325
  28. data/llms.txt +23 -25
  29. metadata +3 -453
  30. data/app/channels/railwatch/environment_channel.rb +0 -62
  31. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  32. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  33. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  34. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  35. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  36. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  37. data/app/controllers/railwatch/commands_controller.rb +0 -16
  38. data/app/controllers/railwatch/comments_controller.rb +0 -11
  39. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  40. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  41. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  42. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  43. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  44. data/app/controllers/railwatch/executions_controller.rb +0 -13
  45. data/app/controllers/railwatch/issues_controller.rb +0 -258
  46. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  47. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  48. data/app/controllers/railwatch/logs_controller.rb +0 -40
  49. data/app/controllers/railwatch/mails_controller.rb +0 -19
  50. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  51. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  52. data/app/controllers/railwatch/overview_controller.rb +0 -38
  53. data/app/controllers/railwatch/people_controller.rb +0 -25
  54. data/app/controllers/railwatch/processes_controller.rb +0 -33
  55. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  56. data/app/controllers/railwatch/queries_controller.rb +0 -59
  57. data/app/controllers/railwatch/releases_controller.rb +0 -75
  58. data/app/controllers/railwatch/requests_controller.rb +0 -37
  59. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  60. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  61. data/app/controllers/railwatch/spans_controller.rb +0 -47
  62. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  63. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  64. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  65. data/app/controllers/railwatch/traces_controller.rb +0 -72
  66. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  67. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  68. data/app/controllers/railwatch/visits_controller.rb +0 -53
  69. data/app/frontend/components/app-content.tsx +0 -26
  70. data/app/frontend/components/app-logo-icon.tsx +0 -23
  71. data/app/frontend/components/app-logo.tsx +0 -14
  72. data/app/frontend/components/app-shell.tsx +0 -38
  73. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  74. data/app/frontend/components/app-sidebar.tsx +0 -94
  75. data/app/frontend/components/app-wordmark.tsx +0 -29
  76. data/app/frontend/components/breadcrumbs.tsx +0 -46
  77. data/app/frontend/components/icon.tsx +0 -16
  78. data/app/frontend/components/input-error.tsx +0 -18
  79. data/app/frontend/components/nav-footer.tsx +0 -46
  80. data/app/frontend/components/nav-main.tsx +0 -41
  81. data/app/frontend/components/nav-user.tsx +0 -50
  82. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  83. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  84. data/app/frontend/components/railwatch/code.tsx +0 -28
  85. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  86. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  87. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  88. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  89. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  90. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  91. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  92. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  93. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  94. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  95. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  96. data/app/frontend/components/railwatch/frames.tsx +0 -295
  97. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  98. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  99. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  100. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  101. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  102. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  103. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  104. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  105. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  106. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  107. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  108. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  109. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  110. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  111. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  112. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  113. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  114. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  115. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  116. data/app/frontend/components/railwatch/stat.tsx +0 -185
  117. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  118. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  119. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  120. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  121. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  122. data/app/frontend/components/ui/avatar.tsx +0 -51
  123. data/app/frontend/components/ui/badge.tsx +0 -46
  124. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  125. data/app/frontend/components/ui/button.tsx +0 -60
  126. data/app/frontend/components/ui/card.tsx +0 -92
  127. data/app/frontend/components/ui/chart.tsx +0 -374
  128. data/app/frontend/components/ui/checkbox.tsx +0 -32
  129. data/app/frontend/components/ui/collapsible.tsx +0 -31
  130. data/app/frontend/components/ui/command.tsx +0 -182
  131. data/app/frontend/components/ui/dialog.tsx +0 -158
  132. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  133. data/app/frontend/components/ui/hover-card.tsx +0 -42
  134. data/app/frontend/components/ui/input.tsx +0 -21
  135. data/app/frontend/components/ui/label.tsx +0 -24
  136. data/app/frontend/components/ui/popover.tsx +0 -87
  137. data/app/frontend/components/ui/select.tsx +0 -185
  138. data/app/frontend/components/ui/separator.tsx +0 -28
  139. data/app/frontend/components/ui/sheet.tsx +0 -137
  140. data/app/frontend/components/ui/sidebar.tsx +0 -726
  141. data/app/frontend/components/ui/skeleton.tsx +0 -13
  142. data/app/frontend/components/ui/sonner.tsx +0 -41
  143. data/app/frontend/components/ui/table.tsx +0 -114
  144. data/app/frontend/components/ui/tabs.tsx +0 -91
  145. data/app/frontend/components/ui/textarea.tsx +0 -18
  146. data/app/frontend/components/ui/tooltip.tsx +0 -61
  147. data/app/frontend/components/user-info.tsx +0 -32
  148. data/app/frontend/components/user-menu-content.tsx +0 -94
  149. data/app/frontend/entrypoints/application.css +0 -244
  150. data/app/frontend/entrypoints/inertia.tsx +0 -92
  151. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  152. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  153. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  154. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  157. data/app/frontend/hooks/use-appearance.tsx +0 -67
  158. data/app/frontend/hooks/use-clipboard.ts +0 -32
  159. data/app/frontend/hooks/use-flash.tsx +0 -23
  160. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  161. data/app/frontend/hooks/use-initials.tsx +0 -15
  162. data/app/frontend/hooks/use-live.ts +0 -205
  163. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  164. data/app/frontend/hooks/use-mobile.ts +0 -21
  165. data/app/frontend/hooks/use-percentile.ts +0 -24
  166. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  167. data/app/frontend/hooks/use-row-nav.ts +0 -106
  168. data/app/frontend/hooks/use-step.ts +0 -27
  169. data/app/frontend/hooks/use-ticker.ts +0 -12
  170. data/app/frontend/hooks/use-window.ts +0 -58
  171. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  172. data/app/frontend/layouts/app-layout.tsx +0 -21
  173. data/app/frontend/layouts/env-layout.tsx +0 -53
  174. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  175. data/app/frontend/lib/cable.ts +0 -10
  176. data/app/frontend/lib/env-pages.ts +0 -236
  177. data/app/frontend/lib/execution-path.ts +0 -55
  178. data/app/frontend/lib/explain.ts +0 -50
  179. data/app/frontend/lib/filter.ts +0 -25
  180. data/app/frontend/lib/flamegraph.ts +0 -155
  181. data/app/frontend/lib/format.ts +0 -71
  182. data/app/frontend/lib/keyboard.ts +0 -18
  183. data/app/frontend/lib/mount.ts +0 -23
  184. data/app/frontend/lib/source-link.ts +0 -217
  185. data/app/frontend/lib/utils.ts +0 -6
  186. data/app/frontend/pages/alerts/index.tsx +0 -171
  187. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  188. data/app/frontend/pages/cache_events/index.tsx +0 -127
  189. data/app/frontend/pages/commands/index.tsx +0 -143
  190. data/app/frontend/pages/deploys/index.tsx +0 -94
  191. data/app/frontend/pages/deploys/show.tsx +0 -309
  192. data/app/frontend/pages/deprecations/index.tsx +0 -176
  193. data/app/frontend/pages/exceptions/index.tsx +0 -271
  194. data/app/frontend/pages/executions/show.tsx +0 -821
  195. data/app/frontend/pages/issues/index.tsx +0 -399
  196. data/app/frontend/pages/issues/show.tsx +0 -1367
  197. data/app/frontend/pages/jobs/index.tsx +0 -381
  198. data/app/frontend/pages/jobs/klass.tsx +0 -215
  199. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  200. data/app/frontend/pages/logs/index.tsx +0 -303
  201. data/app/frontend/pages/mails/index.tsx +0 -218
  202. data/app/frontend/pages/notifications/index.tsx +0 -161
  203. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  204. data/app/frontend/pages/overview/show.tsx +0 -333
  205. data/app/frontend/pages/people/index.tsx +0 -102
  206. data/app/frontend/pages/people/show.tsx +0 -149
  207. data/app/frontend/pages/processes/index.tsx +0 -335
  208. data/app/frontend/pages/profiles/index.tsx +0 -110
  209. data/app/frontend/pages/profiles/show.tsx +0 -105
  210. data/app/frontend/pages/queries/index.tsx +0 -492
  211. data/app/frontend/pages/queries/show.tsx +0 -276
  212. data/app/frontend/pages/releases/index.tsx +0 -190
  213. data/app/frontend/pages/releases/release-health.tsx +0 -68
  214. data/app/frontend/pages/releases/release-path.ts +0 -25
  215. data/app/frontend/pages/releases/show.tsx +0 -262
  216. data/app/frontend/pages/requests/index.tsx +0 -235
  217. data/app/frontend/pages/requests/route.tsx +0 -159
  218. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  219. data/app/frontend/pages/spans/index.tsx +0 -229
  220. data/app/frontend/pages/spans/show.tsx +0 -220
  221. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  222. data/app/frontend/pages/tenants/index.tsx +0 -236
  223. data/app/frontend/pages/tenants/show.tsx +0 -477
  224. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  225. data/app/frontend/pages/thresholds/index.tsx +0 -414
  226. data/app/frontend/pages/traces/show.tsx +0 -208
  227. data/app/frontend/pages/transactions/index.tsx +0 -220
  228. data/app/frontend/pages/view_renders/index.tsx +0 -241
  229. data/app/frontend/pages/visits/index.tsx +0 -321
  230. data/app/frontend/public/icon.png +0 -0
  231. data/app/frontend/public/icon.svg +0 -5
  232. data/app/frontend/routes/index.d.ts +0 -2136
  233. data/app/frontend/routes/index.js +0 -2112
  234. data/app/frontend/types/globals.d.ts +0 -9
  235. data/app/frontend/types/index.ts +0 -288
  236. data/app/frontend/types/vite-env.d.ts +0 -9
  237. data/app/helpers/railwatch/assets_helper.rb +0 -52
  238. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  239. data/app/jobs/railwatch/application_job.rb +0 -7
  240. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  241. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  242. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  243. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  244. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  245. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  246. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  247. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  248. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  249. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  250. data/app/jobs/railwatch/rollup_job.rb +0 -132
  251. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  252. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  253. data/app/models/railwatch/alert.rb +0 -229
  254. data/app/models/railwatch/alert_rule.rb +0 -121
  255. data/app/models/railwatch/anomaly_rule.rb +0 -51
  256. data/app/models/railwatch/application.rb +0 -44
  257. data/app/models/railwatch/application_record.rb +0 -33
  258. data/app/models/railwatch/comment.rb +0 -36
  259. data/app/models/railwatch/deploy.rb +0 -67
  260. data/app/models/railwatch/environment.rb +0 -68
  261. data/app/models/railwatch/execution_presenter.rb +0 -185
  262. data/app/models/railwatch/filter_query.rb +0 -184
  263. data/app/models/railwatch/followup_receipt.rb +0 -27
  264. data/app/models/railwatch/ingest/batch.rb +0 -355
  265. data/app/models/railwatch/ingest/mapper.rb +0 -585
  266. data/app/models/railwatch/ingest/payload.rb +0 -96
  267. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  268. data/app/models/railwatch/ingest/writer.rb +0 -137
  269. data/app/models/railwatch/issue.rb +0 -277
  270. data/app/models/railwatch/issue_activity.rb +0 -23
  271. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  272. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  273. data/app/models/railwatch/maintenance_task.rb +0 -53
  274. data/app/models/railwatch/saved_view.rb +0 -63
  275. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  276. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  277. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  278. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  279. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  280. data/app/models/railwatch/telemetry/child.rb +0 -53
  281. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  282. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  283. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  284. data/app/models/railwatch/telemetry/exception.rb +0 -28
  285. data/app/models/railwatch/telemetry/execution.rb +0 -152
  286. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  287. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  288. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  289. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  290. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  291. data/app/models/railwatch/telemetry/log.rb +0 -91
  292. data/app/models/railwatch/telemetry/mail.rb +0 -13
  293. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  294. data/app/models/railwatch/telemetry/notification.rb +0 -13
  295. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  296. data/app/models/railwatch/telemetry/person.rb +0 -50
  297. data/app/models/railwatch/telemetry/process.rb +0 -10
  298. data/app/models/railwatch/telemetry/profile.rb +0 -37
  299. data/app/models/railwatch/telemetry/query.rb +0 -57
  300. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  301. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  302. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  303. data/app/models/railwatch/telemetry/session.rb +0 -24
  304. data/app/models/railwatch/telemetry/span.rb +0 -19
  305. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  306. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  307. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  308. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  309. data/app/models/railwatch/telemetry/visit.rb +0 -24
  310. data/app/models/railwatch/telemetry_record.rb +0 -107
  311. data/app/models/railwatch/threshold.rb +0 -66
  312. data/app/models/railwatch/user.rb +0 -47
  313. data/app/models/railwatch/viewer.rb +0 -13
  314. data/app/models/railwatch/window.rb +0 -66
  315. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  316. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  317. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  318. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  319. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  320. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  321. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  322. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  323. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  324. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  325. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  327. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  328. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  329. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  330. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  331. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  332. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  333. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  334. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  335. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  336. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  337. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  338. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  340. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  341. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  342. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  343. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  344. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  345. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  347. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  348. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  349. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  350. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  351. data/docs/embedded.md +0 -503
  352. data/lib/puma/plugin/railwatch.rb +0 -215
  353. data/lib/railwatch/dashboard_assets.rb +0 -45
  354. data/lib/railwatch/embedded.rb +0 -50
  355. data/lib/railwatch/export/client.rb +0 -124
  356. data/lib/railwatch/export/lease.rb +0 -56
  357. data/lib/railwatch/export/outbox.rb +0 -361
  358. data/lib/railwatch/export/policy.rb +0 -79
  359. data/lib/railwatch/export/sender.rb +0 -130
  360. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  361. data/lib/railwatch/maintenance.rb +0 -194
  362. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  363. data/lib/railwatch/transport/local.rb +0 -78
  364. data/lib/railwatch/transport/socket.rb +0 -183
  365. data/lib/railwatch/writer.rb +0 -370
  366. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  367. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  368. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  369. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  372. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  373. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  374. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  375. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  376. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  377. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  378. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  379. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  380. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  381. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  382. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  383. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  384. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  385. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  386. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  387. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  388. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  389. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  390. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  391. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  392. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  393. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  394. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  395. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  396. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  397. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  398. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  399. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  400. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  401. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  402. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  403. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  404. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  405. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  406. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  407. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  408. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  409. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  410. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  411. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  412. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  413. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  414. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  415. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  416. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  417. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  418. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  419. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  420. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  421. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  422. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  423. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  424. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  425. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  426. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  427. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  428. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  429. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  430. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  431. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  432. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  433. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  434. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  435. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  436. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  437. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  438. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  439. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  440. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  441. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  442. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  443. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  444. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  445. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  446. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  447. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  448. data/public/railwatch/icon.png +0 -0
  449. data/public/railwatch/icon.svg +0 -5
  450. data/public/railwatch/manifest.json +0 -1245
  451. data/public/railwatch/rails-vite.json +0 -1
  452. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -60,23 +60,18 @@ module Railwatch
60
60
  Rack::QueryParser::ParameterTypeError
61
61
  ].freeze
62
62
 
63
- # What the dashboard controllers inherit from unless the host names its
64
- # own. Also how `dashboard_gate` tells "they wired their own auth" from
65
- # "they left the default".
63
+ # What railwatch-server's dashboard proxy inherits from unless the host
64
+ # names its own.
66
65
  DEFAULT_BASE_CONTROLLER = "ActionController::Base"
67
66
 
68
- attr_accessor :export_enabled, :export_policy, :export_url, :export_token, :export_max_bytes,
69
- :export_max_deliveries, :export_max_age
70
- attr_accessor :enabled, :token, :ingest_url, :allow_http, :server, :environment, :transport,
71
- :issue_prefix, :repository_url, :retention_days, :dashboard_user, :writer_socket,
67
+ attr_accessor :enabled, :token, :ingest_url, :allow_http, :server, :environment,
72
68
  :http_basic_auth_enabled, :http_basic_auth_user, :http_basic_auth_password, :base_controller_class,
73
- :dashboard_open,
74
69
  :sample, :log_level, :capture_request_payload,
75
70
  :capture_exception_source, :capture_exception_locals, :redact_headers, :redact_params,
76
71
  :buffer_size, :buffer_bytes, :execution_buffer_bytes, :batch_bytes,
77
72
  :backpressure,
78
73
  :flush_interval, :flush_threshold,
79
- :connect_timeout, :timeout, :shutdown_timeout, :migration_busy_timeout,
74
+ :connect_timeout, :timeout, :shutdown_timeout,
80
75
  :slow_query_threshold_ms, :n_plus_one_threshold,
81
76
  :max_view_renders_per_execution, :ignored_cache_key_prefixes,
82
77
  :beacon_enabled, :beacon_rate_limit, :beacon_global_rate_limit, :beacon_allowed_origins,
@@ -99,49 +94,24 @@ module Railwatch
99
94
  def initialize
100
95
  @enabled = env_bool("RAILWATCH_ENABLED", true)
101
96
  @token = ENV["RAILWATCH_TOKEN"]
102
- @transport = ENV.fetch("RAILWATCH_TRANSPORT", "http").to_sym
103
- # Embedded dashboard settings; ignored when transport is :http.
104
- @issue_prefix = ENV["RAILWATCH_ISSUE_PREFIX"]
105
- @repository_url = ENV["RAILWATCH_REPOSITORY_URL"]
106
- @retention_days = env_int("RAILWATCH_RETENTION_DAYS", 7)
107
- @dashboard_user = nil
108
- # Mirroring an embedded install's telemetry to a remote receiver. Off
109
- # unless asked for: an embedded install's promise is that nothing leaves
110
- # the machine, and a token being present is not consent.
111
- @export_enabled = env_bool("RAILWATCH_EXPORT_ENABLED", false)
112
- @export_policy = ENV.fetch("RAILWATCH_EXPORT_POLICY", "everything").to_sym
113
- @export_url = ENV["RAILWATCH_EXPORT_URL"]
114
- @export_token = ENV["RAILWATCH_EXPORT_TOKEN"]
115
- @export_max_bytes = env_int("RAILWATCH_EXPORT_MAX_BYTES", 256 * 1024 * 1024)
116
- @export_max_deliveries = env_int("RAILWATCH_EXPORT_MAX_DELIVERIES", 100_000)
117
- @export_max_age = env_int("RAILWATCH_EXPORT_MAX_AGE_SECONDS", 86_400)
118
- # Dashboard access, the way Mission Control Jobs does it: HTTP Basic
97
+ # The gate in front of the dashboard railwatch-server serves at
98
+ # /railwatch, the way Mission Control Jobs does it: HTTP Basic
119
99
  # authentication is on and CLOSED by default. With no user and password
120
100
  # configured every dashboard request is 401, so an install that forgot
121
101
  # to set anything up is never a public page. Credentials come from
122
102
  # Rails credentials (railwatch.http_basic_auth_user/_password, written
123
103
  # by `bin/rails railwatch:authentication:configure`), from these env
124
104
  # vars, or by assignment here. A host with its own admin auth turns
125
- # Basic off and names a base controller, or wraps the mount in a
126
- # routes constraint.
105
+ # Basic off and names a base controller. Nothing here reads them unless
106
+ # railwatch-server is installed.
127
107
  @http_basic_auth_enabled = env_bool("RAILWATCH_HTTP_BASIC_AUTH_ENABLED", true)
128
108
  @http_basic_auth_user = ENV["RAILWATCH_HTTP_BASIC_AUTH_USER"]
129
109
  @http_basic_auth_password = ENV["RAILWATCH_HTTP_BASIC_AUTH_PASSWORD"]
130
- # Only consulted when HTTP Basic is off: the host saying, in as many
131
- # words, "I accept that anyone who can reach this URL can read it".
132
- # A private network or a VPN is a real answer; this is how you say so,
133
- # and it is what silences the boot warning and opens the live channel.
134
- @dashboard_open = env_bool("RAILWATCH_DASHBOARD_OPEN", false)
135
- # The dashboard controllers inherit from this class, so a host's own
136
- # before_actions (require an admin, redirect to sign-in) run in front of
137
- # every page. Default is the engine's own base, which authenticates
138
- # nothing itself beyond HTTP Basic above.
110
+ # railwatch-server's dashboard proxy inherits from this class, so a
111
+ # host's own before_actions (require an admin, redirect to sign-in) run
112
+ # in front of every page. The default authenticates nothing itself
113
+ # beyond HTTP Basic above.
139
114
  @base_controller_class = ENV.fetch("RAILWATCH_BASE_CONTROLLER_CLASS", DEFAULT_BASE_CONTROLLER)
140
- # Embedded mode's writer process (lib/railwatch/writer.rb): the Unix
141
- # socket the Puma workers hand their batches to. Relative paths are
142
- # under Rails.root. nil disables the writer and every process writes
143
- # its own batches, as before.
144
- @writer_socket = ENV.fetch("RAILWATCH_WRITER_SOCKET", "tmp/sockets/railwatch-writer.sock")
145
115
  @ingest_url = ENV.fetch("RAILWATCH_INGEST_URL", "https://railwatch.rebulk.com")
146
116
  @allow_http = env_bool("RAILWATCH_ALLOW_HTTP", false)
147
117
  @project_root = defined?(Rails) ? Rails.root : Dir.pwd
@@ -187,13 +157,6 @@ module Railwatch
187
157
  @connect_timeout = env_float("RAILWATCH_CONNECT_TIMEOUT", 1.0)
188
158
  @timeout = env_float("RAILWATCH_TIMEOUT", 3.0)
189
159
  @shutdown_timeout = env_float("RAILWATCH_SHUTDOWN_TIMEOUT", 2.0)
190
- # Seconds a migration of the railwatch databases waits for SQLite's
191
- # write lock (Patches::MigrationBusyTimeout). A deploy migrates while
192
- # the old release's writer is still committing to the same file; the
193
- # database's own `timeout` (5 s) is too short to get between its
194
- # transactions when the disk is busy. Kept under a typical health-check
195
- # window (Kamal's deploy_timeout defaults to 30 s and is often 90 s).
196
- @migration_busy_timeout = env_float("RAILWATCH_MIGRATION_BUSY_TIMEOUT", 60.0)
197
160
  @slow_query_threshold_ms = env_float("RAILWATCH_SLOW_QUERY_MS", 5.0)
198
161
  @n_plus_one_threshold = env_int("RAILWATCH_N_PLUS_ONE_THRESHOLD", 5)
199
162
  @max_view_renders_per_execution = 20
@@ -318,67 +281,13 @@ module Railwatch
318
281
  end
319
282
 
320
283
  def enabled?
321
- @enabled && (local? || token.present?)
284
+ @enabled && token.present?
322
285
  end
323
286
 
324
- # :local writes telemetry into the engine's own database in-process;
325
- # anything else ships it to ingest_url over HTTPS.
326
- def local? = transport.to_s == "local"
327
-
328
- # The receiver admits an unseen delivery for seven days; queueing one for
329
- # longer cannot help.
330
- MAX_EXPORT_AGE = 7 * 24 * 60 * 60
331
-
332
- # Mirroring is a thing an embedded install opts into; it is meaningless
333
- # for an install that is already sending everything over HTTP.
334
- def export? = export_enabled && local? && export_problem.nil?
335
-
336
- # Why export is configured but unusable, or nil when it is fine. The
337
- # doctor reports this; nothing silently half-enables.
338
- def export_problem
339
- return nil unless export_enabled
340
- return "export needs transport :local; an :http install already sends everything" unless local?
341
- return "RAILWATCH_EXPORT_POLICY #{export_policy} is not implemented" unless export_policy.to_s == "everything"
342
- return "no export token: set RAILWATCH_EXPORT_TOKEN or RAILWATCH_TOKEN" if resolved_export_token.to_s.empty?
343
- return "no export url: set RAILWATCH_EXPORT_URL or RAILWATCH_INGEST_URL" if resolved_export_url.to_s.empty?
344
- return "export url must be HTTPS (or set RAILWATCH_ALLOW_HTTP=true)" unless url_allowed?(resolved_export_url)
345
- # Past this a receiver stops recognising a delivery's id, so holding one
346
- # any longer just means discovering later that it can never be sent.
347
- if export_max_age > MAX_EXPORT_AGE
348
- return "RAILWATCH_EXPORT_MAX_AGE_SECONDS cannot exceed #{MAX_EXPORT_AGE} (the receiver stops recognising a delivery past that)"
349
- end
350
-
351
- nil
352
- end
353
-
354
- # The receiver, and the credential we are bound to it with. Both fall back
355
- # to the ordinary ingest settings so switching an install from embedded to
356
- # cloud needs no second set of values.
357
- def resolved_export_url
358
- url = export_url.presence || (ingest_url.presence && URI.join(ingest_url, "/ingest").to_s)
359
- url&.sub(%r{/\z}, "")
360
- rescue URI::Error
361
- # BadURIError (a relative ingest_url) is not an InvalidURIError, and
362
- # letting it out of here takes the doctor down with it.
363
- nil
364
- end
365
-
366
- def resolved_export_token = export_token.presence || token
367
-
368
- # Absolute path of the writer socket, or nil when the writer is off.
369
- def writer_socket_path
370
- path = writer_socket.to_s
371
- return nil if path.empty?
372
-
373
- root = defined?(Rails) && Rails.respond_to?(:root) && Rails.root ? Rails.root.to_s : Dir.pwd
374
- File.expand_path(path, root)
375
- end
287
+ # Always false: there is no embedded transport any more. Only
288
+ # railwatch-server 0.1.0 calls it.
289
+ def local? = false
376
290
 
377
- # Who the embedded dashboard shows as the signed-in operator. A host
378
- # passes a lambda taking the request (cookies, warden, whatever it uses)
379
- # and returning a User, {id:, name:, email:} or nil. Authentication and
380
- # authorisation stay the host's job: put the mount behind its own
381
- # constraint. This only names the person for comments and saved views.
382
291
  # Both halves present. Read late (not at boot) so credentials set from an
383
292
  # initializer, an env var or the configure task all count.
384
293
  def http_basic_auth_configured?
@@ -386,17 +295,17 @@ module Railwatch
386
295
  end
387
296
 
388
297
  # Development with Basic on and nothing configured is open, so a first
389
- # run is `rails g railwatch:install` and a page, not a password step
390
- # first. Rails already shows full error pages there. Every other
391
- # environment stays closed until credentials exist.
298
+ # run is a page, not a password step first. Rails already shows full
299
+ # error pages there. Every other environment stays closed until
300
+ # credentials exist.
392
301
  def http_basic_auth_waived?
393
302
  http_basic_auth_enabled && !http_basic_auth_configured? && defined?(Rails) && Rails.env.development?
394
303
  end
395
304
 
396
305
  # Whether the request carries the configured HTTP Basic credentials.
397
306
  # False when Basic is on and nothing is configured (closed), true when
398
- # Basic is off (the host's base controller or routes constraint is the
399
- # gate then). Shared by the dashboard controller and the live channel.
307
+ # Basic is off (the host's base controller is the gate then).
308
+ # railwatch-server's dashboard proxy calls it.
400
309
  def http_basic_auth_ok?(request)
401
310
  return true unless http_basic_auth_enabled
402
311
  return true if http_basic_auth_waived?
@@ -408,45 +317,6 @@ module Railwatch
408
317
  end == true
409
318
  end
410
319
 
411
- # Which gate is in force, for the doctor, the boot warning and the live
412
- # channel. :basic when HTTP Basic is on (closed until credentials exist),
413
- # :controller when the host named its own base controller, :resolver when
414
- # it gave a dashboard_user that can refuse, :open when it said the
415
- # dashboard is deliberately public, and :undeclared when Basic is off and
416
- # none of those is true -- which usually means a routes constraint the
417
- # gem cannot see, and might mean nothing at all.
418
- def dashboard_gate
419
- return :basic if http_basic_auth_enabled
420
- return :controller unless base_controller_class == DEFAULT_BASE_CONTROLLER
421
- return :resolver if dashboard_user
422
- return :open if dashboard_open
423
-
424
- :undeclared
425
- end
426
-
427
- # Whether a live-update subscription is allowed. Action Cable runs on the
428
- # host's own /cable endpoint, which a routes constraint around the
429
- # engine's mount does not cover and a base controller cannot reach, so an
430
- # undeclared gate refuses rather than assuming.
431
- def dashboard_channel_allowed?(request)
432
- return http_basic_auth_ok?(request) if http_basic_auth_enabled
433
- if dashboard_user
434
- return !!(dashboard_user.respond_to?(:call) ? dashboard_user.call(request) : dashboard_user)
435
- end
436
-
437
- # A controller name is not evidence of authorization on /cable.
438
- !!dashboard_open
439
- end
440
-
441
- def resolve_dashboard_user(request)
442
- resolved = dashboard_user.respond_to?(:call) ? dashboard_user.call(request) : dashboard_user
443
- case resolved
444
- when User then resolved
445
- when Hash then User.new(id: resolved[:id] || User::ID, name: resolved[:name].to_s.presence || "Operator", email: resolved[:email])
446
- else User.default
447
- end
448
- end
449
-
450
320
  def ingest_url_allowed?
451
321
  url_allowed?(ingest_url)
452
322
  end
@@ -1,88 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The dashboard controllers are Inertia controllers; a host that does not use
4
- # Inertia itself never requires the gem, so the engine does.
5
- require "inertia_rails"
6
- # The engine's jobs (grouping, rollups, scans) are Active Job classes even
7
- # though embedded mode calls their perform directly, so a host built with
8
- # `rails new --minimal` (no Active Job) still needs the framework loaded.
9
- # Part of the rails gem this one depends on, so nothing new is pulled in.
10
- require "active_job/railtie"
11
-
12
3
  module Railwatch
13
- # isolate_namespace would give every model a railwatch_ prefix. Only the
14
- # engine's own records (issues, comments, deploys: Railwatch::ApplicationRecord)
15
- # carry it; the telemetry tables are the hosted platform's schema and stay
16
- # unprefixed. Defined before isolate_namespace runs, which only adds a
17
- # prefix when none is set.
18
- def self.table_name_prefix = ""
19
-
4
+ # Mounted for the browser beacon (POST <mount>/beacon), its only route.
20
5
  class Engine < ::Rails::Engine
21
6
  isolate_namespace Railwatch
22
7
 
23
- config.railwatch = ActiveSupport::OrderedOptions.new
24
-
25
- # The dashboard bundle is built into the gem at release time; serve it
26
- # from here so a host app needs no asset pipeline integration at all.
27
- # Where it goes in the stack is DashboardAssets.install!'s decision.
28
- initializer "railwatch.dashboard_assets" do |app|
29
- Railwatch::DashboardAssets.install!(app, root: root.join("public/railwatch").to_s)
30
- end
31
-
32
- # Action Cable is optional: live dashboard updates need it, nothing else
33
- # does. A host without it (`rails new --minimal`, --skip-action-cable)
34
- # must not eager-load the engine's channel, which inherits from a class
35
- # that does not exist there.
36
- initializer "railwatch.channels", before: :setup_main_autoloader do
37
- Rails.autoloaders.main.ignore(root.join("app/channels")) unless defined?(::ActionCable)
38
- end
39
-
40
- # The dashboard bundle subscribes to "EnvironmentChannel" by name; Action
41
- # Cable constantizes that, so the engine's channel needs the bare name.
42
- # Only defined when the host has not got one of its own.
43
- initializer "railwatch.live_channel" do
44
- next unless defined?(::ActionCable)
45
-
46
- # Action Cable constantizes the subscription's channel name at
47
- # subscribe time; the alias only has to exist by then, and it must
48
- # not clobber a host channel of the same name.
49
- config.to_prepare do
50
- Object.const_set(:EnvironmentChannel, Railwatch::EnvironmentChannel) unless Object.const_defined?(:EnvironmentChannel)
51
- end
52
- end
53
-
54
8
  # The request middleware goes first so wall time includes every other
55
9
  # middleware, exactly like Nightwatch's GlobalMiddleware.
56
10
  initializer "railwatch.middleware", before: :load_config_initializers do |app|
57
11
  app.middleware.insert_before 0, Railwatch::Middleware::Request
58
12
  end
59
13
 
60
- # HTTP installs have no embedded databases or job backend. Loading their
61
- # models still registers them with Active Record, whose schema-cache boot
62
- # hook asks every descendant for its connection_pool (e.g. with
63
- # activerecord-tenanted). Decide after the host's initializer selects the
64
- # transport. Keep autoloading available for the local installer and tools;
65
- # only local installs should eagerly load the embedded models and jobs.
66
- # Dashboard controllers also reference model constants in their class
67
- # bodies. The beacon is the only controller a cloud install needs.
68
- initializer "railwatch.embedded_eager_loading", after: :load_config_initializers, before: :setup_main_autoloader do
69
- next if Railwatch.config.local?
70
-
71
- Rails.autoloaders.main.do_not_eager_load(root.join("app/models"))
72
- Rails.autoloaders.main.do_not_eager_load(root.join("app/jobs"))
73
- root.glob("app/controllers/railwatch/*.rb").each do |path|
74
- Rails.autoloaders.main.do_not_eager_load(path) unless path.basename.to_s == "beacon_controller.rb"
75
- end
76
- end
77
-
78
14
  # Before anything subscribes or starts a thread, and after the app's own
79
15
  # initializer has had its say about config: a console captures nothing.
80
16
  initializer "railwatch.console", after: :load_config_initializers, before: "railwatch.subscribe" do
81
17
  Railwatch::Console.silence!
82
18
  end
83
19
 
84
- # Same source Mission Control Jobs reads: railwatch.http_basic_auth_user
85
- # and _password in Rails credentials, which `bin/rails
20
+ # The dashboard gate railwatch-server's proxy applies. Same source
21
+ # Mission Control Jobs reads: railwatch.http_basic_auth_user and
22
+ # _password in Rails credentials, which `bin/rails
86
23
  # railwatch:authentication:configure` writes. An initializer or env var
87
24
  # that already set them wins.
88
25
  initializer "railwatch.http_basic_auth", after: :load_config_initializers do |app|
@@ -91,49 +28,26 @@ module Railwatch
91
28
  config.http_basic_auth_password ||= app.credentials.dig(:railwatch, :http_basic_auth_password)
92
29
  end
93
30
 
94
- # Three things worth one line in the log at boot, because all are
95
- # invisible until something is already wrong: a Rails/json pair that
96
- # cannot decode, an embedded dashboard with nothing declared in front of
97
- # it, and one that is closed to everyone because Basic has no
98
- # credentials outside development.
31
+ # Worth one line in the log at boot, because it is invisible until
32
+ # something is already wrong: a Rails/json pair that cannot decode.
99
33
  initializer "railwatch.warnings", after: :load_config_initializers do
100
34
  config.after_initialize do
101
35
  next unless Railwatch.enabled?
102
36
 
103
37
  Rails.logger.warn("[railwatch] #{Railwatch::JsonCompat.advice}") if Railwatch::JsonCompat.broken?
104
-
105
- if Railwatch.config.local? && Railwatch.config.dashboard_gate == :basic &&
106
- !Railwatch.config.http_basic_auth_configured? && !Rails.env.local?
107
- Rails.logger.warn(
108
- "[railwatch] the dashboard is closed: HTTP Basic is on and no credentials are configured for " \
109
- "#{Rails.env}, so every request to it is 401. Run `RAILS_ENV=#{Rails.env} bin/rails " \
110
- "railwatch:authentication:configure`, or gate it with your own auth and set " \
111
- "`c.http_basic_auth_enabled = false` (docs/embedded.md)."
112
- )
113
- end
114
-
115
- if Railwatch.config.local? && Railwatch.config.dashboard_gate == :undeclared && !Rails.env.local?
116
- Rails.logger.warn(
117
- "[railwatch] the dashboard at the engine's mount has no gate this gem can see: HTTP Basic is off and " \
118
- "no base_controller_class, dashboard_user or dashboard_open is set. If a routes constraint or your " \
119
- "network already gates it, authorize /cable separately with `c.dashboard_user`, or set `c.dashboard_open = true` " \
120
- "only when the network protects both endpoints. Otherwise anyone who can reach the dashboard can read every query, log line and exception this app records."
121
- )
122
- end
123
38
  end
124
39
  end
125
40
 
126
41
  # A rake process starts its command execution before this point -- the
127
42
  # top-level task is invoked before its `:environment` prerequisite boots
128
- # Rails -- so a reporter can already exist, built from a config that had
129
- # not yet been told the app is embedded. This is the first moment the
130
- # app's own configuration is final.
43
+ # Rails -- so a reporter can already exist, built from a config that was
44
+ # not final. This is the first moment the app's own configuration is.
131
45
  initializer "railwatch.adopt_final_config", after: :load_config_initializers do
132
46
  Railwatch.adopt_final_config!
133
47
  end
134
48
 
135
49
  initializer "railwatch.transport_security", after: :load_config_initializers do
136
- next if Railwatch.config.local? || Railwatch.config.ingest_url_allowed?
50
+ next if Railwatch.config.ingest_url_allowed?
137
51
 
138
52
  Rails.logger.warn(
139
53
  "Railwatch will not send telemetry to #{Railwatch.config.ingest_url}: plain HTTP is allowed only for loopback " \
@@ -243,28 +157,6 @@ module Railwatch
243
157
  at_exit { Railwatch::Sessions.stop! }
244
158
  end
245
159
 
246
- # The embedded install's maintenance clock (release health, scans,
247
- # pruning). Same shape and ordering rationale as "railwatch.health":
248
- # stopped before the reporter's final flush. Maintenance.start! is a
249
- # no-op unless the transport is local.
250
- initializer "railwatch.maintenance" do
251
- next unless Railwatch.enabled?
252
-
253
- Railwatch::Maintenance.start!
254
- at_exit { Railwatch::Maintenance.stop! }
255
- end
256
-
257
- # Draining the export queue. Same shape and ordering as the others:
258
- # stopped before the reporter's final flush, so its last claim is
259
- # released rather than left to expire. Sender.start! is a no-op unless
260
- # export is configured and usable.
261
- initializer "railwatch.export" do
262
- next unless Railwatch.enabled?
263
-
264
- Railwatch::Export::Sender.start!
265
- at_exit { Railwatch::Export::Sender.stop! }
266
- end
267
-
268
160
  # lib/tasks/railwatch_tasks.rake is picked up by Rails::Engine's default
269
161
  # lib/tasks convention; the rake_tasks block above only installs the
270
162
  # Rake::Task patch.
@@ -274,7 +166,6 @@ end
274
166
  require "railwatch/console"
275
167
  require "railwatch/health"
276
168
  require "railwatch/sessions"
277
- require "railwatch/maintenance"
278
169
  require "railwatch/middleware/request"
279
170
  require "railwatch/job_tracing"
280
171
  require "railwatch/controller_helpers"
@@ -4,7 +4,6 @@ require "railwatch/patches/net_http"
4
4
  require "railwatch/patches/rake_task"
5
5
  require "railwatch/patches/runner_command"
6
6
  require "railwatch/patches/inertia"
7
- require "railwatch/patches/migration_busy_timeout"
8
7
 
9
8
  module Railwatch
10
9
  module Patches
@@ -23,16 +22,6 @@ module Railwatch
23
22
  # From Rails::Engine#load_tasks, which has already required rake.
24
23
  def install_rake_task!
25
24
  ::Rake::Task.prepend(RakeTask) unless ::Rake::Task.ancestors.include?(RakeTask)
26
- install_migration_busy_timeout!
27
- end
28
-
29
- # db:prepare and db:migrate run as rake tasks, so this goes in with the
30
- # rake patch. Unlike it, this one is not gated on Railwatch.enabled?: a
31
- # container entrypoint migrates with Railwatch switched off, and that is
32
- # exactly the migration that has to wait out the old release's writer.
33
- def install_migration_busy_timeout!
34
- tasks = ::ActiveRecord::Tasks::DatabaseTasks.singleton_class
35
- tasks.prepend(MigrationBusyTimeout) unless tasks.ancestors.include?(MigrationBusyTimeout)
36
25
  end
37
26
 
38
27
  # From Rails::Application#load_runner, which RunnerCommand#perform calls
@@ -183,20 +183,11 @@ module Railwatch
183
183
  # where the command patch starts the execution, and sampling that
184
184
  # execution builds the reporter -- before the task's `:environment`
185
185
  # prerequisite boots Rails and runs config/initializers. A token in the
186
- # environment is enough for Railwatch to be enabled that early, so an
187
- # embedded app gets a reporter aimed at HTTP, chosen from a config that
188
- # had not yet been told the app is embedded. Left alone it stays aimed
189
- # there: the memo outlives the configuration that produced it, and every
190
- # record from that process goes to the receiver instead of the app's own
191
- # database, skipping the export queue and the replay receipts it earns.
186
+ # environment is enough for Railwatch to be enabled that early.
192
187
  #
193
188
  # Nothing has been recorded at that point -- starting an execution
194
189
  # decides sampling and pushes nothing -- so deciding again here is free.
195
190
  def adopt_final_config!
196
- # The swap forked_transport already makes, for the same reason: the
197
- # situation this transport was chosen for is not the situation now.
198
- reselected = Railwatch.local_transport if @config.local?
199
- @transport = reselected if reselected && reselected.class != @transport.class
200
191
  # Sized from the config as it stood, which was the default one. Safe
201
192
  # only while the buffer is still empty: resizing means a new buffer,
202
193
  # and anything already in the old one would go with it.
@@ -248,12 +239,6 @@ module Railwatch
248
239
  end
249
240
 
250
241
  def forked_transport
251
- # A child may be a different kind of process from its parent: the
252
- # writer forked from a Puma master must write SQLite itself, not hand
253
- # batches back to the socket it is about to serve.
254
- reselected = Railwatch.local_transport if @config.local?
255
- return reselected if reselected && reselected.class != @transport.class
256
-
257
242
  transport = @transport.dup
258
243
  transport.reset_after_fork! if transport.respond_to?(:reset_after_fork!)
259
244
  transport
@@ -447,9 +432,8 @@ module Railwatch
447
432
  end
448
433
  return unless gave_up
449
434
 
450
- # Losing a batch is not a debug-level event: with an ingest (or an
451
- # embedded writer) that never comes back this is the only place the
452
- # loss is ever reported, and the dropped counter it leaves behind
435
+ # Losing a batch is not a debug-level event: with an ingest that never
436
+ # comes back this is the only place the loss is ever reported, and the dropped counter it leaves behind
453
437
  # rides on the NEXT successful delivery, which may never happen.
454
438
  #
455
439
  # Reported here, after the lock is released, never inside it. The
@@ -89,8 +89,7 @@ module Railwatch
89
89
  # $PROGRAM_NAME after boot and would otherwise turn the supervisor,
90
90
  # dispatcher, and scheduler into "web" on every health sample.
91
91
  def role
92
- if defined?(Railwatch::Writer) && Railwatch::Writer.running? then "writer"
93
- elsif defined?(::SolidQueue) && ($PROGRAM_NAME.include?("jobs") || $PROGRAM_NAME.start_with?("solid-queue-") || ARGV.first.to_s.start_with?("solid_queue:")) then "worker"
92
+ if defined?(::SolidQueue) && ($PROGRAM_NAME.include?("jobs") || $PROGRAM_NAME.start_with?("solid-queue-") || ARGV.first.to_s.start_with?("solid_queue:")) then "worker"
94
93
  elsif defined?(::Rails::Console) then "console"
95
94
  elsif $PROGRAM_NAME.end_with?("rake") then "command"
96
95
  elsif defined?(::Puma) then "web"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Railwatch
4
- VERSION = "0.9.4"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/railwatch.rb CHANGED
@@ -21,19 +21,9 @@ require "railwatch/current"
21
21
  require "railwatch/record"
22
22
  require "railwatch/buffer"
23
23
  require "railwatch/transport/wire_encoder"
24
- require "railwatch/export/policy"
25
- require "railwatch/export/lease"
26
- require "railwatch/export/outbox"
27
- require "railwatch/export/client"
28
- require "railwatch/export/sender"
29
24
  require "railwatch/transport/http"
30
- require "railwatch/transport/local"
31
- require "railwatch/transport/socket"
32
- require "railwatch/writer"
33
25
  require "railwatch/authentication"
34
26
  require "railwatch/json_compat"
35
- require "railwatch/embedded"
36
- require "railwatch/ingest_request_body_limit"
37
27
  require "railwatch/reporter"
38
28
  require "railwatch/sampler"
39
29
  require "railwatch/redactor"
@@ -69,7 +59,7 @@ module Railwatch
69
59
  end
70
60
 
71
61
  def reporter
72
- @reporter ||= Reporter.new(config, transport: local_transport)
62
+ @reporter ||= Reporter.new(config)
73
63
  end
74
64
 
75
65
  # Called once config/initializers has run, so a reporter built before
@@ -87,16 +77,6 @@ module Railwatch
87
77
  @reporter&.adopt_final_config!
88
78
  end
89
79
 
90
- # Embedded mode: a Puma worker hands its batches to the writer process
91
- # over the socket; the writer itself, and any process when no socket is
92
- # configured, writes them straight into SQLite. nil means HTTP.
93
- def local_transport
94
- return nil unless config.local?
95
- return Transport::Local.new(config) if Writer.running? || config.writer_socket_path.nil?
96
-
97
- Transport::Socket.new(config)
98
- end
99
-
100
80
  def redactor
101
81
  @redactor ||= Redactor.new(config)
102
82
  end
@@ -124,9 +104,7 @@ module Railwatch
124
104
  Subscribers::Users.restart_after_fork!
125
105
  Subscribers::ProcessInfo.restart_after_fork!
126
106
  Health.restart_after_fork!
127
- Export::Sender.restart_after_fork!
128
107
  Sessions.restart_after_fork!
129
- Maintenance.restart_after_fork!
130
108
  end
131
109
 
132
110
  # --- execution lifecycle -------------------------------------------------
@@ -619,45 +597,16 @@ module Railwatch
619
597
  end
620
598
 
621
599
  require "railwatch/subscribers"
622
- require "railwatch/dashboard_assets"
623
600
 
624
601
  module Railwatch
625
- # Where the engine's two databases migrate from. database.yml names them
626
- # (`migrations_paths: <%= Railwatch.migrations_path(:railwatch) %>`) so a
627
- # host's db:prepare creates the tables on install and migrates them after
628
- # every gem update, from the gem's own history, the way Active Storage
629
- # migrates its tables. Nothing is copied into the app.
630
- def self.migrations_path(database)
631
- File.expand_path("../db/#{database}_migrate", __dir__)
632
- end
633
-
634
- # Where links the models build (a trace from an execution page, a saved
635
- # view's page) resolve. The engine's own routes serve the embedded
636
- # dashboard; a host that mounts these pages on its own routes, as
637
- # Railwatch Cloud does, points this at them.
638
- def self.url_helpers
639
- @url_helpers || Engine.routes.url_helpers
640
- end
641
-
642
- def self.url_helpers=(helpers)
643
- @url_helpers = helpers
644
- end
645
-
646
- # The one browser client, relative to the gem root. The dashboard bundle
647
- # is built from it and the install generator copies it into a host.
648
- BROWSER_CLIENT = "app/frontend/lib/railwatch.ts"
602
+ # The Inertia browser client, relative to the gem root. The install
603
+ # generator copies it into a host's app/frontend/lib.
604
+ BROWSER_CLIENT = "lib/generators/railwatch/install/templates/railwatch.ts"
649
605
 
650
606
  def self.browser_client_path
651
607
  File.expand_path("../#{BROWSER_CLIENT}", __dir__)
652
608
  end
653
609
 
654
- # The dashboard's frontend source. Railwatch Cloud builds its bundle from
655
- # this directory plus its own hosting pages, so there is one copy of every
656
- # dashboard page and component.
657
- def self.frontend_path
658
- File.expand_path("../app/frontend", __dir__)
659
- end
660
-
661
610
  # One realistic wire record per record type, exactly as the gem's own
662
611
  # subscribers emit it, generated by spec/railwatch/wire_fixtures_spec.rb.
663
612
  # A receiver tests its mapper against these instead of hand-writing what
@@ -669,12 +618,5 @@ module Railwatch
669
618
  def self.wire_fixtures
670
619
  @wire_fixtures ||= JSON.parse(File.read(wire_fixtures_path)).freeze
671
620
  end
672
-
673
- # Raised when the engine's models are used in an app whose database.yml
674
- # has no entry for them. They must never fall back to the host's primary
675
- # connection: the telemetry tables are unprefixed (`sessions`, `visits`,
676
- # `people`, `notifications`, `logs`), so a query there would read, and a
677
- # write would corrupt, the application's own tables.
678
- class DatabaseNotConfigured < StandardError; end
679
621
  end
680
622
  require "railwatch/engine" if defined?(Rails::Engine)