Skip to content

feat(sentry): trim native scope per privacy tier and filter native metrics#200

Merged
gmaclennan merged 7 commits into
mainfrom
claude/sentry-native-scope-tiers
Jul 8, 2026
Merged

feat(sentry): trim native scope per privacy tier and filter native metrics#200
gmaclennan merged 7 commits into
mainfrom
claude/sentry-native-scope-tiers

Conversation

@gmaclennan

Copy link
Copy Markdown
Member

Closes #79
Closes #191

The Android and iOS Sentry SDKs attach a lot of device detail to every event by default. This trims that to what each privacy tier promises (plan §9b.3), slims the boot transaction (§9b.4), keeps the backend's free-memory/free-disk numbers fresh on every event at the usage tier (§9b.6), and adds the forbidden-tag metric filter the JS/Node paths already have to the native countMetric paths.

On Android a new TierScopeEventProcessor (registered in SentryFgsBridge.init, which now receives the applicationUsageData flag) rebuilds the device/os/app contexts from a per-tier allowlist — diagnostic keeps manufacturer/brand/model/model id/family/arch/simulator/core count/memory size plus OS name+version and app id/version/build; the usage tier adds back kernel version, Build.DISPLAY, app name, the culture block, and screen metrics. Storage size is bucketed to standard marketed sizes (32–1024 GB) at both tiers, and comapeo.boot transactions at diagnostic lose the boot.kind tag and all span data while keeping phase spans, timings, and bare phase-name descriptions. iOS mirrors this with SentryScopeTier, wired as beforeSend/beforeSendSpan in SentryNativeBridge.initFromConfig and operating on the dictionary-shaped cocoa contexts. Rebuilding from an allowlist (rather than nulling a denylist) means fields added by future SDK versions are dropped by default.

For #191, SentryFgsBridge.countMetric and SentryNativeBridge.countMetric now run a hand-mirrored isForbiddenMetric check (SentryMetricScrub.kt / SentryMetricScrub.swift) before reaching the SDK, matching the shared list in src/sentry-scrub.ts and backend/before-send.js; all four copies now point at each other.

On the backend, a node_resources event processor (backend/lib/node-resources.js) re-reads os.freemem/os.totalmem and fs.statfsSync of the private storage dir on every capture. It is usage-tier-only (read-at-capture frequency is itself usage-shape data) and checks the live tier state per event so repeated init() in tests stays order-independent. loader.mjs now forwards the storage-dir positional to initSentry.

Tests: per-tier field assertions on both platforms (SentryScopeTierTest.kt, SentryScopeTierTests.swift), boot-transaction slimming both tiers, forbidden-metric drop vs. ordinary exit-metric pass on both platforms (including an SDK-level check on Android via the metrics beforeSend seam), and backend tests for freshness, statfs failure tolerance, and the usage-tier gating end-to-end through the envelope sink. Docs updated where they described the old fatal-only freemem behaviour.

…trics

Implements the native-scope field split (§9b.3), boot-transaction
slimming (§9b.4), and backend free-memory/disk refresh (§9b.6) from
issue #79, plus the forbidden-tag filter for native metric emissions
from issue #191.

- Android: TierScopeEventProcessor rebuilds device/os/app contexts from
  a per-tier allowlist, drops culture at diagnostic, buckets storage
  size, and slims comapeo.boot transactions (boot.kind tag + span data)
  at diagnostic. Wired via SentryFgsBridge.init.
- iOS: SentryScopeTier does the same via beforeSend/beforeSendSpan in
  SentryNativeBridge.initFromConfig.
- Both countMetric paths now run a hand-mirrored isForbiddenMetric
  check matching src/sentry-scrub.ts and backend/before-send.js.
- Backend: node-resources event processor re-reads os.freemem/statfs on
  every capture, usage tier only, wired from loader.mjs positionals.

Closes #79
Closes #191
@github-actions github-actions Bot added the feature New feature (changelog) label Jul 8, 2026
Comment thread android/src/main/java/com/comapeo/core/SentryFgsBridge.kt Outdated
Comment thread android/src/main/java/com/comapeo/core/SentryScopeTier.kt
Comment thread android/src/main/java/com/comapeo/core/SentryScopeTier.kt
claude added 2 commits July 8, 2026 15:40
…s, quieter metric-drop log

