SE

sentry-policy-guardrails

Enforce Sentry configuration consistency across teams through shared policies and automated audits.

Install

mkdir -p .claude/skills/sentry-policy-guardrails && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8975" && unzip -o skill.zip -d .claude/skills/sentry-policy-guardrails && rm skill.zip

Installs to .claude/skills/sentry-policy-guardrails

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Enforce organizational governance and policy guardrails for Sentry usage.
73 charsno explicit “when” trigger
Advanced

Key capabilities

  • Enforce mandatory PII scrubbing via shared packages
  • Standardize Sentry initialization across services
  • Validate service naming conventions
  • Implement tier-based trace rate caps
  • Audit projects for configuration drift

How it works

It uses a shared npm package to wrap Sentry initialization, forcing all services to adopt uniform PII scrubbing, tagging, and sampling policies.

Inputs & outputs

You give it
Service-specific Sentry configuration options
You get back
Compliant Sentry initialization with enforced security defaults

When to use sentry-policy-guardrails

  • Enforce PII scrubbing policies
  • Standardize tagging across microservices
  • Audit projects for configuration drift
  • Implement centralized Sentry initialization

About this skill

Sentry Policy Guardrails

Overview

Organizational governance framework that prevents Sentry configuration drift across multiple services. A shared npm package (@company/sentry-config) wraps Sentry.init() 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

  • @sentry/node v8+ installed in target services
  • Internal npm registry available (GitHub Packages, Artifactory, or similar)
  • Team structure and project ownership defined in Sentry
  • SENTRY_AUTH_TOKEN with org:read and project:read scopes
  • Compliance requirements identified (SOC 2, GDPR, HIPAA)

Instructions

Step 1 — Build the Shared Configuration Package

Create @company/sentry-config that wraps Sentry.init() with non-negotiable defaults.

Mandatory PII scrubbing (cannot be bypassed):

// @company/sentry-config/src/scrubbers.ts
import type { Event } from '@sentry/node';

const SENSITIVE_HEADERS = [
  'authorization', 'cookie', 'set-cookie',
  'x-api-key', 'x-auth-token', 'x-csrf-token',
];

const PII_PATTERNS = [
  { pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{1,7}\b/g, replacement: '[CC_REDACTED]' },
  { pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, replacement: '[EMAIL_REDACTED]' },
  { pattern: /\b\d{3}-\d{2}-\d{4}\b/g, replacement: '[SSN_REDACTED]' },
];

export function scrubEvent(event: Event): Event | null {
  if (event.request?.headers) {
    for (const h of SENSITIVE_HEADERS) delete event.request.headers[h];
  }
  if (event.message) event.message = scrubPII(event.message);
  if (event.exception?.values) {
    for (const exc of event.exception.values) {
      if (exc.value) exc.value = scrubPII(exc.value);
    }
  }
  return event;
}

function scrubPII(str: string): string {
  for (const { pattern, replacement } of PII_PATTERNS) {
    str = str.replace(new RegExp(pattern.source, pattern.flags), replacement);
  }
  return str;
}

Governed init with naming validation, tag injection, and tier-based caps:

// @company/sentry-config/src/index.ts
import * as Sentry from '@sentry/node';
import { scrubEvent } from './scrubbers';

const ENFORCED: Partial<Sentry.NodeOptions> = {
  sendDefaultPii: false,
  debug: false,
  maxBreadcrumbs: 50,
  sampleRate: 1.0,
  maxValueLength: 500,
};

const VALID_ENVS = ['production', 'staging', 'development', 'canary', 'sandbox'];
const TIER_TRACE_CAPS: Record<string, number> = { critical: 0.5, standard: 0.2, internal: 0.05 };

interface PolicyOptions {
  serviceName: string;           // kebab-case, 3-40 chars
  dsn: string;
  version: string;
  tags: { service: string; team: string; tier: 'critical' | 'standard' | 'internal' };
  environment?: string;
  tracesSampleRate?: number;     // capped by tier
  beforeSend?: Sentry.NodeOptions['beforeSend'];
}

