chronos-ruby 0.1.0.pre.2 → 0.3.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/README.md +51 -21
  4. data/docs/adr/ADR-005-sanitize-before-backlog.md +2 -2
  5. data/docs/adr/ADR-011-bounded-resilience-and-remote-control.md +27 -0
  6. data/docs/architecture.md +5 -4
  7. data/docs/compatibility.md +6 -6
  8. data/docs/configuration.md +27 -1
  9. data/docs/data-collected.md +10 -7
  10. data/docs/examples/plain-ruby.md +14 -0
  11. data/docs/modules/async-queue.md +6 -2
  12. data/docs/modules/notice-pipeline.md +6 -3
  13. data/docs/modules/remote-configuration.md +54 -0
  14. data/docs/modules/retry-backlog.md +60 -0
  15. data/docs/modules/sanitization.md +19 -0
  16. data/docs/modules/serialization.md +9 -0
  17. data/docs/modules/transport.md +5 -3
  18. data/docs/performance.md +39 -2
  19. data/docs/privacy-lgpd.md +80 -10
  20. data/docs/troubleshooting.md +2 -2
  21. data/lib/chronos/adapters/net_http_transport.rb +21 -3
  22. data/lib/chronos/agent.rb +16 -8
  23. data/lib/chronos/application/capture_exception.rb +20 -14
  24. data/lib/chronos/application/circuit_breaker.rb +70 -0
  25. data/lib/chronos/application/delivery_pipeline.rb +248 -0
  26. data/lib/chronos/application/remote_configuration.rb +173 -0
  27. data/lib/chronos/application/retry_policy.rb +57 -0
  28. data/lib/chronos/configuration.rb +178 -21
  29. data/lib/chronos/core/notice.rb +1 -1
  30. data/lib/chronos/core/payload_serializer.rb +39 -89
  31. data/lib/chronos/core/runtime_info.rb +1 -1
  32. data/lib/chronos/core/safe_serializer.rb +119 -0
  33. data/lib/chronos/core/sanitizer.rb +161 -0
  34. data/lib/chronos/core/sensitive_value_filter.rb +118 -0
  35. data/lib/chronos/internal/bounded_queue.rb +1 -1
  36. data/lib/chronos/internal/memory_backlog.rb +65 -0
  37. data/lib/chronos/internal/worker_pool.rb +5 -6
  38. data/lib/chronos/ports/transport.rb +4 -3
  39. data/lib/chronos/version.rb +1 -1
  40. data/lib/chronos.rb +9 -1
  41. metadata +30 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 203d12bad0a790efdb2bb89bcbc9127f067fc096507c6345aefbbdc7ed13966c
4
- data.tar.gz: 2fb4eb956405f099b10b3e6f9429800446faf24127bc35b3e092dce316c1edb2
3
+ metadata.gz: db591fb9c6019444d079a5a5c683be8776b68010e3665e4a09656c2bdf568b46
4
+ data.tar.gz: 601f34cdaa1f9e06e2748a820b4228bad3c354a8c7feb562473d4268dc217117
5
5
  SHA512:
6
- metadata.gz: 30be78abcf064b43e2f395c7493f5e7b037c8f1b8ac8dd5a4fc507ea86eae7c89fe0104f094551e3e92d68b4dded9010949fc9a79e43c01880217266bcebaa9c
7
- data.tar.gz: 907ba0c16f873d533714b1a06448e857e7a4c8bfcf2b524a150bf0906244d507cf5e6b961d3db219bf63da6d3bf6933474e25360f3715d3e93b0e7d07929db21
6
+ metadata.gz: 1c4adaeb1222449ba2b7a736f1e28e701d00a1faf3733f42ffa753759854c3deed7a4c9e4c33f2ea997aa9445f057b51641b3829fb49b2012e6668687a1df4b0
7
+ data.tar.gz: 3809e42fbaed2f3290464cdc7a1e962a70e39fd8c3f7071b6016ee53c53406fca84a250f375e0a46506560c9bdc603f4ceb13721b79b4c6225b28a1e94abdf49
data/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@ All notable changes are documented here. The project follows Semantic Versioning
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ### Fixed
8
+
9
+ - release publishing now updates RubyGems to a Ruby 2.6-compatible version that supports `GEM_HOST_API_KEY`.
10
+ - Updated the legacy development toolchain to non-vulnerable Rake and RuboCop versions.
11
+ - legacy CI now resolves Bundler 1.17.3 through `Gem.bin_path` on RubyGems versions that do not support the `_version_` executable selector.
12
+ - documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
13
+
14
+ ## [0.3.0.pre.1] - 2026-07-20
15
+
16
+ ### Added
17
+
18
+ - explicit delivery-state counters for accepted, queued, serialized, sent, retried, dropped, and rejected events;
19
+ - finite exponential retry with bounded jitter and `Retry-After` support;
20
+ - retry classification for network errors, HTTP 408, 429, and 5xx responses;
21
+ - closed, open, and half-open circuit breaker states;
22
+ - fixed-capacity in-memory backlog restricted to sanitized serialized events;
23
+ - bounded remote configuration for sampling, event types, payload limits, exact fingerprint ignores, send interval, and kill switch;
24
+ - resilience contracts, unit tests, module documentation, ADR, executable example, and outage benchmark.
25
+
26
+ ### Changed
27
+
28
+ - asynchronous and synchronous delivery now pass through `DeliveryPipeline`;
29
+ - `WorkerPool` delegates delivery policy instead of sending directly through a transport;
30
+ - version advanced to `0.3.0.pre.1`.
31
+
32
+ ### Known limitations
33
+
34
+ - backlog is not persisted and is lost when the process exits;
35
+ - backlog draining requires later delivery activity;
36
+ - no dedicated remote-configuration polling endpoint;
37
+ - no automatic Rack, Rails, or job integration.
38
+
39
+ ## [0.2.0.pre.1] - 2026-07-19
40
+
41
+ ### Added
42
+
43
+ - recursive sensitive-key sanitization with String, Symbol, and Regexp matchers;
44
+ - content detection for Bearer tokens, JWTs, e-mail addresses, CPF, CNPJ, and payment cards;
45
+ - configurable identifier hashing and IPv4 anonymization;
46
+ - bounded safe serialization with cycle and node-budget protection;
47
+ - custom privacy filters with contained failures;
48
+ - privacy contract tests, audit example, module documentation, and filtering benchmark.
49
+
50
+ ### Changed
51
+
52
+ - all exception fields are sanitized before JSON serialization and transport;
53
+ - configuration snapshots now recursively freeze privacy settings;
54
+ - ADR-005 is accepted for the sanitization boundary.
55
+
56
+ ### Known limitations
57
+
58
+ - sensitive-data detection is defensive and cannot replace an application privacy review;
59
+ - IPv6 anonymization is not implemented;
60
+ - no retry or backlog;
61
+ - no automatic Rack, Rails, or job integration.
62
+
7
63
  ## [0.1.0.pre.2] - 2026-07-19
