chronos-ruby 0.7.0.pre.1 → 0.9.0.pre.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +55 -9
  4. data/contracts/apm-batch-v1.schema.json +1 -1
  5. data/contracts/dependencies-v1.schema.json +38 -0
  6. data/contracts/deploy-v1.schema.json +26 -0
  7. data/contracts/event-v1.schema.json +15 -1
  8. data/docs/adr/ADR-016-explicit-observability-integrations.md +25 -0
  9. data/docs/adr/ADR-017-deploy-events-and-correlation.md +25 -0
  10. data/docs/architecture.md +12 -1
  11. data/docs/compatibility.md +4 -0
  12. data/docs/configuration.md +24 -3
  13. data/docs/data-collected.md +11 -4
  14. data/docs/examples/plain-ruby.md +13 -1
  15. data/docs/modules/apm-aggregation.md +2 -2
  16. data/docs/modules/cache-observability.md +16 -0
  17. data/docs/modules/configuration.md +4 -0
  18. data/docs/modules/dependencies.md +18 -0
  19. data/docs/modules/deploy-tracking.md +86 -0
  20. data/docs/modules/external-http.md +27 -0
  21. data/docs/modules/rails-legacy.md +1 -1
  22. data/docs/modules/sidekiq-legacy.md +1 -1
  23. data/docs/modules/telemetry-events.md +4 -2
  24. data/docs/performance.md +30 -1
  25. data/docs/privacy-lgpd.md +20 -2
  26. data/docs/troubleshooting.md +25 -1
  27. data/lib/chronos/agent.rb +43 -0
  28. data/lib/chronos/application/apm_aggregator.rb +7 -10
  29. data/lib/chronos/application/apm_error_classifier.rb +27 -0
  30. data/lib/chronos/application/capture_telemetry.rb +18 -1
  31. data/lib/chronos/application/dependency_reporter.rb +140 -0
  32. data/lib/chronos/application/remote_configuration.rb +3 -1
  33. data/lib/chronos/capistrano.rb +4 -0
  34. data/lib/chronos/configuration/apm_validation.rb +28 -1
  35. data/lib/chronos/configuration.rb +25 -3
  36. data/lib/chronos/core/cache_normalizer.rb +99 -0
  37. data/lib/chronos/core/correlation_context.rb +65 -0
  38. data/lib/chronos/core/deploy_normalizer.rb +102 -0
  39. data/lib/chronos/core/payload_serializer.rb +4 -1
  40. data/lib/chronos/core/telemetry_event.rb +31 -4
  41. data/lib/chronos/integrations/capistrano.rb +61 -0
  42. data/lib/chronos/integrations/net_http.rb +165 -0
  43. data/lib/chronos/net_http.rb +4 -0
  44. data/lib/chronos/observability_facade.rb +50 -0
  45. data/lib/chronos/rails/notifications_subscriber.rb +6 -5
  46. data/lib/chronos/version.rb +1 -1
  47. data/lib/chronos.rb +8 -0
  48. metadata +19 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9cf448c12955078e3a714df2d41bcd94d89f840fea406dc6595f72661e8abc2
4
- data.tar.gz: 1401d283792e930caa252b21191de3d8fcc02bf5b2936416ed28eec2e319dab3
3
+ metadata.gz: 34b5d9d1193017931d129dc366922b276d9b85ced0321e78d3f4a5b1a8c13e40
4
+ data.tar.gz: 72277b74f51825118ce6cb2dda83652f035bbfef3cb56c18b7ee98cc3e6d1d54
5
5
  SHA512:
6
- metadata.gz: 43f80510ce3d78e6c9b4b571f7d79cb66b8a4fad8f36b66996ea5cc42868c6ec7b1a05ae1556c8442569787f2f350e2884a779e36af649734eced377396c892b
7
- data.tar.gz: 30b0f93cfba0ce2fb856b4c65eff6e1ebfa906c8c45ec5c6e287ddc080bc23bd63e4e1a7a4d80a56f40b5001c768719c3b52a2d7e9b08d08b39a2512b476db30
6
+ metadata.gz: 2c6b44a856bbb8975c6a4dc9aba5d9bcd2cf9a9d44456c19fe373860872491ff310f11a2a1b4c7588306ae2795caf4af8cf008e38c10bcdbe0322fca9a803ece
7
+ data.tar.gz: c4ff3f4da09810013c0e4d352bb18cd04c0a652c160816465d4b088490d5eea5f722c326ab8a6dcc9806f7b74c050ee0c77e8c6a4c604ddf650cb08fc66a3fd4
data/CHANGELOG.md CHANGED
@@ -12,6 +12,55 @@ All notable changes are documented here. The project follows Semantic Versioning
12
12
  - legacy CI now resolves Bundler 1.17.3 through `Gem.bin_path` on RubyGems versions that do not support the `_version_` executable selector.
13
13
  - documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
14
14
 