export function initSentry(opts: PolicyOptions): void {
  if (!opts.dsn) throw new Error('@company/sentry-config: dsn required');
  if (!/^[a-z][a-z0-9-]{2,39}$/.test(opts.serviceName)) {
    throw new Error(`Invalid service name "${opts.serviceName}" — use lowercase kebab-case, 3-40 chars`);
  }

  const env = (opts.environment || process.env.NODE_ENV || 'development').toLowerCase();
  if (!VALID_ENVS.includes(env)) {
    throw new Error(`Invalid environment "${env}". Allowed: ${VALID_ENVS.join(', ')}`);
  }

  const tierCap = TIER_TRACE_CAPS[opts.tags.tier] ?? 0.2;
  const sha = (process.env.GIT_SHA || process.env.COMMIT_SHA || '').substring(0, 7);
  const release = sha
    ? `${opts.serviceName}@${opts.version}+${sha}`
    : `${opts.serviceName}@${opts.version}`;

  Sentry.init({
    dsn: opts.dsn,
    environment: env,
    release,
    serverName: opts.serviceName,
    ...ENFORCED,
    debug: env !== 'production',
    tracesSampleRate: Math.min(opts.tracesSampleRate ?? 0.1, tierCap),

    beforeSend(event, hint) {
      const scrubbed = scrubEvent(event);  // Mandatory — always runs first
      if (!scrubbed) return null;
      return opts.beforeSend ? (opts.beforeSend as Function)(scrubbed, hint) : scrubbed;
    },

    initialScope: {
      tags: {
        service: opts.tags.service,
        team: opts.tags.team,
        tier: opts.tags.tier,
        deployment: process.env.DEPLOYMENT_ID || 'unknown',
        region: process.env.AWS_REGION || process.env.GCP_REGION || 'unknown',
      },
    },
  });
}

Service usage (two lines to adopt):

import { initSentry } from '@company/sentry-config';

initSentry({
  serviceName: 'payments-api',
  dsn: process.env.SENTRY_DSN!,
  version: '2.4.1',
  tags: { service: 'payments-api', team: 'payments', tier: 'critical' },
});

Step 2 — Enforce Compliance via CI

Add a GitHub Actions workflow to every service repository that blocks policy violations.

# .github/workflows/sentry-policy.yml
name: Sentry Policy Check
on: [pull_request]

jobs:
  sentry-compliance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Block hardcoded DSNs
        run: |
          if grep -rn "https://[a-f0-9]*@.*ingest.*sentry" \
            --include="*.ts" --include="*.js" \
            --exclude-dir=node_modules --exclude-dir=dist src/; then
            echo "::error::Hardcoded Sentry DSN — use SENTRY_DSN env var"
            exit 1
          fi

      - name: Block sendDefaultPii true
        run: |
          if grep -rn "sendDefaultPii.*true" \
            --include="*.ts" --include="*.js" \
            --exclude-dir=node_modules src/; then
            echo "::error::sendDefaultPii must be false"
            exit 1
          fi

      - name: Block direct Sentry.init calls
        run: |
          HITS=$(grep -rn "Sentry\.init(" --include="*.ts" --include="*.js" \
            --exclude-dir=node_modules --exclude="*sentry-config*" src/ || true)
          if [ -n "$HITS" ]; then
            echo "::error::Use initSentry() from @company/sentry-config"
            echo "$HITS"
            exit 1
          fi

      - name: Verify shared config dependency
        run: |
          if ! grep -q "@company/sentry-config" package.json; then
            echo "::warning::Not using shared Sentry config package"
          fi

Optional ESLint rule for IDE feedback:

// eslint-rules/no-direct-sentry-init.js
module.exports = {
  meta: { type: 'problem', messages: { bad: 'Use initSentry() from @company/sentry-config' } },
  create(ctx) {
    return {
      CallExpression(node) {
        if (node.callee.type === 'MemberExpression' &&
            node.callee.object.name === 'Sentry' &&
            node.callee.property.name === 'init' &&
            !ctx.getFilename().includes('sentry-config')) {
          ctx.report({ node, messageId: 'bad' });
        }
      },
    };
  },
};

Step 3 — Drift Detection and Cost Governance

Monthly drift audit across all Sentry projects:

#!/bin/bash
# scripts/audit-sentry-drift.sh
set -euo pipefail

ORG="${SENTRY_ORG:?required}" TOKEN="${SENTRY_AUTH_TOKEN:?required}"
API="https://sentry.io/api/0"
VIOLATIONS=0

echo "=== Sentry Drift Audit — $(date -u +%Y-%m-%d) ==="

PROJECTS=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$API/organizations/$ORG/projects/?all_projects=1" \
  | python3 -c "import json,sys; [print(p['slug']) for p in json.load(sys.stdin)]")

for P in $PROJECTS; do
  echo "--- $P ---"
  curl -s -H "Authorization: Bearer $TOKEN" "$API/projects/$ORG/$P/" \
    | python3 -c "
