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
data/docs/faq.md CHANGED
@@ -51,26 +51,25 @@ A second gate, `bench/no_db_writes.rb`, drives 200 requests and a job with
51
51
  a `sql.active_record` subscriber watching for any `INSERT`/`UPDATE`/
52
52
  `DELETE` issued from a frame inside `lib/railwatch`, and fails if it finds
53
53
  one. **Railwatch never writes to your application's database.** Records
54
- live in memory and are delivered by a background thread, to the cloud or
55
- to the embedded writer process, which owns its own two SQLite files. That
54
+ live in memory and are delivered by a background thread over HTTP to the
55
+ Railwatch server, which owns its own storage. That
56
56
  is not a nicety: instrumentation that takes a write lock is what turns a
57
57
  single-writer SQLite app into a "database is locked" incident.
58
58
 
59
59
  ## Where does the data go, and how long is it kept?
60
60
 
61
- Embedded (the default): into the app's own `railwatch_telemetry` SQLite
62
- database under `storage/`, written by one writer process Puma forks.
63
- Raw rows are pruned nightly to `retention_days` (7 by default), and
64
- hourly rollups back the charts. Nothing leaves the machine unless you
65
- turn on export. See [Embedded mode](embedded.md).
61
+ One gzip-NDJSON POST to `{ingest_url}/ingest` per batch, to Railwatch
62
+ Cloud, a self-hosted railwatch-rs, or the server the `railwatch-server`
63
+ gem runs next to the app. The gem keeps nothing on disk itself.
66
64
 
67
- Railwatch Cloud: one gzip-NDJSON POST to `{ingest_url}/ingest` per
68
- batch. The platform stores each monitored environment's telemetry in its
65
+ Railwatch Cloud stores each monitored environment's telemetry in its
69
66
  own database, prunes raw rows on a retention window, and keeps hourly
70
67
  rollups for the charts. Retention there is set by the account's plan