8
64
 
9
65
  ### Changed
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 to Chronos. Version 0.1 is the legacy foundation: it captures exceptions manually, builds a bounded JSON event, and delivers it synchronously or through a fixed-size asynchronous queue.
3
+ Chronos Ruby is the framework-independent client for sending Ruby application errors to Chronos. Version 0.3 adds bounded resilience to the privacy-focused legacy foundation: manual exceptions are sanitized before queueing, retried under a finite policy, retained in a fixed-size memory backlog during outages, and controlled by a restricted remote policy.
4
4
 
5
5
  ## What the gem collects
6
6
 
7
- For each exception, version 0.1 can collect:
7
+ For each exception, version 0.3 can collect:
8
8
 
9
9
  - exception class, message, structured backtrace, and chained causes;
10
10
  - timestamp, severity, tags, and an optional fingerprint;
@@ -16,11 +16,11 @@ See [Data collected](docs/data-collected.md) for the complete field table.
16
16
 
17
17
  ## What is not collected by default
18
18
 
19
- Chronos Ruby does not inspect environment variables, request bodies, cookies, HTTP headers, source code, database contents, or installed gems. Version 0.1 does not automatically sanitize application-supplied context. Do not pass passwords, tokens, cookies, personal documents, or payment data. Advanced redaction belongs to version 0.2.
19
+ 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.
20
20
 
21
21
  ## Supported Ruby and Rails versions
22
22
 
23
- Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.1 is independent of Rails and does not yet declare support for any Rails integration. All supported combinations must pass dedicated CI before being listed as supported.
23
+ Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.3 is independent of Rails and does not yet declare support for any Rails integration. All supported combinations must pass dedicated CI before being listed as supported.
24
24
 
25
25
  See [Compatibility](docs/compatibility.md).
26
26
 
@@ -29,7 +29,7 @@ See [Compatibility](docs/compatibility.md).
29
29
  The current public build is a pre-release. Add its exact version to the application's `Gemfile`:
30
30
 
31
31
  ```ruby
32
- gem "chronos-ruby", "0.1.0.pre.2"
32
+ gem "chronos-ruby", "0.3.0.pre.1"
33
33
  ```
34
34
 
35
35
  Install with a Bundler version compatible with the application. For the oldest supported runtime:
@@ -47,7 +47,7 @@ gem install chronos-ruby --pre
47
47
 
48
48
  ## Rails installation
49
49
 
50
- Rails automatic integration is not part of version 0.1. A Rails application may use the plain Ruby API, but this does not constitute declared Rails support. Rack and Rails adapters are planned for later legacy releases.
50
+ Rails automatic integration is not part of version 0.3. A Rails application may use the plain Ruby API, but this does not constitute declared Rails support. Rack and Rails adapters are planned for later legacy releases.
51
51
 
52
52
  ## Minimum configuration
53
53
 
@@ -70,7 +70,7 @@ HTTPS verification is enabled by default. HTTP requires explicitly setting `ssl_
70
70
 
71
71
  ## Automatic capture
72
72
 
73
- Automatic exception capture is not implemented in version 0.1. Applications must call `Chronos.notify` or `Chronos.notify_sync`. Rack, Rails, and worker hooks will be introduced only after their compatibility suites exist.
73
+ Automatic exception capture is not implemented in version 0.3. Applications must call `Chronos.notify` or `Chronos.notify_sync`. Rack, Rails, and worker hooks will be introduced only after their compatibility suites exist.
74
74
 
75
75
  ## Manual capture
76
76
 
@@ -101,15 +101,26 @@ User data is opt-in and must contain only values your application is allowed to
101
101
  Chronos.notify(error, :user => {"id" => "customer-42", "role" => "operator"})
102
102
  ```