15
+ ## [0.9.0.pre.1] - 2026-07-21
16
+
17
+ ### Added
18
+
19
+ - synchronous `Chronos.notify_deploy` API with bounded environment, revision, version, repository, actor, deploy ID, service, region, and instance fields;
20
+ - fixed bounded release/deploy correlation on every new exception and telemetry envelope;
21
+ - explicit configuration for `revision`, `deploy_id`, `region`, and `instance_id`;
22
+ - automatic deploy IDs and credential removal from common HTTP/SCP repository references;
23
+ - optional idempotent Capistrano post-publish task loaded through `chronos/capistrano`;
24
+ - manual, Kamal-command, and GitHub Actions deployment examples;
25
+ - deploy/correlation contracts, tests, benchmark, module documentation, and ADR-017.
26
+
27
+ ### Changed
28
+
29
+ - dependency inventory can be refreshed once after a successful deploy notification with the new release;
30
+ - deploy events use their normalized environment/service/release values in the common envelope;
31
+ - version advanced to `0.9.0.pre.1`.
32
+
33
+ ### Known limitations
34
+
35
+ - `notify_deploy` does not mutate the immutable correlation of an already running agent;
36
+ - Kamal support is command/documentation based rather than a plugin;
37
+ - Capistrano must load its task DSL before the optional Chronos entry point;
38
+ - deployment policy must decide whether a `false` notification result blocks publication.
39
+
40
+ ## [0.8.0.pre.1] - 2026-07-20
41
+
42
+ ### Added
43
+
44
+ - optional per-instance `Net::HTTP` instrumentation with sanitized host, method, status, duration, timeout and connection-error classification;
45
+ - outbound trace/request header propagation without collecting URLs, Authorization, request bodies, response bodies, or error messages;
46
+ - external HTTP metric aggregation and traced-request `external_http` breakdown;
47
+ - cache operation, duration, hit/miss, backend, namespace, and opt-in project-scoped SHA-256 key identity;
48
+ - bounded dependency inventory containing loaded gem versions, Ruby, optional Rails/web server/database/Sidekiq detection, and configured release;
49
+ - version 0.8 configuration, contracts, tests, executable example, benchmark, module documentation, and ADR-016.
50
+
51
+ ### Changed
52
+
53
+ - dependency inventory is queued at most once per configured agent instead of being attached to every exception;
54
+ - cache notification fields now use bounded backend/namespace/outcome names and omit raw keys by default;
55
+ - version advanced to `0.8.0.pre.1`.
56
+
57
+ ### Known limitations
58
+
59
+ - outbound HTTP instrumentation must be enabled and installed on each selected `Net::HTTP` instance;
60
+ - Faraday, HTTP.rb, Excon, and RestClient adapters are not included;
61
+ - cache key hashing is opt-in and low-entropy keys may remain guessable;
62
+ - dependency detection uses already loaded gems and does not open a database connection or inspect lockfiles.
63
+
15
64
  ## [0.7.0.pre.1] - 2026-07-20
16
65
 
17
66
  ### Added
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Chronos Ruby
2
2
 
3
- Chronos Ruby is the framework-independent client for sending Ruby application errors and bounded telemetry to Chronos. Version 0.7 adds bounded essential APM aggregation for requests, SQL, and jobs, including histograms, error rates, component breakdown, normalized SQL fingerprints, and local diagnostic signals.
3
+ Chronos Ruby is the framework-independent client for sending Ruby application errors and bounded telemetry to Chronos. Version 0.9 adds synchronous deploy tracking and bounded release correlation across every event.
4
4
 
5
5
  ## What the gem collects
6
6
 
7
- For each exception, version 0.7 can collect:
7
+ Version 0.9 can collect:
8
8
 
9
9
  - exception class, message, structured backtrace, and chained causes;
10
10
  - timestamp, severity, tags, and an optional fingerprint;
@@ -13,12 +13,14 @@ For each exception, version 0.7 can collect:
13
13
  - application version, environment, and service name.
14
14
  - Rack method, normalized route, status, duration, request ID, host, query-free path, optional user agent, controller/action, response size, trace ID, and already-parsed parameters when the middleware is used;
15
15
  - bounded breadcrumbs explicitly supplied by the application or integration.
16
+ - release, revision, deploy ID, environment, service, region, and instance correlation;
17
+ - explicit bounded deployment metadata supplied through the public API or integration.
16
18
 
17
19
  See [Data collected](docs/data-collected.md) for the complete field table.
18
20
 
19
21
  ## What is not collected by default
20
22
 
21
- Chronos Ruby does not inspect environment variables, request bodies, cookies, HTTP headers, source code, database contents, or installed gems. Application-supplied fields are recursively sanitized, but applications should still avoid sending unnecessary personal, health, financial, or authentication data.
23
+ Chronos Ruby does not inspect environment variables, request/response bodies, cookies, Authorization headers, source code, database contents, lockfiles, or gem paths. Dependency reporting reads only already loaded gem names and versions once per agent. Application-supplied fields are recursively sanitized, but applications should still avoid sending unnecessary personal, health, financial, or authentication data.
22
24
 
23
25
  ## Supported Ruby and Rails versions
24
26
 
@@ -31,7 +33,7 @@ See [Compatibility](docs/compatibility.md).
31
33
  The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
32
34
 
33
35
  ```ruby
34
- gem "chronos-ruby", "0.7.0.pre.1"
36
+ gem "chronos-ruby", "0.9.0.pre.1"
35
37
  ```
36
38
 
37
39
  Install with a Bundler version compatible with the application. For the oldest supported runtime:
@@ -52,7 +54,7 @@ gem install chronos-ruby --pre
52
54
  Version 0.5 exposes Rails support explicitly, keeping Rails and ActiveSupport out of plain Ruby applications:
53
55
 
54
56
  ```ruby
55
- gem "chronos-ruby", "0.7.0.pre.1", :require => "chronos/rails"
57
+ gem "chronos-ruby", "0.9.0.pre.1", :require => "chronos/rails"
56
58
  ```
57
59
 
58
60
  Generate the initializer with:
@@ -191,16 +193,50 @@ Version `0.6.0.pre.1` adds optional Sidekiq 4/5 middleware:
191
193
 
192
194
  ```ruby
193
195
  gem "sidekiq", "~> 5.0"
194
- gem "chronos-ruby", "0.7.0.pre.1", :require => "chronos/sidekiq"
196
+ gem "chronos-ruby", "0.9.0.pre.1", :require => "chronos/sidekiq"
195
197
  ```
