foam-otel 1.9.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -25
  3. data/lib/foam/otel/version.rb +9 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2e506ae900650923638df263e5775efa20dd8ab7a63540eefb9d5156c1d008c
4
- data.tar.gz: 64ea4091c85c9541ec1f08dd5560608e560ec80e8ee6918f95098b976b0ec82e
3
+ metadata.gz: 767e21dce7e53eb8a74e430c736f2ba94e22c882022733fff7fb8edb058c118e
4
+ data.tar.gz: 4d9e7b6789d79daec4eb0482a22ac68520263c0a267e1b4e4def7eae97d9e642
5
5
  SHA512:
6
- metadata.gz: 4c776696c10dc442394e5b023db7b47d178ec4b328addd693ba72b60e726c19f923533f0f1645e7b62ec322e63d6fcd0b39b1ca917cd9aa66403295c0b67e266
7
- data.tar.gz: 615ec0ee4400e6e11baa6a6572fcafdccdeb6764f9bb2e65afcf4ff72a8185e9ba45086cd5e8e039934ea1043fe165d94784983539ac0d3e361a6b28f72a986e
6
+ metadata.gz: 4512202149fa0ee2b50093b7485b07373a09ed29315b6489d3e59d0c111ecf3d023e77e49f9af271ca97df33210e2028ba3376e4a43516284bd6f0fe195a8627
7
+ data.tar.gz: 9d15c2c183dfdac3ca20f0a3eccae5c6f2e45134206a697604a1b70e7a9046c775d0b5e805662a162aee2b0d89e62f810cdea8e43a72b9f15db783f1b07ccb0d
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Foam's OpenTelemetry core for Ruby services. A thin, safe wrapper over the
4
4
  official OpenTelemetry libraries: foam owns the pipeline (providers, batch
5
- processors, OTLP export to the foam fleet endpoint), turns on automatic
5
+ processors, OTLP export to the foam endpoint), turns on automatic
6
6
  tier-1/2 instrumentation, and hands you a small set of never-throw helpers —
7
7
  and nothing else. **Redaction is fully opt-in above two always-on exceptions:
8
8
  by default foam captures every value RAW** (no PII preset) **except (1) the
@@ -42,7 +42,8 @@ gem "opentelemetry-instrumentation-graphql" # e.g. GraphQL
42
42
  gem "opentelemetry-instrumentation-resque" # e.g. Resque
43
43
  ```
44
44
 
45
- **Supported versions** (tested in CI, `spec/version_spec.rb`): Ruby **>= 3.1**;
45
+ **Supported versions** (tested in CI, `spec/version_spec.rb`): Ruby **>= 3.4**
46
+ (the version the CI matrix runs — the floor follows the tested matrix);
46
47
  OpenTelemetry API **`opentelemetry-api` ~> 1.1** (1.x). The metrics and logs
47
48
  SDKs are pre-1.0 upstream (metrics alpha, logs development) and are pinned with
48
49
  pessimistic constraints; foam wraps them behind stable helpers so app code never
@@ -69,7 +70,7 @@ Foam::Otel.init(
69
70
  ```
70
71
 
71
72
  **Outcome**: foam registers the tracer/meter/logger providers, exports OTLP to
72
- the foam fleet endpoint, and auto-activates the **universal floor** — the
73
+ the foam endpoint, and auto-activates the **universal floor** — the
73
74
  official rack/rails instrumentation plus the bundled HTTP (Net::HTTP/Faraday),
74
75
  datastore (pg/mysql2/redis/mongo), and Sidekiq instrumentations — each