103
103
 
104
- Avoid names, e-mail addresses, documents, tokens, and other personal or secret fields in version 0.1.
104
+ Version 0.3 sanitizes this context before delivery and before it can enter retry storage. Data minimization remains the application's responsibility.
105
105
 
106
106
  ## Breadcrumbs
107
107
 
108
- Breadcrumbs are not implemented in version 0.1.
108
+ Breadcrumbs are not implemented in version 0.3.
109
109
 
110
110
  ## Filters and LGPD
111
111
 
112
- Version 0.1 limits depth, collection length, string length, backtrace length, and total payload size. It does not provide the recursive sensitive-data filter planned for version 0.2. Review all supplied context before production use. See [Privacy and LGPD](docs/privacy-lgpd.md).
112
+ Version 0.3 recursively redacts sensitive keys and detects Bearer tokens, JWTs, e-mail addresses, CPF, CNPJ, and valid payment-card candidates in free text. IPv4 addresses are anonymized by default. Applications can add blocklist matchers, hash selected identifiers, or install custom filters:
113
+
114
+ ```ruby
115
+ Chronos.configure do |config|
116
+ # required options omitted
117
+ config.blocklist_keys += [:medical_record, /bank_account/i]
118
+ config.hash_keys += [:customer_id]
119
+ config.filters << proc { |key, value| key.to_s == "internal_reference" ? "[REMOVED]" : value }
120
+ end
121
+ ```
122
+
123
+ Sanitization runs before queueing and transport. See [Privacy and LGPD](docs/privacy-lgpd.md) for behavior, limitations, health and financial examples, and a payload audit procedure.
113
124
 
114
125
  ## Ignore rules
115
126
 
@@ -122,19 +133,19 @@ Chronos.configure do |config|
122
133
  end
123
134
  ```
124
135
 
125
- Exception-specific ignore rules are not available in version 0.1.
136
+ Local exception-specific ignore callbacks are not available in version 0.3. The server may provide a bounded list of exact fingerprints to ignore; it cannot provide regular expressions or executable rules.
126
137
 
127
138
  ## Performance monitoring
128
139
 
129
- Request, SQL, cache, job, and external HTTP monitoring are not implemented in version 0.1. The local capture pipeline is bounded and HTTP delivery runs outside the caller thread when `Chronos.notify` is used.
140
+ Request, SQL, cache, job, and external HTTP monitoring are not implemented in version 0.3. The local capture pipeline is bounded and HTTP delivery runs outside the caller thread when `Chronos.notify` is used.
130
141
 
131
142
  ## Sidekiq and Active Job
132
143
 
133
- Sidekiq and Active Job integrations are not implemented in version 0.1. Calling the manual API from a job is possible, but automatic capture and deduplication are not yet guaranteed.
144
+ Sidekiq and Active Job integrations are not implemented in version 0.3. Calling the manual API from a job is possible, but automatic capture and deduplication are not yet guaranteed.
134
145
 
135
146
  ## Deploy tracking
136
147
 
137
- Deploy notifications are not implemented in version 0.1. `app_version` may be included in exception events for release correlation.
148
+ Deploy notifications are not implemented in version 0.3. `app_version` may be included in exception events for release correlation.
138
149
 
139
150
  ## Asynchronous queue
140
151
 
@@ -143,24 +154,33 @@ The queue has a fixed capacity and drops the newest event when full. Worker thre
143
154
  ```mermaid
144
155
  flowchart LR
145
156
  E[Exception] --> N[Notice builder]
146
- N --> S[Bounded serializer]
147
- S --> Q[Bounded queue]
157
+ N --> P[Privacy sanitizer]
158
+ P --> S[Safe bounded serializer]
159
+ S --> D[Delivery pipeline]
160
+ D --> Q[Bounded queue]
148
161
  Q --> W[Fixed worker pool]
