railwatch 0.9.3 → 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 (454) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +37 -33
  3. data/CHANGELOG.md +66 -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 +57 -71
  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/buffer.rb +9 -0
  21. data/lib/railwatch/configuration.rb +21 -151
  22. data/lib/railwatch/engine.rb +9 -118
  23. data/lib/railwatch/patches.rb +0 -11
  24. data/lib/railwatch/reporter.rb +92 -21
  25. data/lib/railwatch/sampler.rb +10 -2
  26. data/lib/railwatch/subscribers/process_info.rb +1 -2
  27. data/lib/railwatch/version.rb +1 -1
  28. data/lib/railwatch.rb +8 -64
  29. data/lib/tasks/railwatch_tasks.rake +3 -325
  30. data/llms.txt +23 -25
  31. metadata +3 -453
  32. data/app/channels/railwatch/environment_channel.rb +0 -62
  33. data/app/controllers/concerns/railwatch/telemetry_identity.rb +0 -25
  34. data/app/controllers/railwatch/alerts_controller.rb +0 -48
  35. data/app/controllers/railwatch/anomaly_rules_controller.rb +0 -31
  36. data/app/controllers/railwatch/attachments_controller.rb +0 -22
  37. data/app/controllers/railwatch/broadcasts_controller.rb +0 -15
  38. data/app/controllers/railwatch/cache_events_controller.rb +0 -30
  39. data/app/controllers/railwatch/commands_controller.rb +0 -16
  40. data/app/controllers/railwatch/comments_controller.rb +0 -11
  41. data/app/controllers/railwatch/dashboard_controller.rb +0 -70
  42. data/app/controllers/railwatch/deploys_controller.rb +0 -34
  43. data/app/controllers/railwatch/deprecations_controller.rb +0 -54
  44. data/app/controllers/railwatch/environment_scoped.rb +0 -81
  45. data/app/controllers/railwatch/exceptions_controller.rb +0 -52
  46. data/app/controllers/railwatch/executions_controller.rb +0 -13
  47. data/app/controllers/railwatch/issues_controller.rb +0 -258
  48. data/app/controllers/railwatch/jobs_controller.rb +0 -63
  49. data/app/controllers/railwatch/llm_calls_controller.rb +0 -124
  50. data/app/controllers/railwatch/logs_controller.rb +0 -40
  51. data/app/controllers/railwatch/mails_controller.rb +0 -19
  52. data/app/controllers/railwatch/notifications_controller.rb +0 -11
  53. data/app/controllers/railwatch/outgoing_requests_controller.rb +0 -19
  54. data/app/controllers/railwatch/overview_controller.rb +0 -38
  55. data/app/controllers/railwatch/people_controller.rb +0 -25
  56. data/app/controllers/railwatch/processes_controller.rb +0 -33
  57. data/app/controllers/railwatch/profiles_controller.rb +0 -65
  58. data/app/controllers/railwatch/queries_controller.rb +0 -59
  59. data/app/controllers/railwatch/releases_controller.rb +0 -75
  60. data/app/controllers/railwatch/requests_controller.rb +0 -37
  61. data/app/controllers/railwatch/saved_views_controller.rb +0 -53
  62. data/app/controllers/railwatch/scheduled_tasks_controller.rb +0 -45
  63. data/app/controllers/railwatch/spans_controller.rb +0 -47
  64. data/app/controllers/railwatch/storage_ops_controller.rb +0 -15
  65. data/app/controllers/railwatch/tenants_controller.rb +0 -44
  66. data/app/controllers/railwatch/thresholds_controller.rb +0 -47
  67. data/app/controllers/railwatch/traces_controller.rb +0 -72
  68. data/app/controllers/railwatch/transactions_controller.rb +0 -21
  69. data/app/controllers/railwatch/view_renders_controller.rb +0 -28
  70. data/app/controllers/railwatch/visits_controller.rb +0 -53
  71. data/app/frontend/components/app-content.tsx +0 -26
  72. data/app/frontend/components/app-logo-icon.tsx +0 -23
  73. data/app/frontend/components/app-logo.tsx +0 -14
  74. data/app/frontend/components/app-shell.tsx +0 -38
  75. data/app/frontend/components/app-sidebar-header.tsx +0 -54
  76. data/app/frontend/components/app-sidebar.tsx +0 -94
  77. data/app/frontend/components/app-wordmark.tsx +0 -29
  78. data/app/frontend/components/breadcrumbs.tsx +0 -46
  79. data/app/frontend/components/icon.tsx +0 -16
  80. data/app/frontend/components/input-error.tsx +0 -18
  81. data/app/frontend/components/nav-footer.tsx +0 -46
  82. data/app/frontend/components/nav-main.tsx +0 -41
  83. data/app/frontend/components/nav-user.tsx +0 -50
  84. data/app/frontend/components/railwatch/chart-hover.tsx +0 -34
  85. data/app/frontend/components/railwatch/chart-panel.tsx +0 -170
  86. data/app/frontend/components/railwatch/code.tsx +0 -28
  87. data/app/frontend/components/railwatch/command-palette.tsx +0 -132
  88. data/app/frontend/components/railwatch/copy-block.tsx +0 -52
  89. data/app/frontend/components/railwatch/copy-id.tsx +0 -49
  90. data/app/frontend/components/railwatch/cursor-load-more.tsx +0 -39
  91. data/app/frontend/components/railwatch/data-table.tsx +0 -195
  92. data/app/frontend/components/railwatch/digit-roll.tsx +0 -312
  93. data/app/frontend/components/railwatch/empty-state.tsx +0 -159
  94. data/app/frontend/components/railwatch/env-nav.tsx +0 -130
  95. data/app/frontend/components/railwatch/env-switcher.tsx +0 -145
  96. data/app/frontend/components/railwatch/filter-bar.tsx +0 -153
  97. data/app/frontend/components/railwatch/flamegraph.tsx +0 -324
  98. data/app/frontend/components/railwatch/frames.tsx +0 -295
  99. data/app/frontend/components/railwatch/json-viewer.tsx +0 -159
  100. data/app/frontend/components/railwatch/live-dot.tsx +0 -78
  101. data/app/frontend/components/railwatch/live-toggle.tsx +0 -76
  102. data/app/frontend/components/railwatch/metric-chart.tsx +0 -112
  103. data/app/frontend/components/railwatch/mobile-page-switcher.tsx +0 -89
  104. data/app/frontend/components/railwatch/onboarding.tsx +0 -91
  105. data/app/frontend/components/railwatch/origin-identity.tsx +0 -84
  106. data/app/frontend/components/railwatch/page-header.tsx +0 -42
  107. data/app/frontend/components/railwatch/percentile-picker.tsx +0 -18
  108. data/app/frontend/components/railwatch/relative-time.tsx +0 -18
  109. data/app/frontend/components/railwatch/saved-views.tsx +0 -232
  110. data/app/frontend/components/railwatch/segmented.tsx +0 -64
  111. data/app/frontend/components/railwatch/series-chart.tsx +0 -400
  112. data/app/frontend/components/railwatch/shortcuts-dialog.tsx +0 -89
  113. data/app/frontend/components/railwatch/signal-toast.tsx +0 -121
  114. data/app/frontend/components/railwatch/sort-header.tsx +0 -37
  115. data/app/frontend/components/railwatch/source-link.tsx +0 -57
  116. data/app/frontend/components/railwatch/sparkline-cell.tsx +0 -14
  117. data/app/frontend/components/railwatch/sql-block.tsx +0 -116
  118. data/app/frontend/components/railwatch/stat.tsx +0 -185
  119. data/app/frontend/components/railwatch/status-badge.tsx +0 -79
  120. data/app/frontend/components/railwatch/step-picker.tsx +0 -57
  121. data/app/frontend/components/railwatch/timeline.tsx +0 -405
  122. data/app/frontend/components/railwatch/utilisation-bar.tsx +0 -44
  123. data/app/frontend/components/railwatch/window-picker.tsx +0 -147
  124. data/app/frontend/components/ui/avatar.tsx +0 -51
  125. data/app/frontend/components/ui/badge.tsx +0 -46
  126. data/app/frontend/components/ui/breadcrumb.tsx +0 -109
  127. data/app/frontend/components/ui/button.tsx +0 -60
  128. data/app/frontend/components/ui/card.tsx +0 -92
  129. data/app/frontend/components/ui/chart.tsx +0 -374
  130. data/app/frontend/components/ui/checkbox.tsx +0 -32
  131. data/app/frontend/components/ui/collapsible.tsx +0 -31
  132. data/app/frontend/components/ui/command.tsx +0 -182
  133. data/app/frontend/components/ui/dialog.tsx +0 -158
  134. data/app/frontend/components/ui/dropdown-menu.tsx +0 -255
  135. data/app/frontend/components/ui/hover-card.tsx +0 -42
  136. data/app/frontend/components/ui/input.tsx +0 -21
  137. data/app/frontend/components/ui/label.tsx +0 -24
  138. data/app/frontend/components/ui/popover.tsx +0 -87
  139. data/app/frontend/components/ui/select.tsx +0 -185
  140. data/app/frontend/components/ui/separator.tsx +0 -28
  141. data/app/frontend/components/ui/sheet.tsx +0 -137
  142. data/app/frontend/components/ui/sidebar.tsx +0 -726
  143. data/app/frontend/components/ui/skeleton.tsx +0 -13
  144. data/app/frontend/components/ui/sonner.tsx +0 -41
  145. data/app/frontend/components/ui/table.tsx +0 -114
  146. data/app/frontend/components/ui/tabs.tsx +0 -91
  147. data/app/frontend/components/ui/textarea.tsx +0 -18
  148. data/app/frontend/components/ui/tooltip.tsx +0 -61
  149. data/app/frontend/components/user-info.tsx +0 -32
  150. data/app/frontend/components/user-menu-content.tsx +0 -94
  151. data/app/frontend/entrypoints/application.css +0 -244
  152. data/app/frontend/entrypoints/inertia.tsx +0 -92
  153. data/app/frontend/fonts/CommitMono-Bold.woff2 +0 -0
  154. data/app/frontend/fonts/CommitMono-Regular.woff2 +0 -0
  155. data/app/frontend/fonts/Roboto-Black.woff2 +0 -0
  156. data/app/frontend/fonts/Roboto-Bold.woff2 +0 -0
  157. data/app/frontend/fonts/Roboto-Medium.woff2 +0 -0
  158. data/app/frontend/fonts/Roboto-Regular.woff2 +0 -0
  159. data/app/frontend/hooks/use-appearance.tsx +0 -67
  160. data/app/frontend/hooks/use-clipboard.ts +0 -32
  161. data/app/frontend/hooks/use-flash.tsx +0 -23
  162. data/app/frontend/hooks/use-hotkeys.ts +0 -63
  163. data/app/frontend/hooks/use-initials.tsx +0 -15
  164. data/app/frontend/hooks/use-live.ts +0 -205
  165. data/app/frontend/hooks/use-mobile-navigation.ts +0 -8
  166. data/app/frontend/hooks/use-mobile.ts +0 -21
  167. data/app/frontend/hooks/use-percentile.ts +0 -24
  168. data/app/frontend/hooks/use-reduced-motion.ts +0 -25
  169. data/app/frontend/hooks/use-row-nav.ts +0 -106
  170. data/app/frontend/hooks/use-step.ts +0 -27
  171. data/app/frontend/hooks/use-ticker.ts +0 -12
  172. data/app/frontend/hooks/use-window.ts +0 -58
  173. data/app/frontend/layouts/app/app-sidebar-layout.tsx +0 -107
  174. data/app/frontend/layouts/app-layout.tsx +0 -21
  175. data/app/frontend/layouts/env-layout.tsx +0 -53
  176. data/app/frontend/layouts/persistent-layout.tsx +0 -18
  177. data/app/frontend/lib/cable.ts +0 -10
  178. data/app/frontend/lib/env-pages.ts +0 -236
  179. data/app/frontend/lib/execution-path.ts +0 -55
  180. data/app/frontend/lib/explain.ts +0 -50
  181. data/app/frontend/lib/filter.ts +0 -25
  182. data/app/frontend/lib/flamegraph.ts +0 -155
  183. data/app/frontend/lib/format.ts +0 -71
  184. data/app/frontend/lib/keyboard.ts +0 -18
  185. data/app/frontend/lib/mount.ts +0 -23
  186. data/app/frontend/lib/source-link.ts +0 -217
  187. data/app/frontend/lib/utils.ts +0 -6
  188. data/app/frontend/pages/alerts/index.tsx +0 -171
  189. data/app/frontend/pages/broadcasts/index.tsx +0 -171
  190. data/app/frontend/pages/cache_events/index.tsx +0 -127
  191. data/app/frontend/pages/commands/index.tsx +0 -143
  192. data/app/frontend/pages/deploys/index.tsx +0 -94
  193. data/app/frontend/pages/deploys/show.tsx +0 -309
  194. data/app/frontend/pages/deprecations/index.tsx +0 -176
  195. data/app/frontend/pages/exceptions/index.tsx +0 -271
  196. data/app/frontend/pages/executions/show.tsx +0 -821
  197. data/app/frontend/pages/issues/index.tsx +0 -399
  198. data/app/frontend/pages/issues/show.tsx +0 -1367
  199. data/app/frontend/pages/jobs/index.tsx +0 -381
  200. data/app/frontend/pages/jobs/klass.tsx +0 -215
  201. data/app/frontend/pages/llm_calls/index.tsx +0 -664
  202. data/app/frontend/pages/logs/index.tsx +0 -303
  203. data/app/frontend/pages/mails/index.tsx +0 -218
  204. data/app/frontend/pages/notifications/index.tsx +0 -161
  205. data/app/frontend/pages/outgoing_requests/index.tsx +0 -309
  206. data/app/frontend/pages/overview/show.tsx +0 -333
  207. data/app/frontend/pages/people/index.tsx +0 -102
  208. data/app/frontend/pages/people/show.tsx +0 -149
  209. data/app/frontend/pages/processes/index.tsx +0 -335
  210. data/app/frontend/pages/profiles/index.tsx +0 -110
  211. data/app/frontend/pages/profiles/show.tsx +0 -105
  212. data/app/frontend/pages/queries/index.tsx +0 -492
  213. data/app/frontend/pages/queries/show.tsx +0 -276
  214. data/app/frontend/pages/releases/index.tsx +0 -190
  215. data/app/frontend/pages/releases/release-health.tsx +0 -68
  216. data/app/frontend/pages/releases/release-path.ts +0 -25
  217. data/app/frontend/pages/releases/show.tsx +0 -262
  218. data/app/frontend/pages/requests/index.tsx +0 -235
  219. data/app/frontend/pages/requests/route.tsx +0 -159
  220. data/app/frontend/pages/scheduled_tasks/index.tsx +0 -220
  221. data/app/frontend/pages/spans/index.tsx +0 -229
  222. data/app/frontend/pages/spans/show.tsx +0 -220
  223. data/app/frontend/pages/storage_ops/index.tsx +0 -150
  224. data/app/frontend/pages/tenants/index.tsx +0 -236
  225. data/app/frontend/pages/tenants/show.tsx +0 -477
  226. data/app/frontend/pages/tenants/tenant-path.ts +0 -26
  227. data/app/frontend/pages/thresholds/index.tsx +0 -414
  228. data/app/frontend/pages/traces/show.tsx +0 -208
  229. data/app/frontend/pages/transactions/index.tsx +0 -220
  230. data/app/frontend/pages/view_renders/index.tsx +0 -241
  231. data/app/frontend/pages/visits/index.tsx +0 -321
  232. data/app/frontend/public/icon.png +0 -0
  233. data/app/frontend/public/icon.svg +0 -5
  234. data/app/frontend/routes/index.d.ts +0 -2136
  235. data/app/frontend/routes/index.js +0 -2112
  236. data/app/frontend/types/globals.d.ts +0 -9
  237. data/app/frontend/types/index.ts +0 -288
  238. data/app/frontend/types/vite-env.d.ts +0 -9
  239. data/app/helpers/railwatch/assets_helper.rb +0 -52
  240. data/app/jobs/railwatch/anomaly_scan_job.rb +0 -11
  241. data/app/jobs/railwatch/application_job.rb +0 -7
  242. data/app/jobs/railwatch/auto_resolve_issues_job.rb +0 -19
  243. data/app/jobs/railwatch/check_scheduled_tasks_job.rb +0 -113
  244. data/app/jobs/railwatch/detect_anomalies_job.rb +0 -171
  245. data/app/jobs/railwatch/detect_performance_issues_job.rb +0 -95
  246. data/app/jobs/railwatch/group_exceptions_job.rb +0 -91
  247. data/app/jobs/railwatch/optimize_telemetry_job.rb +0 -23
  248. data/app/jobs/railwatch/performance_scan_job.rb +0 -11
  249. data/app/jobs/railwatch/prune_telemetry_job.rb +0 -129
  250. data/app/jobs/railwatch/release_health_rollup_job.rb +0 -64
  251. data/app/jobs/railwatch/rollup_catchup_job.rb +0 -21
  252. data/app/jobs/railwatch/rollup_job.rb +0 -132
  253. data/app/jobs/railwatch/scheduled_task_scan_job.rb +0 -11
  254. data/app/models/concerns/railwatch/detection_snapshotting.rb +0 -20
  255. data/app/models/railwatch/alert.rb +0 -229
  256. data/app/models/railwatch/alert_rule.rb +0 -121
  257. data/app/models/railwatch/anomaly_rule.rb +0 -51
  258. data/app/models/railwatch/application.rb +0 -44
  259. data/app/models/railwatch/application_record.rb +0 -33
  260. data/app/models/railwatch/comment.rb +0 -36
  261. data/app/models/railwatch/deploy.rb +0 -67
  262. data/app/models/railwatch/environment.rb +0 -68
  263. data/app/models/railwatch/execution_presenter.rb +0 -185
  264. data/app/models/railwatch/filter_query.rb +0 -184
  265. data/app/models/railwatch/followup_receipt.rb +0 -27
  266. data/app/models/railwatch/ingest/batch.rb +0 -355
  267. data/app/models/railwatch/ingest/mapper.rb +0 -585
  268. data/app/models/railwatch/ingest/payload.rb +0 -96
  269. data/app/models/railwatch/ingest/rollup_absorber.rb +0 -170
  270. data/app/models/railwatch/ingest/writer.rb +0 -137
  271. data/app/models/railwatch/issue.rb +0 -277
  272. data/app/models/railwatch/issue_activity.rb +0 -23
  273. data/app/models/railwatch/issue_detection_presenter.rb +0 -309
  274. data/app/models/railwatch/issue_detection_snapshot.rb +0 -77
  275. data/app/models/railwatch/maintenance_task.rb +0 -53
  276. data/app/models/railwatch/saved_view.rb +0 -63
  277. data/app/models/railwatch/telemetry/aggregations.rb +0 -251
  278. data/app/models/railwatch/telemetry/attachment.rb +0 -31
  279. data/app/models/railwatch/telemetry/bounded_gzip.rb +0 -101
  280. data/app/models/railwatch/telemetry/broadcast.rb +0 -13
  281. data/app/models/railwatch/telemetry/cache_event.rb +0 -16
  282. data/app/models/railwatch/telemetry/child.rb +0 -53
  283. data/app/models/railwatch/telemetry/cursor_page.rb +0 -99
  284. data/app/models/railwatch/telemetry/deprecation.rb +0 -13
  285. data/app/models/railwatch/telemetry/enqueued_job.rb +0 -13
  286. data/app/models/railwatch/telemetry/exception.rb +0 -28
  287. data/app/models/railwatch/telemetry/execution.rb +0 -152
  288. data/app/models/railwatch/telemetry/export_delivery.rb +0 -32
  289. data/app/models/railwatch/telemetry/export_destination.rb +0 -66
  290. data/app/models/railwatch/telemetry/health_sample.rb +0 -72
  291. data/app/models/railwatch/telemetry/ingest_batch.rb +0 -31
  292. data/app/models/railwatch/telemetry/llm_call.rb +0 -37
  293. data/app/models/railwatch/telemetry/log.rb +0 -91
  294. data/app/models/railwatch/telemetry/mail.rb +0 -13
  295. data/app/models/railwatch/telemetry/n_plus_one.rb +0 -92
  296. data/app/models/railwatch/telemetry/notification.rb +0 -13
  297. data/app/models/railwatch/telemetry/outgoing_request.rb +0 -13
  298. data/app/models/railwatch/telemetry/person.rb +0 -50
  299. data/app/models/railwatch/telemetry/process.rb +0 -10
  300. data/app/models/railwatch/telemetry/profile.rb +0 -37
  301. data/app/models/railwatch/telemetry/query.rb +0 -57
  302. data/app/models/railwatch/telemetry/query_shape.rb +0 -45
  303. data/app/models/railwatch/telemetry/release_health.rb +0 -69
  304. data/app/models/railwatch/telemetry/rollup.rb +0 -157
  305. data/app/models/railwatch/telemetry/session.rb +0 -24
  306. data/app/models/railwatch/telemetry/span.rb +0 -19
  307. data/app/models/railwatch/telemetry/storage_op.rb +0 -13
  308. data/app/models/railwatch/telemetry/tenant.rb +0 -240
  309. data/app/models/railwatch/telemetry/transaction.rb +0 -13
  310. data/app/models/railwatch/telemetry/view_render.rb +0 -13
  311. data/app/models/railwatch/telemetry/visit.rb +0 -24
  312. data/app/models/railwatch/telemetry_record.rb +0 -107
  313. data/app/models/railwatch/threshold.rb +0 -66
  314. data/app/models/railwatch/user.rb +0 -47
  315. data/app/models/railwatch/viewer.rb +0 -13
  316. data/app/models/railwatch/window.rb +0 -66
  317. data/app/views/layouts/railwatch/dashboard.html.erb +0 -25
  318. data/db/railwatch_migrate/20260916000000_create_railwatch_tables.rb +0 -151
  319. data/db/railwatch_migrate/20260917000000_create_railwatch_maintenance_tasks.rb +0 -18
  320. data/db/railwatch_migrate/20260917120000_create_railwatch_followup_receipts.rb +0 -20
  321. data/db/railwatch_migrate/20260918120000_widen_host_user_ids.rb +0 -71
  322. data/db/railwatch_telemetry_migrate/20260903000000_enable_incremental_vacuum.rb +0 -62
  323. data/db/railwatch_telemetry_migrate/20260903000001_create_telemetry.rb +0 -481
  324. data/db/railwatch_telemetry_migrate/20260903000002_rename_tenant_to_app_tenant.rb +0 -14
  325. data/db/railwatch_telemetry_migrate/20260903000003_add_statement_count_to_transactions.rb +0 -9
  326. data/db/railwatch_telemetry_migrate/20260903000004_add_role_and_channel.rb +0 -10
  327. data/db/railwatch_telemetry_migrate/20260903000005_add_locals_to_exceptions.rb +0 -9
  328. data/db/railwatch_telemetry_migrate/20260903000006_add_spans_health_vitals_and_fts.rb +0 -82
  329. data/db/railwatch_telemetry_migrate/20260903000007_rename_span_attributes_to_payload.rb +0 -10
  330. data/db/railwatch_telemetry_migrate/20260903000008_add_profiles_and_attachments.rb +0 -60
  331. data/db/railwatch_telemetry_migrate/20260903000009_add_truncated_to_attachments.rb +0 -9
  332. data/db/railwatch_telemetry_migrate/20260903000010_create_sessions_and_release_health.rb +0 -51
  333. data/db/railwatch_telemetry_migrate/20260903000011_add_fingerprint_to_exceptions.rb +0 -11
  334. data/db/railwatch_telemetry_migrate/20260904000012_add_failed_to_broadcasts.rb +0 -10
  335. data/db/railwatch_telemetry_migrate/20260904010000_add_filter_cursor_indexes.rb +0 -37
  336. data/db/railwatch_telemetry_migrate/20260904020000_add_n_plus_ones_execution_id_index.rb +0 -11
  337. data/db/railwatch_telemetry_migrate/20260904120000_create_query_shapes.rb +0 -15
  338. data/db/railwatch_telemetry_migrate/20260906120000_add_backpressure_factor_to_ingest_batches.rb +0 -9
  339. data/db/railwatch_telemetry_migrate/20260907000000_rename_lantern_version_on_processes.rb +0 -10
  340. data/db/railwatch_telemetry_migrate/20260913000000_rename_nightrail_version_on_processes.rb +0 -9
  341. data/db/railwatch_telemetry_migrate/20260914000000_drop_orphan_durable_ingest_tables.rb +0 -18
  342. data/db/railwatch_telemetry_migrate/20260914010000_drop_orphan_durable_ingest_columns.rb +0 -25
  343. data/db/railwatch_telemetry_migrate/20260915000000_create_llm_calls.rb +0 -55
  344. data/db/railwatch_telemetry_migrate/20260915120000_add_detail_to_llm_calls.rb +0 -21
  345. data/db/railwatch_telemetry_migrate/20260915200000_add_explained_index_to_queries.rb +0 -16
  346. data/db/railwatch_telemetry_migrate/20260915210000_add_slowest_index_to_queries.rb +0 -18
  347. data/db/railwatch_telemetry_migrate/20260917010000_add_batch_ledger_to_ingest_batches.rb +0 -16
  348. data/db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb +0 -146
  349. data/db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb +0 -34
  350. data/db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb +0 -15
  351. data/db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb +0 -17
  352. data/db/railwatch_telemetry_migrate/20261008000000_add_in_transaction_to_outgoing_requests.rb +0 -10
  353. data/docs/embedded.md +0 -503
  354. data/lib/puma/plugin/railwatch.rb +0 -215
  355. data/lib/railwatch/dashboard_assets.rb +0 -45
  356. data/lib/railwatch/embedded.rb +0 -50
  357. data/lib/railwatch/export/client.rb +0 -124
  358. data/lib/railwatch/export/lease.rb +0 -56
  359. data/lib/railwatch/export/outbox.rb +0 -361
  360. data/lib/railwatch/export/policy.rb +0 -79
  361. data/lib/railwatch/export/sender.rb +0 -130
  362. data/lib/railwatch/ingest_request_body_limit.rb +0 -10
  363. data/lib/railwatch/maintenance.rb +0 -194
  364. data/lib/railwatch/patches/migration_busy_timeout.rb +0 -73
  365. data/lib/railwatch/transport/local.rb +0 -78
  366. data/lib/railwatch/transport/socket.rb +0 -183
  367. data/lib/railwatch/writer.rb +0 -370
  368. data/public/railwatch/assets/CommitMono-Bold-D6h61ieg.woff2 +0 -0
  369. data/public/railwatch/assets/CommitMono-Regular-zr8w7Obm.woff2 +0 -0
  370. data/public/railwatch/assets/Roboto-Black-auA4GeOK.woff2 +0 -0
  371. data/public/railwatch/assets/Roboto-Bold-CJLnO8j1.woff2 +0 -0
  372. data/public/railwatch/assets/Roboto-Medium-Cm2bwKpj.woff2 +0 -0
  373. data/public/railwatch/assets/Roboto-Regular-Chaq1-PV.woff2 +0 -0
  374. data/public/railwatch/assets/app-layout-by7G3sa1.js +0 -41
  375. data/public/railwatch/assets/application-Cc5v_Vk9.css +0 -1
  376. data/public/railwatch/assets/arrow-up-DCZ7emux.js +0 -1
  377. data/public/railwatch/assets/braces-CykT5_5u.js +0 -1
  378. data/public/railwatch/assets/card-C-PPSA0g.js +0 -1
  379. data/public/railwatch/assets/chart-Cov79YC5.js +0 -39
  380. data/public/railwatch/assets/chart-hover-DdK4E-2h.js +0 -1
  381. data/public/railwatch/assets/chart-panel-BPaYRXJB.js +0 -1
  382. data/public/railwatch/assets/code-BYjC4YZ_.js +0 -1
  383. data/public/railwatch/assets/copy-id-LPfoj9cC.js +0 -1
  384. data/public/railwatch/assets/cursor-load-more-DSdhgpGl.js +0 -1
  385. data/public/railwatch/assets/data-table-CQ_YRWQs.js +0 -1
  386. data/public/railwatch/assets/env-layout-CdMWbUkm.js +0 -1
  387. data/public/railwatch/assets/execution-path-7MYt-Qbn.js +0 -1
  388. data/public/railwatch/assets/filter-bar-CrzpQ_vD.js +0 -1
  389. data/public/railwatch/assets/flamegraph-BIFEdTAg.js +0 -2
  390. data/public/railwatch/assets/format-B9SDkrWj.js +0 -1
  391. data/public/railwatch/assets/frames-C5t-x4f0.js +0 -1
  392. data/public/railwatch/assets/index--NQ7us-L.js +0 -1
  393. data/public/railwatch/assets/index-2kgYe_99.js +0 -1
  394. data/public/railwatch/assets/index-4YEPHxtO.js +0 -1
  395. data/public/railwatch/assets/index-AOCC1GDJ.js +0 -1
  396. data/public/railwatch/assets/index-BEo-u0eO.js +0 -1
  397. data/public/railwatch/assets/index-BJ1V3w2N.js +0 -1
  398. data/public/railwatch/assets/index-BPoHsBKb.js +0 -1
  399. data/public/railwatch/assets/index-BZzyGUM3.js +0 -1
  400. data/public/railwatch/assets/index-BgVq7yKL.js +0 -1
  401. data/public/railwatch/assets/index-BqFGEZvW.js +0 -1
  402. data/public/railwatch/assets/index-ByuHJ1CH.js +0 -1
  403. data/public/railwatch/assets/index-C2j50AL2.js +0 -1
  404. data/public/railwatch/assets/index-CLEdTkvW.js +0 -1
  405. data/public/railwatch/assets/index-CWgq8qBu.js +0 -1
  406. data/public/railwatch/assets/index-CXQ7_oVj.js +0 -2
  407. data/public/railwatch/assets/index-CmyYrjS2.js +0 -1
  408. data/public/railwatch/assets/index-Cn9_HjDo.js +0 -1
  409. data/public/railwatch/assets/index-CsMAm9UK.js +0 -1
  410. data/public/railwatch/assets/index-D0Kk_oGY.js +0 -1
  411. data/public/railwatch/assets/index-DC9ZptJh.js +0 -1
  412. data/public/railwatch/assets/index-DNzg7jmV.js +0 -1
  413. data/public/railwatch/assets/index-DQ8uJO5f.js +0 -1
  414. data/public/railwatch/assets/index-Deq14BxB.js +0 -1
  415. data/public/railwatch/assets/index-IaV3Blzp.js +0 -1
  416. data/public/railwatch/assets/index-TYNWcJQs.js +0 -1
  417. data/public/railwatch/assets/index-WonH2fcZ.js +0 -1
  418. data/public/railwatch/assets/index-WxWUaH_d.js +0 -1
  419. data/public/railwatch/assets/index-mUNrl4ky.js +0 -1
  420. data/public/railwatch/assets/inertia-DfNRARuA.js +0 -97
  421. data/public/railwatch/assets/json-viewer-DMsF5glp.js +0 -1
  422. data/public/railwatch/assets/klass-CmHYk5rz.js +0 -1
  423. data/public/railwatch/assets/origin-identity-1R3bqa3R.js +0 -1
  424. data/public/railwatch/assets/percentile-picker-73z5PAps.js +0 -1
  425. data/public/railwatch/assets/relative-time-Dsc_cCdX.js +0 -1
  426. data/public/railwatch/assets/release-health-BV5OaOWd.js +0 -1
  427. data/public/railwatch/assets/route-CWh58C7K.js +0 -1
  428. data/public/railwatch/assets/segmented-DB4zh-Am.js +0 -1
  429. data/public/railwatch/assets/select-DBKjFlOX.js +0 -1
  430. data/public/railwatch/assets/series-chart-BaiDG795.js +0 -1
  431. data/public/railwatch/assets/show-B4ICCFGh.js +0 -1
  432. data/public/railwatch/assets/show-B5lOU9Gv.js +0 -1
  433. data/public/railwatch/assets/show-BYPB74t4.js +0 -2
  434. data/public/railwatch/assets/show-C7A0m2M8.js +0 -1
  435. data/public/railwatch/assets/show-DT5X1zqE.js +0 -1
  436. data/public/railwatch/assets/show-D_NBdXsn.js +0 -2
  437. data/public/railwatch/assets/show-DgPCnRg8.js +0 -1
  438. data/public/railwatch/assets/show-Yl69HF0x.js +0 -1
  439. data/public/railwatch/assets/show-_QxuUQCm.js +0 -3
  440. data/public/railwatch/assets/show-g__jnN36.js +0 -1
  441. data/public/railwatch/assets/show-l4ztfjl9.js +0 -1
  442. data/public/railwatch/assets/sort-header-BcG8ulEH.js +0 -1
  443. data/public/railwatch/assets/source-link-B2183i2-.js +0 -1
  444. data/public/railwatch/assets/sparkline-cell-B47ipscB.js +0 -1
  445. data/public/railwatch/assets/stat-DT2hhsqS.js +0 -1
  446. data/public/railwatch/assets/status-badge-CmbRKlY7.js +0 -1
  447. data/public/railwatch/assets/tenant-path-C4rNGauE.js +0 -1
  448. data/public/railwatch/assets/use-clipboard-oTNeP4ax.js +0 -1
  449. data/public/railwatch/assets/use-live-CLZZj245.js +0 -1
  450. data/public/railwatch/icon.png +0 -0
  451. data/public/railwatch/icon.svg +0 -5
  452. data/public/railwatch/manifest.json +0 -1245
  453. data/public/railwatch/rails-vite.json +0 -1
  454. /data/{app/frontend/lib → lib/generators/railwatch/install/templates}/railwatch.ts +0 -0
