From 531da8a6b7914700707dc46a6e919a62d579c615 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 29 Jun 2026 21:48:55 +0100 Subject: [PATCH] build(telemetry): link Sentry conditionally and document privacy model Add an opt-in REPOPROMPT_ENABLE_SENTRY build gate that links sentry-cocoa and defines REPOPROMPT_SENTRY_ENABLED, register the sentry-cocoa license, add the DSN Info.plist slot, and document the telemetry privacy model. Refs GH-183 --- AppBundle/Info.plist.template | 1 + Package.resolved | 2 +- Package.swift | 162 ++++++++++-------- Scripts/source_layout_guardrails.sh | 1 + Scripts/swiftpm_notice_guardrails.sh | 8 +- ThirdPartyLicenses/swiftpm/SHA256SUMS | 1 + ThirdPartyLicenses/swiftpm/inventory.tsv | 1 + .../swiftpm/sentry-cocoa/LICENSE.md | 21 +++ docs/privacy/telemetry.md | 75 ++++++++ 9 files changed, 198 insertions(+), 74 deletions(-) create mode 100644 ThirdPartyLicenses/swiftpm/sentry-cocoa/LICENSE.md create mode 100644 docs/privacy/telemetry.md diff --git a/AppBundle/Info.plist.template b/AppBundle/Info.plist.template index a99b4463d..7ecebbe05 100644 --- a/AppBundle/Info.plist.template +++ b/AppBundle/Info.plist.template @@ -18,6 +18,7 @@ RepoPromptSigningMode__SIGNING_MODE__ RepoPromptLocalSigningCertificateSHA256__LOCAL_SIGNING_CERTIFICATE_SHA256__ RepoPromptLocalSecureStorageGeneration__LOCAL_SECURE_STORAGE_GENERATION__ + RepoPromptSentryDSN LSEnvironmentREPOPROMPT_LAUNCH_SOURCElaunchservices CFBundleURLTypesCFBundleTypeRoleViewerCFBundleURLNamecom.pvncher.repoprompt.ceCFBundleURLSchemesrepoprompt-ceCFBundleURLIconFileAppIcon NSAppTransportSecurityNSAllowsArbitraryLoads diff --git a/Package.resolved b/Package.resolved index 66ba7a266..3abd08bac 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "3f8b765387a6b49bd558b0680976f08ac2a7ddf5b7304a2b50797106c1d1f5f4", + "originHash" : "ea3108afaabf4e97ce8567da0d84569101da7d4767884265a9d3b816c24fce86", "pins" : [ { "identity" : "async-http-client", diff --git a/Package.swift b/Package.swift index 643daf128..b6e98e80f 100644 --- a/Package.swift +++ b/Package.swift @@ -4,6 +4,93 @@ import PackageDescription let packageRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent().path +// Telemetry (Sentry) is linked only when explicitly requested. The official +// Developer ID release pipeline sets REPOPROMPT_ENABLE_SENTRY=1; local builds use +// the same gate for intentional Sentry testing. +let environment = ProcessInfo.processInfo.environment +let sentryEnabled = environment["REPOPROMPT_ENABLE_SENTRY"] == "1" + +var packageDependencies: [Package.Dependency] = [ + .package(url: "https://github.com/apple/swift-log.git", exact: "1.6.3"), + .package(url: "https://github.com/sindresorhus/KeyboardShortcuts.git", exact: "2.3.0"), + .package(url: "https://github.com/gonzalezreal/swift-markdown-ui", exact: "2.4.1"), + .package(url: "https://github.com/swiftlang/swift-markdown", exact: "0.6.0"), + .package(url: "https://github.com/SwiftyJSON/SwiftyJSON", exact: "5.0.2"), + .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", exact: "2.8.0"), + .package(url: "https://github.com/apple/swift-system.git", exact: "1.6.4"), + .package(url: "https://github.com/provencher/swift-sdk.git", revision: "85dec2fc7a27252bc33dc7728be6af6b3bd398c0"), + .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter.git", exact: "0.8.0"), + .package(url: "https://github.com/tree-sitter/tree-sitter-c", revision: "3efee11f784605d44623d7dadd6cd12a0f73ea92"), + .package(url: "https://github.com/UserNobody14/tree-sitter-dart", revision: "80e23c07b64494f7e21090bb3450223ef0b192f4"), + .package(url: "https://github.com/tree-sitter/tree-sitter-go", revision: "c350fa54d38af725c40d061a602ee3205ef1e072"), + .package(url: "https://github.com/tree-sitter/tree-sitter-java", revision: "e10607b45ff745f5f876bfa3e94fbcc6b44bdc11"), + .package(url: "https://github.com/tree-sitter/tree-sitter-javascript", revision: "39798e26b6d4dbcee8e522b8db83f8b2df33a5ea"), + .package(url: "https://github.com/tree-sitter/tree-sitter-python", revision: "c5fca1a186e8e528115196178c28eefa8d86b0b0"), + .package(url: "https://github.com/tree-sitter/tree-sitter-rust", revision: "2eaf126458a4d6a69401089b6ba78c5e5d6c1ced"), + .package(url: "https://github.com/tree-sitter/tree-sitter-typescript", revision: "75b3874edb2dc714fb1fd77a32013d0f8699989f"), + .package(url: "https://github.com/tree-sitter/tree-sitter-ruby", revision: "7a010836b74351855148818d5cb8170dc4df8e6a"), + .package(url: "https://github.com/alex-pinkus/tree-sitter-swift", revision: "9253825dd2570430b53fa128cbb40cb62498e75d"), + .package(url: "https://github.com/tree-sitter/tree-sitter-c-sharp.git", revision: "b27b091bfdc5f16d0ef76421ea5609c82a57dff0"), + .package(url: "https://github.com/tree-sitter/tree-sitter-cpp", revision: "e5cea0ec884c5c3d2d1e41a741a66ce13da4d945"), + .package(url: "https://github.com/provencher/tree-sitter-php", revision: "0a99deca13c4af1fb9adcb03c958bfc9f4c740a9"), + .package(url: "https://github.com/jamesrochabrun/SwiftAnthropic", revision: "b7d030cd7453f314c780f5492385f73d704cbd5d"), + .package(url: "https://github.com/provencher/SwiftOpenAI", revision: "1211782eb337e7968124448a20d9260df1952012"), + .package(url: "https://github.com/ChimeHQ/Neon.git", exact: "0.6.0"), + .package(path: "Vendor/UniversalCharsetDetection"), + .package(url: "https://github.com/loopwork-ai/JSONSchema.git", exact: "1.3.0"), + .package(url: "https://github.com/loopwork-ai/ontology.git", exact: "0.6.0"), + .package(path: "Packages/RepoPromptAgentProviders") +] + +var repoPromptDependencies: [Target.Dependency] = [ + "RepoPromptShared", + "RepoPromptC", "CSwiftPCRE2", "TreeSitterScannerSupport", + "Sparkle", + .product(name: "Logging", package: "swift-log"), + .product(name: "KeyboardShortcuts", package: "KeyboardShortcuts"), + .product(name: "MarkdownUI", package: "swift-markdown-ui"), + .product(name: "Markdown", package: "swift-markdown"), + .product(name: "SwiftyJSON", package: "SwiftyJSON"), + .product(name: "MCP", package: "swift-sdk"), + .product(name: "SwiftTreeSitter", package: "SwiftTreeSitter"), + .product(name: "TreeSitterC", package: "tree-sitter-c"), + .product(name: "TreeSitterDart", package: "tree-sitter-dart"), + .product(name: "TreeSitterGo", package: "tree-sitter-go"), + .product(name: "TreeSitterJava", package: "tree-sitter-java"), + .product(name: "TreeSitterJavaScript", package: "tree-sitter-javascript"), + .product(name: "TreeSitterPython", package: "tree-sitter-python"), + .product(name: "TreeSitterRust", package: "tree-sitter-rust"), + .product(name: "TreeSitterTypeScript", package: "tree-sitter-typescript"), + .product(name: "TreeSitterRuby", package: "tree-sitter-ruby"), + .product(name: "TreeSitterSwift", package: "tree-sitter-swift"), + .product(name: "TreeSitterCSharp", package: "tree-sitter-c-sharp"), + .product(name: "TreeSitterCPP", package: "tree-sitter-cpp"), + .product(name: "TreeSitterPHP", package: "tree-sitter-php"), + .product(name: "SwiftAnthropic", package: "SwiftAnthropic"), + .product(name: "SwiftOpenAI", package: "SwiftOpenAI"), + .product(name: "Neon", package: "Neon"), + .product(name: "UniversalCharsetDetection", package: "UniversalCharsetDetection"), + .product(name: "Cuchardet", package: "UniversalCharsetDetection"), + .product(name: "JSONSchema", package: "JSONSchema"), + .product(name: "Ontology", package: "ontology"), + .product(name: "RepoPromptClaudeCompatibleProvider", package: "RepoPromptAgentProviders") +] + +var repoPromptSwiftSettings: [SwiftSetting] = [ + .define("DEBUG", .when(configuration: .debug)), + .enableUpcomingFeature("BareSlashRegexLiterals"), + .unsafeFlags([ + "-import-objc-header", "\(packageRoot)/Sources/RepoPrompt/Support/RepoPrompt-Bridging-Header.h", + "-disable-bridging-pch" + ]) +] + +if sentryEnabled { + packageDependencies.append(.package(url: "https://github.com/getsentry/sentry-cocoa", exact: "9.17.1")) + repoPromptDependencies.append(.product(name: "Sentry", package: "sentry-cocoa")) + repoPromptSwiftSettings.append(.define("REPOPROMPT_SENTRY_ENABLED")) +} + let package = Package( name: "RepoPromptCE", platforms: [.macOS(.v14)], @@ -11,82 +98,13 @@ let package = Package( .executable(name: "RepoPrompt", targets: ["RepoPrompt"]), .executable(name: "repoprompt-mcp", targets: ["RepoPromptMCP"]) ], - dependencies: [ - .package(url: "https://github.com/apple/swift-log.git", exact: "1.6.3"), - .package(url: "https://github.com/sindresorhus/KeyboardShortcuts.git", exact: "2.3.0"), - .package(url: "https://github.com/gonzalezreal/swift-markdown-ui", exact: "2.4.1"), - .package(url: "https://github.com/swiftlang/swift-markdown", exact: "0.6.0"), - .package(url: "https://github.com/SwiftyJSON/SwiftyJSON", exact: "5.0.2"), - .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", exact: "2.8.0"), - .package(url: "https://github.com/apple/swift-system.git", exact: "1.6.4"), - .package(url: "https://github.com/provencher/swift-sdk.git", revision: "85dec2fc7a27252bc33dc7728be6af6b3bd398c0"), - .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter.git", exact: "0.8.0"), - .package(url: "https://github.com/tree-sitter/tree-sitter-c", revision: "3efee11f784605d44623d7dadd6cd12a0f73ea92"), - .package(url: "https://github.com/UserNobody14/tree-sitter-dart", revision: "80e23c07b64494f7e21090bb3450223ef0b192f4"), - .package(url: "https://github.com/tree-sitter/tree-sitter-go", revision: "c350fa54d38af725c40d061a602ee3205ef1e072"), - .package(url: "https://github.com/tree-sitter/tree-sitter-java", revision: "e10607b45ff745f5f876bfa3e94fbcc6b44bdc11"), - .package(url: "https://github.com/tree-sitter/tree-sitter-javascript", revision: "39798e26b6d4dbcee8e522b8db83f8b2df33a5ea"), - .package(url: "https://github.com/tree-sitter/tree-sitter-python", revision: "c5fca1a186e8e528115196178c28eefa8d86b0b0"), - .package(url: "https://github.com/tree-sitter/tree-sitter-rust", revision: "2eaf126458a4d6a69401089b6ba78c5e5d6c1ced"), - .package(url: "https://github.com/tree-sitter/tree-sitter-typescript", revision: "75b3874edb2dc714fb1fd77a32013d0f8699989f"), - .package(url: "https://github.com/tree-sitter/tree-sitter-ruby", revision: "7a010836b74351855148818d5cb8170dc4df8e6a"), - .package(url: "https://github.com/alex-pinkus/tree-sitter-swift", revision: "9253825dd2570430b53fa128cbb40cb62498e75d"), - .package(url: "https://github.com/tree-sitter/tree-sitter-c-sharp.git", revision: "b27b091bfdc5f16d0ef76421ea5609c82a57dff0"), - .package(url: "https://github.com/tree-sitter/tree-sitter-cpp", revision: "e5cea0ec884c5c3d2d1e41a741a66ce13da4d945"), - .package(url: "https://github.com/provencher/tree-sitter-php", revision: "0a99deca13c4af1fb9adcb03c958bfc9f4c740a9"), - .package(url: "https://github.com/jamesrochabrun/SwiftAnthropic", revision: "b7d030cd7453f314c780f5492385f73d704cbd5d"), - .package(url: "https://github.com/provencher/SwiftOpenAI", revision: "1211782eb337e7968124448a20d9260df1952012"), - .package(url: "https://github.com/ChimeHQ/Neon.git", exact: "0.6.0"), - .package(path: "Vendor/UniversalCharsetDetection"), - .package(url: "https://github.com/loopwork-ai/JSONSchema.git", exact: "1.3.0"), - .package(url: "https://github.com/loopwork-ai/ontology.git", exact: "0.6.0"), - .package(path: "Packages/RepoPromptAgentProviders") - ], + dependencies: packageDependencies, targets: [ .executableTarget( name: "RepoPrompt", - dependencies: [ - "RepoPromptShared", - "RepoPromptC", "CSwiftPCRE2", "TreeSitterScannerSupport", - "Sparkle", - .product(name: "Logging", package: "swift-log"), - .product(name: "KeyboardShortcuts", package: "KeyboardShortcuts"), - .product(name: "MarkdownUI", package: "swift-markdown-ui"), - .product(name: "Markdown", package: "swift-markdown"), - .product(name: "SwiftyJSON", package: "SwiftyJSON"), - .product(name: "MCP", package: "swift-sdk"), - .product(name: "SwiftTreeSitter", package: "SwiftTreeSitter"), - .product(name: "TreeSitterC", package: "tree-sitter-c"), - .product(name: "TreeSitterDart", package: "tree-sitter-dart"), - .product(name: "TreeSitterGo", package: "tree-sitter-go"), - .product(name: "TreeSitterJava", package: "tree-sitter-java"), - .product(name: "TreeSitterJavaScript", package: "tree-sitter-javascript"), - .product(name: "TreeSitterPython", package: "tree-sitter-python"), - .product(name: "TreeSitterRust", package: "tree-sitter-rust"), - .product(name: "TreeSitterTypeScript", package: "tree-sitter-typescript"), - .product(name: "TreeSitterRuby", package: "tree-sitter-ruby"), - .product(name: "TreeSitterSwift", package: "tree-sitter-swift"), - .product(name: "TreeSitterCSharp", package: "tree-sitter-c-sharp"), - .product(name: "TreeSitterCPP", package: "tree-sitter-cpp"), - .product(name: "TreeSitterPHP", package: "tree-sitter-php"), - .product(name: "SwiftAnthropic", package: "SwiftAnthropic"), - .product(name: "SwiftOpenAI", package: "SwiftOpenAI"), - .product(name: "Neon", package: "Neon"), - .product(name: "UniversalCharsetDetection", package: "UniversalCharsetDetection"), - .product(name: "Cuchardet", package: "UniversalCharsetDetection"), - .product(name: "JSONSchema", package: "JSONSchema"), - .product(name: "Ontology", package: "ontology"), - .product(name: "RepoPromptClaudeCompatibleProvider", package: "RepoPromptAgentProviders") - ], + dependencies: repoPromptDependencies, path: "Sources/RepoPrompt", - swiftSettings: [ - .define("DEBUG", .when(configuration: .debug)), - .enableUpcomingFeature("BareSlashRegexLiterals"), - .unsafeFlags([ - "-import-objc-header", "\(packageRoot)/Sources/RepoPrompt/Support/RepoPrompt-Bridging-Header.h", - "-disable-bridging-pch" - ]) - ] + swiftSettings: repoPromptSwiftSettings ), .executableTarget( name: "RepoPromptMCP", diff --git a/Scripts/source_layout_guardrails.sh b/Scripts/source_layout_guardrails.sh index a61ed7428..d0334e721 100755 --- a/Scripts/source_layout_guardrails.sh +++ b/Scripts/source_layout_guardrails.sh @@ -293,6 +293,7 @@ allowed_tracked_docs=( "docs/architecture/source-layout.md" "docs/architecture/xcode-workspace.md" "docs/open-source-readiness.md" + "docs/privacy/telemetry.md" "docs/releasing.md" "docs/testing.md" "docs/worktrees.md" diff --git a/Scripts/swiftpm_notice_guardrails.sh b/Scripts/swiftpm_notice_guardrails.sh index 5fb85e074..a8565f24a 100755 --- a/Scripts/swiftpm_notice_guardrails.sh +++ b/Scripts/swiftpm_notice_guardrails.sh @@ -35,7 +35,13 @@ jq -r ' ' Package.resolved | sort > "$TMP_DIR/resolved.tsv" cut -f1-3 "$INVENTORY" | sort > "$TMP_DIR/inventory.tsv" -diff -u "$TMP_DIR/resolved.tsv" "$TMP_DIR/inventory.tsv" || +# SwiftPM does not keep the env-gated Sentry package in Package.resolved once +# REPOPROMPT_ENABLE_SENTRY is restored to its default-off state, but official +# release builds still link it via Package.swift's exact dependency. Keep its +# copied notice under guardrail coverage without treating it as lockfile drift. +grep -v $'^sentry-cocoa\t' "$TMP_DIR/inventory.tsv" > "$TMP_DIR/inventory-for-lockfile.tsv" + +diff -u "$TMP_DIR/resolved.tsv" "$TMP_DIR/inventory-for-lockfile.tsv" || fail "SwiftPM notice inventory does not match Package.resolved" while IFS=$'\t' read -r identity _resolved _location bundle; do diff --git a/ThirdPartyLicenses/swiftpm/SHA256SUMS b/ThirdPartyLicenses/swiftpm/SHA256SUMS index 0c0b896fe..5bf83f341 100644 --- a/ThirdPartyLicenses/swiftpm/SHA256SUMS +++ b/ThirdPartyLicenses/swiftpm/SHA256SUMS @@ -7,6 +7,7 @@ d11b5eea8e9ae2f6dd06545633e8daf0dfcbc7dbb911a20d1504c905e5808775 neon/LICENSE 4438d8a78e985d37ba3c4db356557423b3739b2b2a0abb2ecefcafda4009c16b networkimage/LICENSE 5a79e5efc0acd18c5db2b9079445f87942b4f342eebbf7f792ea296825563d82 ontology/LICENSE 72512faf072d3a4c0d9ae21552e0a3367e87b719ac511d06d2b5a0e427e8796d rearrange/LICENSE +1784335a04bbe8014b9bf0f6a373f7b3804855792545702199dbdd6e2c5b6ad1 sentry-cocoa/LICENSE.md 770af8291f708538d8ff885a0bbc4e045cd700531741c4f99528d435c14d7f55 swift-algorithms/LICENSE.txt cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30 swift-asn1/LICENSE.txt 570ef2dc97e91403cb8a9b322a66cdd4107821d225a20e7df892700d7ad9586b swift-asn1/NOTICE.txt diff --git a/ThirdPartyLicenses/swiftpm/inventory.tsv b/ThirdPartyLicenses/swiftpm/inventory.tsv index 6b36ace6d..80f7cd2d6 100644 --- a/ThirdPartyLicenses/swiftpm/inventory.tsv +++ b/ThirdPartyLicenses/swiftpm/inventory.tsv @@ -6,6 +6,7 @@ neon 0.6.0 https://github.com/ChimeHQ/Neon.git neon/ networkimage 6.0.1 https://github.com/gonzalezreal/NetworkImage networkimage/ ontology 0.6.0 https://github.com/loopwork-ai/ontology.git ontology/ rearrange 1.8.1 https://github.com/ChimeHQ/Rearrange rearrange/ +sentry-cocoa 9.17.1 https://github.com/getsentry/sentry-cocoa sentry-cocoa/ swift-algorithms 1.2.1 https://github.com/apple/swift-algorithms.git swift-algorithms/ swift-asn1 1.7.0 https://github.com/apple/swift-asn1.git swift-asn1/ swift-async-algorithms 1.1.3 https://github.com/apple/swift-async-algorithms.git swift-async-algorithms/ diff --git a/ThirdPartyLicenses/swiftpm/sentry-cocoa/LICENSE.md b/ThirdPartyLicenses/swiftpm/sentry-cocoa/LICENSE.md new file mode 100644 index 000000000..6b2b8e3e4 --- /dev/null +++ b/ThirdPartyLicenses/swiftpm/sentry-cocoa/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/privacy/telemetry.md b/docs/privacy/telemetry.md new file mode 100644 index 000000000..939e24e5e --- /dev/null +++ b/docs/privacy/telemetry.md @@ -0,0 +1,75 @@ +# Telemetry + +RepoPrompt CE can report crashes, errors, app hangs, and diagnostic signals that do not include +user content to [Sentry](https://sentry.io) to help us find and fix problems that affect people +running the app. +This document describes when telemetry is active, what is and is not collected, and how it is +configured. + +## When telemetry is active + +Telemetry is active only in official, notarized Developer ID builds that are configured with the +RepoPrompt CE Sentry project. + +Telemetry is **not active by default** for: + +- DEBUG builds, +- self-compiled / locally built CE, +- locally self-signed production builds, +- release-candidate (ad-hoc) builds, +- UI-test and stress-test launches. + +This is enforced at build time and runtime. Builds without the telemetry-enabled app binary and a +Sentry DSN do not initialize telemetry. If you build CE yourself, telemetry is off unless you +explicitly configure a local build for Sentry testing. + +## What is collected + +When active, telemetry can capture: + +- crash reports and unhandled errors, with stack traces, +- app-hang reports, +- the app version/build and OS version, +- a bounded set of explicitly instrumented breadcrumbs leading up to an event, +- explicitly instrumented traces/spans for coarse app workflow timing, without user content, +- explicitly instrumented metrics for aggregate usage shape, without user content. + +The configuration is deliberately conservative: + +- no personally identifying information (`sendDefaultPii = false`), +- no session replay, MetricKit, structured logs, automatic network breadcrumbs, or automatic file I/O tracing, +- only explicitly instrumented breadcrumbs, spans, and metrics that do not include user content, +- breadcrumb and on-disk event caches are bounded. + +## What is not sent + +RepoPrompt CE's app-owned telemetry is data-minimized by construction: call sites emit typed +lifecycle events instead of raw app data. The telemetry model uses closed enums, +booleans, counts, and buckets; it does not accept arbitrary `String` values for event attributes. +The following are not collected by the manual telemetry instrumentation: + +- prompts and conversation transcripts, +- selected file contents, +- absolute file system paths and workspace names, +- tool arguments and results, and MCP payloads, +- AI provider request/response bodies, +- command output, +- run IDs and tool invocation IDs, +- custom or free-form external tool names, +- raw model names, +- environment variables, +- API keys, tokens, bearer credentials, and passwords, +- screenshots, view hierarchy, and session replay. + +Sentry native crash reports include SDK-provided crash context such as stack traces, exception +messages, app version/build, OS version/build, device model, locale, memory values, a generated user +identifier, and coarse geo country. + +## Processor + +Telemetry data is processed by Sentry (a third-party SaaS provider) acting as a data processor on +behalf of the RepoPrompt CE project. + +Sentry project-side data scrubbing is also configured for the project as defense-in-depth. +It is not the primary privacy boundary: RepoPrompt CE avoids sending sensitive data from the +app in the first place. \ No newline at end of file