railwatch 0.5.1 → 0.6.1

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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +45 -25
  3. data/CHANGELOG.md +147 -0
  4. data/README.md +50 -31
  5. data/app/controllers/railwatch/dashboard_controller.rb +1 -0
  6. data/app/jobs/railwatch/rollup_job.rb +3 -1
  7. data/app/models/railwatch/application_record.rb +2 -2
  8. data/app/models/railwatch/ingest/rollup_absorber.rb +1 -1
  9. data/app/models/railwatch/telemetry_record.rb +2 -2
  10. data/docs/ai-and-mcp.md +9 -3
  11. data/docs/configuration.md +97 -40
  12. data/docs/embedded.md +96 -43
  13. data/docs/faq.md +28 -15
  14. data/docs/getting-started.md +121 -42
  15. data/docs/records.md +13 -10
  16. data/docs/replacing-nightwatch.md +16 -14
  17. data/docs/replacing-sentry.md +19 -10
  18. data/docs/security.md +24 -3
  19. data/docs/self-hosting.md +9 -1
  20. data/docs/testing.md +14 -4
  21. data/docs/troubleshooting.md +72 -29
  22. data/lib/generators/railwatch/install/install_generator.rb +32 -16
  23. data/lib/generators/railwatch/install/templates/initializer.rb.tt +6 -6
  24. data/lib/puma/plugin/railwatch.rb +48 -3
  25. data/lib/railwatch/configuration.rb +16 -1
  26. data/lib/railwatch/engine.rb +14 -3
  27. data/lib/railwatch/reporter.rb +52 -16
  28. data/lib/railwatch/transport/http.rb +4 -9
  29. data/lib/railwatch/version.rb +1 -1
  30. data/lib/railwatch.rb +23 -1
  31. data/lib/tasks/railwatch_tasks.rake +11 -4
  32. data/llms.txt +21 -14
  33. data/public/railwatch/assets/{app-layout-DDyQa72H.js → app-layout-Zc0v-hYh.js} +1 -1
  34. data/public/railwatch/assets/{app-wordmark-o9CODKP0.js → app-wordmark-BA_60AVb.js} +1 -1
  35. data/public/railwatch/assets/{appearance-BwuCXabr.js → appearance-CcfP9tZ7.js} +1 -1
  36. data/public/railwatch/assets/application-BrN3Sz94.css +1 -0
  37. data/public/railwatch/assets/{arrow-up-C6PxDiY3.js → arrow-up-CLQ-7heQ.js} +1 -1
  38. data/public/railwatch/assets/{auth-layout-BRt8MGFD.js → auth-layout-C05gEBIQ.js} +1 -1
  39. data/public/railwatch/assets/{badge-CAxXV8za.js → badge-DRae8XwK.js} +1 -1
  40. data/public/railwatch/assets/{braces-DgomTCNf.js → braces-rSYydpLY.js} +1 -1
  41. data/public/railwatch/assets/{card-cAtqCxWl.js → card-DPjFKfen.js} +1 -1
  42. data/public/railwatch/assets/{chart-BBeBkkNa.js → chart-DWh7l8yM.js} +1 -1
  43. data/public/railwatch/assets/{chart-hover-B1M9jc0y.js → chart-hover-CfoZUY4J.js} +1 -1
  44. data/public/railwatch/assets/{chart-panel-DUQTz_C8.js → chart-panel-CC49WTQL.js} +1 -1
  45. data/public/railwatch/assets/{checkbox-CmhMHWZO.js → checkbox-DPkLUiwM.js} +1 -1
  46. data/public/railwatch/assets/{code-DESvxyTj.js → code-CLmYS6FU.js} +1 -1
  47. data/public/railwatch/assets/{copy-block-BkSU5832.js → copy-block-CGcXxp8J.js} +1 -1
  48. data/public/railwatch/assets/{copy-id-D03GhN9F.js → copy-id-vBYHQwxg.js} +1 -1
  49. data/public/railwatch/assets/{cursor-load-more-CRyuMeQb.js → cursor-load-more-Ddwxe5dZ.js} +1 -1
  50. data/public/railwatch/assets/{data-table-BIlt7Rtm.js → data-table-CzKTEE-O.js} +1 -1
  51. data/public/railwatch/assets/{edit-O0NSBWxo.js → edit-B1kmWkzd.js} +1 -1
  52. data/public/railwatch/assets/{edit-DJ0D0wHN.js → edit-D9cx4pbG.js} +1 -1
  53. data/public/railwatch/assets/{edit-Bb6MKoe4.js → edit-yv6j9p-V.js} +1 -1
  54. data/public/railwatch/assets/{empty-state-C38il627.js → empty-state-CW4wclK_.js} +1 -1
  55. data/public/railwatch/assets/{env-layout-REF7OM4q.js → env-layout-Kz7wks1x.js} +1 -1
  56. data/public/railwatch/assets/{execution-path-FYLq1TwC.js → execution-path-FAuIzOXB.js} +1 -1
  57. data/public/railwatch/assets/{filter-bar-CYog9Alp.js → filter-bar-CJDjWFib.js} +1 -1
  58. data/public/railwatch/assets/{flamegraph-DSs69foN.js → flamegraph-EaGkP2NT.js} +1 -1
  59. data/public/railwatch/assets/{frames-BUi2J5Mk.js → frames-zZuIaNH9.js} +1 -1
  60. data/public/railwatch/assets/{google-sign-in-button-BQiIKFdd.js → google-sign-in-button-2_zgbgVy.js} +1 -1
  61. data/public/railwatch/assets/{index-ZOGOB8SA.js → index-B49SWz7K.js} +1 -1
  62. data/public/railwatch/assets/{index-DqTFTP8p.js → index-BHlY4wKe.js} +1 -1
  63. data/public/railwatch/assets/{index-DrcKVG2f.js → index-BZpPtyFY.js} +1 -1
  64. data/public/railwatch/assets/{index-Dh4IRLFI.js → index-BeVK6jCL.js} +1 -1
  65. data/public/railwatch/assets/{index-umIAl-pL.js → index-BfbSo01U.js} +1 -1
  66. data/public/railwatch/assets/{index-BoUBioBP.js → index-BfgncAv6.js} +1 -1
  67. data/public/railwatch/assets/{index-C3A_9imx.js → index-BhNszK1k.js} +1 -1
  68. data/public/railwatch/assets/{index-tpz-OGUP.js → index-Bu01uWvw.js} +1 -1
  69. data/public/railwatch/assets/{index-CGs4m_fa.js → index-C1s_hK3p.js} +1 -1
  70. data/public/railwatch/assets/{index-ZSZg9rtq.js → index-C8Cggnbw.js} +1 -1
  71. data/public/railwatch/assets/{index-so4lRrRq.js → index-CBip6V4z.js} +1 -1
  72. data/public/railwatch/assets/{index-DvjY3dPD.js → index-CMJGss5R.js} +1 -1
  73. data/public/railwatch/assets/{index-DtHmuB9Q.js → index-CRo3yK20.js} +1 -1
  74. data/public/railwatch/assets/{index-r0tSIplE.js → index-CWB_p2J8.js} +1 -1
  75. data/public/railwatch/assets/{index-CICUIFHL.js → index-CWHpndGc.js} +1 -1
  76. data/public/railwatch/assets/{index-CFFpnzIS.js → index-Ca_S4Sc3.js} +1 -1
  77. data/public/railwatch/assets/{index-DSvlZVWG.js → index-CanPDDOa.js} +1 -1
  78. data/public/railwatch/assets/{index-CrZ3vHDL.js → index-Cie90Yat.js} +1 -1
  79. data/public/railwatch/assets/{index-FhUaPPab.js → index-CiepQ_pR.js} +1 -1
  80. data/public/railwatch/assets/{index-C_upSl_k.js → index-Cm1uCIGN.js} +1 -1
  81. data/public/railwatch/assets/{index-sTYvcbkh.js → index-CzitnnSC.js} +1 -1
  82. data/public/railwatch/assets/{index-C7OtLq_3.js → index-DEUfClv3.js} +1 -1
  83. data/public/railwatch/assets/index-DJKwo-mI.js +1 -0
  84. data/public/railwatch/assets/{index-DDI_Zx5V.js → index-DK6y0YHp.js} +1 -1
  85. data/public/railwatch/assets/{index-C-PmdhXA.js → index-DMNPpLH9.js} +1 -1
  86. data/public/railwatch/assets/{index-BaR1U9An.js → index-DZO1mSfX.js} +1 -1
  87. data/public/railwatch/assets/{index-CsoN51vW.js → index-Db5wj3M5.js} +1 -1
  88. data/public/railwatch/assets/{index-BiiyMcA0.js → index-Dcy5WktB.js} +1 -1
  89. data/public/railwatch/assets/{index-8-hnAhOD.js → index-DvG0-7Lx.js} +1 -1
  90. data/public/railwatch/assets/{index-QpTtwFwu.js → index-Dvj1wuka.js} +1 -1
  91. data/public/railwatch/assets/{index-DW2CBbxU.js → index-KyZX46qX.js} +1 -1
  92. data/public/railwatch/assets/{index-BeOh2t_S.js → index-Ze-KP-sl.js} +1 -1
  93. data/public/railwatch/assets/{index-CiPo4Gob.js → index-mhRbWLBM.js} +1 -1
  94. data/public/railwatch/assets/{index-CpkI015n.js → index-x099JL5f.js} +1 -1
  95. data/public/railwatch/assets/{index-JdCVBrw8.js → index-x28zb_nf.js} +1 -1
  96. data/public/railwatch/assets/{inertia-DLew8ZNx.js → inertia-Cuyz2ZHO.js} +2 -2
  97. data/public/railwatch/assets/{input-error-cvM6_Jht.js → input-error-hog6gGxg.js} +1 -1
  98. data/public/railwatch/assets/{json-viewer-D922McGi.js → json-viewer-DH2W9HXf.js} +1 -1
  99. data/public/railwatch/assets/{klass-CrwICqN8.js → klass-DHbDelLk.js} +1 -1
  100. data/public/railwatch/assets/{label-GWl7I6sf.js → label-DdCBgiUn.js} +1 -1
  101. data/public/railwatch/assets/{layout-0ZAnD3zl.js → layout-Cueyl7c5.js} +1 -1
  102. data/public/railwatch/assets/{live-dot-D1n_BreY.js → live-dot-BZgYTYdt.js} +1 -1
  103. data/public/railwatch/assets/{nav-DPxr1NNC.js → nav-BSSGObDZ.js} +1 -1
  104. data/public/railwatch/assets/{new-D-ZzUK9a.js → new-B8FSb8Bl.js} +1 -1
  105. data/public/railwatch/assets/{new-DEVkYv-z.js → new-C39_v2Ll.js} +1 -1
  106. data/public/railwatch/assets/{new-Cdl6pqST.js → new-DVOPwaC5.js} +1 -1
  107. data/public/railwatch/assets/{new-DHAHDrN7.js → new-Dd40nvJR.js} +1 -1
  108. data/public/railwatch/assets/{new-Dz4lZf1L.js → new-SxnYe1SE.js} +1 -1
  109. data/public/railwatch/assets/{new-GMrRFurX.js → new-eP5vKD3Y.js} +1 -1
  110. data/public/railwatch/assets/onboarding-CUpZl5KB.js +1 -0
  111. data/public/railwatch/assets/{origin-identity-Bk9yHWZ1.js → origin-identity-BYt2uiuo.js} +1 -1
  112. data/public/railwatch/assets/{percentile-picker-DfSx9yJO.js → percentile-picker-CeQgllxD.js} +1 -1
  113. data/public/railwatch/assets/{relative-time-CjIjb8Lg.js → relative-time-D5UbF4oO.js} +1 -1
  114. data/public/railwatch/assets/{release-health-4b3tivEf.js → release-health-uP-GF3_V.js} +1 -1
  115. data/public/railwatch/assets/{route-C_5BUtHK.js → route-DQAY8JEr.js} +1 -1
  116. data/public/railwatch/assets/{segmented-BgbT3wZa.js → segmented-BeIe4uqk.js} +1 -1
  117. data/public/railwatch/assets/{select-DmunxCKE.js → select-2R16457Z.js} +1 -1
  118. data/public/railwatch/assets/{separator-BXzEdZ_8.js → separator-D5I0UCB5.js} +1 -1
  119. data/public/railwatch/assets/series-chart-gnrzhmM6.js +1 -0
  120. data/public/railwatch/assets/show-2BkeNRUC.js +2 -0
  121. data/public/railwatch/assets/{show-mU38uGTg.js → show-B0X1hRQH.js} +1 -1
  122. data/public/railwatch/assets/{show-CAl7xcex.js → show-BKUV5l5q.js} +1 -1
  123. data/public/railwatch/assets/{show-DnR1Dnjd.js → show-BQJD_CsF.js} +1 -1
  124. data/public/railwatch/assets/{show-Dn-GwFZL.js → show-BhmD6Sxp.js} +1 -1
  125. data/public/railwatch/assets/{show-Y74rM0VT.js → show-C3KcnVo2.js} +1 -1
  126. data/public/railwatch/assets/{show-Dily73Xk.js → show-C95cHd06.js} +1 -1
  127. data/public/railwatch/assets/{show-DXs4deaC.js → show-CdB4uVQS.js} +1 -1
  128. data/public/railwatch/assets/{show-DI8IhNUH.js → show-CoCqcVIp.js} +1 -1
  129. data/public/railwatch/assets/{show-vQ4bndYD.js → show-D2LqjEsU.js} +1 -1
  130. data/public/railwatch/assets/{show-DcpTFiLi.js → show-DNpnKyTh.js} +1 -1
  131. data/public/railwatch/assets/{show-B2zLAW83.js → show-DOlTxbig.js} +1 -1
  132. data/public/railwatch/assets/{show-SvLOcPrx.js → show-DQCkttL8.js} +1 -1
  133. data/public/railwatch/assets/{show-BLpWUHWD.js → show-DcxesipC.js} +1 -1
  134. data/public/railwatch/assets/{show-DYskfl3-.js → show-IGJoc_X0.js} +1 -1
  135. data/public/railwatch/assets/{show-DSP9Cq_C.js → show-YsNpqbcI.js} +1 -1
  136. data/public/railwatch/assets/{show-DlRVS18-.js → show-qNV6H8SH.js} +1 -1
  137. data/public/railwatch/assets/{sort-header-Dcq9bzmo.js → sort-header-mEJUM3Av.js} +1 -1
  138. data/public/railwatch/assets/{sparkline-cell-BON3qQUB.js → sparkline-cell-Dwg7awwh.js} +1 -1
  139. data/public/railwatch/assets/{stat-DFEyFxkO.js → stat-ZtxGU8lE.js} +1 -1
  140. data/public/railwatch/assets/{status-badge-BaUKP7Yo.js → status-badge-CH5P-Xkj.js} +1 -1
  141. data/public/railwatch/assets/{tenant-path-DPZPc985.js → tenant-path-CQoP-BeF.js} +1 -1
  142. data/public/railwatch/assets/{text-link-BO77t9Xk.js → text-link-DHJ8BXx5.js} +1 -1
  143. data/public/railwatch/assets/{textarea-DTqrCiV0.js → textarea-BeQtQyl5.js} +1 -1
  144. data/public/railwatch/assets/{timeline-D5rJ0es2.js → timeline-CaKQVu48.js} +1 -1
  145. data/public/railwatch/assets/{transition-DMIrZVth.js → transition-ksDpqhKJ.js} +1 -1
  146. data/public/railwatch/assets/{use-clipboard-ByoUGQqA.js → use-clipboard-DNefo-ky.js} +1 -1
  147. data/public/railwatch/assets/{use-live-D7xKz2ma.js → use-live-DMTuhKfB.js} +1 -1
  148. data/public/railwatch/manifest.json +1286 -1286
  149. metadata +116 -116
  150. data/public/railwatch/assets/application-B7h1MIhi.css +0 -1
  151. data/public/railwatch/assets/index-CFRLPs4J.js +0 -1
  152. data/public/railwatch/assets/onboarding-D1vwaHYT.js +0 -1
  153. data/public/railwatch/assets/series-chart-Xf49v9cv.js +0 -1
  154. data/public/railwatch/assets/show-SHwZjXb7.js +0 -2