196
198
 
197
199
  The client middleware propagates only trace/request identifiers in a versioned Sidekiq-envelope field and never changes worker arguments. The server records class, queue, JID, retry count, duration, calculable queue latency, bounded arguments/tags, status, and error class. Values pass through the shared sanitizer before delivery. Failed jobs are notified once and the original exception is re-raised. See [Sidekiq 4/5 legacy integration](docs/modules/sidekiq-legacy.md).
198
200
 
199
201
  The Rails subscriber continues to record basic Active Job class, queue, and duration. Full Active Job context propagation plus optional Resque and Delayed Job adapters are subsequent version 0.6 increments.
200
202
 
203
+ ## External HTTP, cache, and dependencies
204
+
205
+ Version 0.8 instruments only explicitly selected `Net::HTTP` connection objects, avoiding a global monkey patch:
206
+
207
+ ```ruby
208
+ Chronos.configure do |config|
209
+ # required connection settings omitted
210
+ config.external_http_enabled = true
211
+ config.external_http_trace_headers = true
212
+ end
213
+
214
+ http = Net::HTTP.new("payments.example.com", 443)
215
+ http.use_ssl = true
216
+ Chronos.instrument_net_http(http)
217
+ ```
218
+
219
+ The wrapper records only sanitized host, method, status, duration, timeout, connection-error flag, and error class. It propagates `X-Chronos-Trace-ID` and `X-Chronos-Request-ID` when available and never reads URL path/query, Authorization, request body, response body, or error message.
220
+
221
+ Rails cache telemetry omits raw keys by default. Set `cache_key_mode = :sha256` to emit a project-scoped hash; `:none` is the default. Dependency reporting is enabled by default, reads at most 100 already loaded gem specs, and emits one independent `dependencies` event per agent. Set `dependency_reporting = false` to disable it. See [External HTTP](docs/modules/external-http.md), [Cache observability](docs/modules/cache-observability.md), and [Dependency inventory](docs/modules/dependencies.md).
222
+
201
223
  ## Deploy tracking
202
224
 
203
- Deploy notifications are not implemented in version 0.4. `app_version` may be included in exception events for release correlation.
225
+ Version 0.9 sends deployment metadata synchronously and adds a bounded correlation block to every event:
226
+
227
+ ```ruby
228
+ Chronos.notify_deploy(
229
+ :environment => "production",
230
+ :revision => ENV["GIT_SHA"],
231
+ :version => ENV["APP_VERSION"],
232
+ :repository => "owner/repository",
233
+ :actor => ENV["DEPLOY_USER"]
234
+ )
235
+ ```
236
+
237
+ Configure `app_version`, `revision`, `deploy_id`, `environment`, `service_name`, `region`, and `instance_id` in each newly deployed process so subsequent telemetry carries the same release identity. The gem never scans environment variables or Git automatically.
238
+
239
+ Optional Capistrano support loads through `chronos/capistrano`. Manual, Kamal-command, and GitHub Actions examples share the explicit deploy command under `examples/deploy/`. See [Deploy tracking and release correlation](docs/modules/deploy-tracking.md).
204
240
 
205
241
  ## Asynchronous queue
206
242
 
@@ -273,6 +309,14 @@ Chronos.configure do |config|
273
309
  config.apm_enabled = true
274
310
  config.apm_max_groups = 200
275
311
  config.apm_flush_count = 100
312
+ config.external_http_enabled = false
313
+ config.cache_key_mode = :none
314
+ config.dependency_reporting = true
315
+ config.app_version = ENV["APP_VERSION"]
316
+ config.revision = ENV["GIT_SHA"]
317
+ config.deploy_id = ENV["DEPLOY_ID"]
318
+ config.region = ENV["REGION"]
319
+ config.instance_id = ENV["INSTANCE_ID"]
276
320
  end
277
321
  ```
278
322
 
@@ -284,7 +328,7 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
284
328
 
285
329
  ## Benchmark
286
330
 
287
- Run the version 0.7 benchmarks with:
331
+ Run the version 0.9 benchmarks with:
288
332
 
289
333
  ```bash
290
334
  bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
@@ -296,13 +340,15 @@ bundle _1.17.3_ exec ruby benchmarks/request_overhead.rb
296
340
  bundle _1.17.3_ exec ruby benchmarks/rails_notifications.rb
297
341
  bundle _1.17.3_ exec ruby benchmarks/sidekiq_middleware.rb
298
342
  bundle _1.17.3_ exec ruby benchmarks/apm_aggregation.rb
