auditea 0.1.0.beta.1 → 0.1.0.beta.3
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 +60 -1
- data/DESIGN.md +13 -0
- data/README.md +136 -16
- data/RELEASE.md +184 -22
- data/auditea.gemspec +2 -2
- data/lib/auditea/configuration.rb +22 -1
- data/lib/auditea/host_inventory/background_refresh.rb +64 -0
- data/lib/auditea/host_inventory/collector.rb +246 -0
- data/lib/auditea/host_inventory/emitter.rb +105 -0
- data/lib/auditea/host_inventory.rb +37 -0
- data/lib/auditea/instrumentation/active_job.rb +3 -0
- data/lib/auditea/instrumentation/error_subscriber.rb +93 -0
- data/lib/auditea/instrumentation/exception.rb +134 -16
- data/lib/auditea/instrumentation/structural_request.rb +136 -0
- data/lib/auditea/inventory/background_refresh.rb +64 -0
- data/lib/auditea/inventory/collector.rb +301 -0
- data/lib/auditea/inventory/digest.rb +59 -0
- data/lib/auditea/inventory/emitter.rb +105 -0
- data/lib/auditea/inventory/string_bound.rb +43 -0
- data/lib/auditea/inventory.rb +36 -0
- data/lib/auditea/middleware/context.rb +27 -0
- data/lib/auditea/railtie.rb +8 -0
- data/lib/auditea/sanitizer.rb +14 -1
- data/lib/auditea/version.rb +1 -1
- data/lib/auditea.rb +45 -0
- metadata +15 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 587d3b57a35018f55c5e82c15a485715913e911a714971797a47fde50d788251
|
|
4
|
+
data.tar.gz: beae506f6609e933baa4d4513e9015cc7b11c919a75ed83ba912eeb546aae0e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16e6ebde275b1f03f12028ca1c746fdedd9efb907706560ecae52b1fed188e90944f0e827c40e7a1cb909742d381fe37d26deb385ee66d82965c8185444ac99f
|
|
7
|
+
data.tar.gz: e0c7097c1f146d724befdaf2df850e30a556c670a06406f459003b46a2b52fc77687b36d3fa366d3db4662e13718ddf70ea12698480555858bf51650ee9781c8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.0.beta.
|
|
3
|
+
## 0.1.0.beta.3 (unreleased)
|
|
4
|
+
|
|
5
|
+
### Exception capture parity (AUD-ERROR-PARITY01)
|
|
6
|
+
|
|
7
|
+
- Public `Auditea.capture_exception(error, handled:, mechanism:, context:, metadata:)` → `exception.raised`
|
|
8
|
+
- Rails `ErrorReporter` subscriber via public `subscribe` API (Rails 7.2 / 8.1); graceful no-op when absent
|
|
9
|
+
- Identity-safe WeakMap dedupe of exception objects (no integer `object_id` reuse false negatives); cleared at request/job boundaries
|
|
10
|
+
- When `CurrentContext` is already cleared (post-unwind `application.action_dispatch`), derive method/path/request_id/controller/action from the ErrorReporter controller/request via public APIs only
|
|
11
|
+
- `process_action.action_controller` remains as fallback when ErrorReporter is not installed
|
|
12
|
+
- Structured evidence: type, message, bounded frames (≤40) with deterministic `in_app` when knowable, `handled`, `mechanism`, HTTP path/method (no query secrets), controller/action when available
|
|
13
|
+
- ActiveJob: ErrorReporter sources matching `active_job` are skipped when `capture_active_job` is enabled (dedicated `job.failed` path unchanged)
|
|
14
|
+
- Real Rails 7.2 / 8.1 integration coverage for unhandled/handled controller exceptions and ActiveJob no-duplicate behavior
|
|
15
|
+
- Provenance unchanged: sdk/ruby/rails versions + configured `release`
|
|
16
|
+
|
|
17
|
+
## 0.1.0.beta.2
|
|
18
|
+
|
|
19
|
+
### Runtime / dependency inventory (opt-in, default OFF)
|
|
20
|
+
|
|
21
|
+
- Emit `runtime.inventory` (category `security`) when enabled
|
|
22
|
+
- Ruby engine/version/platform, gem platform, Rails when present, Bundler, RubyGems
|
|
23
|
+
- Ruby-linked OpenSSL constants only (`OpenSSL::VERSION` / build library) — **not** system OpenSSL
|
|
24
|
+
- Locked Bundler gems when available (`completeness: bundle_locked`), else loaded gems (`loaded_only`); may be `truncated`
|
|
25
|
+
- Deterministic package order + SHA-256 digest; package count/string bounds; valid UTF-8
|
|
26
|
+
- Privacy: no filesystem paths, Gemfile/lockfile raw contents, registry/Git URLs, credentials, tokens, env dumps
|
|
27
|
+
- Config: `AUDITEA_CAPTURE_RUNTIME_INVENTORY` / `config.capture_runtime_inventory` **default false**
|
|
28
|
+
- Rails `after_initialize` may emit once when enabled; ~daily process-local refresh thereafter
|
|
29
|
+
- Rack path only **schedules** single-flight background refresh (never inline collection/delivery)
|
|
30
|
+
- Sanitizer allows larger `evidence.inventory.packages` arrays (bounded)
|
|
31
|
+
|
|
32
|
+
### Host inventory (opt-in, default OFF)
|
|
33
|
+
|
|
34
|
+
- Emit `host.inventory` with opaque `host_key`, OS/arch/kernel facts, optional display label
|
|
35
|
+
- Multi-host-per-Source / container replica differentiation; no raw machine-id or hostname in payload
|
|
36
|
+
- Linux `os_version` from `/etc/os-release` (not `uname.version` kernel build string)
|
|
37
|
+
- Architecture, kernel name/release, container hint boolean only
|
|
38
|
+
- No OS package/CVE collection (server-side / future)
|
|
39
|
+
- Config: `AUDITEA_CAPTURE_HOST_INVENTORY` **default false**
|
|
40
|
+
- Rack path only schedules single-flight background refresh
|
|
41
|
+
|
|
42
|
+
### Release provenance
|
|
43
|
+
|
|
44
|
+
- Configured application release is sent as `provenance.release` (server canonical evidence)
|
|
45
|
+
- Precedence when building configuration: explicit `config.release=` (configure block) overrides env;
|
|
46
|
+
at initialize: `AUDITEA_RELEASE` → `RELEASE` → `HEROKU_RELEASE_VERSION`
|
|
47
|
+
- No `.git` inspection, no `git rev-parse`, no cloud deployment APIs
|
|
48
|
+
|
|
49
|
+
### Safety / transport (carried forward + hardened)
|
|
50
|
+
|
|
51
|
+
- Fail-open by default (`raise_errors` default false)
|
|
52
|
+
- HTTPS required except loopback HTTP
|
|
53
|
+
- Process-aware async queue; single worker; flush waits for in-flight work
|
|
54
|
+
- Request/exception/ActiveJob capture behavior unchanged in intent from beta.1 defaults
|
|
55
|
+
(`capture_requests` default false; `capture_exceptions` / `capture_active_job` default true)
|
|
56
|
+
|
|
57
|
+
### Packaging / support
|
|
58
|
+
|
|
59
|
+
- MIT license; `rubygems_mfa_required`; push host RubyGems.org
|
|
60
|
+
- Supported CI matrix: Ruby 3.2/3.3 + Rails 7.2; Ruby 3.4/4.0.6 + Rails 8.1
|
|
61
|
+
|
|
62
|
+
## 0.1.0.beta.1
|
|
4
63
|
|
|
5
64
|
- Initial SDK: capture, structured logs, Rails request/exception observation, ActiveJob failures
|
|
6
65
|
- EVENT_CONTRACT_V1 transport with HTTPS/loopback policy, status classification, byte limits, batch index results
|
data/DESIGN.md
CHANGED
|
@@ -38,11 +38,24 @@ PR: https://github.com/reznis/auditea-ruby/pull/2
|
|
|
38
38
|
| Privacy filter | KEEP CONCEPT + REFACTOR (all egress + redacted_fields) |
|
|
39
39
|
| Exception capture | REFACTOR (fixed frames) |
|
|
40
40
|
| Security surface | KEEP CONCEPT (deferred beyond beta.1 core) |
|
|
41
|
+
| Runtime / dependency inventory | REWRITE for vNext (`runtime.inventory`, opt-in, locked bundle) |
|
|
41
42
|
| `Thread.new` per event | REWRITE → bounded queue |
|
|
42
43
|
| `AUDITEA_DEBUG` full payload logs | DROP |
|
|
43
44
|
| Auto Rails.logger forward | DROP for beta.1; adapter boundary reserved |
|
|
44
45
|
|
|
46
|
+
## AUD-P013 Phase A — runtime inventory
|
|
47
|
+
|
|
48
|
+
- Action: `runtime.inventory` / category `security`
|
|
49
|
+
- Prefer materialized current-platform `Bundler.definition.specs` (not every lockfile variant; `force_ruby_platform` respected via Bundler) → `bundle_locked`; fallback `Gem.loaded_specs` → `loaded_only` when the locked path cannot be fully materialized; `truncated` when package/size bounds are hit
|
|
50
|
+
- OpenSSL fields are Ruby-linked only (not host `openssl` / OS packages)
|
|
51
|
+
- No paths, URLs, credentials in package sources; git may include revision SHA only
|
|
52
|
+
- Deterministic package ordering + SHA-256 digest (no timestamps/PIDs/hostnames)
|
|
53
|
+
- Default `AUDITEA_CAPTURE_RUNTIME_INVENTORY=false`
|
|
54
|
+
- Measured typical SDK Gemfile inventory ≪ 128 KiB; bound packages (400) + size budget before truncate
|
|
55
|
+
- Sanitizer special-cases `evidence.inventory.packages` array max (400) so ordinary 100-cap does not silently drop gems
|
|
56
|
+
|
|
45
57
|
## Compatibility
|
|
46
58
|
|
|
47
59
|
- Ruby >= 3.2, including current Ruby 4.0.x (CI: 3.2, 3.3, 3.4, 4.0.6)
|
|
48
60
|
- Rails 7.2 and Rails 8.1 gemfiles under `gemfiles/`
|
|
61
|
+
|
data/README.md
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Ruby / Rails SDK for [AudiTea](https://github.com/reznis/AudiTea) event collection.
|
|
4
4
|
|
|
5
|
-
Version: **0.1.0.beta.
|
|
5
|
+
Version: **0.1.0.beta.3** (not published yet)
|
|
6
6
|
|
|
7
|
-
Captures durable technical and business evidence. AudiTea **server** owns taxonomy, dictionary, human labels, and reinterpretation.
|
|
7
|
+
Captures durable technical and business evidence. AudiTea **server** owns taxonomy, dictionary, human labels, vulnerability matching, correlation, and reinterpretation.
|
|
8
|
+
|
|
9
|
+
The SDK does **not** perform vulnerability scanning, OS package/CVE inventory, deployment detection, root-cause analysis, AI, or APM/metrics.
|
|
8
10
|
|
|
9
11
|
## Install (after RubyGems beta publication)
|
|
10
12
|
|
|
11
13
|
```ruby
|
|
12
14
|
# Gemfile
|
|
13
|
-
gem "auditea", "0.1.0.beta.
|
|
15
|
+
gem "auditea", "0.1.0.beta.3"
|
|
14
16
|
```
|
|
15
17
|
|
|
16
18
|
```bash
|
|
@@ -27,18 +29,80 @@ Prefer the RubyGems artifact for ordinary TEST deployments once published.
|
|
|
27
29
|
|
|
28
30
|
## Configure
|
|
29
31
|
|
|
32
|
+
### Required
|
|
33
|
+
|
|
34
|
+
| Setting | Purpose |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `endpoint` / `AUDITEA_ENDPOINT` | AudiTea server base URL (HTTPS, or HTTP only to loopback) |
|
|
37
|
+
| `source_token` / `AUDITEA_SOURCE_TOKEN` | Source credential (`at_src_…`) |
|
|
38
|
+
|
|
39
|
+
Without a Source token (and with the SDK enabled), capture is skipped (`not_configured`) — the host app keeps running. Note: there is a **default endpoint**, so clearing `AUDITEA_ENDPOINT` alone is not a reliable disable.
|
|
40
|
+
|
|
41
|
+
### Recommended for first controlled TEST pilot
|
|
42
|
+
|
|
43
|
+
| Setting | Recommendation | Why |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `release` / `RELEASE` | Set an opaque app release id | Server Observed Releases use `provenance.release` |
|
|
46
|
+
| `capture_exceptions` | `true` (default) | Useful error evidence with restrained volume |
|
|
47
|
+
| `capture_runtime_inventory` | `true` for pilot | Dependency evidence; default is OFF — enable deliberately |
|
|
48
|
+
| `capture_host_inventory` | `true` only if multi-host/container identity matters | Default OFF; enable per environment decision |
|
|
49
|
+
| `capture_requests` | `false` initially | Avoid high-volume request spam on first connect |
|
|
50
|
+
|
|
51
|
+
### Optional
|
|
52
|
+
|
|
53
|
+
Async tuning (`AUDITEA_ASYNC`, batch/flush/queue/timeouts), `AUDITEA_INVENTORY_REFRESH_INTERVAL`, `AUDITEA_HOST_KEY` / `AUDITEA_HOST_LABEL`, `capture_request_params`, `raise_errors` (debug only).
|
|
54
|
+
|
|
55
|
+
### First TEST initializer (example)
|
|
56
|
+
|
|
30
57
|
```ruby
|
|
31
58
|
# config/initializers/auditea.rb
|
|
32
59
|
Auditea.configure do |config|
|
|
33
|
-
config.endpoint = ENV.fetch("AUDITEA_ENDPOINT"
|
|
34
|
-
config.source_token = ENV.fetch("AUDITEA_SOURCE_TOKEN")
|
|
35
|
-
|
|
60
|
+
config.endpoint = ENV.fetch("AUDITEA_ENDPOINT")
|
|
61
|
+
config.source_token = ENV.fetch("AUDITEA_SOURCE_TOKEN")
|
|
62
|
+
|
|
63
|
+
# Opaque release id. Prefer AUDITEA_RELEASE (matches Configuration initialize
|
|
64
|
+
# precedence). Explicit config.release always wins over env.
|
|
65
|
+
config.release = ENV["AUDITEA_RELEASE"] || ENV["RELEASE"]
|
|
66
|
+
|
|
36
67
|
config.capture_exceptions = true
|
|
37
|
-
config.
|
|
68
|
+
config.capture_runtime_inventory = true
|
|
69
|
+
|
|
70
|
+
# First pilot: keep request volume low unless you need path evidence immediately.
|
|
71
|
+
config.capture_requests = false
|
|
72
|
+
|
|
73
|
+
# Enable only when you want opaque host/node identity in this environment.
|
|
74
|
+
config.capture_host_inventory = ENV["AUDITEA_CAPTURE_HOST_INVENTORY"] == "true"
|
|
75
|
+
|
|
76
|
+
# Never leave raise_errors on in shared TEST/prod hosts.
|
|
77
|
+
config.raise_errors = false
|
|
38
78
|
end
|
|
39
79
|
```
|
|
40
80
|
|
|
41
|
-
|
|
81
|
+
### Release precedence
|
|
82
|
+
|
|
83
|
+
1. Explicit `config.release = …` in the configure block (wins)
|
|
84
|
+
2. Else at `Configuration` initialize: `AUDITEA_RELEASE` → `RELEASE` → `HEROKU_RELEASE_VERSION`
|
|
85
|
+
|
|
86
|
+
No git inspection. No cloud metadata calls.
|
|
87
|
+
|
|
88
|
+
### Pilot rollback / disable (safe)
|
|
89
|
+
|
|
90
|
+
Preferred immediate disable:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
AUDITEA_ENABLED=false
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
(or `config.enabled = false` and redeploy)
|
|
97
|
+
|
|
98
|
+
Other valid paths:
|
|
99
|
+
|
|
100
|
+
1. Remove / revoke the Source token (`AUDITEA_SOURCE_TOKEN`) — without a token, capture is `not_configured`
|
|
101
|
+
2. Revoke the Source credential server-side (SDK fail-opens on auth failure)
|
|
102
|
+
3. Disable individual capture flags (`capture_exceptions`, inventories, requests)
|
|
103
|
+
4. Remove the initializer and/or gem from the Gemfile and redeploy
|
|
104
|
+
|
|
105
|
+
Do **not** treat “remove endpoint” as a reliable kill switch while a valid Source token remains: Configuration has a default endpoint.
|
|
42
106
|
|
|
43
107
|
## Capture APIs
|
|
44
108
|
|
|
@@ -50,9 +114,17 @@ Auditea.capture(
|
|
|
50
114
|
evidence: { business: { from_status: "draft", to_status: "approved" } }
|
|
51
115
|
)
|
|
52
116
|
|
|
117
|
+
Auditea.capture_exception(
|
|
118
|
+
error,
|
|
119
|
+
handled: true,
|
|
120
|
+
mechanism: "application_rescue"
|
|
121
|
+
)
|
|
122
|
+
|
|
53
123
|
Auditea.log(:info, "Export finished", context: { export_id: export.id })
|
|
54
124
|
```
|
|
55
125
|
|
|
126
|
+
Automatic Rails exception observation uses `Rails.error` (ErrorReporter) when available, with `process_action.action_controller` as fallback. Same-exception dedupe is request/thread-local by object id so ErrorReporter and controller notifications do not double-count.
|
|
127
|
+
|
|
56
128
|
Wire format follows AudiTea **EVENT_CONTRACT_V1**:
|
|
57
129
|
|
|
58
130
|
- Bearer Source credential authentication
|
|
@@ -60,6 +132,52 @@ Wire format follows AudiTea **EVENT_CONTRACT_V1**:
|
|
|
60
132
|
- No client tenancy/scope fields (`workspace_id`, `environment`, …)
|
|
61
133
|
- Extensible `evidence` / `context` / `provenance` / `metadata` bags
|
|
62
134
|
|
|
135
|
+
## Runtime inventory (opt-in, default off)
|
|
136
|
+
|
|
137
|
+
When enabled, the SDK can emit a `runtime.inventory` security event describing:
|
|
138
|
+
|
|
139
|
+
- Ruby engine / version / platform / gem platform
|
|
140
|
+
- Rails version (when Rails is present)
|
|
141
|
+
- Bundler + RubyGems versions
|
|
142
|
+
- OpenSSL **linked to Ruby** — not host/system OpenSSL
|
|
143
|
+
- AudiTea SDK version and configured release
|
|
144
|
+
- Locked Bundler gems when available (`completeness: bundle_locked`), otherwise loaded gems (`loaded_only`)
|
|
145
|
+
|
|
146
|
+
**Not collected:** filesystem paths, Gemfile/lockfile raw contents, Git remote URLs, private registry URLs, credentials, tokens, environment variables, usernames, hostnames.
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
Auditea.capture_runtime_inventory!
|
|
150
|
+
Auditea.maybe_capture_runtime_inventory!
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
AUDITEA_CAPTURE_RUNTIME_INVENTORY=true
|
|
155
|
+
# optional: AUDITEA_INVENTORY_REFRESH_INTERVAL=86400
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
With Rails, when capture is enabled, the Railtie may emit once after `after_initialize`. Opportunistic Rack refresh only **schedules** a single-flight background job when due — collection/delivery are never inline on the request path.
|
|
159
|
+
|
|
160
|
+
Enabling inventory reveals internal gem names to AudiTea. Keep it off unless that is intentional.
|
|
161
|
+
|
|
162
|
+
## Host inventory (opt-in)
|
|
163
|
+
|
|
164
|
+
Emits `host.inventory` with an opaque host/node key and platform/OS facts.
|
|
165
|
+
|
|
166
|
+
**Collected (bounded):** opaque `host_key`, optional display `label`, OS family/name/version, architecture, kernel name/release, logical CPU count, container hint boolean, SDK name/version.
|
|
167
|
+
|
|
168
|
+
**Not collected:** raw hostname, machine-id plaintext, filesystem paths, env vars, credentials, CPU/memory/disk utilization, processes, ports, packages/CVEs, cloud metadata dumps.
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
Auditea.capture_host_inventory!
|
|
172
|
+
Auditea.maybe_capture_host_inventory!
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
AUDITEA_CAPTURE_HOST_INVENTORY=true
|
|
177
|
+
# optional: AUDITEA_HOST_KEY=...
|
|
178
|
+
# optional: AUDITEA_HOST_LABEL=...
|
|
179
|
+
```
|
|
180
|
+
|
|
63
181
|
## Rails
|
|
64
182
|
|
|
65
183
|
The Railtie installs:
|
|
@@ -67,21 +185,18 @@ The Railtie installs:
|
|
|
67
185
|
- request context middleware (path **without** query string, request_id, method)
|
|
68
186
|
- optional `process_action` observation → `network.request.completed`
|
|
69
187
|
- optional exception observation → `exception.raised`
|
|
70
|
-
|
|
71
|
-
Structural evidence is preserved so the server can later distinguish routes such as `/researchers/projects/18` vs `/bcer/1/projects/18` without SDK redeploy.
|
|
188
|
+
- optional inventory emit after initialize when enabled
|
|
72
189
|
|
|
73
190
|
### Structured logs
|
|
74
191
|
|
|
75
|
-
Use `Auditea.log` explicitly.
|
|
76
|
-
|
|
77
|
-
**Automatic `Rails.logger` forwarding is intentionally disabled in beta.1** (`Auditea::Instrumentation::LoggerAdapter`).
|
|
192
|
+
Use `Auditea.log` explicitly. **Automatic `Rails.logger` forwarding is intentionally disabled.**
|
|
78
193
|
|
|
79
194
|
## Transport
|
|
80
195
|
|
|
81
196
|
- Sync or single-worker async queue (not one thread per event)
|
|
82
197
|
- HTTPS required (HTTP allowed only for localhost / 127.0.0.1 / ::1)
|
|
83
198
|
- Single events → `POST /api/v1/events`
|
|
84
|
-
- Batches → `POST /api/v1/event_batches`
|
|
199
|
+
- Batches → `POST /api/v1/event_batches`
|
|
85
200
|
- Client-side 128 KiB / 1 MiB / 100-event limits
|
|
86
201
|
- Retries with injectable exponential backoff+jitter on 5xx / 429 / network
|
|
87
202
|
- Process-aware queue for pre-fork servers
|
|
@@ -95,7 +210,11 @@ AUDITEA_SOURCE_TOKEN=at_src_... \
|
|
|
95
210
|
bundle exec ruby examples/manual_capture.rb
|
|
96
211
|
```
|
|
97
212
|
|
|
98
|
-
Never commit a real token.
|
|
213
|
+
Never commit a real token.
|
|
214
|
+
|
|
215
|
+
## Release process
|
|
216
|
+
|
|
217
|
+
See [RELEASE.md](RELEASE.md). Do not `gem push` without explicit human approval after server readiness.
|
|
99
218
|
|
|
100
219
|
## License
|
|
101
220
|
|
|
@@ -108,10 +227,11 @@ bundle install
|
|
|
108
227
|
bundle exec rspec
|
|
109
228
|
bundle exec rubocop
|
|
110
229
|
gem build auditea.gemspec
|
|
230
|
+
bash script/release_artifact_smoke.sh
|
|
111
231
|
|
|
112
232
|
# Rails compatibility (optional local)
|
|
113
233
|
BUNDLE_GEMFILE=gemfiles/rails_7.2.gemfile bundle install
|
|
114
234
|
BUNDLE_GEMFILE=gemfiles/rails_7.2.gemfile bundle exec rspec
|
|
115
235
|
BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle install
|
|
116
236
|
BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle exec rspec
|
|
117
|
-
```
|
|
237
|
+
```
|
data/RELEASE.md
CHANGED
|
@@ -1,43 +1,205 @@
|
|
|
1
|
-
# Release: auditea 0.1.0.beta.
|
|
1
|
+
# Release: auditea 0.1.0.beta.3
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Authoritative runbook for controlled beta publication of the Ruby SDK.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
2. Merge to `main`
|
|
7
|
-
3. Exact-head CI green on merge commit
|
|
8
|
-
4. License approved: **MIT**
|
|
9
|
-
5. RubyGems MFA / owner account for `reznis` with push rights
|
|
10
|
-
6. OTP / API key for `gem push` stored outside the repository (never commit secrets)
|
|
5
|
+
**Do not publish until every gate below is green and a human explicitly says GO.**
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
This document does **not** authorize:
|
|
8
|
+
|
|
9
|
+
- `gem push`
|
|
10
|
+
- GitHub Release / publication tags
|
|
11
|
+
- Nagano or any real TEST installation
|
|
12
|
+
- production rollout
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Publication requires all of
|
|
17
|
+
|
|
18
|
+
1. SDK **exact candidate SHA** reviewed (not a GitHub merge ref labeled as exact-head)
|
|
19
|
+
2. Built `.gem` artifact audited (filename, SHA-256, size, file count, candidate source SHA, `Auditea::VERSION`)
|
|
20
|
+
3. **Artifact-generated** payload gate PASS (payloads from the installed candidate gem, not checkout `lib/`)
|
|
21
|
+
4. SDK matrix PASS (Ruby 3.2/3.3 + Rails 7.2; Ruby 3.4/4.0.6 + Rails 8.1)
|
|
22
|
+
5. **Current AudiTea server strict interop PASS** — recorded live server SHA (Gate B)
|
|
23
|
+
6. **AUD-BETA01** reviewed and merged
|
|
24
|
+
7. Beta server operational (deployed separately under human approval; health verified)
|
|
25
|
+
8. Explicit human **GO**
|
|
26
|
+
9. RubyGems MFA / credentials available **outside** the repository
|
|
27
|
+
|
|
28
|
+
If AudiTea `main` advances after strict interop and before publication in a way that touches ingestion / runtime inventory / host inventory / observed-release boundaries, **rerun Gate B** against the then-current main and record the new SHA.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Two interoperability gates (do not conflate)
|
|
33
|
+
|
|
34
|
+
### Gate A — pinned AudiTea envelope compatibility
|
|
35
|
+
|
|
36
|
+
Lightweight SDK CI check against the **vendored** `Ingestion::ContractV1` snapshot.
|
|
37
|
+
|
|
38
|
+
- Pin SHA: `script/support/INGESTION_CONTRACT_PIN.txt`
|
|
39
|
+
- Labels accurately: **pinned envelope compatibility**
|
|
40
|
+
- Does **not** prove runtime/host/release projector success
|
|
41
|
+
- May become stale relative to current AudiTea
|
|
42
|
+
- Must **not** be described as complete current-server interoperability
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
AUDITEA_INTEROP_MODE=envelope bash script/release_candidate_gate.sh
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Gate B — live current-server strict interoperability (mandatory release gate)
|
|
49
|
+
|
|
50
|
+
Runs against an **actual local checkout** of current AudiTea. No silent fallback to the vendored pin.
|
|
51
|
+
|
|
52
|
+
Gate B is **fail-closed test-only**:
|
|
53
|
+
|
|
54
|
+
- forces `RAILS_ENV=test`
|
|
55
|
+
- unsets inherited `DATABASE_URL` (so an operator export cannot redirect to development/production)
|
|
56
|
+
- refuses to continue unless `Rails.env.test?` and the resolved DB name ends in `_test`
|
|
57
|
+
- prepares the AudiTea test DB via `rails db:prepare`
|
|
58
|
+
- wraps all fixture/ingest/projection work in an outer transaction that is always rolled back
|
|
59
|
+
- clears ActiveJob test-adapter enqueues so no durable queue/projection work remains
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
export AUDITEA_SERVER_ROOT=/actual/path/to/AudiTea
|
|
63
|
+
export AUDITEA_INTEROP_MODE=strict
|
|
64
|
+
EXPECTED_CANDIDATE_SHA="$(git rev-parse HEAD)" bash script/release_candidate_gate.sh
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Required behavior when live/current mode is requested:
|
|
68
|
+
|
|
69
|
+
- missing checkout → **FAIL**
|
|
70
|
+
- wrong/incomplete checkout → **FAIL**
|
|
71
|
+
- missing expected server components → **FAIL**
|
|
72
|
+
- non-test Rails env / unsafe DB → **FAIL**
|
|
73
|
+
|
|
74
|
+
Exercises real domain paths (from the installed candidate `.gem` payloads):
|
|
75
|
+
|
|
76
|
+
| Payload | Server path |
|
|
77
|
+
|---|---|
|
|
78
|
+
| ordinary event | `EventIngestor` → accepted/persisted |
|
|
79
|
+
| exception | ingest → `Errors::Classifier` / `IssueReconciler` |
|
|
80
|
+
| `runtime.inventory` | ingest → `Runtime::InventoryProjector` |
|
|
81
|
+
| `host.inventory` | ingest → `Hosts::InventoryProjector` |
|
|
82
|
+
| `provenance.release` | ingest → `Releases::ObservedReleaseProjector` |
|
|
83
|
+
|
|
84
|
+
Private-repo CI cannot clone AudiTea automatically. For beta.2 it is acceptable that Gate A is automated in SDK CI while Gate B is an explicit local/human release gate — **but Gate B must PASS before publication**, and its AudiTea SHA must be recorded.
|
|
85
|
+
|
|
86
|
+
**After AUD-BETA01 merges**, repeat Gate B against the then-current AudiTea `main` immediately before publication.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Code gates (SDK)
|
|
91
|
+
|
|
92
|
+
1. Exact SDK head reviewed on the release-readiness PR (`github.event.pull_request.head.sha` for PR jobs)
|
|
93
|
+
2. Full CI matrix green on that exact head
|
|
94
|
+
3. Local quality: `bundle exec rspec`, `bundle exec rubocop`, `bundle exec bundle-audit check --update`
|
|
95
|
+
4. Unified release-candidate artifact gate:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
EXPECTED_CANDIDATE_SHA="$(git rev-parse HEAD)" \
|
|
99
|
+
AUDITEA_INTEROP_MODE=envelope \
|
|
100
|
+
bash script/release_candidate_gate.sh
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Conceptually:
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
build .gem from candidate SHA
|
|
107
|
+
→ install into isolated GEM_HOME
|
|
108
|
+
→ prove loaded files come from installed gem
|
|
109
|
+
→ generate interoperability payloads from that installed gem
|
|
110
|
+
→ Gate A pinned envelope compatibility
|
|
111
|
+
→ Gate B live strict (when AUDITEA_INTEROP_MODE=strict)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
5. License remains **MIT**; `rubygems_mfa_required` + `allowed_push_host=https://rubygems.org` intact
|
|
115
|
+
6. Fail-open defaults verified; runtime/host inventory and request capture default **OFF**
|
|
116
|
+
|
|
117
|
+
Terminology:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
exact-head != merge-ref
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Do not call one the other. Merge-ref testing may be useful separately; it is not exact-head evidence.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Server gate (AudiTea)
|
|
128
|
+
|
|
129
|
+
Publication and first TEST install remain blocked until:
|
|
130
|
+
|
|
131
|
+
1. **AUD-BETA01** independently reviewed and merged
|
|
132
|
+
2. Beta server deployed separately under human approval
|
|
133
|
+
3. Health verified (`/up`, auth, jobs, migrations, backups as applicable)
|
|
134
|
+
4. Gate B live strict interop recorded against current AudiTea (see above)
|
|
135
|
+
|
|
136
|
+
BETA02 must not depend on unmerged BETA01 implementation details for SDK correctness, but **actual gem publication and first TEST installation require the operational server**.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Publication gate (human)
|
|
141
|
+
|
|
142
|
+
1. Explicit human **GO** after SDK + server gates
|
|
143
|
+
2. RubyGems owner/authentication for `reznis` verified
|
|
144
|
+
3. MFA / OTP / API key available **outside the repository** (never commit)
|
|
145
|
+
4. No automatic publish-on-tag workflow for this beta
|
|
146
|
+
|
|
147
|
+
PR status may say `READY FOR HUMAN PUBLICATION REVIEW` while still listing external operator gates (BETA01, server deploy). It must **not** claim `current server strict interop PASS` unless Gate B actually ran against the recorded real checkout.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Publication (only after explicit human approval)
|
|
13
152
|
|
|
14
153
|
```bash
|
|
15
154
|
git checkout main
|
|
16
155
|
git pull
|
|
17
|
-
|
|
18
|
-
bundle exec rspec
|
|
156
|
+
# confirm VERSION and CHANGELOG still say 0.1.0.beta.3
|
|
19
157
|
gem build auditea.gemspec
|
|
20
|
-
#
|
|
158
|
+
# inspect package once more
|
|
159
|
+
gem push auditea-0.1.0.beta.3.gem
|
|
21
160
|
```
|
|
22
161
|
|
|
23
|
-
|
|
162
|
+
Then mark CHANGELOG released with the publication date in a follow-up commit.
|
|
24
163
|
|
|
25
|
-
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Post-publication verification
|
|
26
167
|
|
|
27
168
|
```bash
|
|
28
|
-
gem
|
|
169
|
+
gem install auditea -v 0.1.0.beta.3 --pre
|
|
170
|
+
ruby -rauditea -e 'puts Auditea::VERSION'
|
|
171
|
+
# expect: 0.1.0.beta.3
|
|
29
172
|
```
|
|
30
173
|
|
|
31
|
-
|
|
174
|
+
Prefer installing the RubyGems artifact (not a git branch) into the controlled TEST app.
|
|
175
|
+
|
|
176
|
+
---
|
|
32
177
|
|
|
33
|
-
##
|
|
178
|
+
## Pilot gate
|
|
179
|
+
|
|
180
|
+
- Start with **1** controlled Rails TEST client (optionally a second later)
|
|
181
|
+
- Do **not** broaden to production or multi-app fleets in this beta
|
|
182
|
+
- Use the first-pilot configuration in `README.md`
|
|
183
|
+
- Keep a documented rollback/disable path ready (`AUDITEA_ENABLED=false` preferred)
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Build checklist (local, pre-approval)
|
|
34
188
|
|
|
35
189
|
```bash
|
|
36
|
-
|
|
37
|
-
|
|
190
|
+
bundle install
|
|
191
|
+
bundle exec rspec
|
|
192
|
+
bundle exec rubocop
|
|
193
|
+
bundle exec bundle-audit check --update
|
|
194
|
+
|
|
195
|
+
EXPECTED_CANDIDATE_SHA="$(git rev-parse HEAD)" \
|
|
196
|
+
AUDITEA_INTEROP_MODE=envelope \
|
|
197
|
+
bash script/release_candidate_gate.sh
|
|
38
198
|
|
|
39
|
-
|
|
199
|
+
EXPECTED_CANDIDATE_SHA="$(git rev-parse HEAD)" \
|
|
200
|
+
AUDITEA_INTEROP_MODE=strict \
|
|
201
|
+
AUDITEA_SERVER_ROOT=/actual/path/to/AudiTea \
|
|
202
|
+
bash script/release_candidate_gate.sh
|
|
40
203
|
|
|
41
|
-
|
|
42
|
-
gem "auditea", "0.1.0.beta.1"
|
|
204
|
+
bash script/volume_baseline.sh
|
|
43
205
|
```
|
data/auditea.gemspec
CHANGED
|
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
|
|
11
11
|
spec.summary = "Ruby/Rails SDK for AudiTea event collection"
|
|
12
12
|
spec.description =
|
|
13
|
-
"Capture business events, Rails request evidence, exceptions,
|
|
14
|
-
"for AudiTea /api/v1."
|
|
13
|
+
"Capture business events, Rails request evidence, exceptions, structured logs, " \
|
|
14
|
+
"and optional runtime/host inventory for AudiTea /api/v1."
|
|
15
15
|
spec.homepage = "https://github.com/reznis/auditea-ruby"
|
|
16
16
|
spec.license = "MIT"
|
|
17
17
|
spec.required_ruby_version = ">= 3.2.0"
|
|
@@ -12,13 +12,16 @@ module Auditea
|
|
|
12
12
|
DEFAULT_MAX_RETRIES = 3
|
|
13
13
|
DEFAULT_MESSAGE_MAX = 8_192
|
|
14
14
|
DEFAULT_NESTING_MAX = 16
|
|
15
|
+
DEFAULT_INVENTORY_REFRESH = 86_400
|
|
15
16
|
LOOPBACK_HOSTS = %w[localhost 127.0.0.1 ::1].freeze
|
|
16
17
|
|
|
17
18
|
attr_accessor :endpoint, :source_token, :enabled, :async, :raise_errors,
|
|
18
19
|
:batch_size, :flush_interval, :queue_capacity, :timeout,
|
|
19
20
|
:open_timeout, :write_timeout, :max_retries, :logger, :release, :sdk_name,
|
|
20
21
|
:capture_requests, :capture_exceptions, :capture_request_params,
|
|
21
|
-
:capture_active_job, :
|
|
22
|
+
:capture_active_job, :capture_runtime_inventory, :capture_host_inventory,
|
|
23
|
+
:inventory_refresh_interval, :host_key, :host_label,
|
|
24
|
+
:message_max_bytes, :nesting_max_depth
|
|
22
25
|
|
|
23
26
|
def initialize
|
|
24
27
|
@endpoint = ENV.fetch("AUDITEA_ENDPOINT", DEFAULT_ENDPOINT)
|
|
@@ -40,10 +43,28 @@ module Auditea
|
|
|
40
43
|
@capture_exceptions = truthy?(ENV.fetch("AUDITEA_CAPTURE_EXCEPTIONS", "true"))
|
|
41
44
|
@capture_request_params = truthy?(ENV.fetch("AUDITEA_CAPTURE_REQUEST_PARAMS", "false"))
|
|
42
45
|
@capture_active_job = truthy?(ENV.fetch("AUDITEA_CAPTURE_ACTIVE_JOB", "true"))
|
|
46
|
+
# Opt-in: dependency inventories reveal internal gem names.
|
|
47
|
+
@capture_runtime_inventory = truthy?(ENV.fetch("AUDITEA_CAPTURE_RUNTIME_INVENTORY", "false"))
|
|
48
|
+
# Opt-in: host/platform inventory (opaque node id + OS facts).
|
|
49
|
+
@capture_host_inventory = truthy?(ENV.fetch("AUDITEA_CAPTURE_HOST_INVENTORY", "false"))
|
|
50
|
+
@host_key = ENV.fetch("AUDITEA_HOST_KEY", nil)
|
|
51
|
+
@host_label = ENV.fetch("AUDITEA_HOST_LABEL", nil)
|
|
52
|
+
@inventory_refresh_interval = float_env(
|
|
53
|
+
"AUDITEA_INVENTORY_REFRESH_INTERVAL",
|
|
54
|
+
DEFAULT_INVENTORY_REFRESH
|
|
55
|
+
)
|
|
43
56
|
@message_max_bytes = integer_env("AUDITEA_MESSAGE_MAX_BYTES", DEFAULT_MESSAGE_MAX)
|
|
44
57
|
@nesting_max_depth = integer_env("AUDITEA_NESTING_MAX_DEPTH", DEFAULT_NESTING_MAX)
|
|
45
58
|
end
|
|
46
59
|
|
|
60
|
+
def capture_runtime_inventory?
|
|
61
|
+
!!@capture_runtime_inventory
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def capture_host_inventory?
|
|
65
|
+
!!@capture_host_inventory
|
|
66
|
+
end
|
|
67
|
+
|
|
47
68
|
def enabled?
|
|
48
69
|
!!@enabled
|
|
49
70
|
end
|