data/lib/railwatch.rb CHANGED
@@ -343,10 +343,32 @@ module Railwatch
343
343
 
344
344
  # Called (rescued) whenever the gem itself rescues an internal exception:
345
345
  # a subscriber block raising, or delivery failing after its retry.
346
- # Falls back to the debug log when no callback is registered.
346
+ #
347
+ # With no callback registered, an internal error goes to the debug log:
348
+ # the gem recovered, and a subscriber that raises once is noise in
349
+ # someone else's cron output. A DeliveryError is different. It is raised
350
+ # only when records are already gone -- a batch dropped after the retry
351
+ # ladder, one the receiver permanently refused, or the ones still unsent
352
+ # when at_exit's bounded shutdown ran out of time -- and since 0.3.7 that
353
+ # shutdown is the only delivery a rake task or runner gets. Behind
354
+ # RAILWATCH_DEBUG that loss was invisible: a cron job whose exception
355
+ # never reached the platform looked exactly like one that had nothing to
356
+ # say.
357
+ #
358
+ # It is still not printed by default. A monitoring gem writing into
359
+ # someone else's cron output is the gem changing their application's
360
+ # behaviour, which is not a trade this one makes to report on itself: it
361
+ # stays additive and out of the way. Ask for it with warn_on_data_loss and
362
+ # it becomes one stderr line, or register on_unrecoverable and route it
363
+ # wherever the app already sends such things. Kernel#warn, not
364
+ # Rails.logger, for the same reason as #debug: it must never become a log
365
+ # record about itself.
347
366
  def notify_unrecoverable(error)