71
- tier, not by the gem — 7, 30, or 90 days depending on the plan. For a
72
- self-hosted platform, retention, backups, and pruning are the operator's
73
- responsibility.
68
+ tier, not by the gem — 7, 30, or 90 days depending on the plan. For
69
+ railwatch-rs, self-hosted or through railwatch-server, retention and
70
+ backups are server settings
71
+ ([railwatch-rs SELF-HOST.md](https://github.com/Rebulk/railwatch-rs/blob/main/docs/SELF-HOST.md),
72
+ [railwatch-server README](https://github.com/Rebulk/railwatch-server#readme)).
74
73
 
75
74
  ## What about PII?
76
75
 
@@ -125,15 +124,13 @@ Yes, on both sides, and it's the first-class target.
125
124
 
126
125
  **In your app:** the gem does no I/O on the request path and never writes
127
126
  to the app database, so there is no contention with SQLite's single
128
- writer. Embedded mode's own two databases are separate SQLite files,
129
- whatever the app runs on. SQL normalization is per adapter, so SQLite,
127
+ writer. SQL normalization is per adapter, so SQLite,
130
128
  Postgres, MySQL, and Trilogy all group correctly.
131
129
 
132
- **On the platform:** telemetry is stored one SQLite database per
133
- monitored environment. That is what makes retention pruning, backup, and
134
- restore per-environment operations rather than one enormous table, and
135
- it's why log search gets FTS5 with snippet highlighting. The platform
136
- runs unchanged on Postgres if that's what's configured.
130
+ **On the server:** telemetry is stored in SQLite per monitored
131
+ environment. That is what makes retention pruning, backup, and restore
132
+ per-environment operations rather than one enormous table, and it's why
133
+ log search gets FTS5 with snippet highlighting.
137
134
 
138
135
  ## One database per environment — what does that mean for me?
139
136
 
@@ -168,12 +165,7 @@ Two features are Solid Queue-specific, because they read its tables:
168
165
  with `task_key`, `schedule`, and `drift`) and the queue depth and
169
166
  oldest-job age on `health` records.
170
167
 
171
- ## What happens when the platform is unreachable?
172
-
173
- This section is about the cloud transport. In embedded mode the
174
- equivalent is the writer process being down, covered in
175
- [Embedded mode](embedded.md#the-writer-process), and export keeps its
176
- own durable queue.
168
+ ## What happens when the server is unreachable?
177
169
 
178
170
  Nothing, from your app's point of view. This is the property everything
179
171
  else is built around: **delivery never raises into application code.**
@@ -1,8 +1,19 @@
1
1
  # Getting started
2
2
 
3
- Two commands on a Rails 8 app, then a dashboard at `/railwatch`.
4
- Everything below is the gem's own generator and rake tasks; nothing else
5
- has to be wired by hand.
3
+ Two commands on a Rails 8 app, a token, and the app reports to
4
+ Railwatch. Everything below is the gem's own generator and rake tasks;
5
+ nothing else has to be wired by hand.
6
+
7
+ The `railwatch` gem is the agent: it instruments the app and sends what
8
+ it records over HTTPS to a Railwatch server. The server is one of:
9
+
10
+ - **Railwatch Cloud** at <https://railwatch.rebulk.com>, the default.
11
+ - **Your own [railwatch-rs](https://github.com/Rebulk/railwatch-rs)**,
12
+ one static binary that serves many apps
13
+ ([Self-hosting](self-hosting.md)).
14
+ - **The [`railwatch-server`](https://github.com/Rebulk/railwatch-server)
15
+ gem**, which runs railwatch-rs next to Puma for this one app and serves
16
+ the dashboard at `/railwatch` ([below](#embedded-railwatch-server)).
6
17
 
7
18
  ## Install
8
19
 
@@ -14,89 +25,6 @@ bin/rails generate railwatch:install
14
25
  The gem, its Ruby namespace, and its require path share one name:
15
26
  `railwatch`, `Railwatch::*`, `require "railwatch"`.
16
27
 
17
- With no flags the install is [embedded](embedded.md): telemetry stays in
18
- two SQLite databases the app owns, Puma forks one writer process for
19
- them (`plugin :railwatch` in `config/puma.rb`), and the dashboard is
20
- served from the app. No token, no cloud, no job worker. The generator
21
- creates and migrates both databases before it returns. On a PostgreSQL
22
- or MySQL app without the `sqlite3` gem it adds the gem instead; run
23
- `bundle install` and `bin/rails db:prepare` to finish.
24
-
25
- Restart the app and open `/railwatch`. In development it is open; every
26
- other environment answers 401 until you give it a password:
27
-
28
- ```sh
29
- RAILS_ENV=production bin/rails railwatch:authentication:configure
30
- ```
31
-
32
- [Embedded mode](embedded.md#authentication) covers using your own
33
- admin authentication instead.
34
-
35
- ### What the generator writes
36
-
37
- In every mode:
38
-
39
- - `config/initializers/railwatch.rb`, with every option commented out at
40
- its default (embedded: `c.transport = :local`).
41
- - `mount Railwatch::Engine, at: "/railwatch"` in `config/routes.rb`: the
42
- embedded dashboard and the beacon endpoint the browser client posts to.
43
- - `.kamal/hooks/post-deploy` — only if `config/deploy.yml` already
44
- exists.
45
- - `app/frontend/lib/railwatch.ts` plus the `startRailwatch()` call in your
46
- Inertia entrypoint — only if `app/frontend/` exists. If it can't find
47
- an entrypoint it prints the two lines to add.
48
- - `require "railwatch/rspec"` in `spec/rails_helper.rb`, or
49
- `require "railwatch/minitest"` in `test/test_helper.rb`.
50
-
51
- Embedded adds the two databases to `config/database.yml` and the Puma
52
- plugin line; see [Embedded mode](embedded.md#your-applications-own-database).
53
-
54
- ### Check the wiring
55
-
56
- ```sh
57
- bin/rails railwatch:doctor
58
- ```
59
-
60
- With the app running, the embedded checklist starts like this:
61
-
62
- ```
63
- ✓ transport: local (telemetry stays in this app; dashboard at the engine mount)
64
- ✓ railwatch database: storage/development_railwatch.sqlite3
65
- ✓ railwatch_telemetry database: storage/development_railwatch_telemetry.sqlite3
66
- ✓ railwatch_telemetry migrations: up to date
67
- ✓ railwatch migrations: up to date
68
- ✓ telemetry disk: auto_vacuum=incremental; the nightly prune returns freed pages
69
- ✓ writer process: listening at /app/tmp/sockets/railwatch-writer.sock
70
- ✓ last write: 12 seconds ago
71
- ✓ dashboard access: open in development. Production stays closed until you run `RAILS_ENV=production bin/rails railwatch:authentication:configure`
72
- ✓ request middleware: Railwatch::Middleware::Request at position 0
73
- ✓ engine mounted: POST /railwatch/beacon -> railwatch/beacon#create
74
- ```
75
-
76
- With the app stopped, `writer process` and `maintenance` are `✗`, and say
77
- so. Every line and what to do about a `✗` is in
78
- [`troubleshooting.md`](troubleshooting.md).
79
-
80
- ### Adding Railwatch Cloud
81
-
82
- Railwatch Cloud delivers alerts to Slack, email, webhooks or Linear
83
- (embedded mode only records them), serves the [MCP server](ai-and-mcp.md)
84
- to your AI assistant, and puts many apps and servers in one place. An
85
- embedded install can mirror every record to it and keep `/railwatch`:
86
- set a token ([below](#where-the-token-comes-from)) and
87
- `c.export_enabled = true`. See
88
- [Embedded mode](embedded.md#three-ways-to-run-it).
89
-
90
- ## Railwatch Cloud instead
91
-
92
- To send telemetry only to Railwatch Cloud, with no local databases, run
93
- the same generator with `--cloud`:
94
-
95
- ```sh
96
- bundle add railwatch
97
- bin/rails generate railwatch:install --cloud
98
- ```
99
-
100
28
  With the token already in hand, let the generator read it without placing the
101
29
  secret in shell history or process arguments:
102
30
 
@@ -109,25 +37,34 @@ bin/rails generate railwatch:install \
109
37
 
110
38
  - `--prompt-token` reads without echo. `--token-stdin` is available for a
111
39
  secret-manager pipe; an already exported `RAILWATCH_TOKEN` is also detected.
112
- - Any of `--prompt-token`, `--token-stdin`, `--url=` and `--kamal-secrets`
113
- means the cloud, so none of them needs `--cloud` as well. An exported
114
- `RAILWATCH_TOKEN` on its own does not: without one of these flags the
115
- install is embedded.
116
40
  - A token is written to `.env` only when Git confirms that `.env` is ignored.
117
41
  A tracked or non-ignored dotenv file is refused; use Rails credentials, a
118
42
  deployment secret manager, or add `.env` to `.gitignore` first. Token values
119
43
  are never printed by the generator.
120
- - `--url=` sets `RAILWATCH_INGEST_URL`, for a self-hosted platform. Leave
44
+ - `--url=` sets `RAILWATCH_INGEST_URL`, for a self-hosted server. Leave
121
45
  it off to use the default, `https://railwatch.rebulk.com`.
122
46
  - `--kamal-secrets` appends `RAILWATCH_TOKEN=$RAILWATCH_TOKEN` to
123
47
  `.kamal/secrets` and adds `RAILWATCH_TOKEN` under `env: secret:` in
124
48
  `config/deploy.yml`, which is the pair of edits Kamal needs to pass a
125
49
  secret through to the containers.
50
+ - `--no-doctor` skips the `railwatch:doctor` run at the end.
126
51
 
127
- It writes the same files as the embedded install
128
- ([above](#what-the-generator-writes)) minus the databases and the Puma
129
- plugin, and finishes by running `railwatch:doctor`, so the install either
130
- ends in a clean checklist or tells you what is still missing.
52
+ ### What the generator writes
53
+
54
+ - `config/initializers/railwatch.rb`, with every option commented out at
55
+ its default.
56
+ - `mount Railwatch::Engine, at: "/railwatch"` in `config/routes.rb`: the
57
+ beacon endpoint the browser client posts to (`POST /railwatch/beacon`).
58
+ - `.kamal/hooks/post-deploy` — only if `config/deploy.yml` already
59
+ exists.
60
+ - `app/frontend/lib/railwatch.ts` plus the `startRailwatch()` call in your
61
+ Inertia entrypoint — only if `app/frontend/` exists. If it can't find
62
+ an entrypoint it prints the two lines to add.
63
+ - `require "railwatch/rspec"` in `spec/rails_helper.rb`, or
64
+ `require "railwatch/minitest"` in `test/test_helper.rb`.
65
+
66
+ It finishes by running `railwatch:doctor`, so the install either ends in
67
+ a clean checklist or tells you what is still missing.
131
68
 
132
69
  ### Where the token comes from
133
70
 
@@ -135,18 +72,18 @@ In Railwatch Cloud, create an application, then an environment inside it
135
72
  (`production`, `staging`, one token each). The token is shown once, on
136
73
  the page you land on right after creating the environment — copy it
137
74
  then. If you lose it, rotate it from the environment's settings and
138
- update `RAILWATCH_TOKEN`.
75
+ update `RAILWATCH_TOKEN`. A self-hosted railwatch-rs creates tokens the
76
+ same way (or with `railwatch env create`; see its docs).
139
77
 
140
78
  ```sh
141
79
  bin/rails railwatch:token # prints the URL to create/copy a token
142
80
  ```
143
81
 
144
- A token looks like `rw_` followed by 40 characters. A cloud install is
145
- inert without one: with `transport = :http`, `Railwatch.enabled?` is
146
- `config.enabled && token.present?`, so an app with no token installs no
147
- subscribers and ships nothing.
82
+ A token looks like `rw_` followed by 40 characters. The gem is inert
83
+ without one: `Railwatch.enabled?` is `config.enabled && token.present?`,
84
+ so an app with no token installs no subscribers and ships nothing.
148
85
 
149
- ### Check the cloud wiring
86
+ ### Check the wiring
150
87
 
151
88
  ```sh
152
89
  bin/rails railwatch:doctor
@@ -169,11 +106,67 @@ It exits non-zero on three lines: `token` (missing), `token storage`
169
106
  (a plaintext token in a tracked file), and `ingest reachable`. The rest
170
107
  of the checklist is informational. Run it after the token is in place:
171
108
  `ingest reachable` sends the token, so a missing or wrong token makes
172
- the platform answer 401 and that line is `✗` as well.
109
+ the server answer 401 and that line is `✗` as well. Every line and what
110
+ to do about a `✗` is in [`troubleshooting.md`](troubleshooting.md).
173
111
 
174
112
  `bin/rails railwatch:status` is the one-line version: it pings
175
113
  `{ingest_url}/ingest/ping` and prints the ingest URL, deploy, and server.
176
114
 
115
+ ## Embedded: railwatch-server
116
+
117
+ To keep telemetry inside the app instead, add the
118
+ [`railwatch-server`](https://github.com/Rebulk/railwatch-server) gem
119
+ next to this one. It bundles the railwatch-rs binary for your platform,
120
+ runs it as its own process beside Puma, and points this agent at it:
121
+
122
+ ```ruby
123
+ # Gemfile
124
+ gem "railwatch"
125
+ gem "railwatch-server"
126
+ ```
127
+
128
+ ```ruby
129
+ # config/puma.rb
130
+ plugin :railwatch_server
131
+ ```
132
+
133
+ On first boot it provisions `storage/railwatch/<env>` and sets the
134
+ agent's token and ingest URL itself, so no `RAILWATCH_TOKEN` is needed.
135
+ When `RAILWATCH_TOKEN` is set, it leaves the agent alone and only serves
136
+ the dashboard. The dashboard at `/railwatch` is behind this gem's gate
137
+ settings: HTTP Basic, on and closed (401) until credentials exist, except
138
+ in development when none are configured:
139
+
140
+ ```sh
141
+ RAILS_ENV=production bin/rails railwatch:authentication:configure
142
+ ```
143
+
144
+ To use your own admin authentication instead, turn Basic off and name a
145
+ base controller ([Configuration](configuration.md#dashboard-gate-railwatch-server)).
146
+ Everything else about it (ports, data directory, email, backups) is in
147
+ the [railwatch-server README](https://github.com/Rebulk/railwatch-server#readme).
148
+
149
+ ### Coming from 0.x embedded mode
150
+
151
+ Railwatch 0.x could keep telemetry in two SQLite databases of the app's
152
+ own, written by a Puma writer process, with the dashboard served from the
153
+ gem. 1.0 removes that mode. To move:
154
+
155
+ 1. Remove `c.transport = :local` (or `RAILWATCH_TRANSPORT=local`) from
156
+ the initializer and environment.
157
+ 2. Remove `plugin :railwatch` from `config/puma.rb`. Puma refuses to boot
158
+ with a plugin it cannot find.
159
+ 3. Remove the `railwatch` and `railwatch_telemetry` entries from
160
+ `config/database.yml`.
161
+ 4. Add `railwatch-server` as above, or point the app at Railwatch Cloud
162
+ or a self-hosted railwatch-rs.
163
+
164
+ The old files (`storage/<env>_railwatch.sqlite3`,
165
+ `storage/<env>_railwatch_telemetry.sqlite3`) are not migrated, and
166
+ railwatch-rs has no import for them: its `railwatch import-rails` reads a
167
+ Railwatch Cloud (Rails) install, not these files. History starts again
168
+ in the new server. Delete the files once you no longer want them.
169
+
177
170
  ## Make one request
178
171
 
179
172
  ```sh
@@ -183,8 +176,9 @@ curl http://localhost:3000/
183
176
 
184
177
  Records are batched in-process and flushed every `flush_interval`
185
178
  (2 seconds by default) or every 500 records, whichever comes first, so
186
- the request shows up on the **Requests** page (`/railwatch`, or the
187
- environment in Railwatch Cloud) a couple of seconds after you make it —
179
+ the request shows up on the **Requests** page (the environment in
180
+ Railwatch Cloud or your own server, or `/railwatch` with
181
+ railwatch-server) a couple of seconds after you make it —
188
182
  with its queries, cache reads, view renders, and log lines already
189
183
  attached to it.
190
184
 
@@ -266,14 +260,11 @@ up, on the account.
266
260
 
267
261
  ## Deploying
268
262
 
269
- An embedded install deploys like the rest of the app: the two databases
270
- are files under `storage/`, which the Rails 8 Kamal template already
271
- mounts as a volume, and the Rails 8 Docker entrypoint's
272
- `bin/rails db:prepare` migrates them. Give the
273
- dashboard a password first ([Install](#install)). For deploy markers
274
- from the Kamal hook, set `RAILWATCH_TRANSPORT=local` in the deployer's
275
- environment ([Embedded mode](embedded.md#deploys)). The token steps
276
- below are for a cloud install or export.
263
+ With railwatch-server, the server's data lives under
264
+ `storage/railwatch/<env>`, which has to be on a volume that survives
265
+ deploys (the Rails 8 Kamal template mounts `storage/`); its README covers
266
+ deploys and two containers sharing a volume. The token steps below are
267
+ for Railwatch Cloud or a self-hosted server.
277
268
 
278
269
  ### Kamal
279
270
 
@@ -352,12 +343,9 @@ one — the deploy is still recorded.
352
343
  `sentry-rails`.
353
344
  - [`replacing-nightwatch.md`](replacing-nightwatch.md) — for people
354
345
  coming from Laravel.
355
- - [`embedded.md`](embedded.md) — authentication, the writer process,
356
- maintenance, disk, and export to the cloud.
357
346
  - [`self-hosting.md`](self-hosting.md) — pointing the gem at your own
358
- platform install.
347
+ railwatch-rs server.
359
348
  - [`troubleshooting.md`](troubleshooting.md) — every `railwatch:doctor`
360
349
  line and what a failure means.
361
350
  - [`faq.md`](faq.md) — overhead, retention, PII, unreachable platform.
362
- - [`ai-and-mcp.md`](ai-and-mcp.md) — asking an AI assistant what broke
363
- (Railwatch Cloud).
351
+ - [`ai-and-mcp.md`](ai-and-mcp.md) — asking an AI assistant what broke.
@@ -3,10 +3,11 @@
3
3
  Railwatch is the same product shape for Rails: one package instruments the
4
4
  framework end to end, records are grouped under the execution that
5
5
  produced them, and a dashboard turns them into routes, jobs, queries,
6
- issues, and alerts. Unlike Nightwatch, that dashboard runs inside your
7
- app by default ([embedded mode](embedded.md)); Railwatch Cloud is the
8
- hosted option. If you know Nightwatch, you already know how to read
9
- Railwatch — this page maps the vocabulary and points out the three
6
+ issues, and alerts. Railwatch Cloud is the hosted option; unlike
7
+ Nightwatch, the server can also be self-hosted
8
+ ([railwatch-rs](self-hosting.md)) or run inside your app with the
9
+ [`railwatch-server`](getting-started.md#embedded-railwatch-server) gem.
10
+ If you know Nightwatch, you already know how to read Railwatch — this page maps the vocabulary and points out the three
10
11
  places the Rails answer is genuinely different.
11
12
 
12
13
  Railwatch is an independent product and is not affiliated with Laravel or
@@ -21,8 +22,8 @@ separate daemon POSTs them.
21
22
  Puma and Solid Queue workers *are* long-lived, so Railwatch skips that
22
23
  tier. A single reporter thread per process holds a bounded buffer
23
24
  (16 MiB and 10,000 records by default, oldest dropped and counted) and
24
- flushes batches every 2 seconds or every 500 records: to the embedded
25
- writer process Puma forks, or as gzip-NDJSON POSTs to Railwatch Cloud.
25
+ flushes batches every 2 seconds or every 500 records, as gzip-NDJSON
26
+ POSTs to the Railwatch server.
26
27
  There is no daemon to install, supervise, or forget to restart.
27
28
 
28
29
  The thread is re-armed after `fork`, so clustered Puma workers and
@@ -184,7 +185,7 @@ every setting still has an env var — `NIGHTWATCH_*` becomes `RAILWATCH_*`.
184
185
  | `nightwatch:agent` | Nothing — the reporter thread lives in the app process. |
185
186
  | `nightwatch:status` | `bin/rails railwatch:status` |
186
187
  | `nightwatch:deploy {deploy} --ref --name --url` | `bin/rails railwatch:deploy[ref,name,url]`, or the generated `.kamal/hooks/post-deploy` |
187
- | — | `bin/rails railwatch:doctor`, which checks the whole install and exits non-zero if the embedded databases, the token, or the ingest host is wrong |
188
+ | — | `bin/rails railwatch:doctor`, which checks the whole install and exits non-zero if the token or the ingest host is wrong |
188
189
 
189
190
  ## The three things worth knowing about Rails
190
191
 
@@ -8,10 +8,9 @@ depends on a conditional row.
8
8
 
9
9
  Install Railwatch first ([`getting-started.md`](getting-started.md)); you
10
10
  can run both for a day if you want to compare, since neither knows about
11
- the other. The default install is embedded, with the dashboard at
12
- `/railwatch` and no token. The token and alert steps below apply when
13
- you send to Railwatch Cloud, either as a cloud install or by exporting
14
- from embedded.
11
+ the other. The token steps below apply to Railwatch Cloud and to a
12
+ self-hosted railwatch-rs; with the `railwatch-server` gem the token is
13
+ set for you.
15
14
 
16
15
  ## Decide whether Railwatch covers your workload
17
16
 
@@ -33,7 +32,7 @@ Railwatch does not claim those broader capabilities.
33
32
  | Ruby profiling | Conditional | Requires `vernier` or `stackprof`; there is no profiler bundled into the SDK. |
34
33
  | Browser monitoring | Partial | The optional Inertia client reports visits, Web Vitals, browser errors, and breadcrumbs. Session Replay, native/mobile SDKs, and Sentry's full browser/source-map workflow are outside the currently released Rails-server replacement. |
35
34
  | Runtime compatibility | Narrow today | The currently proved pair is Ruby 3.4 + Rails 8.1. A maintained compatibility matrix and any safe lowering of requirements are tracked by [#26](https://github.com/Rebulk/railwatch/issues/26). |
36
- | Managed integrations and operations | Partial | Railwatch Cloud delivers alerts to email, Slack, webhooks and Linear, plus self-hosting; embedded mode records alerts without delivering them. It does not promise Sentry's broader integration catalog. |
35
+ | Managed integrations and operations | Partial | Railwatch Cloud and railwatch-rs deliver alerts to email, Slack, webhooks and Linear. It does not promise Sentry's broader integration catalog. |
37
36
  | SQL value privacy | Supported by default | Query records carry normalized SQL shapes without literal values, and Active Record binds are never sent. Raw SQL and query plans are separate opt-ins; either can contain values. |
38
37
 
39
38
  For a Rails 8.1 application whose work enters through Rack and Active Job,
@@ -73,11 +72,10 @@ deploy config once the app boots without it.
73
72
  `config/initializers/railwatch.rb` (written by the install generator) is
74
73
  where every option from `Sentry.init` lands. The mapping:
75
74
 
76
- - **`dsn:`** has no equivalent in embedded mode, which stores telemetry in
77
- the app. For Railwatch Cloud it becomes `RAILWATCH_TOKEN`, one token per
78
- environment, created in Railwatch Cloud. Self-hosting adds
79
- `RAILWATCH_INGEST_URL`. On a cloud install the token is also the on/off
80
- switch: with it blank, Railwatch installs nothing.
75
+ - **`dsn:`** becomes `RAILWATCH_TOKEN`, one token per environment,
76
+ created in Railwatch Cloud or your own server. Self-hosting adds
77
+ `RAILWATCH_INGEST_URL`. The token is also the on/off switch: with it
78
+ blank, Railwatch installs nothing.
81
79
  - **`environment:`** becomes `c.environment`, which defaults to
82
80
  `Rails.env` — set it only to report under a different name.
83
81
  - **`release:`** becomes `c.deploy`, which auto-detects the release from
@@ -116,9 +114,7 @@ where every option from `Sentry.init` lands. The mapping:
116
114
  - **Rack `X-Request-Start` queue time** needs no setting: it is parsed
117
115
  into `queue_time` on every `request` record.
118
116
 
119
- A worked initializer for a cloud install, roughly what a `Sentry.init`
120
- block turns into (an embedded install has `c.transport = :local` instead
121
- of the token):
117
+ A worked initializer, roughly what a `Sentry.init` block turns into:
122
118
 
123
119
  ```ruby
124
120
  # config/initializers/railwatch.rb
@@ -424,7 +420,7 @@ startRailwatch({
424
420
  |---|---|
425
421
  | `Sentry.init({ dsn })` | `startRailwatch()`. There is no DSN: the beacon posts to the app's own origin and the *server* decides whether to record it (`c.beacon_enabled`, and whether Railwatch is enabled). The gate you already have on whether `startRailwatch()` runs at all is the only gate. |
426
422
  | `release` | Automatic. The record is stamped with `c.deploy`, the same release the server records carry, so a browser issue and a server issue from one deploy line up without a matching pair of settings to get wrong. |
427
- | `environment` | Automatic — the embedded databases belong to one environment, and on Railwatch Cloud the ingest token identifies it. |
423
+ | `environment` | Automatic — the ingest token identifies the environment. |
428
424
  | `ignoreErrors` | `startRailwatch({ ignoreErrors })`. Strings match anywhere in the message; regexes are tested against it. Both `ResizeObserver` messages are ignored by default. |
429
425
  | `denyUrls` | `startRailwatch({ denyUrls })`, matched against the top stack frame's URL. `/extensions\//i`, `/^chrome:\/\//i`, and `/^moz-extension:\/\//i` are denied by default, **and** any frame from an origin that isn't the app's own is dropped — extensions, injected widgets, tag managers. |
430
426
  | `Sentry.setUser` | Server-side. The beacon is a same-origin POST carrying the session cookie, so the server resolves the user the same way it does for a request (`Railwatch.user`) when `Current.user` or Warden is set by middleware; an app that authenticates in a `before_action` gives Railwatch the same lookup with `c.beacon_user { \|request\| ... }`. Nothing the browser sends names the user, so it cannot be forged. |
@@ -568,10 +564,10 @@ hook matching `rails runner …` previews, or `sentry_runner_noise.rb` under
568
564
  | Inertia visit timing | Real browser page-visit duration, prop byte size, partial reloads, SSR time, and Core Web Vitals, from a client the generator installs. |
569
565
  | Spec matchers as a CI gate | `have_railwatch_queries`, `have_railwatch_n_plus_one`, `have_railwatch_outgoing_requests` fail the pull request that regresses a hot path. |
570
566
  | Zero app-DB writes | The gem holds records in memory and ships them from a background thread; a bench gate asserts no `INSERT`/`UPDATE`/`DELETE` ever originates in `lib/railwatch`. This is why it is safe on single-writer SQLite. |
571
- | A dashboard inside the app | The default install serves the whole dashboard at `/railwatch` from two SQLite files the app owns, with no hosted service at all ([`embedded.md`](embedded.md)). |
567
+ | A dashboard inside the app | The `railwatch-server` gem runs the server next to Puma and serves the whole dashboard at `/railwatch`, with no hosted service at all ([Getting started](getting-started.md#embedded-railwatch-server)). |
572
568
  | One SQLite database per environment | Railwatch Cloud stores each monitored environment's telemetry in its own database file, which makes retention pruning, backup, and restore per-environment operations. |
573
569
  | LLM calls | RubyLLM calls are recorded with tokens, cost, finish reason, tool calls and workflows, in the request or job that made them ([`records.md`](records.md#llm_call)). |
574
- | An MCP server | With Railwatch Cloud, AI assistants can ask what broke after the last deploy, list slow routes, read an execution's timeline, and search logs ([`ai-and-mcp.md`](ai-and-mcp.md)). |
570
+ | An MCP server | AI assistants can ask what broke after the last deploy, list slow routes, read an execution's timeline, and search logs ([`ai-and-mcp.md`](ai-and-mcp.md)). |
575
571
 
576
572
  ## See also
577
573
 
data/docs/security.md CHANGED
@@ -3,25 +3,20 @@
3
3
  This review covers the open-source gem that runs in a customer's Rails
4
4
  application. Railwatch Cloud is a separate service and is outside this review.
5
5
 
6
- ## Embedded dashboard
7
-
8
- In embedded mode (the installer's default) the gem also serves a dashboard at
9
- the engine's mount, showing every query, log line and exception the app
10
- recorded. HTTP Basic authentication is on by default. With no credentials
11
- configured, every dashboard request is 401 in every environment except
12
- development, where it is open; the app logs a warning at boot outside
13
- development, and `railwatch:doctor` reports the gate. Credentials, once set,
14
- apply in development too. An app that turns Basic off gates pages with its own
15
- `base_controller_class` or a routes constraint; live updates over Action Cable
16
- are refused unless Basic, a `dashboard_user` resolver, or `dashboard_open`
17
- authorizes them. See [Embedded mode](embedded.md#authentication).
18
-
19
- The writer socket that web workers hand batches to is created with a 0700
20
- directory and a 0600 socket, so only the app's own user can connect.
6
+ ## Dashboard gate (railwatch-server)
7
+
8
+ The gem serves no dashboard. With the separate `railwatch-server` gem
9
+ installed, a dashboard showing every query, log line and exception the app
10
+ recorded is proxied at `/railwatch`, and its gate is this gem's settings.
11
+ HTTP Basic authentication is on by default. With no credentials configured,
12
+ every dashboard request is 401 in every environment except development,
13
+ where it is open. Credentials, once set, apply in development too. An app
14
+ that turns Basic off gates the dashboard with its own `base_controller_class`,
15
+ which railwatch-server's proxy controller inherits from. See
16
+ [Configuration](configuration.md#dashboard-gate-railwatch-server).
21
17
 
22
18
  ## Transport
23
19
 
24
- This applies to a cloud install and to export from an embedded install.
25
20
  `Railwatch::Transport::Http` sends gzip NDJSON with `Net::HTTP`. HTTPS explicitly
26
21
  sets OpenSSL's `VERIFY_PEER`; a spec pins that setting. The transport does not
27
22
  implement redirect handling, so a redirect response is treated as a permanent
@@ -52,8 +47,7 @@ configuration and record specs:
52
47
  filtered. `Locals.inspect_value` rescues an `inspect` implementation that
53
48
  raises; this behavior is covered by an exception-record spec.
54
49
  - `capture_exception_source` is true. Source lines surrounding in-application
55
- backtrace frames are recorded, and sent to Railwatch Cloud when the install
56
- reports or exports there. Disable it if source context is outside the
50
+ backtrace frames are recorded and sent to the configured server. Disable it if source context is outside the
57
51
  application's telemetry policy.
58
52
 
59
53
  Log record messages are sent exactly as supplied to `Rails.logger`. Railwatch
@@ -107,8 +101,8 @@ application/deployment secret and must not be committed.
107
101
  application-specific credentials and personal data.
108
102
  - Keep secrets out of exception messages, log text, source files, tenant ids,
109
103
  user resolvers, and custom context.
110
- - In embedded mode, set dashboard credentials (or your own gate) before
111
- deploying; see [Embedded mode](embedded.md#authentication).
104
+ - With railwatch-server, set dashboard credentials (or your own gate) before
105
+ deploying; see [Configuration](configuration.md#dashboard-gate-railwatch-server).
112
106
  - Use HTTPS in production and keep TLS verification enabled.
113
107
  - Put a request-body limit at the reverse proxy when the public beacon is
114
108
  enabled, and choose a shared cache if rate limits must span processes.
data/docs/self-hosting.md CHANGED
@@ -1,14 +1,22 @@
1
1
  # Self-hosting
2
2
 
3
- This page is for sending telemetry to a Railwatch Cloud you run
4
- yourself. An [embedded](embedded.md) install keeps everything inside
5
- your app and needs none of it.
3
+ This page is for sending telemetry to a Railwatch server you run
4
+ yourself instead of Railwatch Cloud.
6
5
 
7
- Railwatch Cloud is a Rails app you can run yourself. The gem doesn't care
8
- which install it talks to — point it at yours and everything works the
9
- same.
6
+ The self-hosted server is [railwatch-rs](https://github.com/Rebulk/railwatch-rs)
7
+ (AGPL-3.0): one static binary on SQLite that serves many apps, with the
8
+ same dashboard, alerts, MCP server and JSON API as Railwatch Cloud. Its
9
+ install, configuration, backups and upgrades are documented in its
10
+ [SELF-HOST.md](https://github.com/Rebulk/railwatch-rs/blob/main/docs/SELF-HOST.md).
11
+ The gem doesn't care which server it talks to — point it at yours and
12
+ everything works the same.
10
13
 
11
- ## Point the gem at your platform
14
+ For a single app, the [`railwatch-server`](https://github.com/Rebulk/railwatch-server)
15
+ gem runs that same server next to Puma and wires this gem to it, with no
16
+ host to set up; see [Getting started](getting-started.md#embedded-railwatch-server).
17
+ The rest of this page is for a server on its own host.
18
+
19
+ ## Point the gem at your server
12
20
 
13
21
  ```ruby
14
22
  # config/initializers/railwatch.rb
@@ -21,22 +29,22 @@ end
21
29
  `ingest_url` defaults to `https://railwatch.rebulk.com`, so this is the one
22
30
  setting a self-hosted install always needs; everything the gem sends —
23
31
  records, ping, deploys — hangs off that host. Pass `--url=` to the
24
- installer to have it written for you (it implies `--cloud`):
32
+ installer to have it written for you:
25
33
 
26
34
  ```sh
27
35
  bin/rails generate railwatch:install --url=https://telemetry.example.com
28
36
  ```
29
37
 
30
- To keep an embedded install and mirror it to your platform, set
31
- `RAILWATCH_INGEST_URL` and `RAILWATCH_TOKEN` and turn on
32
- [export](embedded.md#three-ways-to-run-it) instead.
38
+ The railwatch-rs installer prints both values (`RAILWATCH_INGEST_URL`,
39
+ `RAILWATCH_TOKEN`) when it finishes.
33
40
 
34
41
  ## Getting a token
35
42
 
36
- On your install: sign up, create an application, then create an
37
- environment inside it (`production`, `staging` — one token each). The
38
- token is shown once, right after you create the environment; a lost one
39
- is rotated from the environment's settings, not recovered.
43
+ railwatch-rs prints the first environment's token once, at the end of
44
+ its install. Each further environment (`production`, `staging` — one
45
+ token each) gets its own, created from the dashboard or with
46
+ `sudo railwatch env create <name>`. A token is shown once; a lost one is
47
+ replaced, not recovered.
40
48
 
41
49
  ```sh
42
50
  bin/rails railwatch:token # prints the URL to create/copy a token
@@ -55,14 +63,8 @@ so it works as a post-deploy smoke test.
55
63
 
56
64
  ## MCP
57
65
 
58
- Your install serves its own MCP endpoint at `<ingest host>/mcp` —
66
+ Your server serves its own MCP endpoint at `<ingest host>/mcp` —
59
67
  `https://telemetry.example.com/mcp` for the example above. It authenticates
60
68
  with a per-user API token generated from Settings → Profile, the same
61
69
  token the public JSON API at `/api/v1` uses. See
62
70
  [`ai-and-mcp.md`](ai-and-mcp.md) for every client's configuration block.
63
-
64
- ## Running the platform
65
-
66
- Railwatch Cloud's backend is not part of this gem repository. Licensed
67
- self-hosted customers receive separate platform deployment, backup, retention,
68
- quota, and restore documentation from Rebulk.