railwatch 0.1.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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +122 -0
  3. data/CHANGELOG.md +462 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +226 -0
  6. data/app/controllers/railwatch/beacon_controller.rb +254 -0
  7. data/config/routes.rb +5 -0
  8. data/docs/ai-and-mcp.md +227 -0
  9. data/docs/configuration.md +931 -0
  10. data/docs/faq.md +230 -0
  11. data/docs/getting-started.md +279 -0
  12. data/docs/records.md +834 -0
  13. data/docs/replacing-nightwatch.md +216 -0
  14. data/docs/replacing-sentry.md +573 -0
  15. data/docs/security.md +94 -0
  16. data/docs/self-hosting.md +60 -0
  17. data/docs/source-maps.md +60 -0
  18. data/docs/testing.md +175 -0
  19. data/docs/troubleshooting.md +319 -0
  20. data/lib/generators/railwatch/install/install_generator.rb +280 -0
  21. data/lib/generators/railwatch/install/templates/initializer.rb +54 -0
  22. data/lib/generators/railwatch/install/templates/post-deploy +98 -0
  23. data/lib/generators/railwatch/install/templates/railwatch.ts +658 -0
  24. data/lib/railwatch/attachments.rb +83 -0
  25. data/lib/railwatch/backtrace.rb +158 -0
  26. data/lib/railwatch/buffer.rb +122 -0
  27. data/lib/railwatch/clock.rb +25 -0
  28. data/lib/railwatch/configuration.rb +334 -0
  29. data/lib/railwatch/console.rb +48 -0
  30. data/lib/railwatch/context.rb +125 -0
  31. data/lib/railwatch/controller_helpers.rb +21 -0
  32. data/lib/railwatch/current.rb +32 -0
  33. data/lib/railwatch/engine.rb +144 -0
  34. data/lib/railwatch/execution.rb +367 -0
  35. data/lib/railwatch/faraday.rb +73 -0
  36. data/lib/railwatch/health.rb +188 -0
  37. data/lib/railwatch/job_tracing.rb +49 -0
  38. data/lib/railwatch/middleware/request.rb +289 -0
  39. data/lib/railwatch/minitest.rb +43 -0
  40. data/lib/railwatch/patches/inertia.rb +34 -0
  41. data/lib/railwatch/patches/net_http.rb +102 -0
  42. data/lib/railwatch/patches/rake_task.rb +88 -0
  43. data/lib/railwatch/patches/runner_command.rb +120 -0
  44. data/lib/railwatch/patches.rb +43 -0
  45. data/lib/railwatch/profiler.rb +270 -0
  46. data/lib/railwatch/record.rb +119 -0
  47. data/lib/railwatch/redactor.rb +67 -0
  48. data/lib/railwatch/release_detector.rb +97 -0
  49. data/lib/railwatch/reporter.rb +539 -0
  50. data/lib/railwatch/rspec.rb +139 -0
  51. data/lib/railwatch/sampler.rb +17 -0
  52. data/lib/railwatch/secret_safety.rb +62 -0
  53. data/lib/railwatch/sessions.rb +162 -0
  54. data/lib/railwatch/source_maps.rb +59 -0
  55. data/lib/railwatch/spec_helper.rb +147 -0
  56. data/lib/railwatch/sql_normalizer.rb +398 -0
  57. data/lib/railwatch/subscribers/base.rb +54 -0
  58. data/lib/railwatch/subscribers/broadcasts.rb +107 -0
  59. data/lib/railwatch/subscribers/cache.rb +107 -0
  60. data/lib/railwatch/subscribers/deprecations.rb +26 -0
  61. data/lib/railwatch/subscribers/exceptions.rb +304 -0
  62. data/lib/railwatch/subscribers/jobs.rb +282 -0
  63. data/lib/railwatch/subscribers/logs.rb +137 -0
  64. data/lib/railwatch/subscribers/mail.rb +42 -0
  65. data/lib/railwatch/subscribers/notifications.rb +36 -0
  66. data/lib/railwatch/subscribers/process_info.rb +98 -0
  67. data/lib/railwatch/subscribers/queries.rb +183 -0
  68. data/lib/railwatch/subscribers/requests.rb +94 -0
  69. data/lib/railwatch/subscribers/storage.rb +35 -0
  70. data/lib/railwatch/subscribers/users.rb +159 -0
  71. data/lib/railwatch/subscribers/views.rb +54 -0
  72. data/lib/railwatch/subscribers.rb +34 -0
  73. data/lib/railwatch/transport/http.rb +208 -0
  74. data/lib/railwatch/version.rb +5 -0
  75. data/lib/railwatch.rb +550 -0
  76. data/lib/tasks/railwatch_tasks.rake +289 -0
  77. data/llms.txt +38 -0
  78. metadata +157 -0
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railwatch
4
+ # Deploy metadata gathered for railwatch:deploy. The commit list is what lets
5
+ # the platform show a diff of what actually shipped between two deploys; it
6
+ # comes back empty inside an app container, which has the code but not the
7
+ # git history (the Kamal post-deploy hook runs on the deployer, which does).
8
+ module DeployMetadata
9
+ FORMAT = "%H%x1f%an%x1f%s%x1f%cI"
10
+ MAX_COMMITS = 50
11
+
12
+ module_function
13
+
14
+ # Newest first, capped at MAX_COMMITS.
15
+ def commits
16
+ return [] unless File.exist?(".git")
17
+
18
+ git_log.each_line.filter_map do |line|
19
+ sha, author, message, at = line.chomp.split("\x1f")
20
+ { sha: sha, author: author, message: message, at: at } unless sha.nil? || sha.empty?
21
+ end
22
+ end
23
+
24
+ def git_log
25
+ `git log -n #{MAX_COMMITS} --format='#{FORMAT}' 2>/dev/null`
26
+ rescue StandardError
27
+ ""
28
+ end
29
+ end
30
+
31
+ # Where this install's platform lives, derived from config.ingest_url:
32
+ # railwatch:token and railwatch:mcp point at the same host the gem already
33
+ # ships to, so a self-hosted app never gets told to visit railwatch.rebulk.com.
34
+ class Endpoints
35
+ HOSTED_HOST = "railwatch.rebulk.com"
36
+
37
+ def initialize(config)
38
+ @uri = URI.parse(config.ingest_url)
39
+ end
40
+
41
+ def host_url
42
+ port = @uri.port && @uri.port != @uri.default_port ? ":#{@uri.port}" : ""
43
+ "#{@uri.scheme}://#{@uri.host}#{port}"
44
+ end
45
+
46
+ def url(path)
47
+ "#{host_url}#{path}"
48
+ end
49
+
50
+ def self_hosted?
51
+ @uri.host != HOSTED_HOST
52
+ end
53
+ end
54
+ end
55
+
56
+ namespace :railwatch do
57
+ desc "Upload build source maps privately: railwatch:sourcemaps[directory,delete] (directory defaults to public; delete defaults to false)"
58
+ task :sourcemaps, [ :directory, :delete ] => :environment do |_task, args|
59
+ require "railwatch/source_maps"
60
+ directory = args[:directory] || ENV["RAILWATCH_SOURCEMAPS_DIR"] || "public"
61
+ delete = (args[:delete] || ENV["RAILWATCH_SOURCEMAPS_DELETE"]) == "true"
62
+ count = Railwatch::SourceMaps.new(Railwatch.config).upload(directory: directory, delete: delete)
63
+ puts "Uploaded #{count} source maps for #{Railwatch.config.deploy}#{' and deleted acknowledged files' if delete}"
64
+ end
65
+
66
+ desc "Check that the app can reach Railwatch with the configured token"
67
+ task status: :environment do
68
+ unless Railwatch.config.token.present?
69
+ abort "RAILWATCH_TOKEN is not set"
70
+ end
71
+ transport = Railwatch::Transport::Http.new(Railwatch.config)
72
+ if transport.ping
73
+ puts "Railwatch OK: #{Railwatch.config.ingest_url} (deploy=#{Railwatch.config.deploy || 'unset'}, server=#{Railwatch.config.server})"
74
+ else
75
+ abort "Railwatch unreachable at #{Railwatch.config.ingest_url}"
76
+ end
77
+ end
78
+
79
+ desc "Check a Railwatch install end to end: token, ingest, middleware, routes, deploy, hooks, test helpers"
80
+ task doctor: :environment do
81
+ config = Railwatch.config
82
+ blockers = []
83
+ check = lambda do |ok, label, detail, fatal: false|
84
+ puts "#{ok ? "✓" : "✗"} #{label}: #{detail}"
85
+ blockers << label if !ok && fatal
86
+ ok
87
+ end
88
+
89
+ token = config.token.to_s
90
+ check.call(!token.empty?, "token",
91
+ token.empty? ? "RAILWATCH_TOKEN is not set" : Railwatch::SecretSafety.token_preview(token),
92
+ fatal: true)
93
+
94
+ exposed_token_files = Railwatch::SecretSafety.tracked_plaintext_token_files(root: Rails.root)
95
+ check.call(exposed_token_files.empty?, "token storage",
96
+ exposed_token_files.empty? ? "no tracked plaintext Railwatch token found" :
97
+ "plaintext token found in tracked file(s): #{exposed_token_files.join(', ')}",
98
+ fatal: true)
99
+
100
+ ingest = URI.parse(config.ingest_url) rescue nil
101
+ check.call(ingest.is_a?(URI::HTTP), "ingest url", config.ingest_url)
102
+
103
+ transport_security = config.ingest_url_allowed?
104
+ check.call(transport_security, "ingest transport security",
105
+ if ingest&.scheme == "https"
106
+ "HTTPS with certificate verification"
107
+ elsif transport_security
108
+ "plain HTTP explicitly allowed for #{ingest.host}"
109
+ else
110
+ "plain HTTP refused; use HTTPS or set RAILWATCH_ALLOW_HTTP=true"
111
+ end)
112
+
113
+ check.call(Railwatch::Transport::Http.new(config).ping, "ingest reachable",
114
+ "GET #{URI.join(config.ingest_url, '/ingest/ping')}", fatal: true)
115
+
116
+ middleware = Rails.application.middleware.map(&:name)
117
+ position = middleware.index("Railwatch::Middleware::Request")
118
+ check.call(!position.nil?, "request middleware",
119
+ position ? "Railwatch::Middleware::Request at position #{position}" : "not in the middleware stack")
120
+
121
+ mount = Rails.application.routes.routes.find { |r| r.app.respond_to?(:app) && r.app.app == Railwatch::Engine }
122
+ beacon = Railwatch::Engine.routes.routes.any? { |r| r.defaults[:controller] == "railwatch/beacon" && r.defaults[:action] == "create" }
123
+ check.call(!mount.nil? && beacon, "engine mounted",
124
+ mount ? "POST #{mount.path.spec}/beacon -> railwatch/beacon#create" : %(add `mount Railwatch::Engine, at: "/railwatch"` to config/routes.rb))
125
+
126
+ source = config.deploy_source
127
+ if source == "config/initializers/railwatch.rb"
128
+ detected_source = nil
129
+ detected = Railwatch::ReleaseDetector.detect(project_root: Rails.root) { |found| detected_source = found }
130
+ source = detected_source if detected == config.deploy
131
+ end
132
+ check.call(config.deploy.present?, "deploy",
133
+ config.deploy.present? ? "#{config.deploy} (from #{source})" : "none: set RAILWATCH_DEPLOY")
134
+
135
+ check.call(true, "sample rates", config.sample.map { |kind, rate| "#{kind}=#{rate}" }.join(" "))
136
+ check.call(true, "ignored record types", config.ignore.empty? ? "none" : config.ignore.join(", "))
137
+
138
+ check.call(true, "interactive sessions",
139
+ "console=#{config.capture_console ? 'captured' : 'quiet'} " \
140
+ "runner scratch paths=#{config.interactive_runner_paths.join(' ')} " \
141
+ "(a typed/piped runner ships its command record, not its exception)")
142
+
143
+ hook = Rails.root.join(".kamal/hooks/post-deploy")
144
+ check.call(hook.exist? && hook.read.include?("railwatch"), "kamal post-deploy hook",
145
+ hook.exist? ? hook.to_s : "not found (only needed when deploying with Kamal)")
146
+
147
+ client = Rails.root.join("app/frontend/lib/railwatch.ts")
148
+ check.call(client.exist?, "browser client",
149
+ client.exist? ? "#{client} (call startRailwatch() from your Inertia entrypoint)" : "not found (only needed for Inertia visit timing)")
150
+
151
+ # The client file existing is not the same as it running: startRailwatch()
152
+ # has to be called from an entrypoint or no visit is ever timed.
153
+ entrypoints = Dir[Rails.root.join("app/frontend/entrypoints/*")].select { |f| File.file?(f) }
154
+ started = entrypoints.select { |f| File.read(f).include?("startRailwatch") }
155
+ check.call(started.any?, "browser client imported",
156
+ if started.any?
157
+ started.map { |f| Pathname.new(f).relative_path_from(Rails.root).to_s }.join(", ")
158
+ elsif entrypoints.any?
159
+ "no entrypoint in app/frontend/entrypoints calls startRailwatch()"
160
+ else
161
+ "no app/frontend/entrypoints (only needed for Inertia visit timing)"
162
+ end)
163
+
164
+ backend = Railwatch::Profiler.backend
165
+ check.call(!backend.nil?, "profiler backend",
166
+ backend || %(none -- add `gem "vernier"` (Ruby >= 3.2) or `gem "stackprof"` to profile slow executions))
167
+
168
+ rails_helper = Rails.root.join("spec/rails_helper.rb")
169
+ test_helper = Rails.root.join("test/test_helper.rb")
170
+ wired = if rails_helper.exist? && rails_helper.read.include?("railwatch/rspec")
171
+ %(spec/rails_helper.rb requires "railwatch/rspec")
172
+ elsif test_helper.exist? && test_helper.read.include?("railwatch/minitest")
173
+ %(test/test_helper.rb requires "railwatch/minitest")
174
+ end
175
+ check.call(!wired.nil?, "test matchers", wired || %(add `require "railwatch/rspec"` (or "railwatch/minitest") -- see docs/testing.md))
176
+
177
+ abort "\nrailwatch:doctor failed: #{blockers.join(', ')}" if blockers.any?
178
+ puts "\nRailwatch is wired up."
179
+ end
180
+
181
+ desc "Print where to create an ingest token for this app's Railwatch platform"
182
+ task token: :environment do
183
+ base = Railwatch::Endpoints.new(Railwatch.config)
184
+ puts <<~TEXT
185
+ Railwatch platform: #{base.host_url}
186
+
187
+ 1. Sign in (or sign up) at #{base.url("/dashboard")}
188
+ 2. New application, then New environment (production, staging, ...)
189
+ 3. The environment's token (lt_...) is shown once, right after it is created.
190
+
191
+ Then set it where this app reads its environment:
192
+
193
+ RAILWATCH_TOKEN=lt_...#{"\n RAILWATCH_INGEST_URL=#{base.host_url}" if base.self_hosted?}
194
+
195
+ With Kamal: bin/rails generate railwatch:install --prompt-token --kamal-secrets
196
+ Verify: bin/rails railwatch:doctor
197
+
198
+ An existing environment's token can be rotated from its Settings page;
199
+ the prefix shown in the UI is the first 12 characters of the token.
200
+ TEXT
201
+ end
202
+
203
+ desc "Print ready-to-paste MCP client configuration for this app's Railwatch platform"
204
+ task mcp: :environment do
205
+ base = Railwatch::Endpoints.new(Railwatch.config)
206
+ mcp = base.url("/mcp")
207
+ token = "lnt_your_token_here"
208
+ puts <<~TEXT
209
+ Railwatch MCP server: #{mcp}
210
+
211
+ An MCP token is per person, not per app: Settings -> Profile -> "API & MCP
212
+ token" at #{base.url("/settings/profile")}. It starts with lnt_ and is
213
+ shown once. Everything below is scoped to whatever accounts that user
214
+ belongs to.
215
+
216
+ Claude Code
217
+ claude mcp add railwatch --transport http #{mcp} --header "Authorization: Bearer #{token}"
218
+
219
+ Claude Desktop (claude_desktop_config.json) -- via the mcp-remote shim
220
+ {
221
+ "mcpServers": {
222
+ "railwatch": {
223
+ "command": "npx",
224
+ "args": ["-y", "mcp-remote", "#{mcp}", "--header", "Authorization: Bearer #{token}"]
225
+ }
226
+ }
227
+ }
228
+
229
+ Cursor (.cursor/mcp.json)
230
+ {
231
+ "mcpServers": {
232
+ "railwatch": {
233
+ "url": "#{mcp}",
234
+ "headers": { "Authorization": "Bearer #{token}" }
235
+ }
236
+ }
237
+ }
238
+
239
+ VS Code (.vscode/mcp.json)
240
+ {
241
+ "servers": {
242
+ "railwatch": {
243
+ "type": "http",
244
+ "url": "#{mcp}",
245
+ "headers": { "Authorization": "Bearer #{token}" }
246
+ }
247
+ }
248
+ }
249
+
250
+ Zed (settings.json) -- via the mcp-remote shim
251
+ {
252
+ "context_servers": {
253
+ "railwatch": {
254
+ "source": "custom",
255
+ "command": "npx",
256
+ "args": ["-y", "mcp-remote", "#{mcp}", "--header", "Authorization: Bearer #{token}"]
257
+ }
258
+ }
259
+ }
260
+
261
+ Test it without a client
262
+ curl -sS #{mcp} \\
263
+ -H "Authorization: Bearer #{token}" \\
264
+ -H "Content-Type: application/json" \\
265
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
266
+
267
+ What it exposes: tools (applications, issues, slow routes, executions,
268
+ logs, profiles, alerts, query plans, deploys, release health), prompts
269
+ (triage_issue, slow_route, daily_summary), and resources -- including
270
+ Railwatch's own docs at railwatch://docs/<name>. See docs/ai-and-mcp.md.
271
+ TEXT
272
+ end
273
+
274
+ desc "Send deploy metadata to Railwatch: rake railwatch:deploy[ref,name,url]"
275
+ task :deploy, [ :ref, :name, :url ] => :environment do |_t, args|
276
+ deploy = Railwatch.config.deploy or abort "RAILWATCH_DEPLOY (or KAMAL_VERSION) is not set"
277
+ abort "Plain HTTP ingest is disabled; use HTTPS or set RAILWATCH_ALLOW_HTTP=true" unless Railwatch.config.ingest_url_allowed?
278
+ uri = URI.join(Railwatch.config.ingest_url, "/ingest/deploys")
279
+ req = Net::HTTP::Post.new(uri)
280
+ req["Authorization"] = "Bearer #{Railwatch.config.token}"
281
+ req["Content-Type"] = "application/json"
282
+ req.body = JSON.generate(deploy: deploy, ref: args[:ref] || (`git rev-parse HEAD 2>/dev/null`.strip.presence),
283
+ name: args[:name], url: args[:url], server: Railwatch.config.server, timestamp: Time.now.utc.iso8601(6),
284
+ performer: ENV["KAMAL_PERFORMER"], destination: ENV["KAMAL_DESTINATION"],
285
+ service: ENV["KAMAL_SERVICE"], commits: Railwatch::DeployMetadata.commits)
286
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: 5, read_timeout: 10) { |h| h.request(req) }
287
+ res.is_a?(Net::HTTPSuccess) ? puts("Deploy #{deploy} recorded") : abort("Deploy failed: #{res.code} #{res.body}")
288
+ end
289
+ end
data/llms.txt ADDED
@@ -0,0 +1,38 @@
1
+ # Railwatch
2
+
3
+ > Railwatch is a Ruby gem that instruments a Rails application end to end —
4
+ > requests, jobs, scheduled tasks, rake/runner commands, database queries and
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
9
+ > separate error tracker with one gem and one configuration block, costs
10
+ > 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.
16
+
17
+ ## Docs
18
+
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.
21
+ - [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.
23
+ - [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
+ - [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.
26
+ - [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
+ - [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
+ - [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.
30
+ - [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
+
32
+ ## For coding agents
33
+
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).
35
+
36
+ ## Optional
37
+
38
+ - [CHANGELOG](CHANGELOG.md): released versions and what changed in each.
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railwatch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cole Robertson
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-09-14 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '8.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
32
+ - !ruby/object:Gem::Dependency
33
+ name: base64
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '0.2'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '0.2'
46
+ description: Railwatch instruments a Rails app end to end (requests, jobs, queries,
47
+ exceptions, cache, mail, broadcasts, outgoing HTTP, logs) and ships linked events
48
+ to Railwatch Cloud.
49
+ email:
50
+ - cole@rebulk.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - AGENTS.md
56
+ - CHANGELOG.md
57
+ - MIT-LICENSE
58
+ - README.md
59
+ - app/controllers/railwatch/beacon_controller.rb
60
+ - config/routes.rb
61
+ - docs/ai-and-mcp.md
62
+ - docs/configuration.md
63
+ - docs/faq.md
64
+ - docs/getting-started.md
65
+ - docs/records.md
66
+ - docs/replacing-nightwatch.md
67
+ - docs/replacing-sentry.md
68
+ - docs/security.md
69
+ - docs/self-hosting.md
70
+ - docs/source-maps.md
71
+ - docs/testing.md
72
+ - docs/troubleshooting.md
73
+ - lib/generators/railwatch/install/install_generator.rb
74
+ - lib/generators/railwatch/install/templates/initializer.rb
75
+ - lib/generators/railwatch/install/templates/post-deploy
76
+ - lib/generators/railwatch/install/templates/railwatch.ts
77
+ - lib/railwatch.rb
78
+ - lib/railwatch/attachments.rb
79
+ - lib/railwatch/backtrace.rb
80
+ - lib/railwatch/buffer.rb
81
+ - lib/railwatch/clock.rb
82
+ - lib/railwatch/configuration.rb
83
+ - lib/railwatch/console.rb
84
+ - lib/railwatch/context.rb
85
+ - lib/railwatch/controller_helpers.rb
86
+ - lib/railwatch/current.rb
87
+ - lib/railwatch/engine.rb
88
+ - lib/railwatch/execution.rb
89
+ - lib/railwatch/faraday.rb
90
+ - lib/railwatch/health.rb
91
+ - lib/railwatch/job_tracing.rb
92
+ - lib/railwatch/middleware/request.rb
93
+ - lib/railwatch/minitest.rb
94
+ - lib/railwatch/patches.rb
95
+ - lib/railwatch/patches/inertia.rb
96
+ - lib/railwatch/patches/net_http.rb
97
+ - lib/railwatch/patches/rake_task.rb
98
+ - lib/railwatch/patches/runner_command.rb
99
+ - lib/railwatch/profiler.rb
100
+ - lib/railwatch/record.rb
101
+ - lib/railwatch/redactor.rb
102
+ - lib/railwatch/release_detector.rb
103
+ - lib/railwatch/reporter.rb
104
+ - lib/railwatch/rspec.rb
105
+ - lib/railwatch/sampler.rb
106
+ - lib/railwatch/secret_safety.rb
107
+ - lib/railwatch/sessions.rb
108
+ - lib/railwatch/source_maps.rb
109
+ - lib/railwatch/spec_helper.rb
110
+ - lib/railwatch/sql_normalizer.rb
111
+ - lib/railwatch/subscribers.rb
112
+ - lib/railwatch/subscribers/base.rb
113
+ - lib/railwatch/subscribers/broadcasts.rb
114
+ - lib/railwatch/subscribers/cache.rb
115
+ - lib/railwatch/subscribers/deprecations.rb
116
+ - lib/railwatch/subscribers/exceptions.rb
117
+ - lib/railwatch/subscribers/jobs.rb
118
+ - lib/railwatch/subscribers/logs.rb
119
+ - lib/railwatch/subscribers/mail.rb
120
+ - lib/railwatch/subscribers/notifications.rb
121
+ - lib/railwatch/subscribers/process_info.rb
122
+ - lib/railwatch/subscribers/queries.rb
123
+ - lib/railwatch/subscribers/requests.rb
124
+ - lib/railwatch/subscribers/storage.rb
125
+ - lib/railwatch/subscribers/users.rb
126
+ - lib/railwatch/subscribers/views.rb
127
+ - lib/railwatch/transport/http.rb
128
+ - lib/railwatch/version.rb
129
+ - lib/tasks/railwatch_tasks.rake
130
+ - llms.txt
131
+ homepage: https://railwatch.rebulk.com
132
+ licenses:
133
+ - MIT
134
+ metadata:
135
+ homepage_uri: https://railwatch.rebulk.com
136
+ source_code_uri: https://github.com/Rebulk/railwatch
137
+ changelog_uri: https://github.com/Rebulk/railwatch/blob/main/CHANGELOG.md
138
+ documentation_uri: https://github.com/Rebulk/railwatch/blob/main/README.md
139
+ rubygems_mfa_required: 'true'
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '3.4'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubygems_version: 3.6.2
155
+ specification_version: 4
156
+ summary: First-class monitoring for Rails applications.
157
+ test_files: []