import json, sys, re
p = json.load(sys.stdin)
fails = 0
for label, val in [
    ('Data Scrubber', p.get('dataScrubber', False)),
    ('IP Scrubbing', p.get('scrubIPAddresses', False)),
    ('Naming', bool(re.match(r'^[a-z]+-[a-z0-9]+-[a-z]+$', p['slug']))),
]:
    status = 'PASS' if val else 'FAIL'
    print(f'  [{status}] {label}')
    if not val: fails += 1
missing = [f for f in ['password','ssn','credit_card','api_key','secret']
           if f not in p.get('sensitiveFields', [])]
if missing:
    print(f'  [FAIL] Sensitive Fields: missing {missing}')
    fails += 1
else:
    print(f'  [PASS] Sensitive Fields')
sys.exit(1 if fails else 0)
" || VIOLATIONS=$((VIOLATIONS + 1))
done

echo ""
echo "Projects with violations: $VIOLATIONS / $(echo "$PROJECTS" | wc -l)"
[ "$VIOLATIONS" -eq 0 ] && echo "STATUS: ALL COMPLIANT" || { echo "STATUS: DRIFT DETECTED"; exit 1; }

Per-team cost quotas (run weekly):

// scripts/check-team-quotas.ts
const QUOTAS = [
  { team: 'payments',       errors: 10_000, transactions: 50_000 },
  { team: 'platform',       errors: 20_000, transactions: 100_000 },
  { team: 'growth',         errors: 15_000, transactions: 200_000 },
  { team: 'infrastructure', errors: 8_000,  transactions: 20_000 },
];

async function main() {
  const org = process.env.SENTRY_ORG!, token = process.env.SENTRY_AUTH_TOKEN!;
  const headers = { Authorization: `Bearer ${token}` };

  // Fetch 30-day usage grouped by project and category
  const stats = await fetch(
    `https://sentry.io/api/0/organizations/${org}/stats_v2/?` +
    'field=sum(quantity)&groupBy=project&groupBy=category&interval=1d&statsPeriod=30d',
    { headers },
  ).then(r => r.json()) as any;

  // Map projects to teams
  const projects = await fetch(
    `https://sentry.io/api/0/organizations/${org}/projects/?all_projects=1`,
    { headers },
  ).then(r => r.json()) as any[];

  const teamOf = new Map(projects.map((p: any) => [p.slug, p.teams?.[0]?.slug || 'unknown']));
  const usage = new Map<string, { errors: number; txns: number }>();

  for (const g of stats.groups || []) {
    const team = teamOf.get(g.by.project) || 'unknown';
    if (!usage.has(team)) usage.set(team, { errors: 0, txns: 0 });
    const u = usage.get(team)!;
    if (g.by.category === 'error') u.errors += g.totals['sum(quantity)'];
    if (g.by.category === 'transaction') u.txns += g.totals['sum(quantity)'];

---

*Content truncated.*

When not to use it

  • When a single-service architecture makes shared packages unnecessary
  • When compliance requirements do not mandate centralized control

Prerequisites

@sentry/node v8+Internal npm registrySENTRY_AUTH_TOKEN with read scopes

Limitations

  • Requires an internal npm registry for package distribution
  • Services must be updated to use the shared init function

How it compares

This approach enforces governance through code-level defaults and CI checks, rather than relying on manual configuration in the Sentry UI.

Compared to similar skills

sentry-policy-guardrails side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
sentry-policy-guardrails (this skill)027dCautionAdvanced
dependency-auditor19moReviewBeginner
turborepo612moReviewIntermediate
flow-nexus-platform64moReviewBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

dependency-auditor

alirezarezvani

Check dependencies for known vulnerabilities using npm audit, pip-audit, etc. Use when package.json or requirements.txt changes, or before deployments. Alerts on vulnerable dependencies. Triggers on dependency file changes, deployment prep, security mentions.

16

turborepo

vercel

Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.

61191

flow-nexus-platform

ruvnet

Comprehensive Flow Nexus platform management - authentication, sandboxes, app deployment, payments, and challenges

691

nx-workspace-patterns

wshobson

Configure and optimize Nx monorepo workspaces. Use when setting up Nx, configuring project boundaries, optimizing build caching, or implementing affected commands.

589

perf-lighthouse

tech-leads-club

Run Lighthouse audits locally via CLI or Node API, parse and interpret reports, set performance budgets. Use when measuring site performance, understanding Lighthouse scores, setting up budgets, or integrating audits into CI. Triggers on: lighthouse, run lighthouse, lighthouse score, performance audit, performance budget.

1361

zod-4

prowler-cloud

Zod 4 schema validation patterns. Trigger: When creating or updating Zod v4 schemas for validation/parsing (forms, request payloads, adapters), including v3 -> v4 migration patterns.

1260

Search skills

Search the agent skills registry