deepgram-data-handling
Deepgramの統合における音声データ処理のベストプラクティスを実装します。音声ファイルストレージの管理、データ保持ポリシーの実装、文字起こしデータのGDPR/HIPAA準拠を確保する際に利用できます。「deepgramデータ」「音声ストレージ」「文字起こしデータ」「deepgram GDPR」「deepgram HIPAA」「deepgramプライバシー」といったフレーズで起動します。
description の原文を見る
Implement audio data handling best practices for Deepgram integrations. Use when managing audio file storage, implementing data retention policies, or ensuring GDPR/HIPAA compliance for transcription data. Trigger with phrases like "deepgram data", "audio storage", "transcription data", "deepgram GDPR", "deepgram HIPAA", "deepgram privacy".
SKILL.md 本文
Deepgram データハンドリング
概要
ストレージ、保持、およびコンプライアンスを含む、Deepgramでのオーディオデータとトランスクリプションの処理のベストプラクティスです。
前提条件
- データ保護規制の理解
- クラウドストレージの設定
- 暗号化機能
- 定義されたデータ保持ポリシー
データライフサイクル
Upload → Process → Store → Retain → Archive → Delete
↓ ↓ ↓ ↓ ↓ ↓
Encrypt Transcribe Save Review Compress Secure
Delete
コンプライアンスに関する考慮事項
| 規制 | 主要な要件 |
|---|---|
| GDPR | データ最小化、削除権、同意 |
| HIPAA | PHI保護、アクセス制御、監査ログ |
| SOC 2 | セキュリティコントロール、可用性、機密性 |
| PCI DSS | データ暗号化、アクセスログ |
手順
ステップ1: セキュアアップロードの実装
暗号化と検証を使用してオーディオアップロードを処理します。
ステップ2: データ処理の設定
プライバシーコントロールを使用してトランスクリプションを処理します。
ステップ3: ストレージの設定
適切な暗号化とアクセス制御を使用してデータを保存します。
ステップ4: 保持の実装
データ保持と削除ポリシーを自動化します。
例
セキュアアップロードハンドラー
// services/secure-upload.ts
import crypto from 'crypto';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { KMSClient, GenerateDataKeyCommand } from '@aws-sdk/client-kms';
interface UploadOptions {
userId: string;
purpose: string;
retentionDays: number;
encrypted: boolean;
}
export class SecureAudioUpload {
private s3: S3Client;
private kms: KMSClient;
private bucket: string;
private kmsKeyId: string;
constructor() {
this.s3 = new S3Client({});
this.kms = new KMSClient({});
this.bucket = process.env.AUDIO_BUCKET!;
this.kmsKeyId = process.env.KMS_KEY_ID!;
}
async upload(
audioBuffer: Buffer,
options: UploadOptions
): Promise<{ audioId: string; url: string }> {
const audioId = crypto.randomUUID();
// Validate audio
if (!this.isValidAudio(audioBuffer)) {
throw new Error('Invalid audio format');
}
// Generate encryption key
let encryptedData = audioBuffer;
let dataKey: string | undefined;
if (options.encrypted) {
const { encrypted, key } = await this.encryptData(audioBuffer);
encryptedData = encrypted;
dataKey = key;
}
// Calculate content hash
const hash = crypto.createHash('sha256').update(audioBuffer).digest('hex');
// Upload to S3
const key = `audio/${options.userId}/${audioId}`;
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + options.retentionDays);
await this.s3.send(new PutObjectCommand({
Bucket: this.bucket,
Key: key,
Body: encryptedData,
ContentType: 'audio/wav',
Metadata: {
'user-id': options.userId,
'purpose': options.purpose,
'content-hash': hash,
'encrypted': String(options.encrypted),
'data-key': dataKey || '',
'expiration-date': expirationDate.toISOString(),
},
ServerSideEncryption: 'aws:kms',
SSEKMSKeyId: this.kmsKeyId,
}));
return {
audioId,
url: `s3://${this.bucket}/${key}`,
};
}
private isValidAudio(buffer: Buffer): boolean {
// Check for common audio file headers
const headers = {
wav: Buffer.from([0x52, 0x49, 0x46, 0x46]), // RIFF
mp3: Buffer.from([0xFF, 0xFB]),
flac: Buffer.from([0x66, 0x4C, 0x61, 0x43]), // fLaC
};
return Object.values(headers).some(header =>
buffer.slice(0, header.length).equals(header)
);
}
private async encryptData(data: Buffer): Promise<{
encrypted: Buffer;
key: string;
}> {
// Generate data key using KMS
const { Plaintext, CiphertextBlob } = await this.kms.send(
new GenerateDataKeyCommand({
KeyId: this.kmsKeyId,
KeySpec: 'AES_256',
})
);
// Encrypt data with AES-256-GCM
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', Plaintext!, iv);
const encrypted = Buffer.concat([
iv,
cipher.update(data),
cipher.final(),
cipher.getAuthTag(),
]);
return {
encrypted,
key: CiphertextBlob!.toString('base64'),
};
}
}
PII マスキング
// services/pii-redaction.ts
interface RedactionRule {
name: string;
pattern: RegExp;
replacement: string;
}
const redactionRules: RedactionRule[] = [
{
name: 'ssn',
pattern: /\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/g,
replacement: '[SSN REDACTED]',
},
{
name: 'credit_card',
pattern: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
replacement: '[CARD REDACTED]',
},
{
name: 'phone',
pattern: /\b(\+1[-\s]?)?\(?\d{3}\)?[-\s]?\d{3}[-\s]?\d{4}\b/g,
replacement: '[PHONE REDACTED]',
},
{
name: 'email',
pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
replacement: '[EMAIL REDACTED]',
},
{
name: 'date_of_birth',
pattern: /\b(0?[1-9]|1[0-2])[\/\-](0?[1-9]|[12]\d|3[01])[\/\-](19|20)\d{2}\b/g,
replacement: '[DOB REDACTED]',
},
];
export function redactPII(transcript: string): {
redacted: string;
redactions: Array<{ type: string; count: number }>;
} {
let redacted = transcript;
const redactions: Array<{ type: string; count: number }> = [];
for (const rule of redactionRules) {
const matches = redacted.match(rule.pattern);
if (matches && matches.length > 0) {
redactions.push({ type: rule.name, count: matches.length });
redacted = redacted.replace(rule.pattern, rule.replacement);
}
}
return { redacted, redactions };
}
// Deepgram has built-in redaction feature
export async function transcribeWithRedaction(
client: DeepgramClient,
audioUrl: string
): Promise<{ transcript: string; redactedTranscript: string }> {
const { result, error } = await client.listen.prerecorded.transcribeUrl(
{ url: audioUrl },
{
model: 'nova-2',
redact: ['pci', 'ssn', 'numbers'], // Deepgram's built-in redaction
smart_format: true,
}
);
if (error) throw error;
const transcript = result.results.channels[0].alternatives[0].transcript;
const { redacted } = redactPII(transcript);
return { transcript, redactedTranscript: redacted };
}
データ保持ポリシー
// services/retention.ts
import { S3Client, ListObjectsV2Command, DeleteObjectsCommand } from '@aws-sdk/client-s3';
import { db } from './database';
import { logger } from './logger';
interface RetentionPolicy {
name: string;
retentionDays: number;
dataTypes: string[];
complianceReasons: string[];
}
const policies: RetentionPolicy[] = [
{
name: 'standard',
retentionDays: 30,
dataTypes: ['audio', 'transcript'],
complianceReasons: ['business'],
},
{
name: 'legal_hold',
retentionDays: 365 * 7, // 7 years
dataTypes: ['audio', 'transcript', 'metadata'],
complianceReasons: ['legal', 'regulatory'],
},
{
name: 'hipaa',
retentionDays: 365 * 6, // 6 years
dataTypes: ['audio', 'transcript', 'access_logs'],
complianceReasons: ['hipaa'],
},
];
export class RetentionManager {
private s3: S3Client;
private bucket: string;
constructor() {
this.s3 = new S3Client({});
this.bucket = process.env.AUDIO_BUCKET!;
}
async enforceRetention(): Promise<{
checked: number;
deleted: number;
retained: number;
}> {
const stats = { checked: 0, deleted: 0, retained: 0 };
const now = new Date();
// Get all audio files
const { Contents } = await this.s3.send(new ListObjectsV2Command({
Bucket: this.bucket,
Prefix: 'audio/',
}));
if (!Contents) return stats;
const toDelete: string[] = [];
for (const object of Contents) {
stats.checked++;
if (!object.Key) continue;
// Get metadata to determine policy
const metadata = await this.getMetadata(object.Key);
const policy = this.getApplicablePolicy(metadata);
const expirationDate = new Date(metadata.uploadDate);
expirationDate.setDate(expirationDate.getDate() + policy.retentionDays);
if (now > expirationDate && !metadata.legalHold) {
toDelete.push(object.Key);
stats.deleted++;
} else {
stats.retained++;
}
}
// Batch delete
if (toDelete.length > 0) {
await this.deleteObjects(toDelete);
}
logger.info('Retention enforcement completed', stats);
return stats;
}
private getApplicablePolicy(metadata: Record<string, string>): RetentionPolicy {
// Determine which policy applies
if (metadata.legalHold === 'true') {
return policies.find(p => p.name === 'legal_hold')!;
}
if (metadata.hipaa === 'true') {
return policies.find(p => p.name === 'hipaa')!;
}
return policies.find(p => p.name === 'standard')!;
}
private async deleteObjects(keys: string[]): Promise<void> {
const batches = this.chunk(keys, 1000);
for (const batch of batches) {
await this.s3.send(new DeleteObjectsCommand({
Bucket: this.bucket,
Delete: {
Objects: batch.map(Key => ({ Key })),
},
}));
// Also delete from database
await db.transcripts.deleteMany({
audioKey: { $in: batch },
});
}
}
private chunk<T>(arr: T[], size: number): T[][] {
return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
arr.slice(i * size, i * size + size)
);
}
private async getMetadata(key: string): Promise<Record<string, string>> {
// Implementation to get object metadata
return {};
}
}
GDPR 削除権
// services/gdpr.ts
import { db } from './database';
import { S3Client, DeleteObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
import { logger } from './logger';
interface DeletionRequest {
userId: string;
requestedAt: Date;
dataTypes: string[];
verificationToken: string;
}
export class GDPRCompliance {
private s3: S3Client;
constructor() {
this.s3 = new S3Client({});
}
async processRightToErasure(userId: string): Promise<{
success: boolean;
deletedItems: {
transcripts: number;
audioFiles: number;
metadata: number;
};
}> {
const deletedItems = {
transcripts: 0,
audioFiles: 0,
metadata: 0,
};
try {
// 1. Delete transcripts from database
const transcriptResult = await db.transcripts.deleteMany({
userId,
});
deletedItems.transcripts = transcriptResult.deletedCount;
// 2. Delete audio files from S3
const audioFiles = await this.listUserAudioFiles(userId);
for (const file of audioFiles) {
await this.s3.send(new DeleteObjectCommand({
Bucket: process.env.AUDIO_BUCKET!,
Key: file,
}));
deletedItems.audioFiles++;
}
// 3. Delete user metadata
const metadataResult = await db.userMetadata.deleteMany({
userId,
});
deletedItems.metadata = metadataResult.deletedCount;
// 4. Log deletion for audit
await this.logDeletion(userId, deletedItems);
logger.info('GDPR erasure completed', { userId, deletedItems });
return { success: true, deletedItems };
} catch (error) {
logger.error('GDPR erasure failed', {
userId,
error: error instanceof Error ? error.message : 'Unknown',
});
throw error;
}
}
async exportUserData(userId: string): Promise<Buffer> {
// Collect all user data
const userData = {
transcripts: await db.transcripts.find({ userId }).toArray(),
metadata: await db.userMetadata.findOne({ userId }),
usageHistory: await db.usage.find({ userId }).toArray(),
exportedAt: new Date().toISOString(),
};
// Return as JSON
return Buffer.from(JSON.stringify(userData, null, 2));
}
private async listUserAudioFiles(userId: string): Promise<string[]> {
const { Contents } = await this.s3.send(new ListObjectsV2Command({
Bucket: process.env.AUDIO_BUCKET!,
Prefix: `audio/${userId}/`,
}));
return Contents?.map(c => c.Key!).filter(Boolean) || [];
}
private async logDeletion(
userId: string,
deletedItems: Record<string, number>
): Promise<void> {
await db.auditLog.insertOne({
action: 'GDPR_ERASURE',
userId,
deletedItems,
timestamp: new Date(),
});
}
}
監査ログ
// services/audit-log.ts
interface AuditEvent {
timestamp: Date;
action: string;
userId: string;
resourceType: 'audio' | 'transcript' | 'user';
resourceId: string;
details: Record<string, unknown>;
ipAddress?: string;
userAgent?: string;
}
export class AuditLogger {
async log(event: Omit<AuditEvent, 'timestamp'>): Promise<void> {
const fullEvent: AuditEvent = {
...event,
timestamp: new Date(),
};
// Store in tamper-evident log
await db.auditLog.insertOne({
...fullEvent,
hash: this.computeHash(fullEvent),
});
// Also send to external SIEM if configured
if (process.env.SIEM_ENDPOINT) {
await this.sendToSIEM(fullEvent);
}
}
private computeHash(event: AuditEvent): string {
return crypto
.createHash('sha256')
.update(JSON.stringify(event))
.digest('hex');
}
private async sendToSIEM(event: AuditEvent): Promise<void> {
await fetch(process.env.SIEM_ENDPOINT!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event),
});
}
}
リソース
次のステップ
アクセス制御設定については、deepgram-enterprise-rbacに進みます。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- Brmbobo
- リポジトリ
- Brmbobo/Web2podcast
- ライセンス
- MIT
- 最終更新
- 2026/1/26
Source: https://github.com/Brmbobo/Web2podcast / ライセンス: MIT
関連スキル
secure-code-guardian
認証・認可の実装、ユーザー入力の保護、OWASP Top 10の脆弱性対策が必要な場合に使用します。bcrypt/argon2によるパスワードハッシング、パラメータ化ステートメントによるSQLインジェクション対策、CORS/CSPヘッダーの設定、Zodによる入力検証、JWTトークンの構築などのカスタムセキュリティ実装に対応します。認証、認可、入力検証、暗号化、OWASP Top 10対策、セッション管理、セキュリティ強化全般で活用できます。ただし、構築済みのOAuth/SSO統合や単独のセキュリティ監査が必要な場合は、より特化したスキルの検討をお勧めします。
claude-authenticity
APIエンドポイントが本物のClaudeによって支えられているか(ラッパーやプロキシ、偽装ではないか)を、claude-verifyプロジェクトを模した9つの重み付きルールベースチェックで検証できます。また、Claudeの正体を上書きしているプロバイダーから注入されたシステムプロンプトも抽出します。完全に自己完結しており、httpx以外の追加パッケージは不要です。Claude APIキーまたはエンドポイントを検証したい場合、サードパーティのClaudeサービスが本物か確認したい場合、APIプロバイダーのClaude正当性を監査したい場合、複数モデルを並行してテストしたい場合、またはプロバイダーが注入したシステムプロンプトを特定したい場合に使用できます。
anth-security-basics
Anthropic Claude APIのセキュリティベストプラクティスを適用し、キー管理、入力値の検証、プロンプトインジェクション対策を実施します。APIキーの保護、Claudeに送信する前のユーザー入力検証、コンテンツセーフティガードレールの実装が必要な場合に活用できます。「anthropic security」「claude api key security」「secure anthropic」「prompt injection defense」といったフレーズでトリガーされます。
x-ray
x-ray.mdプレ監査レポートを生成します。概要、強化された脅威モデル(プロトコルタイプのプロファイリング、Gitの重み付け攻撃面分析、時間軸リスク分析、コンポーザビリティ依存関係マッピング)、不変条件、統合、ドキュメント品質、テスト分析、開発者・Gitの履歴をカバーしています。「x-ray」「audit readiness」「readiness report」「pre-audit report」「prep this protocol」「protocol prep」「summarize this protocol」のキーワードで実行されます。
semgrep
Semgrepスタティック分析スキャンを実行し、カスタム検出ルールを作成します。Semgrepでのコードスキャン、セキュリティ脆弱性の検出、カスタムYAMLルールの作成、または特定のバグパターンの検出が必要な場合に使用します。重要:ユーザーが「バグをスキャンしたい」「コード品質を確認したい」「脆弱性を見つけたい」「スタティック分析」「セキュリティlint」「コード監査」または「コーディング標準を適用したい」と尋ねた場合も、Semgrepという名称を明記していなくても、このスキルを使用してください。Semgrepは30以上の言語に対応したパターンベースのコードスキャンに最適なツールです。
ghost-bits-cast-attack
Java「ゴーストビッツ」/キャストアタック プレイブック(Black Hat Asia 2026)。16ビット文字が8ビットバイトに暗黙的に縮小されるJavaサービスへの攻撃時に使用します。WAF/IDSを回避して、SQLインジェクション、デシリアライゼーション型RCE、ファイルアップロード(Webシェル)、パストトラバーサル、CRLF インジェクション、リクエストスマグリング、SMTPインジェクションを実行できます。Tomcat、Spring、Jetty、Undertow、Vert.x、Jackson、Fastjson、Apache Commons BCEL、Apache HttpClient、Angus Mail、JDK HttpServer、Lettuce、Jodd、XMLWriterに影響し、WAFバイパスにより多くの「パッチ済み」CVEを再度有効化します。