348
367
  if config.on_unrecoverable
349
368
  config.on_unrecoverable.call(error)
369
+ elsif error.is_a?(Reporter::DeliveryError) && config.warn_on_data_loss
370
+ warn("[railwatch] #{error.message}. Register Railwatch.on_unrecoverable to route this elsewhere, " \
371
+ "or set warn_on_data_loss = false to silence it.")
350
372
  else
351
373
  debug { "unrecoverable internal error: #{error.class}: #{error.message}" }
352
374
  end
@@ -235,7 +235,7 @@ namespace :railwatch do
235
235
  %w[railwatch railwatch_telemetry].each do |name|
236
236
  configured = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: name)
237
237
  check.call(!configured.nil?, "#{name} database",
238
- configured ? configured.database : "not in config/database.yml (bin/rails generate railwatch:install --local)",
238
+ configured ? configured.database : "not in config/database.yml (bin/rails generate railwatch:install)",
239
239
  fatal: true)
240
240
  end
241
241
  { "railwatch_telemetry" => Railwatch::TelemetryRecord, "railwatch" => Railwatch::ApplicationRecord }.each do |name, base|
@@ -326,12 +326,19 @@ namespace :railwatch do
326
326
  # HTTP Basic is on and closed until credentials exist; a deploy that
327
327
  # forgot gets a 401, not a public page, and this says so first.