149
- W --> H[Net::HTTP transport]
162
+ W --> R[Retry and circuit breaker]
163
+ R --> H[Net::HTTP transport]
164
+ R --> B[Bounded memory backlog]
150
165
  ```
151
166
 
152
167
  Use `Chronos.flush(timeout)` to wait for accepted events and `Chronos.close(timeout)` during shutdown. Workers are recreated after a process fork.
153
168
 
154
169
  ## Retry and backlog
155
170
 
156
- Version 0.1 classifies network failures, `429`, `4xx`, and `5xx`, but does not retry or persist events. Failed deliveries are dropped after the attempt. Bounded retry and backlog are planned for version 0.3.
171
+ Version 0.3 retries network errors, HTTP `408`, `429`, and `5xx` responses with exponential backoff, bounded jitter, and a finite attempt count. Other `4xx` responses are permanent and are not retried. A circuit breaker pauses requests after repeated failures, preventing retry storms.
172
+
173
+ After retries are exhausted, the already sanitized `SerializedEvent` may enter a fixed-capacity memory backlog. The backlog drops new items when full, is lost when the process exits, and never writes to disk. A later successful half-open probe drains backlog items as new events arrive.
174
+
175
+ The SaaS may return a JSON policy in the bounded `X-Chronos-Remote-Configuration` response header. Only sampling rate, enabled event types, a lower payload limit, exact ignored fingerprints, send interval, and kill switch are accepted. Remote values cannot change the host, project credentials, TLS, local maximums, code, or regular expressions. See [Retry and backlog](docs/modules/retry-backlog.md) and [Remote configuration](docs/modules/remote-configuration.md).
157
176
 
158
177
  ## How it works internally
159
178
 
160
179
  The code follows hexagonal boundaries:
161
180
 
162
- - `Chronos::Core` contains immutable notices and normalization;
181
+ - `Chronos::Core` contains immutable notices, sanitization, and safe normalization;
163
182
  - `Chronos::Application` coordinates capture;
183
+ - `Chronos::Application::DeliveryPipeline` owns bounded retry and remote policy;
164
184
  - `Chronos::Ports` defines delivery behavior;
165
185
  - `Chronos::Adapters` implements Net::HTTP delivery;
166
186
  - `Chronos::Internal` owns bounded queueing, workers, and defensive logging.
@@ -182,6 +202,14 @@ Chronos.configure do |config|
182
202
  config.workers = 1
183
203
  config.timeout = 5.0
184
204
  config.open_timeout = 2.0
205
+ config.max_retries = 3
206
+ config.retry_base_interval = 0.5
207
+ config.retry_max_interval = 30.0
208
+ config.retry_jitter = 0.25
209
+ config.backlog_size = 100
210
+ config.circuit_failure_threshold = 5
211
+ config.circuit_reset_timeout = 30.0
212
+ config.remote_configuration = true
185
213
  end
186
214
  ```
187
215
 
@@ -193,19 +221,21 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
193
221
 
194
222
  ## Benchmark
195
223
 
196
- Run the version 0.1 benchmarks with:
224
+ Run the version 0.3 benchmarks with:
197
225
 
198
226
  ```bash
199
227
  bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
200
228
  bundle _1.17.3_ exec ruby benchmarks/serialization.rb
229
+ bundle _1.17.3_ exec ruby benchmarks/filtering.rb
201
230
  bundle _1.17.3_ exec ruby benchmarks/queue.rb
231
+ bundle _1.17.3_ exec ruby benchmarks/retry_backlog.rb
202
232
  ```
203
233
 
204
234
  Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
205
235
 
206
236
  ## Migration from Airbrake
207
237
 
208
- An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.1 does not claim API compatibility or automatic replacement.
238
+ An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.3 does not claim API compatibility or automatic replacement.
209
239
 
210
240
  ## Local development
211
241
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Status
4
4
 
5
- Proposed for versions 0.2 and 0.3; no backlog exists in 0.1.
5
+ Accepted and implemented in version 0.3.
6
6
 
7
7
  ## Context
8
8
 
@@ -10,7 +10,7 @@ Persisting unsanitized events would increase privacy and credential exposure ris
10
10
 
11
11
  ## Decision
12
12
 
13
- Any future retry or persistent backlog must receive only data that has already passed sanitization and structural limits.
13
+ `PayloadSerializer` must run `Sanitizer` before `SafeSerializer`, queueing, transport, retry, or backlog. `MemoryBacklog` accepts only `SerializedEvent`. Raw notices remain transient in process memory and never enter delivery storage.
14
14
 
15
15
  ## Alternatives
16
16
 
@@ -0,0 +1,27 @@
1
+ # ADR-011 — Bounded resilience and restricted remote control
2
+
3
+ ## Status
4
+
5
+ Accepted for version 0.3.
6
+
7
+ ## Context
8
+
9
+ Chronos must tolerate prolonged SaaS outages in legacy applications without unbounded memory, unlimited retries, extra per-event threads, unsanitized storage, or a remote-control channel capable of changing security boundaries.
10
+
11
+ ## Decision
12
+
13
+ Place retry orchestration in `DeliveryPipeline`. Use finite exponential backoff with bounded positive jitter, a simple closed/open/half-open circuit breaker, and a fixed-capacity in-memory backlog that accepts only sanitized `SerializedEvent` values. Do not persist events to disk in version 0.3.
14
+
15
+ Accept remote policy only from a size-limited JSON response header after successful authenticated event delivery. Apply a fixed scalar allowlist and local upper bounds. Reject executable content, remote regular expressions, host, credentials, TLS, and capacity changes.
16
+
17
+ ## Alternatives
18
+
19
+ Retry inside `NetHttpTransport` was rejected because transport classification and delivery policy would become coupled. An unbounded queue was rejected because endpoint availability would control host memory. Immediate disk persistence was rejected because permissions, rotation, expiry, checksums, corruption, and privacy need an independent design. Arbitrary remote JSON or code callbacks were rejected as an unsafe control plane.
20
+
21
+ ## Positive consequences
22
+
23
+ Outage memory remains bounded, retry storms are contained, transport stays replaceable, privacy precedes backlog, and remote operators can reduce or stop later collection without changing endpoint security.
24
+
25
+ ## Negative consequences
26
+
27
+ Memory backlog is lost on restart and drains only when later delivery activity occurs. Synchronous notification may wait through retry delays. Process-local sampling is approximate. A response header limits policy size and complexity.
data/docs/architecture.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Chronos Ruby 0.1 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
3
+ Chronos Ruby 0.3 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
4
4
 