343
+ bundle _1.17.3_ exec ruby benchmarks/external_http.rb
344
+ bundle _1.17.3_ exec ruby benchmarks/correlation.rb
299
345
  ```
300
346
 
301
347
  Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
302
348
 
303
349
  ## Migration from Airbrake
304
350
 
305
- An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.7 does not claim API compatibility or automatic replacement.
351
+ An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.9 does not claim API compatibility or automatic replacement.
306
352
 
307
353
  ## Local development
308
354
 
@@ -15,7 +15,7 @@
15
15
  "type": "object",
16
16
  "required": ["metric_type", "dimensions", "count", "error_count", "error_rate", "duration_ms", "histogram", "breakdown_ms", "signals", "status_codes"],
17
17
  "properties": {
18
- "metric_type": {"enum": ["request", "query", "job"]},
18
+ "metric_type": {"enum": ["request", "query", "job", "external_http"]},
19
19
  "dimensions": {"type": "object"},
20
20
  "count": {"type": "integer", "minimum": 1},
21
21
  "error_count": {"type": "integer", "minimum": 0},
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://chronos.example/schemas/dependencies-v1.schema.json",
4
+ "title": "Chronos dependency inventory payload v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["dependencies", "ruby"],
8
+ "properties": {
9
+ "dependencies": {
10
+ "type": "array",
11
+ "maxItems": 200,
12
+ "items": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["name", "version"],
16
+ "properties": {
17
+ "name": {"type": "string", "maxLength": 128},
18
+ "version": {"type": "string", "maxLength": 64}
19
+ }
20
+ }
21
+ },
22
+ "ruby": {
23
+ "type": "object",
24
+ "additionalProperties": false,
25
+ "required": ["version", "engine", "platform"],
26
+ "properties": {
27
+ "version": {"type": "string", "maxLength": 64},
28
+ "engine": {"type": "string", "maxLength": 64},
29
+ "platform": {"type": "string", "maxLength": 128}
30
+ }
31
+ },
32
+ "rails": {"type": ["string", "null"], "maxLength": 128},
33
+ "web_server": {"type": ["string", "null"], "maxLength": 128},
34
+ "database_adapter": {"type": ["string", "null"], "maxLength": 128},
35
+ "sidekiq": {"type": ["string", "null"], "maxLength": 128},
36
+ "release": {"type": ["string", "null"], "maxLength": 128}
37
+ }
38
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://chronos.example/schemas/deploy-v1.schema.json",
4
+ "title": "Chronos deploy payload v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "deploy_id", "environment", "revision", "version", "repository",
9
+ "actor", "service", "region", "instance"
10
+ ],
11
+ "properties": {
12
+ "deploy_id": {"type": "string", "minLength": 1, "maxLength": 128},
13
+ "environment": {"type": "string", "minLength": 1, "maxLength": 128},
14
+ "revision": {"type": ["string", "null"], "maxLength": 128},
15
+ "version": {"type": ["string", "null"], "maxLength": 128},
16
+ "repository": {"type": ["string", "null"], "maxLength": 512},
17
+ "actor": {"type": ["string", "null"], "maxLength": 128},
18
+ "service": {"type": ["string", "null"], "maxLength": 128},
19
+ "region": {"type": ["string", "null"], "maxLength": 128},
20
+ "instance": {"type": ["string", "null"], "maxLength": 128}
21
+ },
22
+ "anyOf": [
23
+ {"properties": {"revision": {"type": "string", "minLength": 1}}},
24
+ {"properties": {"version": {"type": "string", "minLength": 1}}}
25
+ ]
26
+ }
@@ -20,7 +20,7 @@
20
20
  "properties": {
21
21
  "schema_version": {"const": "1.0"},
22
22
  "event_id": {"type": "string", "minLength": 1},
23
- "event_type": {"enum": ["exception", "request", "query", "job", "cache", "metric_batch"]},
23
+ "event_type": {"enum": ["exception", "request", "query", "job", "cache", "external_http", "dependencies", "deploy", "metric_batch"]},
24
24
  "occurred_at": {"type": "string"},
25
25
  "sent_at": {"type": "string"},
26
26
  "project_key": {"type": "string", "minLength": 1},
@@ -34,6 +34,20 @@
34
34
  "instance_id": {"type": ["string", "null"]}
35
35
  }
36
36
  },
37
+ "correlation": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "required": ["release", "revision", "deploy_id", "environment", "service", "region", "instance"],
41
+ "properties": {
42
+ "release": {"type": ["string", "null"], "maxLength": 128},
43
+ "revision": {"type": ["string", "null"], "maxLength": 128},
44
+ "deploy_id": {"type": ["string", "null"], "maxLength": 128},
45
+ "environment": {"type": ["string", "null"], "maxLength": 128},
46
+ "service": {"type": ["string", "null"], "maxLength": 128},
47
+ "region": {"type": ["string", "null"], "maxLength": 128},
48
+ "instance": {"type": ["string", "null"], "maxLength": 128}
49
+ }
50
+ },
37
51
  "runtime": {"type": "object"},
38
52
  "context": {"type": "object"},
39
53
  "payload": {"type": "object"}
@@ -0,0 +1,25 @@
1
+ # ADR-016 — Explicit and privacy-bounded observability integrations
2
+
3
+ ## Status
4
+
5
+ Accepted for version 0.8.
6
+
7
+ ## Context
8
+
9
+ Outbound calls, cache behavior, and runtime dependencies materially improve diagnosis, but global HTTP monkey patches and raw cache/dependency data create compatibility, cardinality, and privacy risks in legacy applications.
10
+
11
+ ## Decision
12
+
13
+ Instrument only explicitly supplied `Net::HTTP` instances with a per-object prepended module. Preserve method, streaming block, response, and exception semantics. Collect only bounded host/outcome/timing fields and propagate only Chronos correlation headers.
14
+
15
+ Normalize Rails cache notifications through a core value normalizer. Omit raw keys by default and permit only an explicit project-scoped SHA-256 mode. Never inspect values.
16
+
17
+ Build one bounded dependency event per agent from already loaded gem specs and feature-detected constants. Do not inspect lockfiles or paths, activate dependencies, or open database connections. Route every event through the existing sanitizer, bounded queue, retry, circuit breaker, and backlog pipeline.
18
+
19
+ ## Alternatives
20
+
21
+ A global `Net::HTTP` patch was rejected because it changes unrelated connections and increases conflict risk. Capturing full URLs, headers, bodies, cache keys, or lockfiles was rejected for privacy and cardinality. Attaching dependencies to every exception was rejected for redundant payload cost. Automatic Faraday and other client adapters were deferred until demand and official middleware boundaries are validated.
22
+
23
+ ## Consequences
24
+
25
+ Applications choose exactly which HTTP clients are instrumented, native errors remain intact, and memory/data volume stay bounded. The tradeoffs are explicit setup per connection, partial dependency inventories when gems load late, pseudonymous rather than anonymous cache hashes, and no coverage for other HTTP clients in this release.
@@ -0,0 +1,25 @@
1
+ # ADR-017 — Synchronous deploy events and immutable release correlation
2
+
3
+ ## Status
4
+
5
+ Accepted for version 0.9.
6
+
7
+ ## Context
8
+
9
+ The SaaS needs stable release dimensions to compare error and performance behavior before and after deployments. Deployment tools are short-lived processes, while application telemetry is long-lived and concurrent. Implicit Git, lockfile, or environment discovery would make behavior surprising and could expose credentials.
10
+
11
+ ## Decision
12
+
13
+ Add a versioned `deploy` telemetry payload delivered synchronously through the existing transport policy. Require an environment and either revision or version, generate a deploy ID when omitted, bound every field, and remove credentials from common repository URL forms.
14
+
15
+ Emit a `correlation` object on every new v1 envelope with release, revision, deploy ID, environment, service, region, and instance. Keep the schema property optional so payloads from earlier 0.x clients remain valid. Populate it from the immutable configuration snapshot for application events and from normalized deploy fields for deploy events. Do not mutate a running agent's correlation after notification.
16
+
17
+ Refresh the bounded dependency inventory after a successful deploy notification. Integrate Capistrano through its task DSL without a runtime dependency. Support Kamal by documented command and GitHub Actions through an example workflow.
18
+
19
+ ## Alternatives
20
+
21
+ Reading Git state, all environment variables, or deployment manifests automatically was rejected for determinism and privacy. Asynchronous-only deploy delivery was rejected because short-lived commands can exit before queue drain. Mutating global release state after `notify_deploy` was rejected because concurrent events could receive mixed correlation. Adding direct dependencies on Capistrano, Kamal, or GitHub SDKs was rejected for the legacy compatibility line.
22
+
23
+ ## Consequences
24
+
25
+ Every event has a stable bounded correlation shape and deploy commands receive an explicit success result. Applications must pass release metadata into each newly started process. Integration setup remains explicit, and delivery failure policy stays under the deployment pipeline's control.
data/docs/architecture.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Chronos Ruby 0.7 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
3
+ Chronos Ruby 0.9 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
4
4
 
5
5
  ```mermaid