@@ -9,17 +9,15 @@ module Railwatch
9
9
  source_root File.expand_path("templates", __dir__)
10
10
 
11
11
  desc "Creates config/initializers/railwatch.rb, a Kamal post-deploy hook, the browser client, and wires the test helpers. " \
12
- "By default telemetry stays in this app, in two SQLite databases, with the dashboard at /railwatch. " \
13
- "--cloud (or any token or URL option) sends it to Railwatch Cloud instead."
12
+ "Telemetry goes to Railwatch Cloud, or to a self-hosted Railwatch server with --url. " \
13
+ "To run the server inside this app instead, add the railwatch-server gem."
14
14
 
15
- class_option :cloud, type: :boolean, default: false,
16
- desc: "Send telemetry to Railwatch Cloud instead of keeping it in this app. Implied by --prompt-token, --token-stdin, --url and --kamal-secrets."
17
15
  class_option :prompt_token, type: :boolean, default: false,
18
16
  desc: "Prompt for the ingest token without echoing it."
19
17
  class_option :token_stdin, type: :boolean, default: false,
20
18
  desc: "Read the ingest token from one line on standard input."
21
19
  class_option :url, type: :string,
22
- desc: "Ingest URL, for a self-hosted Railwatch Cloud. Defaults to https://railwatch.rebulk.com."
20
+ desc: "Ingest URL, for a self-hosted Railwatch server. Defaults to https://railwatch.rebulk.com."
23
21
  class_option :kamal_secrets, type: :boolean, default: false,