5
5
  ```mermaid
6
6
  flowchart TB
@@ -8,7 +8,8 @@ flowchart TB
8
8
  Application --> Core[Core / Notice and serialization]
9
9
  Application --> Ports[Ports / Transport contract]
10
10
  Ports --> Adapter[Adapters / NetHttpTransport]
11
- Application --> Internal[Internal / BoundedQueue and WorkerPool]
11
+ Application --> Delivery[Application / DeliveryPipeline]
12
+ Delivery --> Internal[Internal / BoundedQueue, WorkerPool, and MemoryBacklog]
12
13
  Internal --> Ports
13
14
  ```
14
15
 
@@ -24,8 +25,8 @@ The `Chronos` module is a thin facade. Rails, Rack, ActiveSupport, Sidekiq, and
24
25
 
25
26
  ## Capture flow
26
27
 
27
- An exception becomes an immutable notice, then a bounded JSON envelope. Asynchronous capture inserts the serialized event into a bounded queue. A fixed worker sends it through the transport. Synchronous capture bypasses the queue.
28
+ An exception becomes an immutable notice. `Sanitizer` removes sensitive values before `SafeSerializer` creates a bounded JSON envelope. Asynchronous capture inserts only that sanitized serialized event into the queue. A fixed worker sends it through `DeliveryPipeline`, which applies finite retry, a circuit breaker, and a fixed memory backlog. Synchronous capture bypasses the queue but uses the same privacy and resilience boundaries.
28
29
 
29
30
  ## Failure policy
30
31
 
31
- Explicit invalid configuration raises `Chronos::ConfigurationError`. Capture, serialization, logger, worker, TLS, network, and HTTP failures do not escape into the host application. They produce `false`, a transport result, or a bounded logger diagnostic.
32
+ 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.
@@ -4,14 +4,14 @@ Chronos Ruby 0.x is the legacy line. Technical compatibility does not make an en
4
4
 
5
5
  | Ruby | Rails integration | Status | Evidence |
6
6
  |---|---|---|---|
7
- | 2.2.10 | None in 0.1 | Best effort | Local unit, contract, integration, fork, and fake-server suite |
8
- | 2.3.8 | None in 0.1 | Best effort | Dedicated CI job pending first successful run |
9
- | 2.4.10 | None in 0.1 | Best effort | Dedicated CI job pending first successful run |
10
- | 2.5.9 | None in 0.1 | Best effort | Dedicated CI job pending first successful run |
11
- | 2.6.10 | None in 0.1 | Best effort | Dedicated CI job pending first successful run |
7
+ | 2.2.10 | None in 0.3 | Best effort | Dedicated legacy core CI job |
8
+ | 2.3.8 | None in 0.3 | Best effort | Dedicated legacy core CI job |
9
+ | 2.4.10 | None in 0.3 | Best effort | Dedicated legacy core CI job |
10
+ | 2.5.9 | None in 0.3 | Best effort | Dedicated legacy core CI job |
11
+ | 2.6.10 | None in 0.3 | Best effort | Dedicated legacy core CI job |
12
12
  | 2.7 and newer | None in 0.x | Unsupported | Belongs to transitional or modern lines |
13
13
 
14
- No Rails version is declared supported by version 0.1. Rails applications may call the plain Ruby API manually. Rails support requires a real example application, framework integration tests, and a successful dedicated matrix job.
14
+ No Rails version is declared supported by version 0.3. Rails applications may call the plain Ruby API manually. Rails support requires a real example application, framework integration tests, and a successful dedicated matrix job.
15
15
 
16
16
  Status meanings:
17
17
 
@@ -24,6 +24,23 @@
24
24
  | `user_agent` | Optional | Agent version | HTTP user agent |
25
25
  | `max_payload_size` | Optional | `1048576` | Maximum serialized payload bytes |
26
26
  | `gzip` | Optional | `false` | Compresses request bodies with gzip |