75
76
  presence-gated, and gated on foam owning the traces slot (a foreign-owned
@@ -111,7 +112,7 @@ turns keep their tool names/arguments and tool-result turns keep their
111
112
  `tool_call_id` linkage inside the message content. `gen_ai.operation.name`
112
113
  (and the `{operation} {model}` span name) follows the semconv operation for
113
114
  each API shape — `chat` for the OpenAI/Anthropic/ruby_llm chat seams,
114
- `generate_content` for Gemini's generateContent, fleet-identical with the
115
+ `generate_content` for Gemini's generateContent, identical to foam's
115
116
  js/python Gemini paths (**wire change in 1.2.1**: Gemini spans previously
116
117
  said `chat`). Covered: the official `openai` SDK
117
118
  (chat.completions.create + responses.create), the official `anthropic` SDK
@@ -148,8 +149,8 @@ listed request and response headers as
148
149
  > **Honest limit (deliberate divergence from foam's js/python cores):**
149
150
  > Ruby inbound captures the **named default list below, NOT all headers** —
150
151
  > the official gem's options are an enumerated allowlist with no
151
- > capture-all form, and that official option is the chosen seam (ruling
152
- > 2026-07-28). A custom header outside the list is **not captured** unless
152
+ > capture-all form, and that official option is the chosen seam.
153
+ > A custom header outside the list is **not captured** unless
153
154
  > you extend the list (recipe below).
154
155
 
155
156
  The default lists (`Foam::Otel::HeaderCapture::DEFAULT_REQUEST_HEADERS` /
@@ -321,7 +322,7 @@ not captured — inbound only.
321
322
  | `name:` | String | yes | — | `service.name`. Blank → raises at boot. |
322
323
  | `environment:` | String | yes | — | `deployment.environment.name`, exported verbatim. A value outside `{production, staging, development, test}` warns but is never rewritten. Blank → raises. |
323
324
  | `enabled:` | Boolean | yes | — | The config switch. `false` = fully inert (no SDK, no providers, no network, helpers no-op). `true` = export, in EVERY environment. No default — you write the logic. |
324
- | `token:` | String | when enabled | — | `Authorization: Bearer` for export. Required (and validated) only when `enabled: true`; wired explicitly from your secret source — never an env fallback read by the gem. **Fleet convention: read it from the `FOAM_OTEL_TOKEN` env var.** Blank while enabled → raises at boot. |
325
+ | `token:` | String | when enabled | — | `Authorization: Bearer` for export. Required (and validated) only when `enabled: true`; wired explicitly from your secret source — never an env fallback read by the gem. **The convention across all foam SDKs: read it from the `FOAM_OTEL_TOKEN` env var.** Blank while enabled → raises at boot. |
325
326
  | `version:` | String | no | nil | `service.version`, verbatim (git SHA recommended). Missing → warns and continues. Never detected at runtime. |
326
327
  | `redact:` | Hash | no | nil | The grouped redaction object (1.5.0, the preferred surface): `{ secrets: [...], pii: [...], detect: [...] }` — symbol or string keys. `secrets:` has `redact_keys` semantics (tail mask) and `pii:` has `redact_pii_keys` semantics (full `[REDACTED]`); when both a legacy option and its `redact` field are set, the lists UNION. `detect:` is the opt-in PII detection tier's entity list — value-shape detection with typed placeholders (see "PII detection (opt-in)" below). An unknown field, a non-Array value, or an unknown `detect` entity name raises at boot. Absent (the default) → behavior byte-identical to before. |
327
328
  | `redact_keys:` | Array<String> | no | `[]` | Alias — the preferred spelling is `redact: { secrets: [...] }` (same semantics; the lists union when both are set). The ONLY field names tail-masked (shape-preserving, e.g. `********f456`). Opt-in and ADDITIVE on top of the always-on credential floor — empty (the default) means no masking beyond the floor. Matches by case-insensitive substring, including keys nested in structured values and query-string keys. A key that is also on the floor stays fully `[REDACTED]` (the floor wins; it is never downgraded to a tail). |
@@ -432,8 +433,7 @@ override is the operator-level `OTEL_EXPORTER_OTLP_ENDPOINT` env var (below).
432
433
 
433
434
  ## The default credential denylist (the always-on floor)
434
435
 
435
- **Fleet ruling 2026-07-26** (binding design:
436
- `docs/decisions/credential-denylist-design.md`; frozen fixture:
436
+ Always on in every foam SDK, identical everywhere (frozen fixture:
437
437
  `contract/credential-denylist.json`). This is the ONE exception to foam's
438
438
  raw-by-default capture, and it has **no off switch** — no init option, no env
439
439
  var, and no door-2 parameter can disable, shrink, or re-spell it.
@@ -482,13 +482,13 @@ byte-identical raw, exactly as documented everywhere else in this README.
482
482
 
483
483
  **Why.** Coverage-over-masking remains the mission, but raw `Authorization`
484
484
  headers, cookies, and API keys on the wire are a breach in waiting for every
485
- customer at once — the fleet ruling carves out exactly this list (and only
486
- this list) as non-negotiable. The list is byte-identical in every foam core
487
- and gate-checked in CI against the fleet fixture
485
+ customer at once — foam carves out exactly this list (and only
486
+ this list) as non-negotiable. The list is byte-identical in every foam SDK
487
+ and gate-checked in CI against the shipped fixture
488
488
  (`spec/credential_floor_spec.rb`).
489
489
 
490
- **Fleet param-name canon** (the customer options above the floor, identical
491
- concept in every core — the floor itself has NO init surface anywhere):
490
+ **Param names across the foam SDKs** (the customer options above the floor,
491
+ identical concept in every SDK — the floor itself has NO init surface anywhere):
492
492
 
493
493
  | Concept | js/otel | js/browser | python | ruby | java |
494
494
  |---|---|---|---|---|---|
@@ -517,8 +517,7 @@ The credential floor masks by NAME and is deliberately value-shape-blind — a
517
517
  live AWS key under the field name `note` would sail past it. Foam telemetry is
518
518
  read downstream by LLMs, so a leaked credential is exfiltratable by prompt
519
519
  injection; "no leakage at all" is the bar. The value-pattern secret layer is
520
- the second always-on control (fleet ruling 2026-07-27,
521
- `docs/decisions/security-fixes-design.md`):
520
+ the second always-on control:
522
521
 
523
522
  **What it masks.** Credential-SHAPED value spans, regardless of the field
524
523
  name they hide under: AWS access-key ids and keyword-anchored AWS secrets,
@@ -561,8 +560,8 @@ value rather than shipping it raw.
561
560
 
562
561
  **Provenance.** The ruleset derives from MIT/Apache-licensed OSS secret
563
562
  scanners (gitleaks, detect-secrets, secretlint) — see `THIRD-PARTY-NOTICES`.
564
- Rules are frozen module constants, byte-consistent across the foam cores,
565
- changed only by fleet ruling.
563
+ Rules are frozen module constants, byte-consistent across all foam SDKs —
564
+ not configurable per app.
566
565
 
567
566
  > **Migration note — 1.4.0 (minor): value-pattern secret masking + redaction
568
567
  > coverage fix + TLS pin.** foam now masks credential-SHAPED values
@@ -589,9 +588,9 @@ The floor and the secret layer protect credentials. PII in VALUES — an email
589
588
  address inside a log line, a card number inside an exception message — still
590
589
  exports RAW by default, because coverage-over-masking is the mission and only
591
590
  you know your privacy posture. As of 1.5.0 you can opt into value-shape PII
592
- detection per entity, via `redact: { detect: [...] }` (fleet ruling
593
- 2026-07-28; frozen fixture: `contract/pii-detect.json` — the entity names and
594
- placeholders are byte-identical in every foam core and gate-checked in CI by
591
+ detection per entity, via `redact: { detect: [...] }` (frozen fixture:
592
+ `contract/pii-detect.json` — the entity names and
593
+ placeholders are byte-identical in every foam SDK and gate-checked in CI by
595
594
  `spec/pii_detect_spec.rb`). Nothing detects unless you list the entity —
596
595
  an empty/absent `detect` list is exactly today's behavior.
597
596
 
@@ -696,7 +695,7 @@ at-exit flush. Anything a hook needs from the request/job thread
696
695
  attribute at capture time (see the `additional_span_processors` stamper
697
696
  pattern); thread-locals are gone by the time the hook runs.
698
697
 
699
- Boot validation is strict (rule 10): a non-callable — or a **lambda/Method
698
+ Boot validation is strict: a non-callable — or a **lambda/Method
700
699
  whose signature cannot accept one positional argument** (e.g. a Sentry-port
701
700
  `->(event, hint) { ... }` with two required params, or a required keyword)
702
701
  — raises `ArgumentError` at `init`, because it would otherwise fault on
@@ -963,7 +962,7 @@ which every tap applies with zero configuration; `diagnostics:` is tap-scoped
963
962
  narration). Construction
964
963
  validates loudly at boot and NEVER
965
964
  throws afterwards — a failure inside a tap can never break their pipeline.
966
- Fleet canon mapping (pinned by `spec/export_surface_spec.rb`):
965
+ Naming across the foam SDKs (pinned by `spec/export_surface_spec.rb`):
967
966
  `create_ingest_span_processor` = `createFoamIngestSpanProcessor`,
968
967
  `create_ingest_log_record_processor` = `createFoamIngestLogRecordProcessor`,
969
968
  `create_ingest_metric_reader` = `createFoamIngestMetricReader`.
@@ -1098,8 +1097,8 @@ end
1098
1097
 
1099
1098
  | Var | Posture |
1100
1099
  | --- | --- |
1101
- | `OTEL_SDK_DISABLED=true` | HONORED — full kill switch, supersedes `enabled: true` (matched trimmed + case-insensitive, so `TRUE` / ` true ` also kill — the fleet's one emergency switch behaves the same across languages). Door-2 ingest entries honor it too: construction returns an inert instance (read once at construction; changing it implies a restart). |
1102
- | `OTEL_EXPORTER_OTLP_ENDPOINT` | HONORED — the one operator-level override of the pinned fleet endpoint (for foam's own conformance rig / enterprise egress). Active → loud `[foam]` warning naming the destination. Applies to door-2 taps identically (and moves the host the required loop step must name). |
1100
+ | `OTEL_SDK_DISABLED=true` | HONORED — full kill switch, supersedes `enabled: true` (matched trimmed + case-insensitive, so `TRUE` / ` true ` also kill — this one emergency switch behaves the same in every foam SDK). Door-2 ingest entries honor it too: construction returns an inert instance (read once at construction; changing it implies a restart). |
1101
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | HONORED — the one operator-level override of the pinned foam endpoint (for foam's own conformance rig / enterprise egress). Active → loud `[foam]` warning naming the destination. Applies to door-2 taps identically (and moves the host the required loop step must name). |
1103
1102
  | `OTEL_PROPAGATORS=none` | HONORED — turns trace propagation OFF (links lost) while telemetry keeps flowing; warns. Any other value warns and is ignored (foam's propagator set is fixed: W3C tracecontext + baggage). |
1104
1103
  | `OTEL_BSP_SCHEDULE_DELAY` / `OTEL_BLRP_SCHEDULE_DELAY` / `OTEL_METRIC_EXPORT_INTERVAL` | HONORED — batch cadence, read natively by the upstream SDK. |
1105
1104
  | `FOAM_CAPTURE_PAYLOADS` | HONORED — the operator clamp over the `capture_payloads:` init option (`off`/`errors`/`always`, case-insensitive), overriding it in BOTH directions with one loud `[foam]` line when it changes the mode. An invalid value warns and falls back to the init option (never crashes a boot). This is the ONE foam-named env var the gem reads — an override valve over an init-declared option, never an on/off switch, token, or config fallback (those still arrive only through `init`'s explicit arguments). Read once at init; changing it implies a restart. |
@@ -186,6 +186,14 @@ module Foam
186
186
  # ORIGINAL resource/scope are force-restored onto every survivor
187
187
  # after the pipeline (identity is not hook-writable — the Go port's
188
188
  # contract; README updated).
189
- VERSION = "1.9.1"
189
+ # 2.0.0: RUNTIME FLOOR RAISE (label-follows-tested-matrix ruling
190
+ # 2026-07-29). required_ruby_version >= 3.1 -> >= 3.4 — the version the
191
+ # CI matrix actually runs. The 3.1 floor was an unproven published
192
+ # promise: no CI leg ran below 3.4, and on 3.1–3.2 (both EOL) Bundler
193
+ # resolves an OLDER upstream otel gem set than the proven lockfile —
194
+ # a combination never tested. MAJOR: narrowing the supported-runtime
195
+ # label is breaking for installs on the dropped versions. No wire/API
196
+ # change — every 1.x contract above still holds.
197
+ VERSION = "2.0.0"
190
198
  end
191
199
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foam-otel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foam
@@ -422,7 +422,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
422
422
  requirements:
423
423
  - - ">="
424
424
  - !ruby/object:Gem::Version
425
- version: '3.1'
425
+ version: '3.4'
426
426
  required_rubygems_version: !ruby/object:Gem::Requirement
427
427
  requirements:
428
428
  - - ">="