6
6
  flowchart TB
@@ -21,6 +21,11 @@ flowchart TB
21
21
  Application --> Telemetry[Core / TelemetryEvent]
22
22
  Telemetry --> APM[Application / ApmAggregator]
23
23
  APM --> Metrics[Core / MetricAggregate and SqlNormalizer]
24
+ NetHTTP[Explicit Net::HTTP instance wrapper] --> Facade
25
+ Rails --> Cache[Core / CacheNormalizer]
26
+ Application --> Dependencies[Application / DependencyReporter]
27
+ Deploy[Manual, Capistrano, Kamal command, GitHub workflow] --> Facade
28
+ Core --> Correlation[Core / DeployNormalizer and CorrelationContext]
24
29
  ```
25
30
 
26
31
  ## Boundaries
@@ -50,6 +55,12 @@ Sidekiq client middleware writes a versioned, allowlisted trace/request context
50
55
 
51
56
  Version 0.7 routes request, query, and job observations through `ApmAggregator`. `SqlNormalizer` removes values before grouping, while `MetricAggregate` owns fixed numerical statistics. Aggregates and per-trace query trackers are bounded and mutex-protected. Threshold or lifecycle drains create `metric_batch` telemetry that passes through the existing sanitizer and delivery pipeline. No APM-specific thread is created.
52
57
 
58
+ Version 0.8 prepends a wrapper only to each explicitly selected `Net::HTTP` object. The wrapper records bounded outcome metadata and feeds the existing APM aggregator; it does not modify `Net::HTTP` globally. `CacheNormalizer` turns public Rails cache notifications into bounded metadata before delivery. `DependencyReporter` reads already loaded runtime metadata once per agent under a mutex and queues it as a separate event.
59
+
60
+ Version 0.9 routes explicit deploy attributes through `DeployNormalizer`, synchronous `CaptureTelemetry`, the common sanitizer, and `DeliveryPipeline`. `CorrelationContext` supplies the same fixed seven-field shape to exception and telemetry serializers. Deploy event overrides apply only while serializing that event; the agent configuration remains immutable for concurrent application capture. A successful deployment resets the dependency reporter once and flushes the refreshed inventory.
61
+
62
+ Capistrano remains an optional integration task with no runtime dependency. Kamal and GitHub Actions call the same manual Ruby command rather than adding SDKs or transport paths.
63
+
53
64
  ## Failure policy
54
65
 
55
66
  Explicit invalid configuration raises `Chronos::ConfigurationError`. Capture, serialization, logger, worker, retry, circuit, TLS, network, HTTP, and remote-policy failures do not escape into the host application. They produce `false`, a classified transport result, a bounded state transition, or a bounded logger diagnostic.
@@ -21,6 +21,10 @@ Version `0.6.0.pre.1` uses the public Sidekiq 4/5 middleware signatures and rema
21
21
 
22
22
  Version `0.7.0.pre.1` keeps the same Ruby/Rails matrix and implements APM aggregation without modern concurrency or SQL-parser dependencies. Its compatibility remains `Best effort` until request/SQL/job aggregate payloads pass the dedicated fake-server gates for every listed runtime.
23
23
 
24
+ Version `0.8.0.pre.1` uses per-object `Module#prepend`, legacy `Net::HTTP`, standard-library SHA-256, and loaded-spec feature detection. It adds no runtime dependency and keeps the same matrix. Outbound HTTP, cache, and dependency gates must pass every listed runtime before support is promoted.
25
+
26
+ Version `0.9.0.pre.1` adds only standard-library URI/SecureRandom processing, bounded hashes, and the existing synchronous delivery path. Capistrano is optional and feature-detected; Kamal and GitHub Actions integrations are commands/examples. The Ruby/Rails matrix remains unchanged and `Best effort` until deploy/correlation payload gates pass every listed runtime.
27
+
24
28
  Status meanings:
25
29
 
26
30
  - Supported: the complete required compatibility gate passes.
@@ -8,8 +8,12 @@
8
8
  | `project_key` | Yes when enabled | `nil` | Secret authentication key sent only as an HTTP header |
9
9
  | `host` | Yes when enabled | `nil` | Absolute Chronos HTTP endpoint; HTTPS is required by default |
10
10
  | `environment` | Recommended | `production` | Application environment |
11
- | `app_version` | Optional | `nil` | Release or revision identifier |
11
+ | `app_version` | Optional | `nil` | Application release/version identifier |
12
12
  | `service_name` | Recommended | `nil` | Logical service name |
13
+ | `revision` | Optional | `nil` | Source revision correlated with every event; maximum 128 bytes |
14
+ | `deploy_id` | Optional | `nil` | Deployment identifier correlated with every event; maximum 128 bytes |
15
+ | `region` | Optional | `nil` | Deployment region correlated with every event; maximum 128 bytes |
16
+ | `instance_id` | Optional | `nil` | Explicit instance correlation; otherwise runtime hostname is used |
13
17
  | `root_directory` | Optional | `Dir.pwd` | Used to identify application backtrace frames |
14
18
  | `logger` | Optional | `nil` | Logger receiving bounded internal diagnostics |
15
19
  | `timeout` | Optional | `5.0` | HTTP read timeout in seconds |
@@ -39,7 +43,7 @@
39
43
  | `remote_configuration` | Optional | `true` | Accepts only the documented bounded remote policy fields |
40
44
  | `remote_config_max_bytes` | Optional | `4096` | Maximum remote policy response-header bytes |
41
45
  | `sampling_rate` | Optional | `1.0` | Local upper bound for event sampling |
42
- | `enabled_event_types` | Optional | exception, request, query, job, cache, metric_batch | Local allowlist for supported event envelopes |
46
+ | `enabled_event_types` | Optional | exception, request, query, job, cache, external_http, dependencies, deploy, metric_batch | Local allowlist for supported event envelopes |
43
47
  | `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
44
48
  | `context_store` | Optional | `:thread_local` | `:thread_local` or an object implementing `get`, `set`, `clear`, and `with_context` |
45
49
  | `breadcrumb_capacity` | Optional | `20` | Positive count of newest breadcrumbs retained per execution |
@@ -48,7 +52,7 @@
48
52
  | `rails_capture_in_console` | Optional | `false` | Enables automatic integration while Rails console is loaded |
49
53
  | `rails_capture_in_test` | Optional | `false` | Enables automatic integration in the Rails test environment |
50
54
  | `rails_capture_user_agent` | Optional | `false` | Adds the Rack user agent to request context |
51
- | `apm_enabled` | Optional | `true` | Aggregates request, query, and job observations into bounded metric batches |
55
+ | `apm_enabled` | Optional | `true` | Aggregates request, query, job, and enabled external HTTP observations into bounded metric batches |
52
56
  | `apm_max_groups` | Optional | `200` | Maximum metric groups and active trace trackers retained locally |
53
57
  | `apm_flush_count` | Optional | `100` | Aggregate observations accepted before a threshold drain |
54
58
  | `apm_batch_size` | Optional | `50` | Metric groups per batch; hard maximum 50 |
@@ -57,6 +61,11 @@
57
61
  | `apm_long_transaction_threshold_ms` | Optional | `1000.0` | Transaction-labelled SQL duration that produces a signal |
58
62
  | `apm_n_plus_one_threshold` | Optional | `5` | Repeated fingerprint count producing one possible-N+1 signal; minimum 2 |
59
63
  | `apm_histogram_buckets` | Optional | Fixed millisecond boundaries | Increasing positive duration boundaries; at most 19 plus `+Inf` |
64
+ | `external_http_enabled` | Optional | `false` | Allows explicit per-instance outbound `Net::HTTP` instrumentation |
65
+ | `external_http_trace_headers` | Optional | `true` | Propagates Chronos trace/request headers on instrumented requests |
66
+ | `cache_key_mode` | Optional | `:none` | `:none` omits keys; `:sha256` emits a project-scoped key hash |
67
+ | `dependency_reporting` | Optional | `true` | Emits one bounded dependency event per configured agent |
68
+ | `dependency_max_items` | Optional | `100` | Loaded gem entries retained in the inventory; range 1–200 |
60
69
 
61
70
  ```ruby
62
71
  Chronos.configure do |config|
@@ -65,6 +74,11 @@ Chronos.configure do |config|
65
74
  config.host = ENV["CHRONOS_HOST"]
66
75
  config.environment = ENV["APP_ENV"] || "production"
67
76
  config.service_name = "billing"
77
+ config.app_version = ENV["APP_VERSION"]
78
+ config.revision = ENV["GIT_SHA"]
79
+ config.deploy_id = ENV["DEPLOY_ID"]
80
+ config.region = ENV["REGION"]
81
+ config.instance_id = ENV["INSTANCE_ID"]
68
82
  config.queue_size = 100
69
83
  config.workers = 1