Addresses the review comments on the native-scope-tier work:

- Error/fatal events now keep the SDK's full device/os/app scope instead of
  the per-tier allowlist — device context is most valuable exactly when
  something crashed. Only `culture` (locale + timezone), a fingerprint surface
  with no debugging value, is still dropped from them at the diagnostic tier.
  Transactions and non-error events are unchanged. (Android + iOS)
- Storage-size buckets now start at 8 GB (8/16/32/…/1024) so devices with
  8/16 GB of storage, which are still in the field, aren't rounded up to 32.
- The "countMetric dropped: forbidden attribute" log drops from warn to debug
  on both platforms — an innocuous, expected drop that can recur often.

Also inlines a redundant local in backend/lib/sentry.js and updates the
per-tier field tables in the plan/integration docs. Adds per-platform tests
for the error-scope exemption and the new low-end buckets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwJPDuhUABN1VftbXVZAb5
Inside the `SentryNativeBridge` enum an unqualified `log(...)` binds to the
enum's own static `log(level:message:)` member, which shadows the global
`Log.swift` helper and doesn't match the `(_ message:, level:)` labels — a
pre-existing compile error in the metric-scrub drop path that broke the Swift
Package Tests. Module-qualify the call as `ComapeoCore.log(...)`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwJPDuhUABN1VftbXVZAb5
@gmaclennan
gmaclennan enabled auto-merge July 8, 2026 15:52
claude added 2 commits July 8, 2026 15:59
`compileDebugUnitTestKotlin` rejected `mutableMapOf("k" to <literal>)` passed
to sentry-java's `SentrySpan(..., data: Map<String, Object>?)` — the inferred
`MutableMap<String, Boolean>` / `<String, String>` don't match the platform
type's invariant lower bound, so Kotlin demands an explicit type. Annotate both
call sites as `mutableMapOf<String, Any>(...)`. Pre-existing in the new test
file; surfaced once the Swift build stopped short-circuiting the run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwJPDuhUABN1VftbXVZAb5
`SentryFgsBridgeTest.countMetricWithForbiddenAttributeIsDropped` throws because
the forbidden-attribute drop path calls `android.util.Log`, whose android.jar
stub throws in JVM unit tests (and the bridge's catch then re-throws via a
second Log call). The module never set `returnDefaultValues`, so this only
passed while a sibling test file's compile error kept the suite from running.
Enable `testOptions.unitTests.returnDefaultValues` so Log no-ops; the real
org.json / sentry-android test deps still take precedence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwJPDuhUABN1VftbXVZAb5
@gmaclennan
gmaclennan added this pull request to the merge queue Jul 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 8, 2026
The forbidden-attribute gate in `countMetric` fires on an expected, innocuous
condition, so the drop doesn't warrant a log — and on iOS the global `log()`
helper forwarded it to Sentry's log pipeline, adding noise on a path whose job
is to keep data off the wire. Drop it silently on both platforms.

This also removes the need for the earlier test-config change: with no
`android.util.Log` call on the drop path, `SentryFgsBridgeTest`'s
forbidden-metric test no longer trips the unmocked-stub throw, so the
`testOptions.unitTests.returnDefaultValues` addition is reverted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwJPDuhUABN1VftbXVZAb5
@gmaclennan
gmaclennan enabled auto-merge July 8, 2026 16:22
…rformance-tohz1o

# Conflicts:
#	docs/sentry-integration-plan.md
@gmaclennan
gmaclennan added this pull request to the merge queue Jul 8, 2026
Merged via the queue into main with commit a348e3c Jul 8, 2026
24 checks passed
@gmaclennan
gmaclennan deleted the claude/sentry-native-scope-tiers branch July 8, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature (changelog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sentry: forbidden-tag filter for native-SDK metric emissions Sentry: native scope minimisation per tier + boot transaction slimming

2 participants