chronos-ruby 0.2.0.pre.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +35 -18
- data/docs/adr/ADR-005-sanitize-before-backlog.md +2 -2
- data/docs/adr/ADR-011-bounded-resilience-and-remote-control.md +27 -0
- data/docs/architecture.md +5 -4
- data/docs/compatibility.md +6 -6
- data/docs/configuration.md +17 -0
- data/docs/data-collected.md +2 -2
- data/docs/examples/plain-ruby.md +6 -0
- data/docs/modules/async-queue.md +6 -2
- data/docs/modules/notice-pipeline.md +2 -1
- data/docs/modules/remote-configuration.md +54 -0
- data/docs/modules/retry-backlog.md +60 -0
- data/docs/modules/transport.md +5 -3
- data/docs/performance.md +27 -2
- data/docs/privacy-lgpd.md +4 -2
- data/docs/troubleshooting.md +1 -1
- data/lib/chronos/adapters/net_http_transport.rb +21 -3
- data/lib/chronos/agent.rb +16 -8
- data/lib/chronos/application/capture_exception.rb +20 -14
- data/lib/chronos/application/circuit_breaker.rb +70 -0
- data/lib/chronos/application/delivery_pipeline.rb +248 -0
- data/lib/chronos/application/remote_configuration.rb +173 -0
- data/lib/chronos/application/retry_policy.rb +57 -0
- data/lib/chronos/configuration.rb +101 -19
- data/lib/chronos/core/payload_serializer.rb +4 -2
- data/lib/chronos/internal/bounded_queue.rb +1 -1
- data/lib/chronos/internal/memory_backlog.rb +65 -0
- data/lib/chronos/internal/worker_pool.rb +5 -6
- data/lib/chronos/ports/transport.rb +4 -3
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +6 -1
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db591fb9c6019444d079a5a5c683be8776b68010e3665e4a09656c2bdf568b46
|
|
4
|
+
data.tar.gz: 601f34cdaa1f9e06e2748a820b4228bad3c354a8c7feb562473d4268dc217117
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c4adaeb1222449ba2b7a736f1e28e701d00a1faf3733f42ffa753759854c3deed7a4c9e4c33f2ea997aa9445f057b51641b3829fb49b2012e6668687a1df4b0
|
|
7
|
+
data.tar.gz: 3809e42fbaed2f3290464cdc7a1e962a70e39fd8c3f7071b6016ee53c53406fca84a250f375e0a46506560c9bdc603f4ceb13721b79b4c6225b28a1e94abdf49
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,31 @@ All notable changes are documented here. The project follows Semantic Versioning
|
|
|
11
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
12
|
- documentation verification now reads source and Markdown files explicitly as UTF-8 on legacy container locales.
|
|
13
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
|
+
|
|
14
39
|
## [0.2.0.pre.1] - 2026-07-19
|
|
15
40
|
|
|
16
41
|
### 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 to Chronos. Version 0.
|
|
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.
|
|
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;
|
|
@@ -20,7 +20,7 @@ Chronos Ruby does not inspect environment variables, request bodies, cookies, HT
|
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,15 @@ 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
|
-
Version 0.
|
|
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.
|
|
108
|
+
Breadcrumbs are not implemented in version 0.3.
|
|
109
109
|
|
|
110
110
|
## Filters and LGPD
|
|
111
111
|
|
|
112
|
-
Version 0.
|
|
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
113
|
|
|
114
114
|
```ruby
|
|
115
115
|
Chronos.configure do |config|
|
|
@@ -133,19 +133,19 @@ Chronos.configure do |config|
|
|
|
133
133
|
end
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
|
|
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.
|
|
137
137
|
|
|
138
138
|
## Performance monitoring
|
|
139
139
|
|
|
140
|
-
Request, SQL, cache, job, and external HTTP monitoring are not implemented in version 0.
|
|
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.
|
|
141
141
|
|
|
142
142
|
## Sidekiq and Active Job
|
|
143
143
|
|
|
144
|
-
Sidekiq and Active Job integrations are not implemented in version 0.
|
|
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.
|
|
145
145
|
|
|
146
146
|
## Deploy tracking
|
|
147
147
|
|
|
148
|
-
Deploy notifications are not implemented in version 0.
|
|
148
|
+
Deploy notifications are not implemented in version 0.3. `app_version` may be included in exception events for release correlation.
|
|
149
149
|
|
|
150
150
|
## Asynchronous queue
|
|
151
151
|
|
|
@@ -156,16 +156,23 @@ flowchart LR
|
|
|
156
156
|
E[Exception] --> N[Notice builder]
|
|
157
157
|
N --> P[Privacy sanitizer]
|
|
158
158
|
P --> S[Safe bounded serializer]
|
|
159
|
-
S -->
|
|
159
|
+
S --> D[Delivery pipeline]
|
|
160
|
+
D --> Q[Bounded queue]
|
|
160
161
|
Q --> W[Fixed worker pool]
|
|
161
|
-
W -->
|
|
162
|
+
W --> R[Retry and circuit breaker]
|
|
163
|
+
R --> H[Net::HTTP transport]
|
|
164
|
+
R --> B[Bounded memory backlog]
|
|
162
165
|
```
|
|
163
166
|
|
|
164
167
|
Use `Chronos.flush(timeout)` to wait for accepted events and `Chronos.close(timeout)` during shutdown. Workers are recreated after a process fork.
|
|
165
168
|
|
|
166
169
|
## Retry and backlog
|
|
167
170
|
|
|
168
|
-
Version 0.
|
|
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).
|
|
169
176
|
|
|
170
177
|
## How it works internally
|
|
171
178
|
|
|
@@ -173,6 +180,7 @@ The code follows hexagonal boundaries:
|
|
|
173
180
|
|
|
174
181
|
- `Chronos::Core` contains immutable notices, sanitization, and safe normalization;
|
|
175
182
|
- `Chronos::Application` coordinates capture;
|
|
183
|
+
- `Chronos::Application::DeliveryPipeline` owns bounded retry and remote policy;
|
|
176
184
|
- `Chronos::Ports` defines delivery behavior;
|
|
177
185
|
- `Chronos::Adapters` implements Net::HTTP delivery;
|
|
178
186
|
- `Chronos::Internal` owns bounded queueing, workers, and defensive logging.
|
|
@@ -194,6 +202,14 @@ Chronos.configure do |config|
|
|
|
194
202
|
config.workers = 1
|
|
195
203
|
config.timeout = 5.0
|
|
196
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
|
|
197
213
|
end
|
|
198
214
|
```
|
|
199
215
|
|
|
@@ -205,20 +221,21 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
|
|
|
205
221
|
|
|
206
222
|
## Benchmark
|
|
207
223
|
|
|
208
|
-
Run the version 0.
|
|
224
|
+
Run the version 0.3 benchmarks with:
|
|
209
225
|
|
|
210
226
|
```bash
|
|
211
227
|
bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
|
|
212
228
|
bundle _1.17.3_ exec ruby benchmarks/serialization.rb
|
|
213
229
|
bundle _1.17.3_ exec ruby benchmarks/filtering.rb
|
|
214
230
|
bundle _1.17.3_ exec ruby benchmarks/queue.rb
|
|
231
|
+
bundle _1.17.3_ exec ruby benchmarks/retry_backlog.rb
|
|
215
232
|
```
|
|
216
233
|
|
|
217
234
|
Results depend on runtime, hardware, and payload. No performance comparison is claimed until repeatable measurements are published.
|
|
218
235
|
|
|
219
236
|
## Migration from Airbrake
|
|
220
237
|
|
|
221
|
-
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.
|
|
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.
|
|
222
239
|
|
|
223
240
|
## Local development
|
|
224
241
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
Accepted
|
|
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
|
-
`PayloadSerializer` must run `Sanitizer` before `SafeSerializer`, queueing, transport, or
|
|
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.
|
|
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 -->
|
|
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. `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
|
|
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
|
|
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.
|
data/docs/compatibility.md
CHANGED
|
@@ -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.
|
|
8
|
-
| 2.3.8 | None in 0.
|
|
9
|
-
| 2.4.10 | None in 0.
|
|
10
|
-
| 2.5.9 | None in 0.
|
|
11
|
-
| 2.6.10 | None in 0.
|
|
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.
|
|
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
|
|
data/docs/configuration.md
CHANGED
|
@@ -29,6 +29,18 @@
|
|
|
29
29
|
| `hash_keys` | Optional | `[]` | Scalar identifier keys replaced by scoped SHA-256 values |
|
|
30
30
|
| `filters` | Optional | `[]` | Callable application-specific privacy filters |
|
|
31
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 |
|
|
32
44
|
|
|
33
45
|
```ruby
|
|
34
46
|
Chronos.configure do |config|
|
|
@@ -41,9 +53,14 @@ Chronos.configure do |config|
|
|
|
41
53
|
config.workers = 1
|
|
42
54
|
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
43
55
|
config.hash_keys += [:customer_id]
|
|
56
|
+
config.max_retries = 3
|
|
57
|
+
config.backlog_size = 100
|
|
58
|
+
config.circuit_failure_threshold = 5
|
|
44
59
|
end
|
|
45
60
|
```
|
|
46
61
|
|
|
47
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`.
|
|
48
63
|
|
|
49
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.
|
data/docs/data-collected.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data collected
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
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
|
|---|---|---|
|
|
@@ -16,7 +16,7 @@ Version 0.2 emits only manually submitted exception events. All fields pass thro
|
|
|
16
16
|
| Tags and fingerprint | Optional and sanitized | Explicit capture arguments |
|
|
17
17
|
| IPv4 address in supplied text | Anonymized | Explicit capture arguments |
|
|
18
18
|
|
|
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.
|
|
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.
|
|
20
20
|
|
|
21
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
22
|
|
data/docs/examples/plain-ruby.md
CHANGED
|
@@ -17,4 +17,10 @@ For a network-free privacy audit, run:
|
|
|
17
17
|
bundle _1.17.3_ exec ruby examples/plain-ruby/privacy_audit.rb
|
|
18
18
|
```
|
|
19
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
|
+
|
|
20
26
|
The example builds the final event locally and demonstrates key redaction, e-mail and Bearer-token detection, application-specific blocklisting, and identifier hashing.
|
data/docs/modules/async-queue.md
CHANGED
|
@@ -10,8 +10,12 @@ stateDiagram-v2
|
|
|
10
10
|
Accepted --> Queued
|
|
11
11
|
Queued --> Sending
|
|
12
12
|
Sending --> Sent
|
|
13
|
-
Sending -->
|
|
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.
|
|
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.
|
|
@@ -13,10 +13,11 @@ flowchart LR
|
|
|
13
13
|
Sanitizer --> SafeSerializer
|
|
14
14
|
SafeSerializer --> PayloadSerializer
|
|
15
15
|
PayloadSerializer --> SerializedEvent
|
|
16
|
+
SerializedEvent --> DeliveryPipeline
|
|
16
17
|
```
|
|
17
18
|
|
|
18
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.
|
|
19
20
|
|
|
20
|
-
Version 0.
|
|
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.
|
|
21
22
|
|
|
22
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.
|
data/docs/modules/transport.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
`Chronos::Ports::Transport` defines delivery behavior. `Chronos::Adapters::NetHttpTransport` implements it with the Ruby standard library.
|
|
4
4
|
|
|
5
|
-
Each event uses bounded open and read timeouts. TLS verification is enabled by default. Authentication and idempotency values are headers. Redirects are not followed. Statuses are classified as success, client error, rate limited, server error, network error, or closed.
|
|
5
|
+
Each event uses bounded open and read timeouts. TLS verification is enabled by default. Authentication and idempotency values are headers. Redirects are not followed. Statuses are classified as success, request timeout, client error, rate limited, server error, network error, or closed. HTTP `408`, `429`, `5xx`, and network failures are retryable; other `4xx` responses are permanent.
|
|
6
6
|
|
|
7
|
-
The adapter creates a connection per event in version 0.
|
|
7
|
+
The adapter creates a connection per event in version 0.3. This is conservative for legacy applications and avoids shared socket lifecycle state. Retry remains in `DeliveryPipeline`, behind the transport port.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
A successful response may contain `X-Chronos-Remote-Configuration`. The adapter rejects the header when it exceeds `remote_config_max_bytes` or is not a JSON object. It never interprets policy or accepts remote endpoint and credential changes; that allowlist belongs to `RemoteConfiguration`.
|
|
10
|
+
|
|
11
|
+
Risks include proxy credential exposure, TLS incompatibility on old operating systems, endpoint latency, and stale remote policy until another response arrives. Tests use a local fake HTTP server to verify headers, `2xx`, `408`, `429`, `500`, timeout, invalid TLS, and bounded remote policy parsing.
|
data/docs/performance.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
Performance is a functional requirement, but version 0.
|
|
3
|
+
Performance is a functional requirement, but version 0.3 makes no unverified speed claim.
|
|
4
4
|
|
|
5
5
|
Current controls:
|
|
6
6
|
|
|
@@ -12,9 +12,12 @@ Current controls:
|
|
|
12
12
|
- worker count is fixed and threads start lazily;
|
|
13
13
|
- producers never wait for queue capacity;
|
|
14
14
|
- HTTP open and read timeouts are explicit;
|
|
15
|
+
- retry count, delay, jitter, and `Retry-After` are capped;
|
|
16
|
+
- the circuit breaker suppresses requests during sustained failure;
|
|
17
|
+
- retry backlog capacity is fixed and may be disabled;
|
|
15
18
|
- shutdown and flush have caller-controlled timeouts.
|
|
16
19
|
|
|
17
|
-
Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. `benchmarks/filtering.rb` measures
|
|
20
|
+
Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. `benchmarks/filtering.rb` measures privacy filtering, while `benchmarks/retry_backlog.rb` measures fixed-memory behavior with an unavailable in-memory transport. Request overhead is not measured because automatic request integration is outside version 0.3.
|
|
18
21
|
|
|
19
22
|
## Version 0.2 development measurement
|
|
20
23
|
|
|
@@ -26,3 +29,25 @@ A local diagnostic run on 2026-07-19 used macOS arm64 (`T8103`), legacy x86_64 R
|
|
|
26
29
|
| Ruby 2.6.3 | 1,904 µs/event | 4,705 µs/event | 1,037 µs/pass |
|
|
27
30
|
|
|
28
31
|
The capture benchmark uses an in-memory transport and includes queue draining. The serialization fixture contains 20 backtrace frames; the filtering fixture contains nested fields and 20 repeated items. These are single aggregate development runs, not published performance claims: they do not provide median or dispersion and must be repeated on controlled hardware before comparison with another agent.
|
|
32
|
+
|
|
33
|
+
## Version 0.3 resilience benchmark
|
|
34
|
+
|
|
35
|
+
Run:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ITERATIONS=10000 bundle _1.17.3_ exec ruby benchmarks/retry_backlog.rb
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The benchmark opens the circuit after one synthetic network failure, retains at most 100 serialized events, and measures bounded rejection into a full backlog. It performs no network I/O and is intended to detect accidental unbounded growth or excessive control-path overhead, not to claim production throughput.
|
|
42
|
+
|
|
43
|
+
A local diagnostic run on 2026-07-20 used Ruby 2.2.10 on macOS arm64 with 1,000 capture, serialization, and filtering iterations plus 10,000 queue and outage iterations:
|
|
44
|
+
|
|
45
|
+
| Measurement | Result |
|
|
46
|
+
|---|---:|
|
|
47
|
+
| Asynchronous local capture | 1,824 µs/event |
|
|
48
|
+
| Full serialization | 3,961 µs/event |
|
|
49
|
+
| Privacy filtering fixture | 777 µs/pass |
|
|
50
|
+
| Bounded queue | 1,648,533 operations/second |
|
|
51
|
+
| Open-circuit backlog handling | 88,922 operations/second |
|
|
52
|
+
|
|
53
|
+
The outage run retained exactly 100 events and rejected 9,900 additional events without growing the backlog. These are single development runs without median or dispersion and are not comparative performance claims.
|
data/docs/privacy-lgpd.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Privacy and LGPD
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.3 sanitizes every exception event before JSON serialization, queueing, retry backlog, or transport. This reduces accidental exposure, but the host application remains responsible for lawful purpose, minimization, access control, retention, and responses to data-subject requests.
|
|
4
4
|
|
|
5
5
|
## Default policy
|
|
6
6
|
|
|
@@ -13,7 +13,9 @@ Version 0.2 sanitizes every exception event before JSON serialization, queueing,
|
|
|
13
13
|
| Payment-card candidates that pass the Luhn check | Replaced with `[FILTERED_CARD]` |
|
|
14
14
|
| IPv4 addresses | Last octet replaced with `0` |
|
|
15
15
|
| Unknown Ruby objects | Represented by class name without calling application serialization |
|
|
16
|
-
| Request/response bodies, cookies, HTTP headers, SQL binds, environment variables | Never collected automatically in version 0.
|
|
16
|
+
| Request/response bodies, cookies, HTTP headers, SQL binds, environment variables | Never collected automatically in version 0.3 |
|
|
17
|
+
|
|
18
|
+
The retry backlog exists only in process memory, accepts only `SerializedEvent`, has a fixed capacity, and disappears on process exit. Version 0.3 does not persist telemetry to disk.
|
|
17
19
|
|
|
18
20
|
Blocklist matching accepts `String`, `Symbol`, and `Regexp`. String and Symbol matching is case-insensitive after punctuation normalization and also protects namespaced keys such as `user_password`.
|
|
19
21
|
|
data/docs/troubleshooting.md
CHANGED
|
@@ -10,7 +10,7 @@ The agent may be unconfigured, disabled, ignored in the current environment, una
|
|
|
10
10
|
|
|
11
11
|
## `Chronos.notify_sync` returns false
|
|
12
12
|
|
|
13
|
-
Check DNS, TLS certificates, credentials, HTTP status, proxy configuration, and timeout values. Version 0.
|
|
13
|
+
Check DNS, TLS certificates, credentials, HTTP status, proxy configuration, and timeout values. Version 0.3 retries only network errors, HTTP `408`, `429`, and `5xx` responses. Inspect `agent.diagnostics` when constructing an agent directly to see retry state, backlog usage, and the circuit state.
|
|
14
14
|
|
|
15
15
|
## Events disappear during shutdown
|
|
16
16
|
|