70
84
  config.blocklist_keys += [:medical_record, /bank_account/i]
@@ -84,6 +98,11 @@ Chronos.configure do |config|
84
98
  config.apm_slow_query_threshold_ms = 500.0
85
99
  config.apm_long_transaction_threshold_ms = 1000.0
86
100
  config.apm_n_plus_one_threshold = 5
101
+ config.external_http_enabled = false
102
+ config.external_http_trace_headers = true
103
+ config.cache_key_mode = :none
104
+ config.dependency_reporting = true
105
+ config.dependency_max_items = 100
87
106
  end
88
107
  ```
89
108
 
@@ -92,3 +111,5 @@ Privacy matcher collections and filters are copied into the immutable runtime sn
92
111
  The gem never reads all environment variables automatically. The host application decides which values to pass. See [Privacy and LGPD](privacy-lgpd.md) before adding application context.
93
112
 
94
113
  Remote configuration can only reduce collection within local bounds. It cannot increase `max_payload_size` or `sampling_rate`, enable an unsupported or locally disabled event type, change the endpoint, replace credentials, disable TLS verification, or install executable matching rules.
114
+
115
+ Release correlation values are copied into the immutable snapshot. Calling `Chronos.notify_deploy` reports a deployment but does not mutate those values for concurrent application events; configure each deployed process with its own release identity.
@@ -1,6 +1,6 @@
1
1
  # Data collected
2
2
 
3
- Version 0.7 emits exceptions, individual cache telemetry, and aggregated request/query/job metric batches. All fields pass through the privacy sanitizer and bounded safe serializer before queueing, retry storage, or delivery.
3
+ Version 0.9 emits exceptions, cache telemetry, dependency/deploy events, and aggregated request/query/job/external-HTTP metric batches. All fields pass through the privacy sanitizer and bounded safe serializer before queueing, retry storage, or delivery.
4
4
 
5
5
  | Data | Default | Source |
6
6
  |---|---|---|
@@ -34,14 +34,21 @@ Version 0.7 emits exceptions, individual cache telemetry, and aggregated request
34
34
  | Sidekiq trace/request IDs | Propagated when present; trace generated otherwise | Chronos job-envelope metadata |
35
35
  | APM counts, error counts/rates, duration total/min/max/average | Aggregated by default | Request, query, and job observations |
36
36
  | Fixed duration histogram and status counts | Aggregated by default | Local bounded counters |
37
- | Component breakdown | database/view/cache/queue/application when observable | Trace-local bounded totals |
37
+ | Component breakdown | database/view/external_http/cache/queue/application when observable | Trace-local bounded totals |
38
38
  | Normalized SQL and SHA-256 fingerprint | Collected without comments, literals, or binds | `sql.active_record` payload |
39
39
  | SQL adapter, operation, inferred table, AR name, cache flag, role/shard | Collected when exposed | Public notification payload and connection feature detection |
40
40
  | Slow SQL source frame | Collected only for threshold-selected slow queries | Bounded application call frame |
41
41
  | APM diagnostic signals | Heuristic counters | Local threshold and repetition detection |
42
- | Cache operation, store, hit flag, duration | Collected; key/value omitted | ActiveSupport cache notifications |
42
+ | External HTTP host, method, status, duration, timeout, connection-error flag, error class | Disabled by default; per-instance opt-in | Instrumented `Net::HTTP` object |
43
+ | Chronos trace/request headers | Propagated when available | Current execution context |
44
+ | Cache operation, backend, namespace, hit/miss, duration | Collected; key/value omitted | ActiveSupport cache notifications |
45
+ | Cache key hash | Disabled by default | Project-scoped SHA-256 when `cache_key_mode = :sha256` |
46
+ | Loaded gem names/versions and Ruby runtime | Once per agent by default; bounded | `Gem.loaded_specs` and Ruby constants |
47
+ | Rails, web server, database adapter, Sidekiq, release | Included when safely detectable/configured | Loaded constants/specs and `app_version` |
48
+ | Event release, revision, deploy ID, environment, service, region, instance | Present as bounded correlation; values optional | Explicit immutable configuration or normalized deploy payload |
49
+ | Deploy environment, revision, version, repository, actor, deploy ID, service, region, instance | Explicit synchronous deploy API | Application/deployment integration arguments |
43
50
 
44
- The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, source code, raw SQL, SQL bind values, database rows, cache keys/values, mail bodies/recipients, or installed gems. Sidekiq JIDs and bounded arguments are the explicit version 0.6 exception to the earlier job-ID/argument exclusion.
51
+ The gem never collects request bodies, response bodies, raw query strings, cookies, HTTP authorization headers, environment variables in bulk, Git state, source code, raw SQL, SQL bind values, database rows, raw cache keys/values, mail bodies/recipients, gem paths, or lockfile contents. Sidekiq JIDs/arguments, loaded gem names/versions, and deploy fields are documented integration data.
45
52
 
46
53
  APM dimensions never include user ID, job ID, raw URL, exception message, bind value, or cache key. Normalized routes replace common numeric/UUID segments. Normalized SQL can retain schema, table, and column identifiers; review those identifiers as part of the privacy audit.
47
54
 
@@ -23,4 +23,16 @@ For a network-free outage and bounded-backlog example, run:
23
23
  bundle _1.17.3_ exec ruby examples/plain-ruby/resilient_delivery.rb
24
24
  ```
25
25
 
