Skip to main content 首页 创作者 jeremylongshore claude-code-plugins-plus-skills sentry-policy-guardrails
sentry-policy-guardrails Enforce organizational governance and policy guardrails for Sentry usage.
Use when standardizing Sentry configuration across services, enforcing
PII scrubbing, building shared config packages, or auditing drift.
Trigger with phrases like "sentry governance", "sentry policy",
"sentry standards", "enforce sentry config", "sentry compliance".
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill sentry-policy-guardrails命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name sentry-policy-guardrails description Enforce organizational governance and policy guardrails for Sentry usage.
Use when standardizing Sentry configuration across services, enforcing
PII scrubbing, building shared config packages, or auditing drift.
Trigger with phrases like "sentry governance", "sentry policy",
"sentry standards", "enforce sentry config", "sentry compliance".
allowed-tools Read, Write, Edit, Grep, Glob, Bash(node:*), Bash(npm:*), Bash(npx:*), Bash(curl:*), Bash(grep:*), Bash(git:*) version 1.51.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","sentry","compliance","governance","policy","ci","cost-governance"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
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):
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 , : },
];
( ): | {
(event. ?. ) {
( h ) event. . [h];
}
(event. ) event. = (event. );
(event. ?. ) {
( exc event. . ) {
(exc. ) exc. = (exc. );
}
}
event;
}
( ): {
( { pattern, replacement } ) {
str = str. ( (pattern. , pattern. ), replacement);
}
str;
}
replacement
'[SSN_REDACTED]'
export
function
scrubEvent
event : Event
Event
null
if
request
headers
for
const
of
SENSITIVE_HEADERS
delete
request
headers
if
message
message
scrubPII
message
if
exception
values
for
const
of
exception
values
if
value
value
scrubPII
value
return
function
scrubPII
str : string
string
for
const
of
PII_PATTERNS
replace
new
RegExp
source
flags
return
Governed init with naming validation, tag injection, and tier-based caps:
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 ;
dsn : string ;
version : string ;
tags : { service : string ; team : string ; tier : 'critical' | 'standard' | 'internal' };
environment ?: string ;
tracesSampleRate ?: number ;
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);
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.
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:
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
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):
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} ` };
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 ;
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)' ];
}
let overBudget = false ;
for (const q of QUOTAS ) {
const u = usage.get (q.team ) || { errors : 0 , txns : 0 };
const ePct = ((u.errors / q.errors ) * 100 ).toFixed (0 );
const tPct = ((u.txns / q.transactions ) * 100 ).toFixed (0 );
const flag = u.errors > q.errors || u.txns > q.transactions ? 'OVER' : 'OK' ;
console .log (`[${q.team} ] errors=${u.errors} /${q.errors} (${ePct} %) txns=${u.txns} /${q.transactions} (${tPct} %) [${flag} ]` );
if (flag === 'OVER' ) overBudget = true ;
}
if (overBudget) { console .error ('ACTION REQUIRED: budget exceeded' ); process.exit (1 ); }
}
main ();
name: Sentry Monthly Audit
on:
schedule: [{ cron: '0 9 1 * *' }]
workflow_dispatch:
jobs:
drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: bash scripts/audit-sentry-drift.sh
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
cost:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npx tsx scripts/check-team-quotas.ts
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
Output
@company/sentry-config shared package with enforced PII scrubbing, naming validation, and tier-based trace rate caps
Mandatory beforeSend chain: headers, credit cards, emails, and SSNs scrubbed before any custom logic
Naming standards enforced at init: release format, validated environments, kebab-case service names
Required tags injected: service, team, tier, deployment, region
CI workflow blocking hardcoded DSNs, PII collection, and direct Sentry.init() calls
Monthly drift audit checking data scrubber, IP scrubbing, sensitive fields, and naming compliance
Per-team cost quota enforcement with weekly budget checks
Error Handling Error Cause Solution Invalid service name at initName not kebab-case or wrong length Rename to lowercase kebab-case, 3-40 chars, letter start Invalid environment at initNon-standard environment string Use: production, staging, development, canary, or sandbox CI blocks Sentry.init() Direct call outside shared config Replace with initSentry() from @company/sentry-config CI blocks hardcoded DSN DSN string in source code Move to SENTRY_DSN environment variable Drift audit [FAIL] on data scrubber Project settings changed in UI Re-enable in Project Settings then Security and Privacy Team over budget Noisy errors or high trace volume Add ignoreErrors filters or lower tracesSampleRate beforeSend scrubbing bypassedService using raw Sentry.init() Adopt @company/sentry-config which chains scrubbing first
Examples import { initSentry } from '@company/sentry-config' ;
initSentry ({
serviceName : 'user-service' ,
dsn : process.env .SENTRY_DSN !,
version : '1.0.0' ,
tags : { service : 'user-service' , team : 'platform' , tier : 'standard' },
});
Critical service with custom filtering:
import { initSentry } from '@company/sentry-config' ;
initSentry ({
serviceName : 'checkout-api' ,
dsn : process.env .SENTRY_DSN !,
version : '3.2.0' ,
tags : { service : 'checkout-api' , team : 'payments' , tier : 'critical' },
tracesSampleRate : 0.4 ,
beforeSend (event ) {
if (event.exception ?.values ?.some (e => e.value ?.includes ('PaymentTimeout' ))) {
return null ;
}
return event;
},
});
Resources
Next Steps
Publish @company/sentry-config to internal registry and onboard first two services
Add the CI policy workflow to all service repositories via shared workflow or template
Schedule the drift audit as a monthly cron and cost check as weekly
Communicate per-team quotas to engineering leads with Slack budget alerts