Sentry Policy Guardrails Overview Organizational governance framework that prevents Sentry configuration drift across multiple services. A shared npm package ( ) wraps to enforce PII scrubbing, naming conventions, tagging standards, and per-tier trace rate caps. CI checks block policy violations before merge, and a monthly drift audit detects projects that have fallen out of compliance. Prerequisites - v8+ installed in target services - Internal npm registry available (GitHub Packages, Artifactory, or similar) - Team structure and project ownership defined in Sentry - with and scopes - Compli…

, p['slug']))),\n]:\n status = 'PASS' if val else 'FAIL'\n print(f' [{status}] {label}')\n if not val: fails += 1\nmissing = [f for f in ['password','ssn','credit_card','api_key','secret']\n if f not in p.get('sensitiveFields', [])]\nif missing:\n print(f' [FAIL] Sensitive Fields: missing {missing}')\n fails += 1\nelse:\n print(f' [PASS] Sensitive Fields')\nsys.exit(1 if fails else 0)\n\" || VIOLATIONS=$((VIOLATIONS + 1))\ndone\n\necho \"\"\necho \"Projects with violations: $VIOLATIONS / $(echo \"$PROJECTS\" | wc -l)\"\n[ \"$VIOLATIONS\" -eq 0 ] && echo \"STATUS: ALL COMPLIANT\" || { echo \"STATUS: DRIFT DETECTED\"; exit 1; }\n```\n\n**Per-team cost quotas (run weekly):**\n\n```typescript\n// scripts/check-team-quotas.ts\nconst QUOTAS = [\n { team: 'payments', errors: 10_000, transactions: 50_000 },\n { team: 'platform', errors: 20_000, transactions: 100_000 },\n { team: 'growth', errors: 15_000, transactions: 200_000 },\n { team: 'infrastructure', errors: 8_000, transactions: 20_000 },\n];\n\nasync function main() {\n const org = process.env.SENTRY_ORG!, token = process.env.SENTRY_AUTH_TOKEN!;\n const headers = { Authorization: `Bearer ${token}` };\n\n // Fetch 30-day usage grouped by project and category\n const stats = await fetch(\n `https://sentry.io/api/0/organizations/${org}/stats_v2/?` +\n 'field=sum(quantity)&groupBy=project&groupBy=category&interval=1d&statsPeriod=30d',\n { headers },\n ).then(r => r.json()) as any;\n\n // Map projects to teams\n const projects = await fetch(\n `https://sentry.io/api/0/organizations/${org}/projects/?all_projects=1`,\n { headers },\n ).then(r => r.json()) as any[];\n\n const teamOf = new Map(projects.map((p: any) => [p.slug, p.teams?.[0]?.slug || 'unknown']));\n const usage = new Map\u003cstring, { errors: number; txns: number }>();\n\n for (const g of stats.groups || []) {\n const team = teamOf.get(g.by.project) || 'unknown';\n if (!usage.has(team)) usage.set(team, { errors: 0, txns: 0 });\n const u = usage.get(team)!;\n if (g.by.category === 'error') u.errors += g.totals['sum(quantity)'];\n if (g.by.category === 'transaction') u.txns += g.totals['sum(quantity)'];\n }\n\n let overBudget = false;\n for (const q of QUOTAS) {\n const u = usage.get(q.team) || { errors: 0, txns: 0 };\n const ePct = ((u.errors / q.errors) * 100).toFixed(0);\n const tPct = ((u.txns / q.transactions) * 100).toFixed(0);\n const flag = u.errors > q.errors || u.txns > q.transactions ? 'OVER' : 'OK';\n console.log(`[${q.team}] errors=${u.errors}/${q.errors} (${ePct}%) txns=${u.txns}/${q.transactions} (${tPct}%) [${flag}]`);\n if (flag === 'OVER') overBudget = true;\n }\n if (overBudget) { console.error('ACTION REQUIRED: budget exceeded'); process.exit(1); }\n}\nmain();\n```\n\n**Schedule both in CI:**\n\n```yaml\n# .github/workflows/sentry-audit.yml\nname: Sentry Monthly Audit\non:\n schedule: [{ cron: '0 9 1 * *' }]\n workflow_dispatch:\n\njobs:\n drift:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: bash scripts/audit-sentry-drift.sh\n env:\n SENTRY_ORG: ${{ secrets.SENTRY_ORG }}\n SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}\n\n cost:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with: { node-version: '20' }\n - run: npx tsx scripts/check-team-quotas.ts\n env:\n SENTRY_ORG: ${{ secrets.SENTRY_ORG }}\n SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}\n```\n\n## Output\n\n- `@company/sentry-config` shared package with enforced PII scrubbing, naming validation, and tier-based trace rate caps\n- Mandatory `beforeSend` chain: headers, credit cards, emails, and SSNs scrubbed before any custom logic\n- Naming standards enforced at init: release format, validated environments, kebab-case service names\n- Required tags injected: service, team, tier, deployment, region\n- CI workflow blocking hardcoded DSNs, PII collection, and direct `Sentry.init()` calls\n- Monthly drift audit checking data scrubber, IP scrubbing, sensitive fields, and naming compliance\n- Per-team cost quota enforcement with weekly budget checks\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Invalid service name` at init | Name not kebab-case or wrong length | Rename to lowercase kebab-case, 3-40 chars, letter start |\n| `Invalid environment` at init | Non-standard environment string | Use: production, staging, development, canary, or sandbox |\n| CI blocks `Sentry.init()` | Direct call outside shared config | Replace with `initSentry()` from `@company/sentry-config` |\n| CI blocks hardcoded DSN | DSN string in source code | Move to `SENTRY_DSN` environment variable |\n| Drift audit `[FAIL]` on data scrubber | Project settings changed in UI | Re-enable in Project Settings then Security and Privacy |\n| Team over budget | Noisy errors or high trace volume | Add `ignoreErrors` filters or lower `tracesSampleRate` |\n| `beforeSend` scrubbing bypassed | Service using raw `Sentry.init()` | Adopt `@company/sentry-config` which chains scrubbing first |\n\n## Examples\n\n**Minimal compliant init:**\n\n```typescript\nimport { initSentry } from '@company/sentry-config';\n\ninitSentry({\n serviceName: 'user-service',\n dsn: process.env.SENTRY_DSN!,\n version: '1.0.0',\n tags: { service: 'user-service', team: 'platform', tier: 'standard' },\n});\n```\n\n**Critical service with custom filtering:**\n\n```typescript\nimport { initSentry } from '@company/sentry-config';\n\ninitSentry({\n serviceName: 'checkout-api',\n dsn: process.env.SENTRY_DSN!,\n version: '3.2.0',\n tags: { service: 'checkout-api', team: 'payments', tier: 'critical' },\n tracesSampleRate: 0.4, // capped to 0.5 for critical tier\n beforeSend(event) {\n if (event.exception?.values?.some(e => e.value?.includes('PaymentTimeout'))) {\n return null; // drop known timeout noise from payment processor\n }\n return event;\n },\n});\n```\n\n## Resources\n\n- [Sentry Organization Settings](https://docs.sentry.io/organization/)\n- [Data Scrubbing](https://docs.sentry.io/product/data-management-settings/scrubbing/)\n- [Sentry API Reference](https://docs.sentry.io/api/)\n- [Quotas and Rate Limits](https://docs.sentry.io/pricing/quotas/)\n- [Security Policy](https://sentry.io/security/)\n\n## Next Steps\n\n- Publish `@company/sentry-config` to internal registry and onboard first two services\n- Add the CI policy workflow to all service repositories via shared workflow or template\n- Schedule the drift audit as a monthly cron and cost check as weekly\n- Communicate per-team quotas to engineering leads with Slack budget alerts\n---","attachment_filenames":["references/alert-policies.md","references/audit-and-compliance.md","references/compliance-enforcement.md","references/configuration-standards.md","references/errors.md","references/examples.md","references/project-naming-standards.md"],"attachments":[{"filename":"references/alert-policies.md","content":"# Alert Policies\n\n## Alert Policies\n\n### Standard Alert Template\n\n```yaml\n# Organization-wide alert standards\nalert_policies:\n critical:\n name: \"Critical Error Rate\"\n conditions:\n - event_frequency > 100 per hour\n actions:\n - pagerduty: critical-oncall\n - slack: \"#incidents\"\n required: true # Teams cannot disable\n\n high_error_rate:\n name: \"High Error Rate\"\n conditions:\n - error_rate > 5%\n actions:\n - slack: team-channel # Team configures channel\n required: true\n\n new_issue:\n name: \"New Issue Notification\"\n conditions:\n - is:unresolved is:new\n actions:\n - slack: team-channel\n required: false # Optional\n```\n\n### Alert Configuration Validation\n\n```typescript\n// Validate team alert configurations\ninterface AlertConfig {\n name: string;\n conditions: string[];\n actions: string[];\n}\n\nfunction validateAlertConfig(config: AlertConfig): string[] {\n const errors: string[] = [];\n\n // Required alerts must exist\n const requiredAlerts = ['Critical Error Rate', 'High Error Rate'];\n for (const required of requiredAlerts) {\n if (config.name === required && config.actions.length === 0) {\n errors.push(`Required alert \"${required}\" must have actions configured`);\n }\n }\n\n // PagerDuty required for critical\n if (config.name.includes('Critical') && !config.actions.some(a => a.includes('pagerduty'))) {\n errors.push('Critical alerts must include PagerDuty action');\n }\n\n return errors;\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1490,"content_sha256":"1cd3981aff677136ebb0a43d5640ca4bb7802294abf4c3c73c87eb686b6d4cb0"},{"filename":"references/audit-and-compliance.md","content":"# Audit And Compliance\n\n## Audit and Compliance\n\n### Configuration Audit Script\n\n```typescript\n// audit-sentry-config.ts\nasync function auditSentryConfiguration(): Promise\u003cAuditReport> {\n const report: AuditReport = {\n timestamp: new Date(),\n findings: [],\n };\n\n // Check all projects\n const projects = await getProjects();\n\n for (const project of projects) {\n // Check naming convention\n if (!validateProjectName(project.name)) {\n report.findings.push({\n severity: 'warning',\n project: project.name,\n issue: 'Project name does not follow naming convention',\n });\n }\n\n // Check required alerts\n const alerts = await getProjectAlerts(project.id);\n const hasCriticalAlert = alerts.some(a => a.name === 'Critical Error Rate');\n if (!hasCriticalAlert) {\n report.findings.push({\n severity: 'error',\n project: project.name,\n issue: 'Missing required Critical Error Rate alert',\n });\n }\n\n // Check team assignment\n if (project.teams.length === 0) {\n report.findings.push({\n severity: 'error',\n project: project.name,\n issue: 'Project not assigned to any team',\n });\n }\n }\n\n return report;\n}\n```\n\n### Compliance Dashboard\n\n```typescript\n// Generate compliance metrics\nasync function getComplianceMetrics(): Promise\u003cComplianceMetrics> {\n const projects = await getProjects();\n const total = projects.length;\n\n return {\n total_projects: total,\n naming_compliant: projects.filter(p => validateProjectName(p.name)).length,\n alerts_configured: projects.filter(p => p.hasRequiredAlerts).length,\n teams_assigned: projects.filter(p => p.teams.length > 0).length,\n compliance_score: calculateComplianceScore(projects),\n };\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1770,"content_sha256":"bd3a714d1d1d837d2d2ec7619e846d1e622af4c017a8976a19d0ee388a964ae8"},{"filename":"references/compliance-enforcement.md","content":"# Compliance Enforcement\n\n## Compliance Enforcement\n\n### Required Data Scrubbing\n\n```typescript\n// Enforced by shared config - cannot be disabled\nfunction scrubSensitiveData(event: Sentry.Event): Sentry.Event {\n // Remove sensitive headers\n if (event.request?.headers) {\n const sensitiveHeaders = [\n 'authorization',\n 'cookie',\n 'x-api-key',\n 'x-auth-token',\n ];\n for (const header of sensitiveHeaders) {\n delete event.request.headers[header];\n }\n }\n\n // Scrub credit cards\n event = scrubPattern(event, /\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/g, '[CARD]');\n\n // Scrub SSNs\n event = scrubPattern(event, /\\b\\d{3}-\\d{2}-\\d{4}\\b/g, '[SSN]');\n\n // Scrub emails in extra/contexts (keep user email if explicitly set)\n event = scrubPattern(event, /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g, '[EMAIL]');\n\n return event;\n}\n\nfunction scrubPattern(event: Sentry.Event, pattern: RegExp, replacement: string): Sentry.Event {\n const scrub = (obj: any): any => {\n if (typeof obj === 'string') {\n return obj.replace(pattern, replacement);\n }\n if (Array.isArray(obj)) {\n return obj.map(scrub);\n }\n if (obj && typeof obj === 'object') {\n return Object.fromEntries(\n Object.entries(obj).map(([k, v]) => [k, scrub(v)])\n );\n }\n return obj;\n };\n\n return scrub(event);\n}\n```\n\n### Environment Enforcement\n\n```typescript\n// Prevent test data in production\nfunction validateEnvironment(event: Sentry.Event): Sentry.Event | null {\n const env = process.env.NODE_ENV;\n\n // Block test data in production\n if (env === 'production') {\n const message = event.message?.toLowerCase() || '';\n const tags = event.tags || {};\n\n if (\n message.includes('test') ||\n tags.test === 'true' ||\n event.user?.email?.includes('@test.com')\n ) {\n console.warn('Blocked test data from production Sentry');\n return null;\n }\n }\n\n return event;\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1955,"content_sha256":"d3cbbcee6f5a846196c7597a592ec7e68da8fc6f5cc3922e845746e70f36c218"},{"filename":"references/configuration-standards.md","content":"# Configuration Standards\n\n## Configuration Standards\n\n### Shared Configuration Package\n\n```typescript\n// packages/sentry-config/index.ts\nimport * as Sentry from '@sentry/node';\n\nexport interface SentryConfigOptions {\n serviceName: string;\n environment: string;\n version?: string;\n additionalTags?: Record\u003cstring, string>;\n}\n\n// Enforced organization defaults\nconst ORGANIZATION_DEFAULTS = {\n // Never send PII without explicit opt-in\n sendDefaultPii: false,\n\n // Standard sample rates\n sampleRate: 1.0,\n tracesSampleRate: 0.1,\n\n // Standard breadcrumb limit\n maxBreadcrumbs: 50,\n\n // Standard ignore patterns\n ignoreErrors: [\n 'ResizeObserver loop limit exceeded',\n 'Non-Error promise rejection',\n /^Network request failed/,\n ],\n};\n\nexport function initSentryWithPolicy(options: SentryConfigOptions): void {\n const dsn = process.env.SENTRY_DSN;\n\n if (!dsn && options.environment === 'production') {\n throw new Error('SENTRY_DSN required in production');\n }\n\n Sentry.init({\n dsn,\n environment: options.environment,\n release: options.version,\n serverName: options.serviceName,\n\n // Apply organization defaults (cannot be overridden)\n ...ORGANIZATION_DEFAULTS,\n\n // Required tags\n initialScope: {\n tags: {\n service: options.serviceName,\n team: process.env.TEAM_NAME || 'unknown',\n ...options.additionalTags,\n },\n },\n\n // Enforced PII scrubbing\n beforeSend: enforcedBeforeSend,\n });\n}\n\nfunction enforcedBeforeSend(\n event: Sentry.Event,\n hint: Sentry.EventHint\n): Sentry.Event | null {\n // Always scrub sensitive data\n return scrubSensitiveData(event);\n}\n```\n\n### Usage in Services\n\n```typescript\n// services/user-service/src/index.ts\nimport { initSentryWithPolicy } from '@mycompany/sentry-config';\n\n// Teams can't bypass organization policies\ninitSentryWithPolicy({\n serviceName: 'user-service',\n environment: process.env.NODE_ENV || 'development',\n version: process.env.GIT_SHA,\n additionalTags: {\n feature_area: 'authentication',\n },\n});\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2050,"content_sha256":"8a3316a831710f667d36caef1e7e342aaee4cfbf0b020cd831c9384822ca1678"},{"filename":"references/errors.md","content":"# Error Handling Reference\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Teams bypassing policies` | Direct SDK init | Require shared config package usage |\n| `Audit failures` | Missing required alerts | Automate compliance checks in CI |\n| `Naming violations` | No validation | Implement project name validation API |\n| `Inconsistent configurations` | Multiple init points | Centralize SDK configuration |\n\n---\n*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":576,"content_sha256":"41a8d86a5b91027255898be43657a41258e724ffe29c34db369419ecf2266ad6"},{"filename":"references/examples.md","content":"# Examples\n\n**Example: Enterprise Policy Implementation**\nRequest: \"Enforce consistent Sentry configuration across 20 services\"\nResult: Shared NPM package with enforced defaults, PII scrubbing, and required alert rules.\n\n---\n*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":373,"content_sha256":"0b6019f8bc52a4bc05e27af6428926098ba233660918e8d1a10ecd8422cc41dd"},{"filename":"references/project-naming-standards.md","content":"# Project Naming Standards\n\n## Project Naming Standards\n\n### Enforced Naming Convention\n\n```typescript\n// Project naming: {team}-{service}-{environment}\n// Examples: payments-api-production, auth-worker-staging\n\nfunction validateProjectName(name: string): boolean {\n const pattern = /^[a-z]+-[a-z]+-(?:production|staging|development)$/;\n return pattern.test(name);\n}\n\n// API to create project with validation\nasync function createProject(\n teamSlug: string,\n serviceName: string,\n environment: string\n): Promise\u003cvoid> {\n const projectName = `${teamSlug}-${serviceName}-${environment}`;\n\n if (!validateProjectName(projectName)) {\n throw new Error(\n `Invalid project name: ${projectName}. ` +\n 'Must follow pattern: {team}-{service}-{environment}'\n );\n }\n\n // Create via Sentry API\n await fetch(`https://sentry.io/api/0/teams/${ORG}/${teamSlug}/projects/`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${SENTRY_TOKEN}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ name: projectName }),\n });\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1077,"content_sha256":"73c051cee1fb22a4d3192f0d9cf802bb504852fea7681370246d5f7d71b0c1fb"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Sentry Policy Guardrails","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Organizational governance framework that prevents Sentry configuration drift across multiple services. A shared npm package (","type":"text"},{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]},{"text":") wraps ","type":"text"},{"text":"Sentry.init()","type":"text","marks":[{"type":"code_inline"}]},{"text":" to enforce PII scrubbing, naming conventions, tagging standards, and per-tier trace rate caps. CI checks block policy violations before merge, and a monthly drift audit detects projects that have fallen out of compliance.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"@sentry/node","type":"text","marks":[{"type":"code_inline"}]},{"text":" v8+ installed in target services","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Internal npm registry available (GitHub Packages, Artifactory, or similar)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Team structure and project ownership defined in Sentry","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SENTRY_AUTH_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"org:read","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"project:read","type":"text","marks":[{"type":"code_inline"}]},{"text":" scopes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Compliance requirements identified (SOC 2, GDPR, HIPAA)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Instructions","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1 — Build the Shared Configuration Package","type":"text"}]},{"type":"paragraph","content":[{"text":"Create ","type":"text"},{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]},{"text":" that wraps ","type":"text"},{"text":"Sentry.init()","type":"text","marks":[{"type":"code_inline"}]},{"text":" with non-negotiable defaults.","type":"text"}]},{"type":"paragraph","content":[{"text":"Mandatory PII scrubbing (cannot be bypassed):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// @company/sentry-config/src/scrubbers.ts\nimport type { Event } from '@sentry/node';\n\nconst SENSITIVE_HEADERS = [\n 'authorization', 'cookie', 'set-cookie',\n 'x-api-key', 'x-auth-token', 'x-csrf-token',\n];\n\nconst PII_PATTERNS = [\n { pattern: /\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{1,7}\\b/g, replacement: '[CC_REDACTED]' },\n { pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g, replacement: '[EMAIL_REDACTED]' },\n { pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g, replacement: '[SSN_REDACTED]' },\n];\n\nexport function scrubEvent(event: Event): Event | null {\n if (event.request?.headers) {\n for (const h of SENSITIVE_HEADERS) delete event.request.headers[h];\n }\n if (event.message) event.message = scrubPII(event.message);\n if (event.exception?.values) {\n for (const exc of event.exception.values) {\n if (exc.value) exc.value = scrubPII(exc.value);\n }\n }\n return event;\n}\n\nfunction scrubPII(str: string): string {\n for (const { pattern, replacement } of PII_PATTERNS) {\n str = str.replace(new RegExp(pattern.source, pattern.flags), replacement);\n }\n return str;\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Governed init with naming validation, tag injection, and tier-based caps:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// @company/sentry-config/src/index.ts\nimport * as Sentry from '@sentry/node';\nimport { scrubEvent } from './scrubbers';\n\nconst ENFORCED: Partial\u003cSentry.NodeOptions> = {\n sendDefaultPii: false,\n debug: false,\n maxBreadcrumbs: 50,\n sampleRate: 1.0,\n maxValueLength: 500,\n};\n\nconst VALID_ENVS = ['production', 'staging', 'development', 'canary', 'sandbox'];\nconst TIER_TRACE_CAPS: Record\u003cstring, number> = { critical: 0.5, standard: 0.2, internal: 0.05 };\n\ninterface PolicyOptions {\n serviceName: string; // kebab-case, 3-40 chars\n dsn: string;\n version: string;\n tags: { service: string; team: string; tier: 'critical' | 'standard' | 'internal' };\n environment?: string;\n tracesSampleRate?: number; // capped by tier\n beforeSend?: Sentry.NodeOptions['beforeSend'];\n}\n\nexport function initSentry(opts: PolicyOptions): void {\n if (!opts.dsn) throw new Error('@company/sentry-config: dsn required');\n if (!/^[a-z][a-z0-9-]{2,39}$/.test(opts.serviceName)) {\n throw new Error(`Invalid service name \"${opts.serviceName}\" — use lowercase kebab-case, 3-40 chars`);\n }\n\n const env = (opts.environment || process.env.NODE_ENV || 'development').toLowerCase();\n if (!VALID_ENVS.includes(env)) {\n throw new Error(`Invalid environment \"${env}\". Allowed: ${VALID_ENVS.join(', ')}`);\n }\n\n const tierCap = TIER_TRACE_CAPS[opts.tags.tier] ?? 0.2;\n const sha = (process.env.GIT_SHA || process.env.COMMIT_SHA || '').substring(0, 7);\n const release = sha\n ? `${opts.serviceName}@${opts.version}+${sha}`\n : `${opts.serviceName}@${opts.version}`;\n\n Sentry.init({\n dsn: opts.dsn,\n environment: env,\n release,\n serverName: opts.serviceName,\n ...ENFORCED,\n debug: env !== 'production',\n tracesSampleRate: Math.min(opts.tracesSampleRate ?? 0.1, tierCap),\n\n beforeSend(event, hint) {\n const scrubbed = scrubEvent(event); // Mandatory — always runs first\n if (!scrubbed) return null;\n return opts.beforeSend ? (opts.beforeSend as Function)(scrubbed, hint) : scrubbed;\n },\n\n initialScope: {\n tags: {\n service: opts.tags.service,\n team: opts.tags.team,\n tier: opts.tags.tier,\n deployment: process.env.DEPLOYMENT_ID || 'unknown',\n region: process.env.AWS_REGION || process.env.GCP_REGION || 'unknown',\n },\n },\n });\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Service usage (two lines to adopt):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { initSentry } from '@company/sentry-config';\n\ninitSentry({\n serviceName: 'payments-api',\n dsn: process.env.SENTRY_DSN!,\n version: '2.4.1',\n tags: { service: 'payments-api', team: 'payments', tier: 'critical' },\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2 — Enforce Compliance via CI","type":"text"}]},{"type":"paragraph","content":[{"text":"Add a GitHub Actions workflow to every service repository that blocks policy violations.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/sentry-policy.yml\nname: Sentry Policy Check\non: [pull_request]\n\njobs:\n sentry-compliance:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - name: Block hardcoded DSNs\n run: |\n if grep -rn \"https://[a-f0-9]*@.*ingest.*sentry\" \\\n --include=\"*.ts\" --include=\"*.js\" \\\n --exclude-dir=node_modules --exclude-dir=dist src/; then\n echo \"::error::Hardcoded Sentry DSN — use SENTRY_DSN env var\"\n exit 1\n fi\n\n - name: Block sendDefaultPii true\n run: |\n if grep -rn \"sendDefaultPii.*true\" \\\n --include=\"*.ts\" --include=\"*.js\" \\\n --exclude-dir=node_modules src/; then\n echo \"::error::sendDefaultPii must be false\"\n exit 1\n fi\n\n - name: Block direct Sentry.init calls\n run: |\n HITS=$(grep -rn \"Sentry\\.init(\" --include=\"*.ts\" --include=\"*.js\" \\\n --exclude-dir=node_modules --exclude=\"*sentry-config*\" src/ || true)\n if [ -n \"$HITS\" ]; then\n echo \"::error::Use initSentry() from @company/sentry-config\"\n echo \"$HITS\"\n exit 1\n fi\n\n - name: Verify shared config dependency\n run: |\n if ! grep -q \"@company/sentry-config\" package.json; then\n echo \"::warning::Not using shared Sentry config package\"\n fi","type":"text"}]},{"type":"paragraph","content":[{"text":"Optional ESLint rule for IDE feedback:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"// eslint-rules/no-direct-sentry-init.js\nmodule.exports = {\n meta: { type: 'problem', messages: { bad: 'Use initSentry() from @company/sentry-config' } },\n create(ctx) {\n return {\n CallExpression(node) {\n if (node.callee.type === 'MemberExpression' &&\n node.callee.object.name === 'Sentry' &&\n node.callee.property.name === 'init' &&\n !ctx.getFilename().includes('sentry-config')) {\n ctx.report({ node, messageId: 'bad' });\n }\n },\n };\n },\n};","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3 — Drift Detection and Cost Governance","type":"text"}]},{"type":"paragraph","content":[{"text":"Monthly drift audit across all Sentry projects:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"#!/bin/bash\n# scripts/audit-sentry-drift.sh\nset -euo pipefail\n\nORG=\"${SENTRY_ORG:?required}\" TOKEN=\"${SENTRY_AUTH_TOKEN:?required}\"\nAPI=\"https://sentry.io/api/0\"\nVIOLATIONS=0\n\necho \"=== Sentry Drift Audit — $(date -u +%Y-%m-%d) ===\"\n\nPROJECTS=$(curl -s -H \"Authorization: Bearer $TOKEN\" \\\n \"$API/organizations/$ORG/projects/?all_projects=1\" \\\n | python3 -c \"import json,sys; [print(p['slug']) for p in json.load(sys.stdin)]\")\n\nfor P in $PROJECTS; do\n echo \"--- $P ---\"\n curl -s -H \"Authorization: Bearer $TOKEN\" \"$API/projects/$ORG/$P/\" \\\n | python3 -c \"\nimport json, sys, re\np = json.load(sys.stdin)\nfails = 0\nfor label, val in [\n ('Data Scrubber', p.get('dataScrubber', False)),\n ('IP Scrubbing', p.get('scrubIPAddresses', False)),\n ('Naming', bool(re.match(r'^[a-z]+-[a-z0-9]+-[a-z]+

Sentry Policy Guardrails Overview Organizational governance framework that prevents Sentry configuration drift across multiple services. A shared npm package ( ) wraps to enforce PII scrubbing, naming conventions, tagging standards, and per-tier trace rate caps. CI checks block policy violations before merge, and a monthly drift audit detects projects that have fallen out of compliance. Prerequisites - v8+ installed in target services - Internal npm registry available (GitHub Packages, Artifactory, or similar) - Team structure and project ownership defined in Sentry - with and scopes - Compli…

, p['slug']))),\n]:\n status = 'PASS' if val else 'FAIL'\n print(f' [{status}] {label}')\n if not val: fails += 1\nmissing = [f for f in ['password','ssn','credit_card','api_key','secret']\n if f not in p.get('sensitiveFields', [])]\nif missing:\n print(f' [FAIL] Sensitive Fields: missing {missing}')\n fails += 1\nelse:\n print(f' [PASS] Sensitive Fields')\nsys.exit(1 if fails else 0)\n\" || VIOLATIONS=$((VIOLATIONS + 1))\ndone\n\necho \"\"\necho \"Projects with violations: $VIOLATIONS / $(echo \"$PROJECTS\" | wc -l)\"\n[ \"$VIOLATIONS\" -eq 0 ] && echo \"STATUS: ALL COMPLIANT\" || { echo \"STATUS: DRIFT DETECTED\"; exit 1; }","type":"text"}]},{"type":"paragraph","content":[{"text":"Per-team cost quotas (run weekly):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"// scripts/check-team-quotas.ts\nconst QUOTAS = [\n { team: 'payments', errors: 10_000, transactions: 50_000 },\n { team: 'platform', errors: 20_000, transactions: 100_000 },\n { team: 'growth', errors: 15_000, transactions: 200_000 },\n { team: 'infrastructure', errors: 8_000, transactions: 20_000 },\n];\n\nasync function main() {\n const org = process.env.SENTRY_ORG!, token = process.env.SENTRY_AUTH_TOKEN!;\n const headers = { Authorization: `Bearer ${token}` };\n\n // Fetch 30-day usage grouped by project and category\n const stats = await fetch(\n `https://sentry.io/api/0/organizations/${org}/stats_v2/?` +\n 'field=sum(quantity)&groupBy=project&groupBy=category&interval=1d&statsPeriod=30d',\n { headers },\n ).then(r => r.json()) as any;\n\n // Map projects to teams\n const projects = await fetch(\n `https://sentry.io/api/0/organizations/${org}/projects/?all_projects=1`,\n { headers },\n ).then(r => r.json()) as any[];\n\n const teamOf = new Map(projects.map((p: any) => [p.slug, p.teams?.[0]?.slug || 'unknown']));\n const usage = new Map\u003cstring, { errors: number; txns: number }>();\n\n for (const g of stats.groups || []) {\n const team = teamOf.get(g.by.project) || 'unknown';\n if (!usage.has(team)) usage.set(team, { errors: 0, txns: 0 });\n const u = usage.get(team)!;\n if (g.by.category === 'error') u.errors += g.totals['sum(quantity)'];\n if (g.by.category === 'transaction') u.txns += g.totals['sum(quantity)'];\n }\n\n let overBudget = false;\n for (const q of QUOTAS) {\n const u = usage.get(q.team) || { errors: 0, txns: 0 };\n const ePct = ((u.errors / q.errors) * 100).toFixed(0);\n const tPct = ((u.txns / q.transactions) * 100).toFixed(0);\n const flag = u.errors > q.errors || u.txns > q.transactions ? 'OVER' : 'OK';\n console.log(`[${q.team}] errors=${u.errors}/${q.errors} (${ePct}%) txns=${u.txns}/${q.transactions} (${tPct}%) [${flag}]`);\n if (flag === 'OVER') overBudget = true;\n }\n if (overBudget) { console.error('ACTION REQUIRED: budget exceeded'); process.exit(1); }\n}\nmain();","type":"text"}]},{"type":"paragraph","content":[{"text":"Schedule both in CI:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"# .github/workflows/sentry-audit.yml\nname: Sentry Monthly Audit\non:\n schedule: [{ cron: '0 9 1 * *' }]\n workflow_dispatch:\n\njobs:\n drift:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: bash scripts/audit-sentry-drift.sh\n env:\n SENTRY_ORG: ${{ secrets.SENTRY_ORG }}\n SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}\n\n cost:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with: { node-version: '20' }\n - run: npx tsx scripts/check-team-quotas.ts\n env:\n SENTRY_ORG: ${{ secrets.SENTRY_ORG }}\n SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]},{"text":" shared package with enforced PII scrubbing, naming validation, and tier-based trace rate caps","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mandatory ","type":"text"},{"text":"beforeSend","type":"text","marks":[{"type":"code_inline"}]},{"text":" chain: headers, credit cards, emails, and SSNs scrubbed before any custom logic","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Naming standards enforced at init: release format, validated environments, kebab-case service names","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Required tags injected: service, team, tier, deployment, region","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CI workflow blocking hardcoded DSNs, PII collection, and direct ","type":"text"},{"text":"Sentry.init()","type":"text","marks":[{"type":"code_inline"}]},{"text":" calls","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Monthly drift audit checking data scrubber, IP scrubbing, sensitive fields, and naming compliance","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Per-team cost quota enforcement with weekly budget checks","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Error Handling","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cause","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invalid service name","type":"text","marks":[{"type":"code_inline"}]},{"text":" at init","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Name not kebab-case or wrong length","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rename to lowercase kebab-case, 3-40 chars, letter start","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invalid environment","type":"text","marks":[{"type":"code_inline"}]},{"text":" at init","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Non-standard environment string","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use: production, staging, development, canary, or sandbox","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CI blocks ","type":"text"},{"text":"Sentry.init()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Direct call outside shared config","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Replace with ","type":"text"},{"text":"initSentry()","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CI blocks hardcoded DSN","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DSN string in source code","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Move to ","type":"text"},{"text":"SENTRY_DSN","type":"text","marks":[{"type":"code_inline"}]},{"text":" environment variable","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Drift audit ","type":"text"},{"text":"[FAIL]","type":"text","marks":[{"type":"code_inline"}]},{"text":" on data scrubber","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Project settings changed in UI","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-enable in Project Settings then Security and Privacy","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Team over budget","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Noisy errors or high trace volume","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"ignoreErrors","type":"text","marks":[{"type":"code_inline"}]},{"text":" filters or lower ","type":"text"},{"text":"tracesSampleRate","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"beforeSend","type":"text","marks":[{"type":"code_inline"}]},{"text":" scrubbing bypassed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Service using raw ","type":"text"},{"text":"Sentry.init()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Adopt ","type":"text"},{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]},{"text":" which chains scrubbing first","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Examples","type":"text"}]},{"type":"paragraph","content":[{"text":"Minimal compliant init:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { initSentry } from '@company/sentry-config';\n\ninitSentry({\n serviceName: 'user-service',\n dsn: process.env.SENTRY_DSN!,\n version: '1.0.0',\n tags: { service: 'user-service', team: 'platform', tier: 'standard' },\n});","type":"text"}]},{"type":"paragraph","content":[{"text":"Critical service with custom filtering:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { initSentry } from '@company/sentry-config';\n\ninitSentry({\n serviceName: 'checkout-api',\n dsn: process.env.SENTRY_DSN!,\n version: '3.2.0',\n tags: { service: 'checkout-api', team: 'payments', tier: 'critical' },\n tracesSampleRate: 0.4, // capped to 0.5 for critical tier\n beforeSend(event) {\n if (event.exception?.values?.some(e => e.value?.includes('PaymentTimeout'))) {\n return null; // drop known timeout noise from payment processor\n }\n return event;\n },\n});","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sentry Organization Settings","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.sentry.io/organization/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data Scrubbing","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.sentry.io/product/data-management-settings/scrubbing/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sentry API Reference","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.sentry.io/api/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Quotas and Rate Limits","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.sentry.io/pricing/quotas/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security Policy","type":"text","marks":[{"type":"link","attrs":{"href":"https://sentry.io/security/","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Next Steps","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Publish ","type":"text"},{"text":"@company/sentry-config","type":"text","marks":[{"type":"code_inline"}]},{"text":" to internal registry and onboard first two services","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add the CI policy workflow to all service repositories via shared workflow or template","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schedule the drift audit as a monthly cron and cost check as weekly","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Communicate per-team quotas to engineering leads with Slack budget alerts","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"sentry-policy-guardrails","tags":["saas","sentry","compliance","governance","policy","ci","cost-governance"],"author":"@skillopedia","source":{"stars":2275,"repo_name":"claude-code-plugins-plus-skills","origin_url":"https://github.com/jeremylongshore/claude-code-plugins-plus-skills/blob/HEAD/plugins/saas-packs/sentry-pack/skills/sentry-policy-guardrails/SKILL.md","repo_owner":"jeremylongshore","body_sha256":"a60a9b94043805414e434d3f1cc35b594413410cdd1e880110da3155379277f2","cluster_key":"081f51fae2bbdbacb2746c27c1132ea084cabe30c37ab1b821c62c4c5e5e3570","clean_bundle":{"format":"clean-skill-bundle-v1","source":"jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/sentry-pack/skills/sentry-policy-guardrails/SKILL.md","attachments":[{"id":"6c136857-d43f-5579-8c9a-702bcd4e5cc5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6c136857-d43f-5579-8c9a-702bcd4e5cc5/attachment.md","path":"references/alert-policies.md","size":1490,"sha256":"1cd3981aff677136ebb0a43d5640ca4bb7802294abf4c3c73c87eb686b6d4cb0","contentType":"text/markdown; charset=utf-8"},{"id":"f45b156c-985c-5b1b-a422-db6315ef70a9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f45b156c-985c-5b1b-a422-db6315ef70a9/attachment.md","path":"references/audit-and-compliance.md","size":1770,"sha256":"bd3a714d1d1d837d2d2ec7619e846d1e622af4c017a8976a19d0ee388a964ae8","contentType":"text/markdown; charset=utf-8"},{"id":"0690b120-2c50-5fbb-8b77-368012a21c50","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0690b120-2c50-5fbb-8b77-368012a21c50/attachment.md","path":"references/compliance-enforcement.md","size":1955,"sha256":"d3cbbcee6f5a846196c7597a592ec7e68da8fc6f5cc3922e845746e70f36c218","contentType":"text/markdown; charset=utf-8"},{"id":"527dbb91-5e60-5173-a109-ee751453f8e1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/527dbb91-5e60-5173-a109-ee751453f8e1/attachment.md","path":"references/configuration-standards.md","size":2050,"sha256":"8a3316a831710f667d36caef1e7e342aaee4cfbf0b020cd831c9384822ca1678","contentType":"text/markdown; charset=utf-8"},{"id":"142f6c4d-ca48-5a92-b114-49318ad60623","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/142f6c4d-ca48-5a92-b114-49318ad60623/attachment.md","path":"references/errors.md","size":576,"sha256":"41a8d86a5b91027255898be43657a41258e724ffe29c34db369419ecf2266ad6","contentType":"text/markdown; charset=utf-8"},{"id":"780828d8-49c4-5fbd-833a-c9d91cdb2d72","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/780828d8-49c4-5fbd-833a-c9d91cdb2d72/attachment.md","path":"references/examples.md","size":373,"sha256":"0b6019f8bc52a4bc05e27af6428926098ba233660918e8d1a10ecd8422cc41dd","contentType":"text/markdown; charset=utf-8"},{"id":"3c26ed00-7696-51d1-8b1b-6383f5b3689f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3c26ed00-7696-51d1-8b1b-6383f5b3689f/attachment.md","path":"references/project-naming-standards.md","size":1077,"sha256":"73c051cee1fb22a4d3192f0d9cf802bb504852fea7681370246d5f7d71b0c1fb","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"693b7a77eee183c01d7e9760b9bf3506613dee6d36d8e239a1e0690a650b48e7","attachment_count":7,"text_attachments":7,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"plugins/saas-packs/sentry-pack/skills/sentry-policy-guardrails/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"security","import_tag":"clean-skills-v1","description":"Enforce organizational governance and policy guardrails for Sentry usage.\nUse when standardizing Sentry configuration across services, enforcing\nPII scrubbing, building shared config packages, or auditing drift.\nTrigger with phrases like \"sentry governance\", \"sentry policy\",\n\"sentry standards\", \"enforce sentry config\", \"sentry compliance\".\n","allowed-tools":"Read, Write, Edit, Grep, Glob, Bash(node:*), Bash(npm:*), Bash(npx:*), Bash(curl:*), Bash(grep:*), Bash(git:*)","compatibility":"Designed for Claude Code, also compatible with Codex and OpenClaw"}},"renderedAt":1782981674620}

Sentry Policy Guardrails Overview Organizational governance framework that prevents Sentry configuration drift across multiple services. A shared npm package ( ) wraps to enforce PII scrubbing, naming conventions, tagging standards, and per-tier trace rate caps. CI checks block policy violations before merge, and a monthly drift audit detects projects that have fallen out of compliance. Prerequisites - v8+ installed in target services - Internal npm registry available (GitHub Packages, Artifactory, or similar) - Team structure and project ownership defined in Sentry - with and scopes - Compli…