chronos-ruby 0.1.0.pre.2 → 0.2.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 +31 -0
- data/README.md +32 -19
- data/docs/adr/ADR-005-sanitize-before-backlog.md +2 -2
- data/docs/architecture.md +2 -2
- data/docs/compatibility.md +1 -1
- data/docs/configuration.md +10 -1
- data/docs/data-collected.md +10 -7
- data/docs/examples/plain-ruby.md +8 -0
- data/docs/modules/async-queue.md +1 -1
- data/docs/modules/notice-pipeline.md +5 -3
- data/docs/modules/sanitization.md +19 -0
- data/docs/modules/serialization.md +9 -0
- data/docs/modules/transport.md +1 -1
- data/docs/performance.md +14 -2
- data/docs/privacy-lgpd.md +78 -10
- data/docs/troubleshooting.md +2 -2
- data/lib/chronos/adapters/net_http_transport.rb +2 -2
- data/lib/chronos/application/capture_exception.rb +2 -2
- data/lib/chronos/configuration.rb +79 -4
- data/lib/chronos/core/notice.rb +1 -1
- data/lib/chronos/core/payload_serializer.rb +35 -87
- data/lib/chronos/core/runtime_info.rb +1 -1
- data/lib/chronos/core/safe_serializer.rb +119 -0
- data/lib/chronos/core/sanitizer.rb +161 -0
- data/lib/chronos/core/sensitive_value_filter.rb +118 -0
- data/lib/chronos/internal/bounded_queue.rb +1 -1
- data/lib/chronos/internal/worker_pool.rb +1 -1
- data/lib/chronos/ports/transport.rb +1 -1
- data/lib/chronos/version.rb +1 -1
- data/lib/chronos.rb +4 -1
- metadata +22 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d204c1595d7029ae32a43aacacdcc561bdc0b264e52a77e8eceb9acd4fa397d
|
|
4
|
+
data.tar.gz: 99696fa1feee48e384c3563473d1c77786fb2e0c54e261a41cf6778c0ce2468c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50ba51c92e8436616b1fe56b012d3c9ede3f8e1ec0d50df9a778f9790b876e3bd781c97cf42bac50b08d320e3ab07d8f675dbcdd746f6884d268fc222fc15d52
|
|
7
|
+
data.tar.gz: 480f16b60ba6162552ffca1f120c6a36d31b5debf639d99060ffda4a052b68d86d68f58849033b227582cf4cd6feac0ade70fe5e0d4e5fcac18148ca463b84bc
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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.2.0.pre.1] - 2026-07-19
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- recursive sensitive-key sanitization with String, Symbol, and Regexp matchers;
|
|
19
|
+
- content detection for Bearer tokens, JWTs, e-mail addresses, CPF, CNPJ, and payment cards;
|
|
20
|
+
- configurable identifier hashing and IPv4 anonymization;
|
|
21
|
+
- bounded safe serialization with cycle and node-budget protection;
|
|
22
|
+
- custom privacy filters with contained failures;
|
|
23
|
+
- privacy contract tests, audit example, module documentation, and filtering benchmark.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- all exception fields are sanitized before JSON serialization and transport;
|
|
28
|
+
- configuration snapshots now recursively freeze privacy settings;
|
|
29
|
+
- ADR-005 is accepted for the sanitization boundary.
|
|
30
|
+
|
|
31
|
+
### Known limitations
|
|
32
|
+
|
|
33
|
+
- sensitive-data detection is defensive and cannot replace an application privacy review;
|
|
34
|
+
- IPv6 anonymization is not implemented;
|
|
35
|
+
- no retry or backlog;
|
|
36
|
+
- no automatic Rack, Rails, or job integration.
|
|
37
|
+
|
|
7
38
|
## [0.1.0.pre.2] - 2026-07-19
|
|
8
39
|
|
|
9
40
|
### 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.
|
|
3
|
+
Chronos Ruby is the framework-independent client for sending Ruby application errors to Chronos. Version 0.2 is the privacy-focused legacy foundation: it captures exceptions manually, sanitizes sensitive data, builds a bounded JSON event, and delivers it synchronously or through a fixed-size asynchronous queue.
|
|
4
4
|
|
|
5
5
|
## What the gem collects
|
|
6
6
|
|
|
7
|
-
For each exception, version 0.
|
|
7
|
+
For each exception, version 0.2 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.
|
|
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.
|
|
23
|
+
Version 0.x targets Ruby 2.2.10 through Ruby 2.6. Version 0.2 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.2.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.2. 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.2. 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
|
-
|
|
104
|
+
Version 0.2 sanitizes this context before delivery. 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.2.
|
|
109
109
|
|
|
110
110
|
## Filters and LGPD
|
|
111
111
|
|
|
112
|
-
Version 0.
|
|
112
|
+
Version 0.2 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.
|
|
136
|
+
Exception-specific ignore rules are not available in version 0.2.
|
|
126
137
|
|
|
127
138
|
## Performance monitoring
|
|
128
139
|
|
|
129
|
-
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.2. 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.
|
|
144
|
+
Sidekiq and Active Job integrations are not implemented in version 0.2. 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.
|
|
148
|
+
Deploy notifications are not implemented in version 0.2. `app_version` may be included in exception events for release correlation.
|
|
138
149
|
|
|
139
150
|
## Asynchronous queue
|
|
140
151
|
|
|
@@ -143,7 +154,8 @@ 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 -->
|
|
157
|
+
N --> P[Privacy sanitizer]
|
|
158
|
+
P --> S[Safe bounded serializer]
|
|
147
159
|
S --> Q[Bounded queue]
|
|
148
160
|
Q --> W[Fixed worker pool]
|
|
149
161
|
W --> H[Net::HTTP transport]
|
|
@@ -153,13 +165,13 @@ Use `Chronos.flush(timeout)` to wait for accepted events and `Chronos.close(time
|
|
|
153
165
|
|
|
154
166
|
## Retry and backlog
|
|
155
167
|
|
|
156
|
-
Version 0.
|
|
168
|
+
Version 0.2 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.
|
|
157
169
|
|
|
158
170
|
## How it works internally
|
|
159
171
|
|
|
160
172
|
The code follows hexagonal boundaries:
|
|
161
173
|
|
|
162
|
-
- `Chronos::Core` contains immutable notices and normalization;
|
|
174
|
+
- `Chronos::Core` contains immutable notices, sanitization, and safe normalization;
|
|
163
175
|
- `Chronos::Application` coordinates capture;
|
|
164
176
|
- `Chronos::Ports` defines delivery behavior;
|
|
165
177
|
- `Chronos::Adapters` implements Net::HTTP delivery;
|
|
@@ -193,11 +205,12 @@ Configuration errors are raised during `Chronos.configure`. Capture and delivery
|
|
|
193
205
|
|
|
194
206
|
## Benchmark
|
|
195
207
|
|
|
196
|
-
Run the version 0.
|
|
208
|
+
Run the version 0.2 benchmarks with:
|
|
197
209
|
|
|
198
210
|
```bash
|
|
199
211
|
bundle _1.17.3_ exec ruby benchmarks/capture_exception.rb
|
|
200
212
|
bundle _1.17.3_ exec ruby benchmarks/serialization.rb
|
|
213
|
+
bundle _1.17.3_ exec ruby benchmarks/filtering.rb
|
|
201
214
|
bundle _1.17.3_ exec ruby benchmarks/queue.rb
|
|
202
215
|
```
|
|
203
216
|
|
|
@@ -205,7 +218,7 @@ Results depend on runtime, hardware, and payload. No performance comparison is c
|
|
|
205
218
|
|
|
206
219
|
## Migration from Airbrake
|
|
207
220
|
|
|
208
|
-
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.
|
|
221
|
+
An Airbrake migration guide will be added before the legacy 1.0 release. Version 0.2 does not claim API compatibility or automatic replacement.
|
|
209
222
|
|
|
210
223
|
## Local development
|
|
211
224
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Accepted for version 0.2; no backlog exists yet.
|
|
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
|
-
|
|
13
|
+
`PayloadSerializer` must run `Sanitizer` before `SafeSerializer`, queueing, transport, or any future retry and persistent backlog. Raw notices remain transient in process memory and never enter delivery storage.
|
|
14
14
|
|
|
15
15
|
## Alternatives
|
|
16
16
|
|
data/docs/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
Chronos Ruby 0.
|
|
3
|
+
Chronos Ruby 0.2 uses hexagonal boundaries so the legacy core remains independent of frameworks and delivery infrastructure.
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
flowchart TB
|
|
@@ -24,7 +24,7 @@ The `Chronos` module is a thin facade. Rails, Rack, ActiveSupport, Sidekiq, and
|
|
|
24
24
|
|
|
25
25
|
## Capture flow
|
|
26
26
|
|
|
27
|
-
An exception becomes an immutable notice
|
|
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 the transport. Synchronous capture bypasses the queue but uses the same privacy boundary.
|
|
28
28
|
|
|
29
29
|
## Failure policy
|
|
30
30
|
|
data/docs/compatibility.md
CHANGED
|
@@ -11,7 +11,7 @@ Chronos Ruby 0.x is the legacy line. Technical compatibility does not make an en
|
|
|
11
11
|
| 2.6.10 | None in 0.1 | Best effort | Dedicated CI job pending first successful run |
|
|
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.2. 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
|
@@ -24,6 +24,11 @@
|
|
|
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 |
|
|
27
32
|
|
|
28
33
|
```ruby
|
|
29
34
|
Chronos.configure do |config|
|
|
@@ -34,7 +39,11 @@ Chronos.configure do |config|
|
|
|
34
39
|
config.service_name = "billing"
|
|
35
40
|
config.queue_size = 100
|
|
36
41
|
config.workers = 1
|
|
42
|
+
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
43
|
+
config.hash_keys += [:customer_id]
|
|
37
44
|
end
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
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
|
+
|
|
49
|
+
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.
|
data/docs/data-collected.md
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
# Data collected
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.2 emits only manually submitted exception events. All fields pass through the privacy sanitizer and bounded safe serializer before 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
|
-
|
|
|
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.
|
|
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.2.
|
|
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.
|
data/docs/examples/plain-ruby.md
CHANGED
|
@@ -10,3 +10,11 @@ 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
|
+
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
|
@@ -14,4 +14,4 @@ stateDiagram-v2
|
|
|
14
14
|
Accepted --> Dropped: queue full
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Version 0.
|
|
17
|
+
Version 0.2 has no disk persistence and no retry. Only sanitized serialized events enter the queue. 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.
|
|
@@ -9,12 +9,14 @@ flowchart LR
|
|
|
9
9
|
NoticeBuilder --> CauseCollector
|
|
10
10
|
NoticeBuilder --> RuntimeInfo
|
|
11
11
|
NoticeBuilder --> Notice
|
|
12
|
-
Notice -->
|
|
12
|
+
Notice --> Sanitizer
|
|
13
|
+
Sanitizer --> SafeSerializer
|
|
14
|
+
SafeSerializer --> PayloadSerializer
|
|
13
15
|
PayloadSerializer --> SerializedEvent
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
`Notice` is immutable. Parsers are stateless. The serializer accepts
|
|
18
|
+
`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
19
|
|
|
18
|
-
Version 0.
|
|
20
|
+
Version 0.2 deliberately excludes sampling, retry, and fingerprint policy. These can be inserted after sanitization without changing transport or queue code.
|
|
19
21
|
|
|
20
22
|
Unit and contract tests verify missing backtraces, cyclic causes, invalid encoding, unsafe objects, payload limits, and the v1 envelope.
|
|
@@ -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.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Safe serialization module
|
|
2
|
+
|
|
3
|
+
`Chronos::Core::SafeSerializer` converts sanitized values into JSON primitives without invoking `to_json` or `to_s` on arbitrary application objects. Unknown objects become a class-name placeholder.
|
|
4
|
+
|
|
5
|
+
The serializer limits depth, visited nodes, hash keys, array items, key bytes, and string bytes. It detects circular hashes and arrays, repairs invalid UTF-8, and contains unreadable values. These structural budgets bound processing time and memory without using asynchronous interruption such as `Timeout` inside application code.
|
|
6
|
+
|
|
7
|
+
`PayloadSerializer` owns the versioned event envelope and composes `Sanitizer` before `SafeSerializer`. If the resulting JSON exceeds `max_payload_size`, caller-controlled fields are compacted. An event that still cannot fit is rejected before queueing.
|
|
8
|
+
|
|
9
|
+
The serializer is stateless across calls and safe for concurrent capture. Tests in `spec/unit/core/safe_serializer_spec.rb` and `spec/unit/core/payload_serializer_spec.rb` cover unsafe objects, cycles, invalid encoding, structural limits, and total payload limits.
|
data/docs/modules/transport.md
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
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.
|
|
6
6
|
|
|
7
|
-
The adapter creates a connection per event in version 0.
|
|
7
|
+
The adapter creates a connection per event in version 0.2. This is conservative for legacy applications and avoids shared socket lifecycle state. Batch optimization and retry may be added behind the same port.
|
|
8
8
|
|
|
9
9
|
Risks include proxy credential exposure, TLS incompatibility on old operating systems, and endpoint latency. Tests use a local fake HTTP server to verify headers, `2xx`, `429`, `500`, timeout, and invalid TLS behavior.
|
data/docs/performance.md
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
Performance is a functional requirement, but version 0.
|
|
3
|
+
Performance is a functional requirement, but version 0.2 makes no unverified speed claim.
|
|
4
4
|
|
|
5
5
|
Current controls:
|
|
6
6
|
|
|
7
7
|
- backtraces are limited to 200 frames;
|
|
8
8
|
- hashes and arrays are bounded during serialization;
|
|
9
9
|
- strings and total payload size are bounded;
|
|
10
|
+
- sanitizer traversal and serializer node count are bounded by the event structure;
|
|
10
11
|
- the asynchronous queue has fixed capacity;
|
|
11
12
|
- worker count is fixed and threads start lazily;
|
|
12
13
|
- producers never wait for queue capacity;
|
|
13
14
|
- HTTP open and read timeouts are explicit;
|
|
14
15
|
- shutdown and flush have caller-controlled timeouts.
|
|
15
16
|
|
|
16
|
-
Run the scripts under `benchmarks/` and record Ruby version, operating system, CPU, warmup, iteration count, median, and dispersion before publishing results. Request overhead is not measured because automatic request integration is outside version 0.
|
|
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 the version 0.2 privacy pass. Request overhead is not measured because automatic request integration is outside version 0.2.
|
|
18
|
+
|
|
19
|
+
## Version 0.2 development measurement
|
|
20
|
+
|
|
21
|
+
A local diagnostic run on 2026-07-19 used macOS arm64 (`T8103`), legacy x86_64 Ruby builds, 100 warmup iterations, and 500 measured iterations per script:
|
|
22
|
+
|
|
23
|
+
| Runtime | Asynchronous local capture | Full serialization | Privacy filtering fixture |
|
|
24
|
+
|---|---:|---:|---:|
|
|
25
|
+
| Ruby 2.2.10 | 1,857 µs/event | 4,047 µs/event | 798 µs/pass |
|
|
26
|
+
| Ruby 2.6.3 | 1,904 µs/event | 4,705 µs/event | 1,037 µs/pass |
|
|
27
|
+
|
|
28
|
+
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.
|
data/docs/privacy-lgpd.md
CHANGED
|
@@ -1,16 +1,84 @@
|
|
|
1
1
|
# Privacy and LGPD
|
|
2
2
|
|
|
3
|
-
Version 0.
|
|
3
|
+
Version 0.2 sanitizes every exception event before JSON serialization, queueing, 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
|
|
|
7
|
-
|
|
7
|
+
| Data | Default behavior |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Keys such as `password`, `secret`, `token`, `authorization`, `cookie`, `session`, `card_number`, `cpf`, and `cnpj` | Replaced with `[FILTERED]` |
|
|
10
|
+
| Bearer tokens and JWT-like strings | Replaced in free text |
|
|
11
|
+
| E-mail addresses | Replaced with `[FILTERED_EMAIL]` |
|
|
12
|
+
| CPF and CNPJ candidates with valid check digits | Replaced with `[FILTERED_DOCUMENT]` |
|
|
13
|
+
| Payment-card candidates that pass the Luhn check | Replaced with `[FILTERED_CARD]` |
|
|
14
|
+
| IPv4 addresses | Last octet replaced with `0` |
|
|
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.2 |
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
2. allowlist context fields in application code;
|
|
11
|
-
3. inspect payloads against a local fake server before production;
|
|
12
|
-
4. keep TLS verification enabled;
|
|
13
|
-
5. document the lawful purpose and retention policy in the SaaS;
|
|
14
|
-
6. disable the agent in environments where collection is not authorized.
|
|
18
|
+
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`.
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
Chronos.configure do |config|
|
|
24
|
+
# required connection settings omitted
|
|
25
|
+
config.blocklist_keys += [:medical_record, /bank_account/i]
|
|
26
|
+
config.allowlist_keys += [:authorization_state]
|
|
27
|
+
config.hash_keys += [:customer_id]
|
|
28
|
+
config.anonymize_ip = true
|
|
29
|
+
|
|
30
|
+
config.filters << proc do |key, value|
|
|
31
|
+
key.to_s == "internal_reference" ? "[REMOVED]" : value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
An allowlisted key bypasses only key-name redaction. Content detection remains active, so an allowlisted field containing a Bearer token or e-mail address is still filtered. Identifier hashing uses SHA-256 scoped by the public project identifier. It is irreversible output, but low-entropy identifiers may still be guessable and should not be treated as anonymized data.
|
|
37
|
+
|
|
38
|
+
Custom filters receive the key and already sanitized value. If a filter raises, that field becomes `[FILTERED]`; the error does not escape into the host application.
|
|
39
|
+
|
|
40
|
+
## Health applications
|
|
41
|
+
|
|
42
|
+
Do not send diagnoses, exam results, prescriptions, patient names, or free-form clinical notes. Use a scoped opaque identifier only when it is necessary and authorized:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
config.blocklist_keys += [:patient_name, :diagnosis, :clinical_notes]
|
|
46
|
+
config.hash_keys += [:patient_id]
|
|
47
|
+
|
|
48
|
+
Chronos.notify(error, :context => {
|
|
49
|
+
"operation" => "schedule_exam",
|
|
50
|
+
"patient_id" => "internal-opaque-id"
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Financial applications
|
|
55
|
+
|
|
56
|
+
Do not send cardholder names, full account identifiers, CVV, authentication tokens, or transaction payloads. Prefer categorical operational context:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
config.blocklist_keys += [:account_number, :pix_key, :bank_payload]
|
|
60
|
+
config.hash_keys += [:customer_id]
|
|
61
|
+
|
|
62
|
+
Chronos.notify(error, :context => {
|
|
63
|
+
"operation" => "authorize_payment",
|
|
64
|
+
"provider" => "example-gateway",
|
|
65
|
+
"customer_id" => "internal-opaque-id"
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Payload audit procedure
|
|
70
|
+
|
|
71
|
+
1. Point `host` to a local fake HTTP server controlled by the development team.
|
|
72
|
+
2. Exercise representative exception paths with synthetic sensitive fixtures.
|
|
73
|
+
3. Inspect the final JSON body, including exception messages and causes.
|
|
74
|
+
4. Search for every fixture value in plaintext.
|
|
75
|
+
5. Add missing application keys to `blocklist_keys` and repeat the audit.
|
|
76
|
+
6. Keep the audit fixtures synthetic and run the privacy contract in CI.
|
|
77
|
+
|
|
78
|
+
Run the included local example with:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
bundle _1.17.3_ exec ruby examples/plain-ruby/privacy_audit.rb
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The output must contain redaction placeholders and must not contain any fixture secret.
|
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.2 does not retry.
|
|
14
14
|
|
|
15
15
|
## Events disappear during shutdown
|
|
16
16
|
|
|
@@ -22,4 +22,4 @@ Workers are recreated after a process fork. Configure before or after forking, t
|
|
|
22
22
|
|
|
23
23
|
## Sensitive data
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Stop sending the affected field, rotate exposed credentials, and follow the incident process for the Chronos SaaS. Add the application-specific key to `blocklist_keys`, create a privacy contract fixture, and repeat the local payload audit. Pattern detection is defensive and cannot recognize every domain-specific identifier.
|
|
@@ -10,14 +10,14 @@ module Chronos
|
|
|
10
10
|
#
|
|
11
11
|
# @responsibility Send serialized events over bounded HTTPS requests.
|
|
12
12
|
# @motivation Use the Ruby standard library to preserve legacy compatibility.
|
|
13
|
-
# @limits It classifies failures but does not retry or persist events in version 0.
|
|
13
|
+
# @limits It classifies failures but does not retry or persist events in version 0.2.
|
|
14
14
|
# @collaborators Configuration::Snapshot, SerializedEvent, and TransportResult.
|
|
15
15
|
# @thread_safety Creates a new Net::HTTP connection per call and synchronizes health state.
|
|
16
16
|
# @compatibility Ruby 2.2.10 through Ruby 2.6; no Rails dependency.
|
|
17
17
|
# @example
|
|
18
18
|
# result = transport.send_event(serialized_event)
|
|
19
19
|
# @errors Network, TLS, and HTTP errors are returned, never raised to callers.
|
|
20
|
-
# @performance One bounded network request per event in version 0.
|
|
20
|
+
# @performance One bounded network request per event in version 0.2.
|
|
21
21
|
class NetHttpTransport
|
|
22
22
|
EVENT_PATH = "/api/v1/events".freeze
|
|
23
23
|
|
|
@@ -2,9 +2,9 @@ module Chronos
|
|
|
2
2
|
module Application
|
|
3
3
|
# Orchestrates exception normalization, serialization, queueing, and delivery.
|
|
4
4
|
#
|
|
5
|
-
# @responsibility Execute the version 0.
|
|
5
|
+
# @responsibility Execute the version 0.2 exception capture pipeline.
|
|
6
6
|
# @motivation Keep the public facade and transport adapters free of use-case logic.
|
|
7
|
-
# @limits It does not implement
|
|
7
|
+
# @limits It does not implement sampling, retry, backlog, or framework hooks.
|
|
8
8
|
# @collaborators NoticeBuilder, PayloadSerializer, WorkerPool, and Transport.
|
|
9
9
|
# @thread_safety Collaborators are immutable or synchronized; calls may run concurrently.
|
|
10
10
|
# @compatibility Ruby 2.2.10 through Ruby 2.6; independent of Rails.
|