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
@@ -11,9 +11,9 @@ values set in the initializer always win over the env var.
11
11
 
12
12
  | Attribute | Env var | Default | Meaning |
13
13
  |---|---|---|---|
14
- | `enabled` | `RAILWATCH_ENABLED` | `true` | Master switch. With `transport = :http`, `Railwatch.enabled?` is also `false` whenever `token` is blank, so setting only `RAILWATCH_TOKEN` is enough to turn a cloud install on. An embedded install needs no token. |
15
- | `token` | `RAILWATCH_TOKEN` | nil | Bearer token for `/ingest`. Required with `transport = :http` and for [export](#export-to-railwatch-cloud). |
16
- | `ingest_url` | `RAILWATCH_INGEST_URL` | `https://railwatch.rebulk.com` | Platform base URL. Point at a self-hosted instance to override. |
14
+ | `enabled` | `RAILWATCH_ENABLED` | `true` | Master switch. `Railwatch.enabled?` is also `false` whenever `token` is blank, so setting only `RAILWATCH_TOKEN` is enough to turn an install on. |
15
+ | `token` | `RAILWATCH_TOKEN` | nil | Bearer token for `/ingest`. Required; with railwatch-server installed it is set for you. |
16
+ | `ingest_url` | `RAILWATCH_INGEST_URL` | `https://railwatch.rebulk.com` | Server base URL. Point at a self-hosted railwatch-rs to override. |
17
17
  | `allow_http` | `RAILWATCH_ALLOW_HTTP` | `false` | Permit a non-loopback plain HTTP ingest URL. HTTPS is required by default; `localhost`, `127.0.0.1`, and `::1` remain available for local self-hosted development. |
18
18
  | `deploy` | `RAILWATCH_DEPLOY` | auto-detected (order below), then nil | Version tag stamped on every record and used by `railwatch:deploy`. Full 40-character SHAs are shortened to 12 characters. |
19
19
  | `detect_deploy` | `RAILWATCH_DETECT_DEPLOY` | `true` | Detect deploys beyond `RAILWATCH_DEPLOY` and `KAMAL_VERSION`. Set false when the app deliberately reports no inferred deploy. |
@@ -25,7 +25,7 @@ values set in the initializer always win over the env var.
25
25
  | `beacon_rate_limit` | `RAILWATCH_BEACON_RATE_LIMIT` | `120` | Beacon POSTs accepted per client IP per minute before `POST /railwatch/beacon` answers 429. The beacon is unauthenticated and keeps every browser error it is sent, so this is what stops a script from spending the app's event quota. Counted in the app's cache store; `0` turns it off. |
26
26
 
27
27
  `Railwatch.enabled?` delegates to `config.enabled?`, which is `@enabled &&
28
- (local? || token.present?)`. There is no separate "is configured" check
28
+ token.present?`. There is no separate "is configured" check
29
29
  elsewhere.
30
30
 
31
31
  Deploy detection stops at the first value found: `RAILWATCH_DEPLOY`,
@@ -411,7 +411,12 @@ result without inserting the payload twice. Records written while a request is i
411
411
  separate bounded buffer, so they never change the retained request's
412
412
  identity. At most one retained batch plus one live buffer are held in
413
413
  memory. The reporter retries with jittered exponential backoff, from one
414
- second up to 60 seconds. It does not busy-loop. A 401 marks the transport
414
+ second up to 60 seconds. It does not busy-loop. A response with a
415
+ `Retry-After` (a 503 for a full disk or busy database, a 429 rate limit) is a
416
+ refusal: the next attempt waits for its `Retry-After` instead, but never
417
+ less than the backoff rung and never more than 15 seconds (jittered down to
418
+ 7.5), because Railwatch's receivers send a fixed 60 there, not a forecast.
419
+ A 401 marks the transport
415
420
  permanently unauthorized: no further HTTP attempts for the process's
416
421
  lifetime. It and other permanent client rejections are reported through
417
422
  `on_unrecoverable`. Delivery never raises into app code.
@@ -433,6 +438,15 @@ affects one probabilistic decision. Set `backpressure` false to keep the
433
438
  factor at 1. The current value is sent as
434
439
  `X-Railwatch-Backpressure-Factor` whenever it is greater than 1.
435
440
 
441
+ A refusal from ingest (see above) sets the factor straight to 8x, and the
442
+ first delivery accepted after it sets it straight back to 1x, so sampling
443
+ recovers within one retry of the receiver recovering. Each refusal prints
444
+ two `[railwatch]` lines, the same way lost records do (see
445
+ `warn_on_data_loss`): one when it starts, and one when ingest accepts
446
+ again, with how long it lasted, how many executions backpressure sampled
447
+ out that the configured rates would have kept, and how many records were
448
+ dropped.
449
+
436
450
  `Railwatch.flush` forces an immediate flush, on the calling thread and
437
451
  without a time limit, so it is yours to call when you know you want to
438
452
  wait. Nothing in the gem calls it for you: a short-lived process keeps its
@@ -571,7 +585,7 @@ Rake tasks and Solid Queue jobs are never interactive.
571
585
 
572
586
  | Attribute | Env var | Default | Meaning |
573
587
  |---|---|---|---|
574
- | `capture_exception_source` | `RAILWATCH_CAPTURE_EXCEPTION_SOURCE_CODE` | `true` | Record source snippet lines surrounding each in-application exception frame (sent to Railwatch Cloud when the install reports there or exports). This is on by default for crash context; disable it when source disclosure is outside the application's telemetry policy. |
588
+ | `capture_exception_source` | `RAILWATCH_CAPTURE_EXCEPTION_SOURCE_CODE` | `true` | Record source snippet lines surrounding each in-application exception frame (sent to whichever server the app reports to). This is on by default for crash context; disable it when source disclosure is outside the application's telemetry policy. |
575
589
  | `capture_exception_locals` | `RAILWATCH_CAPTURE_EXCEPTION_LOCALS` | `false` | Snapshot the raising frame's local variables (up to 25, values truncated to 200 chars, run through the same filter as request params) onto each exception, like Sentry's locals panel. Installs a `TracePoint(:raise)`; opt in per environment. |
576
590
  | `capture_request_payload` | `RAILWATCH_CAPTURE_REQUEST_PAYLOAD` | `false` | Capture (redacted) request params — only for a request that raised, never otherwise. |
577
591
  | `capture_job_arguments` | `RAILWATCH_CAPTURE_JOB_ARGUMENTS` | `false` | Add the job's real arguments (`job.serialize["arguments"]`) to each `job_attempt`/`scheduled_task` record, capped at 8 KiB of JSON. Hash arguments run through the same filter as request params. Off by default because job arguments routinely carry PII; `arguments_preview` (argument *shapes* only) is always on regardless. |
@@ -827,16 +841,19 @@ Railwatch.on_unrecoverable { |error| Rails.error.report(error, handled: true) }
827
841
  ```
828
842
 
829
843
  Called whenever Railwatch rescues one of its own internal errors, ingest
830
- permanently rejects a batch, or shutdown expires with retained records
831
- that could not be sent. Retryable delivery failures stay buffered and do
832
- not fire the callback on every attempt. With no callback registered, a
844
+ permanently rejects a batch, ingest starts or stops refusing deliveries, or
845
+ shutdown expires with retained records that could not be sent. Retryable
846
+ delivery failures stay buffered and do not fire the callback on every
847
+ attempt. With no callback registered, a
833
848
  recovered internal error falls back to `Railwatch.debug`, gated on
834
849
  `RAILWATCH_DEBUG` — the gem carried on and there is nothing to do about it.
835
850
 
836
851
  **Lost records are different, and are reported by default.** A batch
837
852
  dropped after its retry ladder, one the receiver permanently refused, or
838
853
  records still unsent when the bounded shutdown ran out of time each print
839
- one `[railwatch]` stderr line. Telemetry that vanishes silently looks
854
+ one `[railwatch]` stderr line. So do the start and end of an ingest
855
+ refusal, which samples down until it is over (see backpressure above).
856
+ Telemetry that vanishes silently looks
840
857
  exactly like having nothing to report, and a short-lived process — a rake
841
858
  task, a `rails runner`, a cron job — gets one bounded shutdown and no
842
859
  second chance to mention it.
@@ -883,72 +900,50 @@ in `lib/railwatch.rb`, unless noted:
883
900
  `instrument_outgoing`, `flush`, `debug { }`. `pause`/`resume` are the
884
901
  ignore block's building blocks, and are nestable.
885
902
 
886
- ## Embedded mode
903
+ ## Dashboard gate (railwatch-server)
904
+
905
+ These settings do nothing on their own. They gate the dashboard that the
906
+ [`railwatch-server`](https://github.com/Rebulk/railwatch-server) gem
907
+ serves at `/railwatch` inside the app, so one setting covers it whichever
908
+ way it is installed.
887
909
 
888
910
  ```ruby
889
- c.transport = :local # RAILWATCH_TRANSPORT; runtime default "http", but the installer writes :local
890
- c.issue_prefix = "SHOP" # RAILWATCH_ISSUE_PREFIX; default from the app name
891
- c.repository_url = "..." # RAILWATCH_REPOSITORY_URL
892
- c.retention_days = 7 # RAILWATCH_RETENTION_DAYS
893
911
  c.http_basic_auth_enabled = true # RAILWATCH_HTTP_BASIC_AUTH_ENABLED; on, and closed outside development until credentials exist
894
912
  c.http_basic_auth_user = "ops" # RAILWATCH_HTTP_BASIC_AUTH_USER, or credentials railwatch.http_basic_auth_user
895
913
  c.http_basic_auth_password = "..." # RAILWATCH_HTTP_BASIC_AUTH_PASSWORD, or credentials railwatch.http_basic_auth_password
896
914
  c.base_controller_class = "AdminController" # RAILWATCH_BASE_CONTROLLER_CLASS; default ActionController::Base
897
- c.dashboard_open = false # RAILWATCH_DASHBOARD_OPEN; default false, true makes it public on purpose
898
- c.dashboard_user = ->(request) { { id:, name:, email: } or nil }
899
- c.writer_socket = "tmp/sockets/railwatch-writer.sock" # RAILWATCH_WRITER_SOCKET
900
915
  ```
901
916
 
902
- With `transport = :local` the reporter writes each batch into the app's
903
- own `railwatch_telemetry` database instead of POSTing it, and the engine
904
- serves the dashboard at its mount. `enabled?` no longer needs a token.
905
- The others only matter in that mode. The dashboard is behind HTTP Basic
906
- by default and, outside development, answers 401 until `bin/rails
907
- railwatch:authentication:configure` has written credentials; a host with
908
- its own admin auth turns Basic off and sets `base_controller_class` or a
909
- routes constraint. Full walkthrough: [Embedded mode](embedded.md).
910
-
911
- ### Export to Railwatch Cloud
912
-
913
- An embedded install can also mirror every record to Railwatch Cloud.
914
- Off unless `export_enabled` is set; a configured token alone sends
915
- nothing. Only meaningful with `transport = :local`.
916
-
917
- | Attribute | Env var | Default | Meaning |
918
- |---|---|---|---|
919
- | `export_enabled` | `RAILWATCH_EXPORT_ENABLED` | `false` | Mirror every record to the cloud as well as storing it locally. |
920
- | `export_token` | `RAILWATCH_EXPORT_TOKEN` | `token` | Cloud environment token to send with. |
921
- | `export_url` | `RAILWATCH_EXPORT_URL` | `{ingest_url}/ingest` | Where to send. |
922
- | `export_max_bytes` | `RAILWATCH_EXPORT_MAX_BYTES` | `268435456` (256 MiB) | Queue ceiling in bytes. At capacity new work is refused and counted, not swapped for old. |
923
- | `export_max_deliveries` | `RAILWATCH_EXPORT_MAX_DELIVERIES` | `100000` | Queue ceiling in deliveries. |
924
- | `export_max_age` | `RAILWATCH_EXPORT_MAX_AGE_SECONDS` | `86400` | How long a queued delivery is kept. At most seven days, past which the receiver no longer recognises it. |
917
+ HTTP Basic is on by default and, outside development, answers 401 until
918
+ credentials exist: `bin/rails railwatch:authentication:configure` writes
919
+ them to the current environment's Rails credentials, or set the env vars.
920
+ In development with no credentials configured, it is open. An initializer
921
+ or env var that sets them wins over credentials.
925
922
 
926
- `railwatch:doctor` fails when export is enabled and cannot work, and
927
- `railwatch:export:status` shows the queue. See
928
- [Embedded mode](embedded.md#three-ways-to-run-it).
923
+ An app with its own admin authentication turns Basic off and names a
924
+ base controller. railwatch-server's proxy controller inherits from
925
+ `base_controller_class`, so the app's own `before_action`s (require an
926
+ admin, redirect to sign-in) run in front of every dashboard request.
929
927
 
930
928
  ## Rake tasks
931
929
 
932
930
  Ship with the gem via Rails::Engine's default `lib/tasks` convention, in
933
931
  `lib/tasks/railwatch_tasks.rake`:
934
932
 
935
- - **`railwatch:status`** embedded: prints that telemetry is stored in the
936
- app. Cloud: pings `{ingest_url}/ingest/ping` with the configured token,
937
- and aborts if `RAILWATCH_TOKEN` is unset or the ping fails.
933
+ - **`railwatch:status`** pings `{ingest_url}/ingest/ping` with the
934
+ configured token, and aborts if `RAILWATCH_TOKEN` is unset or the ping
935
+ fails.
938
936
  - **`railwatch:doctor`** prints a ✓/✗ checklist of the whole install.
939
- Embedded, it checks both databases and their migrations, telemetry
940
- disk mode, the maintenance clock, the writer process, dashboard access,
941
- and export when enabled. Cloud, it checks the token, where the token is
942
- stored, the ingest URL and its transport security, and
943
- `GET /ingest/ping`. Then, either way: `Railwatch::Middleware::Request`
937
+ It checks the token, where the token is stored, the ingest URL and its
938
+ transport security, and `GET /ingest/ping`. Then
939
+ `Railwatch::Middleware::Request`
944
940
  in the middleware stack, the mounted engine's beacon route,
945
941
  `config.deploy` and its source, sample rates, ignored record types, the
946
942
  Kamal `post-deploy` hook, the browser client and whether an entrypoint
947
943
  calls it, the profiler backend, and the test matchers. It exits
948
944
  non-zero only on the lines marked fatal in
949
945
  [Troubleshooting](troubleshooting.md).
950
- - **`railwatch:deploy[ref,name,url]`** embedded: writes the deploy marker
951
- to the app's `railwatch` database. Cloud: POSTs `{deploy, ref, name,
946
+ - **`railwatch:deploy[ref,name,url]`** POSTs `{deploy, ref, name,
952
947
  url, server, timestamp, performer, destination, service, commits}` to
953
948
  `{ingest_url}/ingest/deploys`. `deploy` comes from `config.deploy`. It
954
949
  aborts if that's unset. `ref` defaults to `git rev-parse HEAD` when not
@@ -957,29 +952,20 @@ Ship with the gem via Rails::Engine's default `lib/tasks` convention, in
957
952
  `{sha, author, message, at}` objects, newest first, from `git log`. It
958
953
  is empty inside an app container, which has no `.git`. That is why the
959
954
  hook below posts from the deployer instead.
960
- - **`railwatch:authentication:configure`** writes the embedded
961
- dashboard's HTTP Basic credentials to the current environment's Rails
962
- credentials.
963
- - **`railwatch:export:status`**, **`railwatch:export:rebind`**,
964
- **`railwatch:export:discard`** show the export queue, clear a
965
- credential block (abandoning work queued under the old token), and
966
- abandon everything queued.
967
- - **`railwatch:vacuum:status`** and **`railwatch:vacuum`** report and
968
- reclaim the telemetry database's disk
969
- ([Embedded mode](embedded.md#giving-the-disk-back)).
970
- - **`railwatch:token`** and **`railwatch:mcp`** print where to create a
971
- cloud ingest token and paste-ready MCP client configuration.
955
+ - **`railwatch:authentication:configure`** writes the
956
+ [dashboard gate](#dashboard-gate-railwatch-server)'s HTTP Basic
957
+ credentials to the current environment's Rails credentials.
958
+ - **`railwatch:token`** and **`railwatch:mcp`** print where to create an
959
+ ingest token and paste-ready MCP client configuration for the server
960
+ `ingest_url` points at.
972
961
  - **`railwatch:sourcemaps[directory,delete]`** uploads browser source
973
962
  maps ([Source maps](source-maps.md)).
974
963
 
975
964
  ## Kamal integration
976
965
 
977
966
  `bin/rails generate railwatch:install` writes `.kamal/hooks/post-deploy`,
978
- but only if `config/deploy.yml` already exists. With
979
- `RAILWATCH_TRANSPORT=local` in the deployer's environment it runs
980
- `bin/rails railwatch:deploy[$KAMAL_VERSION]` in the primary container,
981
- which records the marker in the embedded database, and stops. Otherwise
982
- it no-ops when `RAILWATCH_TOKEN` isn't set. It never fails a deploy.
967
+ but only if `config/deploy.yml` already exists. It no-ops when
968
+ `RAILWATCH_TOKEN` isn't set. It never fails a deploy.
983
969
  Every network call ends in `|| true`.
984
970
 
985
971
  The hook runs on the **deployer machine**, not in a container, which is
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