24
22
  desc: "Wire RAILWATCH_TOKEN through .kamal/secrets and config/deploy.yml's `env: secret:` list."
25
23
  class_option :doctor, type: :boolean, default: true,
@@ -59,59 +57,7 @@ module Railwatch
59
57
  append_to_file "Gemfile", "#{contents.end_with?("\n") ? "" : "\n"}\n" \
60
58
  "# #{Railwatch::JsonCompat::ISSUE}: Rails cannot decode with json 3 yet. Remove when it can.\n" \
61
59
  "#{Railwatch::JsonCompat::PIN}\n"
62
- @needs_bundle = true
63
- say "#{Railwatch::JsonCompat.advice} Added the pin to your Gemfile; run `bundle install`, then " \
64
- "`bin/rails db:prepare`.", :yellow
65
- end
66
-
67
- # Embedded mode stores telemetry in SQLite files whatever the app's own
68
- # database is, so an app on PostgreSQL or MySQL needs the adapter gem
69
- # added before those files can be created.
70
- def ensure_sqlite3_gem
71
- return unless local?
72
- return if Gem.loaded_specs.key?("sqlite3")
73
- return say("Embedded mode needs the sqlite3 gem for its two databases; add `gem \"sqlite3\"` and re-run.", :yellow) unless File.exist?("Gemfile")
74
-
75
- contents = File.read("Gemfile")
76
- unless contents.match?(/^\s*gem ["']sqlite3["']/)
77
- append_to_file "Gemfile", %(#{contents.end_with?("\n") ? "" : "\n"}\n# Railwatch (embedded) keeps its telemetry in two SQLite files of its own.\ngem "sqlite3"\n)
78
- end
79
- @needs_bundle = true
80
- say "Added `gem \"sqlite3\"` to the Gemfile: Railwatch's two databases are SQLite files whatever this app's " \
81
- "own database is. Run `bundle install`, then `bin/rails db:prepare` to create them.", :yellow
82
- end
83
-
84
-
85
- # Two databases of its own, never the app's primary: `railwatch` for
86
- # what people author (issues, comments, saved views, thresholds) and
87
- # `railwatch_telemetry` for what the app reports, which is written
88
- # continuously and pruned. Their migrations live in the gem; the
89
- # database entries point migrations_paths at them, so db:prepare
90
- # creates the tables now and migrates them after every gem update.
91
- # Nothing is copied into the app.
92
- def configure_local_databases
93
- return unless local?
94
-
95
- return say("No config/database.yml found; add railwatch and railwatch_telemetry databases yourself (docs/embedded.md).", :yellow) unless File.exist?("config/database.yml")
96
-
97
- contents = File.read("config/database.yml")
98
- updated = self.class.database_yml_with_railwatch(contents)
99
- return say_status(:identical, "config/database.yml", :blue) if updated == contents
100
-
101
- create_file "config/database.yml", updated, force: true
102
- end
103
-
104
- # The writer process: one per Puma master, forked by the gem's Puma
105
- # plugin, so batches are mapped and written outside the web workers.
106
- def configure_local_writer
107
- return unless local?
108
- return say("No config/puma.rb found; add `plugin :railwatch` to your Puma config yourself (docs/embedded.md).", :yellow) unless File.exist?("config/puma.rb")
109
-
110
- contents = File.read("config/puma.rb")
111
- updated = self.class.puma_rb_with_railwatch(contents)
112
- return say_status(:identical, "config/puma.rb", :blue) if updated == contents
113
-
114
- create_file "config/puma.rb", updated, force: true
60
+ say "#{Railwatch::JsonCompat.advice} Added the pin to your Gemfile; run `bundle install`.", :yellow
115
61
  end
116
62
 
117
63
  def create_kamal_hook
@@ -120,10 +66,9 @@ module Railwatch
120
66
  chmod ".kamal/hooks/post-deploy", 0o755
121
67
  end
122
68
 
123
- # The one browser client, the same file the gem's own dashboard bundle
124
- # is built from (Railwatch.browser_client_path). Copied rather than
125
- # templated: there is nothing to interpolate, and a copy means the host
126
- # sees exactly what the gem tests.
69
+ # The one browser client (Railwatch.browser_client_path). Copied rather
70
+ # than templated: there is nothing to interpolate, and a copy means the
71
+ # host sees exactly what the gem tests.
127
72
  def create_browser_client
128
73
  return unless File.directory?("app/frontend")
129
74
  copy_file Railwatch.browser_client_path.to_s, "app/frontend/lib/railwatch.ts"
@@ -167,8 +112,6 @@ module Railwatch
167
112
  # A token lands in .env only when Git confirms the file is ignored.
168
113
  # URLs are not secret and can still be written to a tracked dotenv file.
169
114
  def write_env
170
- return if local?
171
-
172
115
  token = resolved_token
173
116
  vars = { TOKEN_VAR => token, URL_VAR => options[:url] }.compact
174
117
  return if vars.empty?
@@ -215,58 +158,7 @@ module Railwatch
215
158
  create_file "config/deploy.yml", updated, force: true
216
159
  end
217
160
 
218
- # The two databases exist and are migrated when the generator returns:
219
- # config/database.yml was just rewritten, so this re-reads it and runs
220
- # the same prepare a deploy runs, for both databases only. The host's
221
- # own databases are not touched, and a schema file is never written.
222
- def prepare_local_databases
223
- return unless local?
224
- return unless File.exist?("config/database.yml")
225
- return if @needs_bundle
226
- return unless defined?(Rails) && Rails.respond_to?(:application) && Rails.application
227
-
228
- say "\nbin/rails db:prepare (railwatch, railwatch_telemetry)", :green
229
- require "active_record"
230
- ActiveRecord::Base.configurations = Rails.application.config.database_configuration
231
- %w[railwatch railwatch_telemetry].each do |name|
232
- db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: name)
233
- next say(" #{name}: not in config/database.yml for #{Rails.env}", :yellow) unless db_config
234
-
235
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do
236
- ActiveRecord::Tasks::DatabaseTasks.migrate
237
- end
238
- say_status :prepared, "#{name} (#{db_config.database})", :green
239
- end
240
- @prepared = true
241
- rescue StandardError => e
242
- say "Could not prepare the Railwatch databases here (#{e.class}: #{e.message}). Run `bin/rails db:prepare` yourself.", :yellow
243
- end
244
-
245
161
  def show_next_steps
246
- if local?
247
- say <<~STEPS, :green
248
-
249
- Next steps
250
- 1. Restart the app and open /railwatch. In development it is open.
251
- 2. Before production, give it a password (it is closed there until
252
- you do): RAILS_ENV=production bin/rails railwatch:authentication:configure
253
- Using your own admin auth instead? See docs/embedded.md,
254
- Authentication (base_controller_class or a routes constraint).
255
- 3. #{@prepared ? "Nothing else to run. Both databases were created just now and" : "Create the two databases: bin/rails db:prepare\n Then"}
256
- `bin/rails db:prepare` (which a deploy already runs) migrates
257
- them after every gem update. With `plugin :railwatch` in
258
- config/puma.rb Puma forks one Railwatch writer process that
259
- writes every batch and runs the maintenance clock, so no web
260
- process ever holds the telemetry database. No job worker.
261
- 4. Optional: mirror to Railwatch Cloud for alerts delivered even
262
- when this app is down, MCP for your AI assistant, and every
263
- app in one place. Get a token (bin/rails railwatch:token), set
264
- RAILWATCH_TOKEN, then c.export_enabled = true in the
265
- initializer (docs/embedded.md).
266
- STEPS
267
- return
268
- end
269
-
270
162
  say <<~STEPS, :green
271
163
 
272
164
  Next steps
@@ -281,6 +173,11 @@ module Railwatch
281
173
  2. Verify the install: bin/rails railwatch:doctor
282
174
  3. Connect your AI assistant: bin/rails railwatch:mcp
283
175
  4. Gate performance in CI: see docs/testing.md.
176
+
177
+ Want the dashboard inside this app instead, with no token? Add the
178
+ railwatch-server gem (gem "railwatch-server", then
179
+ `plugin :railwatch_server` in config/puma.rb):
180
+ https://github.com/Rebulk/railwatch-server
284
181
  STEPS
285
182
  end
286
183
 
@@ -290,10 +187,6 @@ module Railwatch
290
187
  # note below says so rather than letting a ✗ look like a broken install.
291
188
  def run_doctor
292
189
  return unless options[:doctor]
293
- # This process read its configuration before the initializer was
294
- # written, so in local mode the doctor would report an http transport
295
- # with no token. The databases it would check were prepared above.
296
- return if local?
297
190
  return unless defined?(Rails) && Rails.respond_to?(:application) && Rails.application
298
191
 
299
192
  say "\nbin/rails railwatch:doctor", :green
@@ -327,117 +220,6 @@ module Railwatch
327
220
  insert_lines(lines, block_end(lines, secret_start), " - #{name}\n")
328
221
  end
329
222
 
330
- # Spelled out rather than `<<: *default`, on purpose. The host's default
331
- # block carries ITS adapter: on a PostgreSQL or MySQL app inheriting it
332
- # would ask that server for a database called
333
- # "storage/production_railwatch.sqlite3". These two are always SQLite
334
- # files the app owns, whatever the app's own database is, so they name
335
- # their adapter themselves -- which also means the file needs no
336
- # `&default` anchor to exist at all.
337
- RAILWATCH_DATABASES = <<~YAML
338
- railwatch:
339
- adapter: sqlite3
340
- database: storage/%<env>s_railwatch.sqlite3
341
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %%>
342
- timeout: 5000
343
- migrations_paths: <%%= Railwatch.migrations_path(:railwatch) %%>
344
- schema_dump: false
345
- railwatch_telemetry:
346
- adapter: sqlite3
347
- database: storage/%<env>s_railwatch_telemetry.sqlite3
348
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %%>
349
- timeout: 5000
350
- migrations_paths: <%%= Railwatch.migrations_path(:railwatch_telemetry) %%>
351
- schema_dump: false
352
- pragmas:
353
- journal_mode: wal
354
- synchronous: normal
355
- mmap_size: 134217728
356
- cache_size: -65536
357
- temp_store: memory
358
- YAML
359
-
360
- # Adds the railwatch and railwatch_telemetry databases to every
361
- # environment in config/database.yml. A flat environment
362
- # (`development:` straight to `<<: *default`) becomes a `primary:`
363
- # entry first, since named databases need the nested form. Text
364
- # insertion rather than a YAML round trip, for the same reason as
365
- # deploy_yml_with_secret: the comments are most of the file.
366
- def self.database_yml_with_railwatch(contents)
367
- lines = contents.lines
368
- # Rails 8.1's non-Docker template leaves every production `database:`
369
- # commented out ("path/to/persistent/storage/..."), so db:prepare
370
- # cannot run in production at all until the host fills them in. The
371
- # storage/ paths are what the Docker template writes and what
372
- # config/deploy.yml mounts; use them.
373
- lines = lines.map do |line|
374
- line.sub(%r{\A(\s+)# database: path/to/persistent/storage/(\S+)$}, '\1database: storage/\2')
375
- end
376
- environments_in(lines).each do |env|
377
- start = lines.index { |line| line.match?(/\A#{env}:\s*(#.*)?$/) }
378
- next unless start
379
-
380
- stop = block_end(lines, start)
381
- block = lines[(start + 1)...stop]
382
- next if block.any? { |line| line.match?(/\A\s+railwatch_telemetry:\s*$/) }
383
-
384
- nested = block.any? { |line| line.match?(/\A [a-z_]+:\s*$/) }
385
- unless nested
386
- lines[(start + 1)...stop] = block.map { |line| line.strip.empty? ? line : " #{line}" }
387
- lines.insert(start + 1, " primary:\n")
388
- stop += 1
389
- end
390
- entries = format(RAILWATCH_DATABASES, env: database_prefix(env)).lines.map { |line| " #{line}" }
391
- lines = insert_lines(lines, stop, entries.join).lines
392
- end
393
- lines.join
394
- end
395
-
396
- # What the database filenames are built from. For test that has to carry
397
- # TEST_ENV_NUMBER, the way Rails' own test database naming does: runners
398
- # like parallel_tests give each worker its own number and expect a
399
- # database per worker. Without it every worker in a run opens the same
400
- # two SQLite files and they fight over them -- observed as
401
- # PendingMigrationError and "disk I/O error" from configure_connection,
402
- # on an app running four workers. An app with no such runner sets no
403
- # TEST_ENV_NUMBER, so the suffix is empty and the name is unchanged.
404
- def self.database_prefix(env)
405
- return env unless env == "test"
406
-
407
- %(test<%= ENV["TEST_ENV_NUMBER"] %>)
408
- end
409
-
410
- # Unconditional: `bundle exec puma` evaluates this file before it loads
411
- # the Rack app, so `if defined?(Railwatch)` would be false there and the
412
- # writer would never start. The plugin itself is inert when Railwatch is
413
- # absent, off, or not in embedded mode.
414
- PUMA_PLUGIN_LINES = <<~RUBY
415
-
416
- # Railwatch (embedded): fork one writer process from Puma so telemetry
417
- # is mapped and written outside the processes serving requests.
418
- plugin :railwatch
419
- RUBY
420
-
421
- # Appends the plugin line once. Puma's config is plain Ruby evaluated top
422
- # to bottom, so the end of the file is always a valid place for it.
423
- def self.puma_rb_with_railwatch(contents)
424
- return contents if contents.include?("plugin :railwatch")
425
-
426
- "#{contents.sub(/\n*\z/, "\n")}#{PUMA_PLUGIN_LINES}"
427
- end
428
-
429
- # Every environment the file defines, not a fixed three: an app with a
430
- # `staging` (or `review`, or `qa`) environment needs the databases
431
- # there too, and hardcoding names silently left it without them. A
432
- # top-level key with a block under it, minus YAML's own anchors.
433
- NON_ENVIRONMENT_KEYS = %w[default shared].freeze
434
-
435
- def self.environments_in(lines)
436
- lines.filter_map { |line| line[/\A([a-z_][a-z0-9_]*):\s*(?:&\S+\s*)?(?:#.*)?$/, 1] }
437
- .reject { |name| NON_ENVIRONMENT_KEYS.include?(name) }
438
- .uniq
439
- end
440
-
441
223
  # Index of the first line after the block opened at `start`: the next
442
224
  # line indented no more deeply than it, ignoring blanks and comments,
443
225
  # then backed up over any trailing blank lines so an insertion lands
@@ -461,16 +243,6 @@ module Railwatch
461
243
 
462
244
  private
463
245
 
464
- # Embedded unless the invocation asks for the cloud: --cloud itself, or
465
- # an option that only means something there. A RAILWATCH_TOKEN already
466
- # in the environment is not asking -- it is picked up when one of these
467
- # is given, never used to choose the mode.
468
- CLOUD_OPTIONS = %i[cloud prompt_token token_stdin url kamal_secrets].freeze
469
-
470
- def local?
471
- CLOUD_OPTIONS.none? { |name| options[name] }
472
- end
473
-
474
246
  def resolved_token
475
247
  @resolved_token ||= begin
476
248
  value = if options[:prompt_token]
@@ -3,47 +3,8 @@
3
3
  # Railwatch: first-class monitoring for Rails. Every option here can also be
4
4
  # set by the RAILWATCH_* env var named in the comment.
5
5
  Railwatch.configure do |c|
6
- <% if local? -%>
7
- # Telemetry stays in this app's own railwatch_telemetry database and the
8
- # dashboard is served at /railwatch. No token, no cloud. Who can open the
9
- # dashboard is the Access block below.
10
- c.transport = :local # RAILWATCH_TRANSPORT
11
- c.ignored_request_paths += ["/railwatch", %r{\A/railwatch/}]
12
- # c.issue_prefix = "APP" # RAILWATCH_ISSUE_PREFIX; issue keys like APP-12
13
- # c.repository_url = "https://github.com/you/app" # RAILWATCH_REPOSITORY_URL; source links from stack traces
14
- # c.retention_days = 7 # RAILWATCH_RETENTION_DAYS; PruneTelemetryJob keeps this much
15
- # Access. The dashboard shows every query, log line and exception this app
16
- # records, so pick one of these. Out of the box it is HTTP Basic: open in
17
- # development, closed everywhere else until credentials exist:
18
- # RAILS_ENV=production bin/rails railwatch:authentication:configure
19
- # or RAILWATCH_HTTP_BASIC_AUTH_USER / _PASSWORD.
20
- #
21
- # Using your own admin auth instead? Turn Basic off and say which, so the
22
- # gem knows the mount is covered and live updates can follow your rule:
23
- # c.http_basic_auth_enabled = false # RAILWATCH_HTTP_BASIC_AUTH_ENABLED
24
- # c.base_controller_class = "AdminController" # every dashboard page inherits it
25
- # c.dashboard_user = ->(request) { ... } # or decide per request, below
26
- #
27
- # Genuinely public (a private network, a VPN, a constraint the gem cannot
28
- # see)? Say so on purpose. Nothing else makes the dashboard public, and
29
- # without one of the above the app logs a warning at every boot:
30
- # c.dashboard_open = false # RAILWATCH_DASHBOARD_OPEN
31
- # Who the dashboard names on comments and saved views. Returning nil also
32
- # refuses the request, so this doubles as your authorisation rule:
33
- # c.dashboard_user = ->(request) { user = Current.user; user&.admin? && { id: user.id, name: user.name, email: user.email } }
34
-
35
- # The browser beacon (Inertia visit timings and JavaScript errors) is a
36
- # public endpoint, like every browser telemetry endpoint: it is bounded by
37
- # an origin allowlist, a per-client rate limit and a ceiling for the whole
38
- # endpoint, not by a credential.
39
- # c.beacon_enabled = false # RAILWATCH_BEACON
40
- # c.beacon_allowed_origins = ["https://app.example.com"] # beyond this app's own
41
- # c.beacon_rate_limit = 120 # per client IP per minute
42
- # c.beacon_global_rate_limit = 6_000 # for the endpoint, per minute
43
- <% else -%>
44
6
  # c.token = ENV["RAILWATCH_TOKEN"] # RAILWATCH_TOKEN (required)
45
- <% end -%>
46
- # c.ingest_url = "https://railwatch.rebulk.com" # RAILWATCH_INGEST_URL
7
+ # c.ingest_url = "https://railwatch.rebulk.com" # RAILWATCH_INGEST_URL; your own server when self-hosting
47
8
  # c.deploy = "release-name" # RAILWATCH_DEPLOY; platform/Git auto-detected
48
9
  # c.detect_deploy = false # RAILWATCH_DETECT_DEPLOY; default true
49
10
 
@@ -75,6 +36,15 @@ Railwatch.configure do |c|
75
36
  # c.redact_params += %w[ssn] # merged with Rails.application.config.filter_parameters
76
37
  # c.ignored_request_paths += ["/internal/health"] # /up and /railwatch/beacon are ignored by default
77
38
 
39
+ # The browser beacon (Inertia visit timings and JavaScript errors) is a
40
+ # public endpoint, like every browser telemetry endpoint: it is bounded by
41
+ # an origin allowlist, a per-client rate limit and a ceiling for the whole
42
+ # endpoint, not by a credential.
43
+ # c.beacon_enabled = false # RAILWATCH_BEACON
44
+ # c.beacon_allowed_origins = ["https://app.example.com"] # beyond this app's own
45
+ # c.beacon_rate_limit = 120 # per client IP per minute
46
+ # c.beacon_global_rate_limit = 6_000 # for the endpoint, per minute
47
+
78
48
  # How the current user is described. Default reads Current.user then Warden.
79
49
  # c.user { |user| { id: user.id, name: user.name, email: user.email } }
80
50
  end
@@ -9,13 +9,6 @@
9
9
  # ingest host instead of shelling into a container. Never fails a deploy.
10
10
  set -e
11
11
 
12
- # Embedded install (transport = :local): the marker is a row in the app's own
13
- # railwatch database, so record it inside the deployed container and stop.
14
- if [ "${RAILWATCH_TRANSPORT:-}" = "local" ]; then
15
- bin/kamal app exec --primary --reuse "bin/rails railwatch:deploy[$KAMAL_VERSION]" || true
16
- exit 0
17
- fi
18
-
19
12
  [ -z "$RAILWATCH_TOKEN" ] && exit 0
20
13
 
21
14
  # Optional: .kamal/hooks/post-deploy --sourcemaps, or set
@@ -3,11 +3,12 @@
3
3
  require "yaml"
4
4
 
5
5
  module Railwatch
6
- # `bin/rails railwatch:authentication:configure`: stores the embedded
7
- # dashboard's HTTP Basic credentials in the current environment's Rails
8
- # credentials, the same way Mission Control Jobs' authentication:configure
9
- # does. Asks for a user, generates the password, appends a `railwatch:`
10
- # entry to the credentials file the environment reads.
6
+ # `bin/rails railwatch:authentication:configure`: stores the HTTP Basic
7
+ # credentials railwatch-server's dashboard proxy checks in the current
8
+ # environment's Rails credentials, the same way Mission Control Jobs'
9
+ # authentication:configure does. Asks for a user, generates the password,
10
+ # appends a `railwatch:` entry to the credentials file the environment
11
+ # reads.
11
12
  module Authentication
12
13
  module_function
13
14
 
@@ -20,6 +20,10 @@ module Railwatch
20
20
  @bytes = 0
21
21
  @dropped = 0
22
22
  @dropped_bytes = 0
23
+ # Every record this queue has ever had to drop for room. Unlike
24
+ # @dropped it is never drained, so a caller can difference it across a
25
+ # span of time.
26
+ @overflowed = 0
23
27
  @mutex = Mutex.new
24
28
  end
25
29
 
@@ -58,6 +62,10 @@ module Railwatch
58
62
  @mutex.synchronize { @bytes }
59
63
  end
60
64
 
65
+ def overflowed
66
+ @mutex.synchronize { @overflowed }
67
+ end
68
+
61
69
  def stats
62
70
  @mutex.synchronize { [ @records.size, @dropped, @bytes, @dropped_bytes ] }
63
71
  end
@@ -110,6 +118,7 @@ module Railwatch
110
118
  def drop(bytes)
111
119
  @dropped += 1
112
120
  @dropped_bytes += bytes
121
+ @overflowed += 1
113
122
  end
114
123
 
115
124
  def drop_oldest