27
+ | `blocklist_keys` | Recommended | Sensitive-key defaults | String, Symbol, or Regexp keys whose values are redacted |
28
+ | `allowlist_keys` | Optional | `[]` | Explicit key-name exceptions; content detection still runs |
29
+ | `hash_keys` | Optional | `[]` | Scalar identifier keys replaced by scoped SHA-256 values |
30
+ | `filters` | Optional | `[]` | Callable application-specific privacy filters |
31
+ | `anonymize_ip` | Recommended | `true` | Replaces the final octet of supplied IPv4 addresses |
32
+ | `max_retries` | Optional | `3` | Maximum retry attempts after the first delivery failure |
33
+ | `retry_base_interval` | Optional | `0.5` | Initial exponential-backoff delay in seconds |
34
+ | `retry_max_interval` | Optional | `30.0` | Maximum local or `Retry-After` delay in seconds |
35
+ | `retry_jitter` | Optional | `0.25` | Random positive jitter fraction between `0.0` and `1.0` |
36
+ | `backlog_size` | Optional | `100` | Maximum sanitized events retained in memory after delivery failure; `0` disables retention |
37
+ | `circuit_failure_threshold` | Optional | `5` | Consecutive retryable failures before opening the circuit |
38
+ | `circuit_reset_timeout` | Optional | `30.0` | Seconds before one half-open delivery probe |
39
+ | `remote_configuration` | Optional | `true` | Accepts only the documented bounded remote policy fields |
40
+ | `remote_config_max_bytes` | Optional | `4096` | Maximum remote policy response-header bytes |
41
+ | `sampling_rate` | Optional | `1.0` | Local upper bound for exception sampling |
42
+ | `enabled_event_types` | Optional | `["exception"]` | Local event-type allowlist; only exception exists in version 0.3 |
43
+ | `max_remote_send_interval` | Optional | `60.0` | Local upper bound for remotely requested send spacing |
27
44
 
28
45
  ```ruby
29
46
  Chronos.configure do |config|
@@ -34,7 +51,16 @@ Chronos.configure do |config|
34
51
  config.service_name = "billing"
35
52
  config.queue_size = 100
36
53
  config.workers = 1
54
+ config.blocklist_keys += [:medical_record, /bank_account/i]
55
+ config.hash_keys += [:customer_id]
56
+ config.max_retries = 3
57
+ config.backlog_size = 100
58
+ config.circuit_failure_threshold = 5
37
59
  end
38
60
  ```
39
61
 
40
- The gem never reads all environment variables automatically. The host application decides which values to pass.
62
+ Privacy matcher collections and filters are copied into the immutable runtime snapshot. Matchers must be String, Symbol, or Regexp values, and every custom filter must respond to `call`.
63
+
64
+ 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.
65
+
66
+ 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.
@@ -1,20 +1,23 @@
1
1
  # Data collected
2
2
 
3
- Version 0.1 emits only manual exception events.
3
+ Version 0.3 emits only manually submitted exception events. 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
  |---|---|---|
7
- | Exception class and message | Collected | Ruby exception |
7
+ | Exception class and sanitized message | Collected | Ruby exception |
8
8
  | Structured backtrace | Collected when present | Ruby exception |
9
- | Cause class and message | Collected when present | `Exception#cause` |
9
+ | Sanitized cause class and message | Collected when present | `Exception#cause` |
10
10
  | Ruby version, engine, platform | Collected | Ruby constants |
11
11
  | PID and opaque thread ID | Collected | Ruby runtime |
12
12
  | Hostname | Collected when available | Standard library |
13
13
  | Environment and service | Configuration | Host application |
14
14
  | Application version | Optional | Host application |
15
- | Context, parameters, session, user | Optional | Explicit capture arguments |
16
- | Tags and fingerprint | Optional | Explicit capture arguments |
15
+ | Context, parameters, session, user | Optional and sanitized | Explicit capture arguments |
16
+ | Tags and fingerprint | Optional and sanitized | Explicit capture arguments |
17
+ | IPv4 address in supplied text | Anonymized | Explicit capture arguments |
17
18
 
18
- The gem never collects request bodies, response bodies, cookies, HTTP authorization headers, environment variables in bulk, source code, SQL bind values, database rows, or installed gems in version 0.1.
19
+ The gem never collects request bodies, response bodies, cookies, HTTP authorization headers, environment variables in bulk, source code, SQL bind values, database rows, or installed gems in version 0.3.
19
20
 
20
- The secret `project_key` is an authentication header and is excluded from the JSON payload and logger diagnostics.
21
+ The secret `project_key` is an authentication header and is excluded from the JSON payload and logger diagnostics. The envelope field named `project_key` contains the public `project_id` required by the current v1 server contract.
22
+
23
+ See [Privacy and LGPD](privacy-lgpd.md) for redaction rules and the payload audit procedure.
@@ -10,3 +10,17 @@ bundle _1.17.3_ exec ruby examples/plain-ruby/example.rb
10
10
  ```
11
11
 
12
12
  The example reports one manual exception and closes the agent with a two-second timeout. Use a local fake endpoint when auditing the payload.
13
+
14
+ For a network-free privacy audit, run:
15
+
16
+ ```bash
17
+ bundle _1.17.3_ exec ruby examples/plain-ruby/privacy_audit.rb
18
+ ```
19
+
20
+ For a network-free outage and bounded-backlog example, run:
21
+
22
+ ```bash
23
+ bundle _1.17.3_ exec ruby examples/plain-ruby/resilient_delivery.rb
24
+ ```
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.
@@ -10,8 +10,12 @@ stateDiagram-v2
10
10
  Accepted --> Queued
11
11
  Queued --> Sending
12
12
  Sending --> Sent
13
- Sending --> Dropped: delivery failure in 0.1
13
+ Sending --> Retried: retryable failure
14
+ Retried --> Sending: policy permits
15
+ Retried --> Backlog: attempts exhausted
16
+ Backlog --> Sending: later recovery probe
17
+ Sending --> Rejected: permanent 4xx
14
18
  Accepted --> Dropped: queue full
15
19
  ```
