rails_mind 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +20 -0
- data/CONTRIBUTING.md +81 -0
- data/LICENSE +21 -0
- data/README.md +59 -0
- data/docs/compatibility.md +66 -0
- data/docs/sdk.md +225 -0
- data/javascript/rails_mind.js +49 -0
- data/lib/generators/rails_mind/install/install_generator.rb +37 -0
- data/lib/generators/rails_mind/install/templates/initializer.rb.tt +20 -0
- data/lib/rails_mind/client.rb +123 -0
- data/lib/rails_mind/collector.rb +153 -0
- data/lib/rails_mind/configuration.rb +81 -0
- data/lib/rails_mind/context.rb +35 -0
- data/lib/rails_mind/diagnostic.rb +39 -0
- data/lib/rails_mind/installation_check.rb +38 -0
- data/lib/rails_mind/integrations/ahoy.rb +68 -0
- data/lib/rails_mind/integrations/open_telemetry.rb +77 -0
- data/lib/rails_mind/integrations/rails.rb +120 -0
- data/lib/rails_mind/railtie.rb +20 -0
- data/lib/rails_mind/redactor.rb +32 -0
- data/lib/rails_mind/transport.rb +49 -0
- data/lib/rails_mind/version.rb +3 -0
- data/lib/rails_mind.rb +34 -0
- data/lib/tasks/rails_mind.rake +15 -0
- metadata +108 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '087e7d8cc40b60a6ed2ab02feb5ffac9f7a6f1592eac30c964b792482630349f'
|
|
4
|
+
data.tar.gz: 1f073cdb04f317e08a486b14361f12faf84ebfdd0c785c075436896eecbc720c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 95b65914e0d950384712582cb3f4b294e4a04a99ec62c42db53fe2252424932879940997815e7bc3fdaab51689866376cf42a9a3bafb107e2c66e9d335d38adc
|
|
7
|
+
data.tar.gz: 848479498910d94b0f73529eacddb16639f8364585dfef7687d4711229a560e6b1a0e25b5654506d69650ff136acb14f5416883b74a44d694708374c875fdd5b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-09-25
|
|
4
|
+
|
|
5
|
+
Initial public release of the RailsMind client under the MIT license.
|
|
6
|
+
|
|
7
|
+
- Collect Rails request, error, and ActiveJob telemetry with bounded buffering,
|
|
8
|
+
redaction, retries, and best-effort delivery.
|
|
9
|
+
- Track explicit business events and propagate visitor, user, account, and trace
|
|
10
|
+
context through supported request and job integrations.
|
|
11
|
+
- Integrate with existing Ahoy, Flipper, and OpenTelemetry installations, plus
|
|
12
|
+
an optional Ahoy/Turbo browser adapter.
|
|
13
|
+
- Install with a Rails generator, inspect configuration with `rails_mind:doctor`,
|
|
14
|
+
and check ingestion with `rails_mind:verify`.
|
|
15
|
+
- Default to `https://railsmind.com` and detect deployed Git revisions from
|
|
16
|
+
supported hosting metadata or a `REVISION` file.
|
|
17
|
+
|
|
18
|
+
This is an early release. See [compatibility](docs/compatibility.md) and
|
|
19
|
+
[delivery and privacy limits](docs/sdk.md#bounds-privacy-and-outages) before use.
|
|
20
|
+
The separate hosted RailsMind service remains proprietary.
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Developing RailsMind
|
|
2
|
+
|
|
3
|
+
This repository owns the MIT-licensed client SDK, browser adapter, tests, and SDK
|
|
4
|
+
documentation. The hosted RailsMind application and its internal integration
|
|
5
|
+
sample are maintained separately and remain proprietary.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
Use Ruby 4.0.6 and Node 22 for the currently verified combination.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
git clone https://github.com/cmartyn/rails_mind.git
|
|
13
|
+
cd rails_mind
|
|
14
|
+
bundle install
|
|
15
|
+
bundle exec rake test
|
|
16
|
+
node --test javascript/rails_mind.test.js
|
|
17
|
+
bundle exec ruby script/package_smoke.rb
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
No database or hosted service is required for this repository's tests. See
|
|
21
|
+
[compatibility](docs/compatibility.md) for coverage and limits.
|
|
22
|
+
|
|
23
|
+
## Testing a development revision
|
|
24
|
+
|
|
25
|
+
Applications normally install a released version from RubyGems.org. To evaluate
|
|
26
|
+
an unreleased change, use a reviewed full commit SHA:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
gem "rails_mind", git: "https://github.com/cmartyn/rails_mind.git", ref: "<full commit SHA>"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Commit the application's lockfile and run its integration tests when upgrading.
|
|
33
|
+
A local `path:` dependency can be used temporarily while developing both
|
|
34
|
+
projects; do not commit that override. GitHub credentials are not required to
|
|
35
|
+
read this public repository.
|
|
36
|
+
|
|
37
|
+
## Packaging and releases
|
|
38
|
+
|
|
39
|
+
`bundle exec ruby script/package_smoke.rb` builds a temporary gem and exercises its
|
|
40
|
+
packaged installer and browser files. `bundle exec rake build` writes the gem to
|
|
41
|
+
`pkg/`. Neither command publishes anything.
|
|
42
|
+
|
|
43
|
+
Publishing uses [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/)
|
|
44
|
+
from `.github/workflows/release.yml`, with no stored RubyGems API key. The gem's
|
|
45
|
+
owner configures these values on RubyGems.org:
|
|
46
|
+
|
|
47
|
+
| Field | Value |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Gem name | `rails_mind` |
|
|
50
|
+
| Repository owner | `cmartyn` |
|
|
51
|
+
| Repository name | `rails_mind` |
|
|
52
|
+
| Workflow filename | `release.yml` |
|
|
53
|
+
| GitHub environment | `release` |
|
|
54
|
+
|
|
55
|
+
For the first release, create a pending trusted publisher under the intended
|
|
56
|
+
owner's RubyGems.org profile. A successful first push registers the gem and assigns
|
|
57
|
+
that account ownership. The repository's `release` environment must allow release
|
|
58
|
+
tags; configure required reviewers when additional approval is desired.
|
|
59
|
+
|
|
60
|
+
Before releasing, update `lib/rails_mind/version.rb` and `CHANGELOG.md`, run the
|
|
61
|
+
checks above, complete staging validation appropriate to the documented
|
|
62
|
+
compatibility promise, and merge the release commit into `main`. Keep known
|
|
63
|
+
limitations explicit; a passing unit test suite does not certify a deployed
|
|
64
|
+
customer application.
|
|
65
|
+
|
|
66
|
+
Create an annotated tag matching the gem version and push it:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
git tag -a v0.1.0 -m "Release rails_mind 0.1.0"
|
|
70
|
+
git push origin v0.1.0
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The workflow verifies the tag/version, reruns the Ruby, JavaScript, and package
|
|
74
|
+
checks, then publishes to RubyGems.org. Confirm the published version can be
|
|
75
|
+
installed before announcing the release. Published version numbers cannot be
|
|
76
|
+
reused; fixes need a new version.
|
|
77
|
+
|
|
78
|
+
## Origin
|
|
79
|
+
|
|
80
|
+
The SDK was extracted into its own repository on 2026-09-15. Its earlier history
|
|
81
|
+
remains in the private application repository; only the client is licensed here.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RailsMind contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# RailsMind SDK
|
|
2
|
+
|
|
3
|
+
The MIT-licensed Ruby client for [RailsMind](https://railsmind.com). One bounded
|
|
4
|
+
collector for Rails requests, errors, jobs, Ahoy events, Flipper observations,
|
|
5
|
+
and selected OpenTelemetry spans. Version 0.1.0 is an early release; see
|
|
6
|
+
[compatibility and validation limits](docs/compatibility.md).
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
gem "rails_mind", "~> 0.1.0"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Install from RubyGems.org and commit your application's `Gemfile.lock` to pin the
|
|
13
|
+
resolved version. This repository contains the open-source client library. The
|
|
14
|
+
hosted RailsMind service is a separate, proprietary application; its source is
|
|
15
|
+
not included or licensed by this repository. See [development](CONTRIBUTING.md)
|
|
16
|
+
and [release notes](CHANGELOG.md).
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
bundle install
|
|
20
|
+
bin/rails generate rails_mind:install --plan
|
|
21
|
+
bin/rails generate rails_mind:install
|
|
22
|
+
bin/rails rails_mind:doctor
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Set **`RAILS_MIND_KEY`** for the destination application environment. It is the only
|
|
26
|
+
required setting; collection stays off without it. The endpoint defaults to
|
|
27
|
+
`https://railsmind.com`. Release IDs are detected from hosting metadata or the
|
|
28
|
+
app's deployed `REVISION` file; telemetry still works when none is available.
|
|
29
|
+
`RAILS_MIND_ENDPOINT` and `RAILS_MIND_RELEASE` are optional overrides. See
|
|
30
|
+
[configuration and release detection](docs/sdk.md#configuration-and-release-detection).
|
|
31
|
+
|
|
32
|
+
After setting the key, run `bin/rails rails_mind:verify` on the app's
|
|
33
|
+
server. Match its check ID in RailsMind **Setup** and wait for **Check processed**.
|
|
34
|
+
Then visit the app to confirm real traffic. This command sends one labeled
|
|
35
|
+
diagnostic check, consumes one event of quota, and leaves business/performance
|
|
36
|
+
metrics alone. It requires a hosted app with installation-check support;
|
|
37
|
+
`rails_mind:doctor` remains read-only. See the
|
|
38
|
+
[verification guide](docs/sdk.md#verify-the-connection) for failure states
|
|
39
|
+
and the limits of this check.
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
RailsMind.with_context(user_id: current_user.id.to_s, account_id: current_account.id.to_s) do
|
|
43
|
+
RailsMind.track("Invoice paid", invoice_id: invoice.id, amount_cents: invoice.amount_cents)
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See [installation and privacy](docs/sdk.md) and [compatibility](docs/compatibility.md).
|
|
48
|
+
|
|
49
|
+
Tests:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
bundle install
|
|
53
|
+
bundle exec rake test
|
|
54
|
+
node --test javascript/rails_mind.test.js
|
|
55
|
+
bundle exec ruby script/package_smoke.rb
|
|
56
|
+
ruby script_benchmark.rb
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Business/request/error events are unsampled, but delivery is best effort. Check `RailsMind.stats` for losses. The SDK does not guarantee durable business-event counts; use a transactional outbox when that is required.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Compatibility and dependency decisions
|
|
2
|
+
|
|
3
|
+
Verified on 2026-09-14. Version numbers come from RubyGems release metadata and the actual installed libraries used in tests. The initial promise is narrow: coexistence with current versions, not importing historical data or replacing existing services.
|
|
4
|
+
|
|
5
|
+
| Foundation | Verified version / license | Current coverage |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Ruby / Rails | 4.0.6 / 8.1.3.1; Ruby / MIT | Real Rails sample and notification/error/ActiveJob tests. Gem allows Ruby ≥3.3, Rails ≥7.2 and <9; older combinations are candidates, not yet certified. |
|
|
8
|
+
| Ahoy | [5.5.0, MIT](https://rubygems.org/gems/ahoy_matey) | Actual custom store and mirror tests; default store data/return value preserved; stable IDs; no hosted-only analytics tables. |
|
|
9
|
+
| Flipper | [1.4.2, MIT](https://rubygems.org/gems/flipper) | Actual group targeting and adapter identity preserved; ActiveSupport observation only. No managed flag mode. |
|
|
10
|
+
| OpenTelemetry SDK | [1.13.0, Apache-2.0](https://rubygems.org/gems/opentelemetry-sdk) | Existing exporter/provider and sampler coexistence tested; sampled-out traces do not suppress errors/business events. |
|
|
11
|
+
| OTel Rails components | [0.42.0 aggregate, Apache-2.0](https://rubygems.org/gems/opentelemetry-instrumentation-rails) | Sample selects Rack 0.31.1, ActionPack 0.18.1, ActionView 0.13.0, ActiveJob 0.13.0. Request/SQL/job telemetry through actual libraries. |
|
|
12
|
+
| OTel PG / Net::HTTP | [0.37.0](https://rubygems.org/gems/opentelemetry-instrumentation-pg) / [0.29.0](https://rubygems.org/gems/opentelemetry-instrumentation-net_http), Apache-2.0 | PG statements omitted; exporter allowlist discards raw SQL/HTTP payloads. Net::HTTP auto-instrumentation installed in the sample; external HTTP service interaction not required for the demo. |
|
|
13
|
+
| GoodJob | [4.19.2, MIT](https://rubygems.org/gems/good_job) | Hosted/sample backend; ActiveJob context integration. Sample uses inline execution, so multi-process GoodJob queue/retry behavior needs staging verification. |
|
|
14
|
+
| Turbo | [turbo-rails 2.0.23, MIT](https://rubygems.org/gems/turbo-rails) | Browser adapter contract tests and a real Chromium navigation check cover visits, cached previews, back/forward restoration, Frames, repeat installation, late start, and explicit events. See the release verification below for scope. |
|
|
15
|
+
| Other error reporters | Rails error interface; no mandatory SDK | Subscriber coexistence tested with a second subscriber. Sentry/Honeybadger/Bugsnag/Rollbar detection only; no vendor-specific version certification. |
|
|
16
|
+
| Sidekiq, Solid Queue, Delayed Job | Detection only | ActiveJob jobs inherit the shared integration when used through Rails; backend-specific APIs/direct jobs are not covered. |
|
|
17
|
+
| Bullet / Prosopite | Optional, deferred dependency | Use in development/isolated reproduction. No automatic production scans or forks. |
|
|
18
|
+
| Field Test | Optional, deferred dependency | Detect installed version; no assignment/exposure/import or experiment analysis in this release. |
|
|
19
|
+
|
|
20
|
+
Current Ahoy 5.5 and OTel 1.13 require Ruby ≥3.3. No integration gems are mandatory at SDK runtime beyond Rails and Net::HTTP. Customers select optional dependencies; the installer does not silently add them. MIT/Apache licenses allow this extension strategy without maintaining forks.
|
|
21
|
+
|
|
22
|
+
The preferred foundations remain suitable for the pilot. Ahoy's store model provides a supported buffered-delivery seam but its database associations do not exist in hosted-only mode. Flipper's authoritative configuration remains in the customer's existing adapter; managed flags need an explicit future mode. OTel Ruby tracing supplies the useful mature signal here; broad metrics/logs/exporter parity is deliberately outside this prototype. [Ahoy store contract](https://github.com/ankane/ahoy#data-stores), [Flipper observation contract](https://www.flippercloud.io/docs/instrumentation), [OpenTelemetry Ruby](https://opentelemetry.io/docs/languages/ruby/).
|
|
23
|
+
|
|
24
|
+
Tests are in `test` and browser lifecycle tests in `javascript`. Historical
|
|
25
|
+
end-to-end Rails checks used a separate internal sample application that is not
|
|
26
|
+
part of this public repository. Re-run the matrix when bumping dependencies.
|
|
27
|
+
No external telemetry service, vendor account, historical migration, production
|
|
28
|
+
overhead budget, or published gem installation has been verified by those checks.
|
|
29
|
+
|
|
30
|
+
The local sample-to-hosted HTTP/GoodJob path was exercised with 101 accepted and processed events across all seven signal kinds, zero SDK losses, matched error/request account+trace correlation, and filtered credential probes. This verifies the local wire contract as well as isolated adapters; it does not certify a deployed customer application.
|
|
31
|
+
|
|
32
|
+
## Run the checks
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
bundle install
|
|
36
|
+
bundle exec rake test
|
|
37
|
+
node --test javascript/rails_mind.test.js
|
|
38
|
+
bundle exec ruby script/package_smoke.rb
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
CI runs Ruby 4.0.6 / Rails 8.1.3.1 on Linux, plus browser adapter contract tests
|
|
42
|
+
on Node 22. The package smoke check builds and installs a gem in a temporary directory, loads that copy,
|
|
43
|
+
and runs the installer against its packaged templates and browser adapter.
|
|
44
|
+
The sample app remains in the hosted application's repository with its own lockfile.
|
|
45
|
+
|
|
46
|
+
## 0.1.0 release verification
|
|
47
|
+
|
|
48
|
+
Rechecked on 2026-09-25 with Ruby 4.0.6, the locked Rails 8.1.3.1 dependencies,
|
|
49
|
+
and Node 22:
|
|
50
|
+
|
|
51
|
+
- Ruby suite: 40 tests, 377 assertions, no failures or skips.
|
|
52
|
+
- Browser adapter contract suite: 3 tests passed.
|
|
53
|
+
- Built-gem installation smoke check: 2 tests, 13 assertions; the installed gem
|
|
54
|
+
loaded its own code, generator templates, and browser adapter.
|
|
55
|
+
- A localhost fixture using the actual JavaScript distributed with
|
|
56
|
+
`turbo-rails` 2.0.23 passed in Chromium: initial load, repeated installation,
|
|
57
|
+
completed visits, cached previews, back/forward restoration, labeled and
|
|
58
|
+
unlabeled Frames, form navigation, explicit business outcomes, pageview
|
|
59
|
+
opt-out, stopping listeners, and late initialization. Cached previews and
|
|
60
|
+
Frame loads produced no extra pageviews; form navigation inferred no business
|
|
61
|
+
outcome. Ahoy was a recording stub, so this check validates browser lifecycle
|
|
62
|
+
behavior rather than Ahoy's server endpoint, cookies, or CSRF handling.
|
|
63
|
+
|
|
64
|
+
These checks cover the client package and its documented integrations locally.
|
|
65
|
+
They do not certify other browsers, every allowed Ruby/Rails combination,
|
|
66
|
+
production delivery guarantees, or a deployed customer application's setup.
|
data/docs/sdk.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Customer SDK
|
|
2
|
+
|
|
3
|
+
The gem is `rails_mind`, namespace `RailsMind`. Install the MIT-licensed client
|
|
4
|
+
from RubyGems.org and commit the application lockfile to retain the selected
|
|
5
|
+
version. The hosted RailsMind service is a separate, proprietary application.
|
|
6
|
+
Version 0.1.0 is an early release with the [documented compatibility limits](compatibility.md).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
# Customer Gemfile
|
|
12
|
+
gem "rails_mind", "~> 0.1.0"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
bundle install
|
|
17
|
+
bin/rails generate rails_mind:install --plan
|
|
18
|
+
bin/rails generate rails_mind:install
|
|
19
|
+
bin/rails rails_mind:doctor
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The plan inspects `Gemfile.lock` and prints selected versions, coexistence decisions, and missing options. Installation adds one initializer, preserves an existing initializer even with `--force`, and does not change migrations, dependencies, Ahoy stores, Flipper adapters, or OpenTelemetry providers. The doctor is read-only and does not contact the server or print credentials.
|
|
23
|
+
|
|
24
|
+
## Configuration and release detection
|
|
25
|
+
|
|
26
|
+
Set only the environment-scoped ingest key on the customer's server:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
RAILS_MIND_KEY=<environment-scoped-credential>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The endpoint defaults to `https://railsmind.com`. To use a local or self-hosted
|
|
33
|
+
RailsMind instance, set `RAILS_MIND_ENDPOINT` (for example,
|
|
34
|
+
`http://127.0.0.1:3000`). An unset or whitespace-only override uses the hosted
|
|
35
|
+
default. HTTPS is required except for localhost/loopback development. The key
|
|
36
|
+
controls application/environment scope; never give it to browser JavaScript.
|
|
37
|
+
Collection stays off without a nonblank key. `apm`, `errors`, `analytics`, and
|
|
38
|
+
`flags` can each be disabled in the initializer.
|
|
39
|
+
|
|
40
|
+
Release detection uses the first nonblank value in this order:
|
|
41
|
+
|
|
42
|
+
1. `RAILS_MIND_RELEASE` — optional explicit override.
|
|
43
|
+
2. `GIT_REVISION` — existing deployment convention.
|
|
44
|
+
3. `RENDER_GIT_COMMIT` — [Render's automatic runtime commit](https://render.com/docs/environment-variables).
|
|
45
|
+
4. `RAILWAY_GIT_COMMIT_SHA` — [Railway deployments triggered by GitHub](https://docs.railway.com/variables/reference).
|
|
46
|
+
5. `HEROKU_BUILD_COMMIT`, then `HEROKU_SLUG_COMMIT` — [Heroku dyno metadata](https://devcenter.heroku.com/articles/dyno-metadata); metadata features must be enabled, and the slug variable is deprecated.
|
|
47
|
+
6. `KAMAL_VERSION` — [Kamal's application-container version](https://github.com/basecamp/kamal/blob/master/lib/kamal/commands/app.rb), only when it is a full 40- or 64-character Git commit SHA.
|
|
48
|
+
7. The `REVISION` file inside `Rails.root` — for [Hatchbox deployments](https://jumpstartrails.com/discussions/appsignal-deploy-tracking) and other deployers that write this file.
|
|
49
|
+
|
|
50
|
+
Values are trimmed. The revision file is read once when configuration is created,
|
|
51
|
+
with a 500-byte limit; absent, blank, oversized, unreadable, or invalidly encoded
|
|
52
|
+
files are ignored. Detection does not run Git or inspect the working directory.
|
|
53
|
+
If no release is available, it remains unset and telemetry is still delivered.
|
|
54
|
+
Provider-specific metadata availability depends on the deployment type; there
|
|
55
|
+
is no universal runtime variable. Build-only and instance-specific IDs are not
|
|
56
|
+
used automatically. `HEROKU_RELEASE_VERSION`, arbitrary Kamal version labels,
|
|
57
|
+
and [Fly.io's `FLY_IMAGE_REF`](https://fly.io/docs/machines/runtime-environment/)
|
|
58
|
+
are also ignored: they can identify deployments or images rather than Git
|
|
59
|
+
revisions, while Assist uses releases to locate the corresponding code. Explicit
|
|
60
|
+
release overrides should identify a commit or ref in the connected repository. For DigitalOcean App Platform, commit metadata is a
|
|
61
|
+
[bindable value](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/),
|
|
62
|
+
not an automatically named runtime variable; optionally bind it to `GIT_REVISION`.
|
|
63
|
+
|
|
64
|
+
Explicit `config.endpoint` and `config.release` assignments still take precedence.
|
|
65
|
+
When upgrading an existing generated initializer, remove the old endpoint/release
|
|
66
|
+
assignments that directly read `ENV` so they do not overwrite the new defaults
|
|
67
|
+
with `nil` or bypass platform detection. The installer preserves existing files.
|
|
68
|
+
The doctor reports whether the effective endpoint, key, and release are present,
|
|
69
|
+
without printing credential values or contacting the server.
|
|
70
|
+
|
|
71
|
+
## Verify the connection
|
|
72
|
+
|
|
73
|
+
From the customer app's server, using the same environment settings as the running app:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
bin/rails rails_mind:verify
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This sends one labeled installation check through the SDK's redaction, collector,
|
|
80
|
+
HTTP transport, and the normal ingestion queue. It prints a check ID and exits
|
|
81
|
+
successfully only when delivery is confirmed without drops. It has a ten-second
|
|
82
|
+
delivery deadline; a timeout can still mean the server received the check, so look
|
|
83
|
+
for its ID before retrying. Credentials are never printed. `before_send` may
|
|
84
|
+
suppress the check; changing its kind or ID is rejected. The optional signal
|
|
85
|
+
switches do not disable this explicitly requested check.
|
|
86
|
+
|
|
87
|
+
Open **Setup** in RailsMind, select the environment that owns the key, and match
|
|
88
|
+
the ID under **Latest installation check**. **Check processed** confirms the path
|
|
89
|
+
through the background worker. HTTP delivery alone does not confirm processing.
|
|
90
|
+
Then visit a page in your app and confirm **Recent telemetry** under app activity:
|
|
91
|
+
the command runs in its own process and cannot certify the configuration of an
|
|
92
|
+
already-running web or job process.
|
|
93
|
+
|
|
94
|
+
The check uses one event of the daily quota and expires with normal retention.
|
|
95
|
+
Its dedicated `check` signal carries the SDK version without customer identity,
|
|
96
|
+
trace, or release fields. It does not add business events, request measurements,
|
|
97
|
+
errors, or releases. Update the hosted app and run its migrations before using
|
|
98
|
+
this command with an older deployment that does not yet accept `check` signals.
|
|
99
|
+
|
|
100
|
+
Setup refreshes connection health every five seconds while visible, without
|
|
101
|
+
replacing unfinished setup forms. It shows active key use, first/latest retained
|
|
102
|
+
event arrival, processing backlog, and app activity. Arrival times come from the
|
|
103
|
+
server, so an old occurrence timestamp does not make a newly received event look
|
|
104
|
+
undelivered. A pending event older than two minutes needs investigation; app
|
|
105
|
+
traffic absent for fifteen minutes is shown as quiet, which may be normal.
|
|
106
|
+
Installation checks do not reset the app-activity clock. The panel reports the
|
|
107
|
+
environment's retained data, not lifetime history or SDK loss counters.
|
|
108
|
+
|
|
109
|
+
If a check fails, inspect the endpoint, active key, outbound HTTPS, and daily
|
|
110
|
+
quota. A delivered check missing from Setup may belong to a different environment.
|
|
111
|
+
If it remains pending, the RailsMind operator should inspect the telemetry worker
|
|
112
|
+
and failed jobs. Ingest keys remain write-only; viewing the confirmation requires
|
|
113
|
+
a signed-in workspace member.
|
|
114
|
+
|
|
115
|
+
## Identity and explicit outcomes
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# ApplicationController: a before_action inside the SDK request boundary.
|
|
119
|
+
RailsMind.identify(
|
|
120
|
+
visitor_id: ahoy.visitor_token,
|
|
121
|
+
user_id: current_user&.id,
|
|
122
|
+
account_id: current_account&.id
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Record an outcome after it commits, or inside an application after_commit.
|
|
126
|
+
RailsMind.track("Invoice paid", invoice_id: invoice.id, amount_cents: invoice.amount_cents)
|
|
127
|
+
|
|
128
|
+
# Explicit reporting also works when the current trace is not retained.
|
|
129
|
+
RailsMind.capture_exception(error, handled: true)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Context is fiber-local and restored around requests/jobs. `identify` replaces the three identity values, so call it with all available values; passing nil clears that value. Preserve Ahoy's visitor ID when a visitor logs in, and emit `$identify` through Ahoy authentication to connect later events. Account IDs represent the current tenant, never a guessed company from email. Logout/account switching must update identity; a new request starts clean. Historical anonymous events are not rewritten or merged across accounts by the SDK.
|
|
133
|
+
|
|
134
|
+
ActiveJob serializes only the context identifier allowlist under `rails_mind_context`, then restores it while executing. Arguments are not inspected. This covers GoodJob through ActiveJob; direct Sidekiq jobs require a future explicit integration. Distributed trace propagation belongs to the existing OpenTelemetry library; choose ActiveJob `propagation_style: :child` for a linked trace tree when configuring a new provider.
|
|
135
|
+
|
|
136
|
+
## Ahoy
|
|
137
|
+
|
|
138
|
+
Ahoy's documented store extension points preserve existing `ahoy.track` calls. Choose one mode explicitly. [Ahoy custom stores](https://github.com/ankane/ahoy#data-stores).
|
|
139
|
+
|
|
140
|
+
For an established installation, keep the current `Ahoy::Store` definition, storage, ID generator, user method, and cookies, then add this after its configuration:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
require "rails_mind/integrations/ahoy"
|
|
144
|
+
RailsMind::Integrations.mirror_ahoy!(Ahoy::Store)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The existing store runs first with its original hash. Its return value and exceptions survive. RailsMind then mirrors visits/events/authentication without modifying the data. Repeated installation does not add another wrapper. Existing event IDs remain stable on transport retries; non-UUID Ahoy IDs map deterministically into UUIDv8 values.
|
|
148
|
+
|
|
149
|
+
For a new hosted-only installation:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
require "rails_mind/integrations/ahoy"
|
|
153
|
+
Ahoy.api = true # Only when using the optional browser adapter.
|
|
154
|
+
Ahoy.geocode = false
|
|
155
|
+
class Ahoy::Store < RailsMind::Integrations::AhoyStore
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
This store requires no growing analytics tables. It does not offer database-backed `Ahoy::Visit` objects, `visitable` associations, geocoding storage, historical import, or cookie-free persisted visit lookup. Applications using those behaviors should keep their current store and mirror. Disable an existing duplicate pageview handler only as a deliberate customer configuration change.
|
|
160
|
+
|
|
161
|
+
## OpenTelemetry
|
|
162
|
+
|
|
163
|
+
Use selected tracing libraries, not `use_all`. The locally verified setup uses
|
|
164
|
+
Rack, ActionPack, ActionView, ActiveJob, PG, and Net::HTTP, with PG configured as
|
|
165
|
+
`db_statement: :omit`. Avoid additionally instrumenting the same SQL operation
|
|
166
|
+
at both ActiveRecord and PG layers. The Rails aggregate instrumentation package
|
|
167
|
+
loads component libraries; selecting only the Rails package itself does not
|
|
168
|
+
activate the child integrations. Follow the public
|
|
169
|
+
[Ruby instrumentation guide](https://opentelemetry.io/docs/languages/ruby/instrumentation/)
|
|
170
|
+
and [PG instrumentation documentation](https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/pg)
|
|
171
|
+
for your application's setup.
|
|
172
|
+
|
|
173
|
+
An existing provider remains authoritative:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# Run after your existing OpenTelemetry::SDK.configure block.
|
|
177
|
+
require "rails_mind/integrations/open_telemetry"
|
|
178
|
+
RailsMind::Integrations::OpenTelemetry.attach!
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Attachment adds one idempotent processor; it does not replace the global provider, sampler, propagator, instrumentation, or other exporters. Its exporter only sanitizes and enqueues locally; the shared worker performs network I/O. Span context is attached at start so identity survives later export. Known safe attributes are allowlisted. Raw span names, exception span events, HTTP bodies/headers, SQL statements, and bind values are not exported to RailsMind. Existing third-party exporters retain their own behavior and privacy settings. [OpenTelemetry exporters](https://opentelemetry.io/docs/languages/ruby/exporters/).
|
|
182
|
+
|
|
183
|
+
Request durations/counts and error reports come independently from Rails notifications/error reporting, even when detailed traces are sampled out. Request measurements cover controller actions, not assets/health checks rejected before ActionController or streaming-body completion. Repeated-query evidence contains only normalized-query hashes and counts; it is a suspicion, not proof of an N+1. Hash normalization handles common literals, not every SQL grammar. Use Bullet/Prosopite or an explicit query-bound test in development to verify fixes. No production object scans are installed.
|
|
184
|
+
|
|
185
|
+
Jobs record duration, failure/retry/discard lifecycle, execution count, queue name, and enqueue-to-start delay where ActiveJob exposes it. Scheduled delay is included in the enqueue-to-start number. Signals do not include arguments. Full metrics/logs SDK integration is deferred; v0.1 computes hosted aggregates from unsampled request events and does not capture application logs.
|
|
186
|
+
|
|
187
|
+
## Flipper and browser behavior
|
|
188
|
+
|
|
189
|
+
The SDK subscribes to `feature_operation.flipper`. It observes names, operations, boolean results, and duration without changing adapters, groups, actors, rollout logic, or cache. Flipper's existing Rails instrumentation emits these events; applications with a custom instrumenter must deliberately opt into ActiveSupport notifications. Actor IDs are not collected. An evaluation is not experiment exposure. Managed flags and experiments are not implemented in this SDK. [Flipper instrumentation](https://www.flippercloud.io/docs/instrumentation).
|
|
190
|
+
|
|
191
|
+
Run `bin/rails generate rails_mind:install --browser` to copy the optional module. Importmap users pin it, then pass their existing Ahoy instance:
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
import ahoy from "ahoy"
|
|
195
|
+
import { start } from "rails_mind"
|
|
196
|
+
const analytics = start({ ahoy, frames: true })
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Add `<meta name="rails-mind-page" content="Invoices#index">` to supply a stable page label. The adapter transmits no URL or query string. It counts the initial page and each completed Turbo visit once, ignores cached preview renders, and keeps `turbo:frame-load` as a separate `$frame_view` only for Frames with `data-analytics-frame`. It does not infer payment/signup success from forms or HTTP responses. Set `pageviews: false` if the application already counts pageviews. Repeated installation returns the same adapter. Browser delivery uses Ahoy's same-origin endpoint/cookies and existing CSRF policy; there is no browser ingestion credential. [Turbo events](https://turbo.hotwired.dev/reference/events).
|
|
200
|
+
|
|
201
|
+
## Bounds, privacy, and outages
|
|
202
|
+
|
|
203
|
+
Defaults are one lazy worker per process, 1,000 queued/in-flight events, 16 KiB per event, at most 100 events and 256 KiB per batch, and a two-second flush interval. Overflow drops the newest event. Retries retain the same encoded bytes/UUIDs; only HTTP 429, 5xx, and transient transport failures retry, at most three retries with bounded backoff. Other 4xx responses drop the batch. Redirects are not followed. Shutdown gets two seconds by default, then discards outstanding telemetry; forks start a fresh child queue rather than resending the parent's buffer.
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
RailsMind.stats
|
|
207
|
+
# => { enqueued:, delivered:, dropped:, retried:, queued:, in_flight:,
|
|
208
|
+
# losses: { "event.overflow" => 3, "error.delivery" => 1 } }
|
|
209
|
+
RailsMind.flush(timeout: 2)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
These are per-process counters. The doctor prints them for its process; inspect running app stats through your existing operational tooling. The hosted app does not currently receive loss counters. Request/business/error signals are unsampled, but process crashes, rejected payloads, redaction hooks, exhausted retries, or queue pressure can still undercount. This is best-effort monitoring, not an accounting ledger. A future transactional outbox can provide durable business-event delivery; the current implementation does not claim that guarantee.
|
|
213
|
+
|
|
214
|
+
Redaction occurs before buffering and again after custom `before_send`. Sensitive property names are filtered, nesting/string/collection sizes are capped, emails and common credential forms are scrubbed, exception messages are omitted by default, and backtraces drop machine-home prefixes. Bodies, cookies, credentials, SQL binds, and job arguments are never selected by integrations. Arbitrary user-provided text may contain sensitive content that generic redaction cannot recognize: use small, explicitly reviewed business properties. Opting into exception messages requires reviewing `capture_exception_message` and your custom scrubbing policy.
|
|
215
|
+
|
|
216
|
+
The final local synthetic burst benchmark on Ruby 4.0.6 processed 10,000 calls in 1.7612 seconds (176.12 μs/call), delivering all 10,000 with zero drops/retries at the 1,000-event queue bound. This measures local sanitization/enqueue with a no-op transport, not production request overhead or network delivery. Earlier runs did overflow; thread scheduling and downstream speed affect the result. Deterministic tests separately verify overflow losses and bounded shutdown during a hung transport. Run `ruby script_benchmark.rb` on the deployment hardware before setting a production budget.
|
|
217
|
+
|
|
218
|
+
## Verified local pipeline
|
|
219
|
+
|
|
220
|
+
On 2026-09-14 the real sample Rails application sent 101 events through this SDK's HTTP transport to the local hosted ingestion API; GoodJob processed all 101. They included 90 spans, 3 requests, 3 business events, 2 Flipper observations, 1 visit, 1 error, and 1 job. There were no collector drops or retries. The intentional error shared its request's trace and account IDs. Every request/business/error/flag/job event had account context; the visit and some early spans preceded identity and therefore did not. The list reported seven repeated queries. Credential-key privacy probes arrived as `[FILTERED]` without their secret values.
|
|
221
|
+
|
|
222
|
+
This historical check used an internal sample application that is not included
|
|
223
|
+
in the public client repository. To verify your own installation, follow
|
|
224
|
+
[Verify the connection](#verify-the-connection). External hosted deployment and
|
|
225
|
+
published-gem installation remain unverified by that historical check.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Uses the application's Ahoy endpoint/cookies. Never put the server token here.
|
|
2
|
+
// Do not run alongside another automatic pageview handler.
|
|
3
|
+
const installations = new WeakMap()
|
|
4
|
+
|
|
5
|
+
export function start({ ahoy, document: doc = document, turbo = true, pageviews = true, frames = false,
|
|
6
|
+
page = () => doc.querySelector('meta[name="rails-mind-page"]')?.content || "page" } = {}) {
|
|
7
|
+
if (!ahoy || typeof ahoy.track !== "function") throw new TypeError("Pass the existing Ahoy instance")
|
|
8
|
+
if (installations.has(doc)) return installations.get(doc)
|
|
9
|
+
const listeners = []
|
|
10
|
+
let visit = 0
|
|
11
|
+
let trackedVisit = -1
|
|
12
|
+
|
|
13
|
+
function on(name, listener) {
|
|
14
|
+
doc.addEventListener(name, listener)
|
|
15
|
+
listeners.push([name, listener])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function trackPage() {
|
|
19
|
+
if (!pageviews || trackedVisit === visit || doc.documentElement.hasAttribute("data-turbo-preview")) return
|
|
20
|
+
trackedVisit = visit
|
|
21
|
+
ahoy.track("$pageview", { page: String(page()).slice(0, 160), navigation: turbo ? "turbo" : "document" })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (turbo) {
|
|
25
|
+
on("turbo:visit", () => { visit += 1 })
|
|
26
|
+
on("turbo:load", trackPage)
|
|
27
|
+
} else {
|
|
28
|
+
on("DOMContentLoaded", trackPage)
|
|
29
|
+
}
|
|
30
|
+
// Supports loading the adapter after the initial page load, without duplicating
|
|
31
|
+
// a subsequent turbo:load for that same initial navigation.
|
|
32
|
+
if (doc.readyState === "complete") trackPage()
|
|
33
|
+
|
|
34
|
+
if (frames) on("turbo:frame-load", (event) => {
|
|
35
|
+
const label = event.target?.dataset?.analyticsFrame
|
|
36
|
+
if (label) ahoy.track("$frame_view", { frame: String(label).slice(0, 100), page: String(page()).slice(0, 160) })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const api = {
|
|
40
|
+
// The caller records a business outcome only after it actually succeeds.
|
|
41
|
+
track: (name, properties = {}) => ahoy.track(name, properties),
|
|
42
|
+
stop() {
|
|
43
|
+
listeners.forEach(([name, listener]) => doc.removeEventListener(name, listener))
|
|
44
|
+
installations.delete(doc)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
installations.set(doc, api)
|
|
48
|
+
return api
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails_mind/diagnostic"
|
|
3
|
+
|
|
4
|
+
module RailsMind
|
|
5
|
+
module Generators
|
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
class_option :plan, type: :boolean, default: false, desc: "Print the integration plan without writing files"
|
|
9
|
+
class_option :browser, type: :boolean, default: false, desc: "Copy the optional Ahoy/Turbo adapter without enabling it"
|
|
10
|
+
|
|
11
|
+
def inspect_application
|
|
12
|
+
say JSON.pretty_generate(Diagnostic.new(root: destination_root).report)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_initializer
|
|
16
|
+
return if options[:plan]
|
|
17
|
+
path = "config/initializers/rails_mind.rb"
|
|
18
|
+
if File.exist?(File.join(destination_root, path))
|
|
19
|
+
say_status :skip, "#{path} already exists", :yellow
|
|
20
|
+
else
|
|
21
|
+
template "initializer.rb.tt", path
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_browser_adapter
|
|
26
|
+
return if options[:plan] || !options[:browser]
|
|
27
|
+
path = "app/javascript/rails_mind.js"
|
|
28
|
+
if File.exist?(File.join(destination_root, path))
|
|
29
|
+
say_status :skip, "#{path} already exists", :yellow
|
|
30
|
+
else
|
|
31
|
+
copy_file File.expand_path("../../../../javascript/rails_mind.js", __dir__), path
|
|
32
|
+
end
|
|
33
|
+
say "Pin rails_mind in importmap.rb and start it with your existing Ahoy instance. See docs/sdk.md."
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
RailsMind.configure do |config|
|
|
2
|
+
config.token = ENV["RAILS_MIND_KEY"] # Environment-scoped server credential. Never expose to browsers.
|
|
3
|
+
|
|
4
|
+
# Endpoint defaults to https://railsmind.com; RAILS_MIND_ENDPOINT overrides it.
|
|
5
|
+
# Release is detected from hosting metadata or Rails.root/REVISION when available.
|
|
6
|
+
# RAILS_MIND_RELEASE is an optional override. Neither variable is required.
|
|
7
|
+
|
|
8
|
+
# Optional capabilities can be switched off independently.
|
|
9
|
+
# config.apm = false
|
|
10
|
+
# config.errors = false
|
|
11
|
+
# config.analytics = false
|
|
12
|
+
# config.flags = false
|
|
13
|
+
|
|
14
|
+
# Exception messages are omitted by default. Review before enabling.
|
|
15
|
+
# config.capture_exception_message = true
|
|
16
|
+
# config.before_send = ->(event) { event } # Return nil to drop an event.
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# No existing Ahoy store, Flipper adapter, or OpenTelemetry provider is changed.
|
|
20
|
+
# Run `bin/rails rails_mind:doctor` and follow docs/sdk.md for opt-in integrations.
|