328
328
  gate = config.dashboard_gate
329
- check.call(gate != :undeclared && !(gate == :basic && !config.http_basic_auth_configured?), "dashboard access",
329
+ check.call(gate != :undeclared && !(gate == :basic && !config.http_basic_auth_configured? && !config.http_basic_auth_waived?),
330
+ "dashboard access",
330
331
  case gate
331
332
  when :basic
332
- config.http_basic_auth_configured? ? "HTTP Basic, user #{config.http_basic_auth_user}" :
333
+ if config.http_basic_auth_configured?
334
+ "HTTP Basic, user #{config.http_basic_auth_user}"
335
+ elsif config.http_basic_auth_waived?
336
+ "open in development. Production stays closed until you run " \
337
+ "`RAILS_ENV=production bin/rails railwatch:authentication:configure`"
338
+ else
333
339
  "closed: HTTP Basic is on with no credentials, so every dashboard request is 401. " \
334
- "Run `bin/rails railwatch:authentication:configure`"
340
+ "Run `bin/rails railwatch:authentication:configure`"
341
+ end
335
342
  when :controller then "your own: c.base_controller_class = #{config.base_controller_class}"
336
343
  when :resolver then "your own: c.dashboard_user decides, and live updates follow it"
337
344
  when :open then "deliberately open: anyone who can reach the mount can read it (c.dashboard_open)"
data/llms.txt CHANGED
@@ -3,35 +3,42 @@
3
3
  > Railwatch is a Ruby gem that instruments a Rails application end to end —
4
4
  > requests, jobs, scheduled tasks, rake/runner commands, database queries and
5
5
  > N+1s, exceptions, cache, mail, notifications, broadcasts, outgoing HTTP,
6
- > Active Storage, view renders, and logs — links every one of them into a
7
- > single execution tree by `execution_id`/`trace_id`, and ships them to
8
- > Railwatch Cloud from a background thread. It replaces a separate APM and a
6
+ > Active Storage, view renders, RubyLLM calls, and logs — links every one of
7
+ > them into a single execution tree by `execution_id`/`trace_id`, and writes
8
+ > them from a background thread into two SQLite files the app owns (embedded,
9
+ > the default) or to Railwatch Cloud. It replaces a separate APM and a
9
10
  > separate error tracker with one gem and one configuration block, costs
10
11
  > under a millisecond of CPU per request, and never writes to the
11
- > application's own database. Install is `bundle add railwatch` followed by
12
- > `bin/rails generate railwatch:install`, which writes the initializer, mounts
13
- > the beacon engine, adds a Kamal post-deploy hook and the Inertia browser
14
- > client where the app has them, wires the test matchers, and then runs
15
- > `bin/rails railwatch:doctor` to print a ✓/✗ line for every piece.
12
+ > application's primary database. Install is `bundle add railwatch` followed
13
+ > by `bin/rails generate railwatch:install`, which by default keeps
14
+ > everything in the app (two SQLite databases, a Puma writer process, the
15
+ > dashboard at `/railwatch`, open in development and password-protected
16
+ > elsewhere); with `--cloud` or a token option it sends to Railwatch Cloud
17
+ > instead, and an embedded install can export to the cloud as well. It
18
+ > writes the initializer, mounts the engine, adds a Kamal post-deploy hook
19
+ > and the Inertia browser client where the app has them, and wires the test
20
+ > matchers. `bin/rails railwatch:doctor` prints a ✓/✗ line for every piece.
16
21
 
17
22
  ## Docs
18
23
 
19
- - [README](README.md): what Railwatch is, the install, sampling, spans, profiling, tail sampling, and the Sentry option mapping table.
20
- - [Getting started](docs/getting-started.md): five-minute install for a Rails 8 app, where the token comes from, the three optional lines, and deploying under Kamal, Docker/Heroku/Render, or no deploy tool at all.
24
+ - [README](README.md): what Railwatch is, the install, embedded mode, and where each document fits.
25
+ - [Getting started](docs/getting-started.md): the two-command embedded install, the Railwatch Cloud install and where its token comes from, the three optional lines, and deploying under Kamal, Docker/Heroku/Render, or no deploy tool at all.
26
+ - [Embedded mode](docs/embedded.md): the default install — the dashboard inside the app, its two SQLite databases, dashboard authentication, the Puma writer process, maintenance without a job worker, disk reclaim, and optional export to Railwatch Cloud.
21
27
  - [Configuration](docs/configuration.md): every configuration option and its `RAILWATCH_*` environment variable, plus the full public facade, redaction, rejection, transport and buffering behaviour, and the rake tasks.
22
- - [Record types](docs/records.md): every record type the gem ships and every attribute on it, sourced from the code that builds it.
28
+ - [Record types](docs/records.md): every record type the gem ships and every attribute on it, including RubyLLM calls (tokens, cost, finish reason, tool calls, workflows), sourced from the code that builds it.
23
29
  - [Testing](docs/testing.md): the RSpec and Minitest matchers (`have_railwatch_queries`, `have_railwatch_n_plus_one`, `record_railwatch_span`, `record_railwatch_exception`, `have_railwatch_outgoing_requests`) and a CI performance-gate recipe.
24
30
  - [Production source maps](docs/source-maps.md): hidden Vite maps, private upload before publishing assets, safe opt-in deletion, resolved browser stacks and default issue grouping.
25
- - [AI assistants and MCP](docs/ai-and-mcp.md): the MCP endpoint, how to get a token, paste-ready client configuration for Claude Code, Claude Desktop, Cursor, VS Code, and Zed, and every tool, prompt, and resource the server exposes.
31
+ - [AI assistants and MCP](docs/ai-and-mcp.md): Railwatch Cloud's MCP endpoint, how to get a token, paste-ready client configuration for Claude Code, Claude Desktop, Cursor, VS Code, and Zed, and every tool, prompt, and resource the server exposes.
26
32
  - [Replacing Sentry](docs/replacing-sentry.md): a step-by-step migration — removing the gems, porting each option, rewriting each call site, breadcrumbs, spans, profiling, attachments, `before_send`, fingerprints, and release health.
27
33
  - [Coming from Laravel Nightwatch](docs/replacing-nightwatch.md): the record-type mapping, what "execution" means in Railwatch, sampling parity, and the facade method names in Ruby.
28
34
  - [Self-hosting](docs/self-hosting.md): pointing the gem at a self-hosted Railwatch Cloud, creating a token there, and checking the connection.
29
- - [Troubleshooting](docs/troubleshooting.md): every failure mode paired with the `railwatch:doctor` line it shows up as — no records, doubled scheduled tasks, WebMock in specs, Puma fork, tail-sampling memory, missing profiler, missing deploy marker, Kamal hook, log search, empty tenants.
35
+ - [Troubleshooting](docs/troubleshooting.md): every `railwatch:doctor` line, embedded and cloud, and the failure modes behind them — no records, doubled scheduled tasks, WebMock in specs, Puma fork, tail-sampling memory, missing profiler, missing deploy marker, Kamal hook, log search, empty tenants.
36
+ - [Security](docs/security.md): transport, capture defaults, the browser beacon, token handling, and application responsibilities.
30
37
  - [FAQ](docs/faq.md): overhead numbers and how they are measured, retention, what is redacted by default versus opt-in, SQLite, and what happens when the platform is unreachable.
31
38
 
32
39
  ## For coding agents
33
40
 
34
- - [AGENTS.md](AGENTS.md): how to install and use Railwatch from inside a Rails app — the facade methods, the spec matchers, `railwatch:doctor`, and the MCP hookup. Duplicated verbatim as [CLAUDE.md](CLAUDE.md).
41
+ - [AGENTS.md](AGENTS.md): how to install and use Railwatch from inside a Rails app — the facade methods, the spec matchers, `railwatch:doctor`, and the Railwatch Cloud MCP hookup.
35
42
 
36
43
  ## Optional
37
44