16
20
 
17
- Version 0.1 has no disk persistence and no retry. Queue statistics expose current size, capacity, accepted events, and dropped events. Tests cover full queues, waiter wakeup, lazy startup, contained failures, timed shutdown, double close, flush, and fork behavior.
21
+ Version 0.3 adds retry and a separate fixed-size memory backlog, but no disk persistence. Only sanitized serialized events enter either structure. Queue statistics expose current size, capacity, accepted events, and dropped events. Delivery diagnostics additionally expose state counters, backlog statistics, and circuit state. Tests cover full queues, waiter wakeup, lazy startup, contained failures, timed shutdown, double close, flush, fork behavior, and prolonged endpoint failure.
@@ -9,12 +9,15 @@ flowchart LR
9
9
  NoticeBuilder --> CauseCollector
10
10
  NoticeBuilder --> RuntimeInfo
11
11
  NoticeBuilder --> Notice
12
- Notice --> PayloadSerializer
12
+ Notice --> Sanitizer
13
+ Sanitizer --> SafeSerializer
14
+ SafeSerializer --> PayloadSerializer
13
15
  PayloadSerializer --> SerializedEvent
16
+ SerializedEvent --> DeliveryPipeline
14
17
  ```
15
18
 
16
- `Notice` is immutable. Parsers are stateless. The serializer accepts only JSON primitives, bounds nested structures, tolerates invalid encoding, and represents unknown objects without arbitrary `to_json` calls.
19
+ `Notice` is immutable. Parsers are stateless. Raw notice values exist only in process memory. The sanitizer removes sensitive keys and recognized personal data before the safe serializer accepts JSON primitives, bounds nested structures, tolerates invalid encoding, and represents unknown objects without arbitrary application serialization.
17
20
 
18
- Version 0.1 deliberately excludes the advanced sanitizer, sampler, and fingerprint policy. These can be inserted before serialization without changing transport or queue code.
21
+ Version 0.3 passes the serialized event to `DeliveryPipeline`. The pipeline applies remote sampling and exact fingerprint policy before serialization where possible, then manages queueing, retry, circuit state, and bounded memory backlog without changing transport or queue code.
19
22
 
20
23
  Unit and contract tests verify missing backtraces, cyclic causes, invalid encoding, unsafe objects, payload limits, and the v1 envelope.
@@ -0,0 +1,54 @@
1
+ # Remote configuration module
2
+
3
+ ## Problem
4
+
5
+ Operators may need to reduce event volume or stop collection during an incident without redeploying a legacy application. Accepting unrestricted server instructions would create remote-code, SSRF, credential, privacy, and memory risks.
6
+
7
+ ## Boundary
8
+
9
+ `NetHttpTransport` only parses a bounded JSON object from the `X-Chronos-Remote-Configuration` success-response header. `Chronos::Application::RemoteConfiguration` owns the allowlist and local upper bounds. `DeliveryPipeline` applies accepted policy to later capture and delivery work.
10
+
11
+ ## Allowed fields
12
+
13
+ | Field | Accepted value | Local restriction |
14
+ |---|---|---|
15
+ | `sampling_rate` | Number from `0.0` to `1.0` | Cannot exceed local `sampling_rate` |
16
+ | `enabled_event_types` | Array of strings | Intersected with local and implemented event types |
17
+ | `max_payload_size` | Integer | Cannot exceed local maximum or fall below 256 bytes |
18
+ | `ignored_fingerprints` | Up to 100 strings | Exact matching only; 256 bytes per value |
19
+ | `send_interval` | Non-negative seconds | Cannot exceed `max_remote_send_interval` |
20
+ | `kill_switch` | Boolean | Stops later capture while active |
21
+
22
+ Unknown keys are ignored. If a recognized value is invalid, the document is rejected atomically and the previous policy remains active.
23
+
24
+ ## Explicitly forbidden
25
+
26
+ Remote configuration cannot change host, proxy, project ID, project key, TLS verification, logger, queue or backlog capacity, retry limits, local upper bounds, Ruby code, object serialization, regular expressions, or arbitrary headers. No `eval`, `send`, `Marshal`, YAML, or application callback is used.
27
+
28
+ ## Example server response
29
+
30
+ ```text
31
+ HTTP/1.1 202 Accepted
32
+ X-Chronos-Remote-Configuration: {"sampling_rate":0.25,"send_interval":1.0,"kill_switch":false}
33
+ ```
34
+
35
+ Disable this channel locally when it is not required:
36
+
37
+ ```ruby
38
+ Chronos.configure do |config|
39
+ # connection settings omitted
40
+ config.remote_configuration = false
41
+ end
42
+ ```
43
+
44
+ ## Risks and limits
45
+
46
+ - The policy is process-local and is not persisted across restarts.
47
+ - A stale policy remains until another accepted response changes it.
48
+ - The header size is bounded before JSON parsing, but upstream proxies may impose a lower header limit.
49
+ - Sampling uses process-local randomness and is not a globally exact rate.
50
+ - Version 0.3 does not poll a separate configuration endpoint.
51
+
52
+ ## Tests
53
+
54
+ Tests cover the field allowlist, local caps, unsupported event types, invalid types, regex rejection, forbidden endpoint and credential keys, response-header byte limits, kill switch, sampling, and atomic preservation of previous state.
@@ -0,0 +1,60 @@
1
+ # Retry and backlog module
2
+
3
+ ## Problem
4
+
5
+ Transient endpoint failures must not discard every event immediately, block application threads indefinitely, or grow memory without a limit.
6
+
7
+ ## Boundary
8
+
9
+ Retry belongs to `Chronos::Application::DeliveryPipeline`, not `NetHttpTransport`, because HTTP classifies an outcome while application policy decides whether another attempt is acceptable. `Chronos::Internal::MemoryBacklog` is separate from the asynchronous queue because it stores failed deliveries, not newly accepted work.
10
+
11
+ ## Data flow
12
+
13
+ ```mermaid
14
+ stateDiagram-v2
15
+ [*] --> accepted
16
+ accepted --> serialized
17
+ serialized --> queued: asynchronous capture
18
+ queued --> sent: successful delivery
19
+ serialized --> sent: synchronous delivery
20
+ queued --> retried: network, 408, 429, or 5xx
21
+ retried --> sent: later attempt succeeds
22
+ retried --> backlog: attempts exhausted or circuit open
23
+ backlog --> sent: later half-open recovery
24
+ backlog --> dropped: backlog full
25
+ queued --> rejected: permanent 4xx
26
+ ```
27
+
28
+ `RetryPolicy` calculates exponential backoff with positive bounded jitter. `Retry-After` is respected only within `retry_max_interval`. `CircuitBreaker` opens after the configured consecutive failure count, rejects concurrent probes, and permits one half-open probe after `circuit_reset_timeout`.
29
+
30
+ The memory backlog accepts only `SerializedEvent`. Sanitization and safe serialization therefore happen before an event can enter retry storage. Its capacity is `backlog_size`; zero disables retention. The oldest stored event is attempted as new delivery activity arrives. No background timer or additional thread exists for backlog draining.
31
+
32
+ ## Configuration and extension
33
+
34
+ ```ruby
35
+ Chronos.configure do |config|
36
+ # connection settings omitted
37
+ config.max_retries = 3
38
+ config.retry_base_interval = 0.5
39
+ config.retry_max_interval = 30.0
40
+ config.retry_jitter = 0.25
41
+ config.backlog_size = 100
42
+ config.circuit_failure_threshold = 5
43
+ config.circuit_reset_timeout = 30.0
44
+ end
45
+ ```
46
+
47
+ Tests may inject a clock, sleeper, random source, backlog, or retry policy into `DeliveryPipeline`. These are internal composition points rather than stable public configuration APIs.
48
+
49
+ ## Risks and limits
50
+
51
+ - Events in memory are lost on process exit.
52
+ - A quiet process does not drain backlog until another event triggers delivery.
53
+ - Backlog order is best effort when multiple workers are active.
54
+ - Retry sleeps occupy a fixed worker, never the application caller for asynchronous capture.
55
+ - Synchronous capture waits for configured retries and should be used only when that latency is acceptable.
56
+ - Disk persistence is deliberately excluded from version 0.3.
57
+
58
+ ## Tests
59
+
60
+ Unit tests cover bounded exponential delay, retry limits, `Retry-After`, permanent `4xx`, circuit opening and half-open recovery, fixed backlog capacity, raw-object rejection, prolonged outage, state counters, and recovery draining. The privacy contract proves that fixture secrets do not enter backlog storage.
@@ -0,0 +1,19 @@
1
+ # Sanitization module
2
+
3
+ `Chronos::Core::Sanitizer` prevents raw secrets and common personal identifiers from crossing the serialization boundary. It is separate from `SafeSerializer`: privacy policy decides what may remain, while serialization decides which Ruby values can safely become JSON.
4
+
5
+ ```mermaid
6
+ flowchart LR
7
+ Notice --> Envelope
8
+ Envelope --> Sanitizer
9
+ Sanitizer --> SafeSerializer
10
+ SafeSerializer --> JSON
11
+ JSON --> Queue
12
+ JSON --> Transport
13
+ ```
14
+
15
+ `Sanitizer` recursively processes hashes and arrays, supports String, Symbol, and Regexp key matchers, anonymizes IPv4 addresses, and optionally hashes selected scalar identifiers. `SensitiveValueFilter` validates and redacts common sensitive patterns in free text. Depth, visited nodes, hash keys, and array items are bounded before safe serialization. Custom filters can remove application-specific fields. A failing filter redacts its field and cannot interrupt the application.
16
+
17
+ The module deliberately does not inspect global variables, environment variables, request bodies, database records, or arbitrary object methods. Pattern detection can produce false positives or miss domain-specific formats, so applications must extend the blocklist and audit representative payloads.
18
+
19
+ Tests in `spec/unit/core/sanitizer_spec.rb` cover recursive keys, allowlisting, content detection, hashing, IP anonymization, and filter failure. `spec/contract/privacy_spec.rb` ensures sensitive fixtures never occur in the final JSON payload.