26
- The example builds the final event locally and demonstrates key redaction, e-mail and Bearer-token detection, application-specific blocklisting, and identifier hashing.
26
+ For network-free per-instance outbound HTTP instrumentation, run:
27
+
28
+ ```bash
29
+ bundle _1.17.3_ exec ruby examples/plain-ruby/external_http.rb
30
+ ```
31
+
32
+ For a network-free deploy event with complete release correlation, run:
33
+
34
+ ```bash
35
+ bundle _1.17.3_ exec ruby examples/plain-ruby/deploy_tracking.rb
36
+ ```
37
+
38
+ The examples demonstrate final event construction, privacy filtering, bounded external HTTP fields, and deploy correlation without opening a socket.
@@ -1,6 +1,6 @@
1
1
  # Essential APM aggregation
2
2
 
3
- Version `0.7.0.pre.1` aggregates request, SQL, and job observations locally before delivery. The agent emits versioned `metric_batch` envelopes containing at most 50 metric groups instead of one event per observation. Aggregates drain when `apm_flush_count` observations are reached and whenever `Chronos.flush` or `Chronos.close` runs. No timer or additional APM thread is created.
3
+ Version `0.8.0.pre.1` aggregates request, SQL, job, and explicitly instrumented external HTTP observations locally before delivery. The agent emits versioned `metric_batch` envelopes containing at most 50 metric groups instead of one event per observation. Aggregates drain when `apm_flush_count` observations are reached and whenever `Chronos.flush` or `Chronos.close` runs. No timer or additional APM thread is created.
4
4
 
5
5
  ## Request metrics
6
6
 
@@ -35,7 +35,7 @@ New groups beyond capacity are dropped and counted in `dropped_groups`. Existing
35
35
 
36
36
  ## Breakdown
37
37
 
38
- The contract supports `database`, `view`, `external_http`, `cache`, `queue`, `application`, and `unknown`. Version 0.7 fills database/view/cache within a traced request, queue/application for jobs, and residual application time for requests. External HTTP instrumentation begins in version 0.8.
38
+ The contract supports `database`, `view`, `external_http`, `cache`, `queue`, `application`, and `unknown`. Version 0.8 fills external HTTP time for explicitly instrumented calls inside a traced request, in addition to database/view/cache, queue/application for jobs, and residual application request time.
39
39
 
40
40
  ## Configuration
41
41
 
@@ -0,0 +1,16 @@
1
+ # Cache observability
2
+
3
+ Rails ActiveSupport cache notifications produce individual bounded `cache` events containing operation, duration, hit/miss outcome, backend, and namespace. When a trace is active, duration also contributes to the request's cache breakdown.
4
+
5
+ Raw cache keys and values are never delivered. The default `cache_key_mode = :none` omits key identity entirely. Applications that need correlation can opt into a project-scoped SHA-256 value:
6
+
7
+ ```ruby
8
+ Chronos.configure do |config|
9
+ # connection settings omitted
10
+ config.cache_key_mode = :sha256
11
+ end
12
+ ```
13
+
14
+ Hashing accepts at most 2,048 bytes of key text and emits a fixed 64-character hexadecimal value. It is pseudonymization rather than anonymization: an attacker who can guess a low-entropy key may reproduce candidate hashes when project scope is known. Do not encode credentials or unnecessary personal data in cache keys.
15
+
16
+ The normalizer never calls cache read/write APIs, inspects cached values, or retains keys. Unsupported or incomplete notification payloads become an `unknown` outcome without raising into Rails.
@@ -9,3 +9,7 @@ Risks include accidentally logging credentials and changing settings while event
9
9
  Tests in `spec/unit/configuration_spec.rb` verify required fields, HTTPS defaults, immutable containers, disabled operation, and bounded numeric settings.
10
10
 
11
11
  Version 0.7 adds bounded APM capacities, thresholds, and histogram boundaries. Invalid zero/negative capacities, batches above 50, N+1 thresholds below two, and non-increasing histogram boundaries are rejected before agent construction. See [Essential APM aggregation](apm-aggregation.md).
12
+
13
+ Version 0.8 adds Boolean HTTP/dependency switches, `:none`/`:sha256` cache-key policy, and a dependency limit from 1 to 200. Invalid values are rejected before instrumentation or collection starts. See [External HTTP](external-http.md), [Cache observability](cache-observability.md), and [Dependency inventory](dependencies.md).
14
+
15
+ Version 0.9 adds optional `revision`, `deploy_id`, `region`, and `instance_id` strings, each limited to 128 bytes. Together with the existing release/environment/service options they form immutable event correlation. See [Deploy tracking and release correlation](deploy-tracking.md).
@@ -0,0 +1,18 @@
1
+ # Dependency inventory
2
+
3
+ Version `0.9.0.pre.1` emits a separate `dependencies` event at most once per configured agent and once again after a successful deploy notification. It is queued before the first capture or during explicit flush/close and is never attached to each exception.
4
+
5
+ The inventory contains Ruby version/engine/platform, bounded names and versions from already loaded gem specs, the configured `app_version` release identifier, and Rails, Sidekiq, web server, or database adapter labels when safely detectable. The agent does not parse `Gemfile.lock`, activate missing gems, inspect gem paths/source, scan environment variables, open a database connection, or collect dependency configuration.
6
+
7
+ ```ruby
8
+ Chronos.configure do |config|
9
+ # connection settings omitted
10
+ config.dependency_reporting = true
11
+ config.dependency_max_items = 100
12
+ end
13
+
14
+ # Optional after application boot, before the first regular event:
15
+ Chronos.report_dependencies
16
+ ```
17
+
18
+ `dependency_max_items` accepts 1–200 and defaults to 100. Entries are sorted by name after bounded selection. Set `dependency_reporting = false` when inventory collection is unnecessary or prohibited. A successful `notify_deploy` resets the once-only guard and uses the deploy version for the refreshed inventory; otherwise gems loaded after the first event appear only after a new agent configuration or process boot.