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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 308c6d6709fed09f200bcfc60829af1b7bd470a018691f456576818ccadc87ee
4
- data.tar.gz: 27a669b879d0d864d427359bae7c01daf27e2a2d492e45ed73b492136d659196
3
+ metadata.gz: 587d3b57a35018f55c5e82c15a485715913e911a714971797a47fde50d788251
4
+ data.tar.gz: beae506f6609e933baa4d4513e9015cc7b11c919a75ed83ba912eeb546aae0e5
5
5
  SHA512:
6
- metadata.gz: 18517d6f3b825b1f1c97daab0bce3d6da97f68b157d87fb6c6aa302d0b6d79e1e202a682d4c427824df2ae71e7a7f51004124b8cc5ee7246d4d898e0e78cdc06
7
- data.tar.gz: c8faa401027af8fadaa8f565994a875c047bed3840722ef101673c7a4565e554965e13e7f1d9b313bd9245499ffb02110145650ade188891278ec06e5aa87d6d
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.1 (unreleased)
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.1**
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.1"
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", "https://app.auditea.com")
34
- config.source_token = ENV.fetch("AUDITEA_SOURCE_TOKEN") # at_src_<public_id>_<secret>
35
- config.capture_requests = true # opt-in Rails request observation
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.release = ENV["RELEASE"]
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
- Environment variables: `AUDITEA_ENDPOINT`, `AUDITEA_SOURCE_TOKEN` (or `AUDITEA_API_KEY`), `AUDITEA_CAPTURE_REQUESTS`, `AUDITEA_CAPTURE_EXCEPTIONS`, `AUDITEA_ASYNC`, `AUDITEA_RELEASE`.
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` with per-index result handling
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. The script prints a minimal safe result only.
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
1
+ # Release: auditea 0.1.0.beta.3
2
2
 
3
- ## Preconditions (blockers)
3
+ Authoritative runbook for controlled beta publication of the Ruby SDK.
4
4
 
5
- 1. Independent human review of the PR
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
- ## Build
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
- bundle install
18
- bundle exec rspec
156
+ # confirm VERSION and CHANGELOG still say 0.1.0.beta.3
19
157
  gem build auditea.gemspec
20
- # => auditea-0.1.0.beta.1.gem
158
+ # inspect package once more
159
+ gem push auditea-0.1.0.beta.3.gem
21
160
  ```
22
161
 
23
- ## Publish
162
+ Then mark CHANGELOG released with the publication date in a follow-up commit.
24
163
 
25
- Publish only from reviewed `main` after its exact-head CI is green:
164
+ ---
165
+
166
+ ## Post-publication verification
26
167
 
27
168
  ```bash
28
- gem push auditea-0.1.0.beta.1.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
- Optional GitHub Actions: use `rubygems/configure-rubygems-credentials` with a repository secret `RUBYGEMS_API_KEY` never commit the secret.
174
+ Prefer installing the RubyGems artifact (not a git branch) into the controlled TEST app.
175
+
176
+ ---
32
177
 
33
- ## Verify
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
- gem install auditea -v 0.1.0.beta.1 --pre
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
- Then install in Semiweb TEST via:
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
- ```ruby
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, and structured logs " \
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, :message_max_bytes, :nesting_max_depth
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