Synthèse automatique
Ce skill fournit les bonnes pratiques de sécurité pour intégrer TwinMind : stockage des clés API, configuration des variables d'environnement, vérification des signatures de webhooks et chiffrement des données sensibles au repos.
Points forts
- Montre explicitement les anti-patterns (clé API codée en dur) et la bonne approche par variables d'environnement ou gestionnaire de secrets
- Inclut une vérification de signature HMAC-SHA256 avec comparaison à temps constant pour éviter les attaques temporelles
- Fournit un exemple complet de chiffrement AES-256-GCM avec IV et tag d'authentification
- Couvre aussi l'hygiène du dépôt : .env.example versionné et règles .gitignore pour les secrets
Limites
- L'exemple de déchiffrement est tronqué dans le SKILL.md et doit être complété par le développeur
- Se limite à des extraits TypeScript/Node.js, sans équivalent Python ou Go
- N'aborde ni la rotation des clés, ni les rôles IAM, ni les exigences de conformité (RGPD, SOC2) au-delà du chiffrement
À utiliser lorsqu'on met en place ou audite l'intégration TwinMind et qu'il faut sécuriser les clés, les webhooks et les enregistrements de réunion stockés.
À ne pas utiliser comme unique référence pour un programme de conformité complet ou pour des intégrations qui ne sont pas basées sur TwinMind.
Analyse de sécurité
SûrThe skill is purely defensive guidance: it recommends environment variables and secrets managers over hardcoded keys, shows HMAC webhook verification with timing-safe comparison, AES-256-GCM encryption at rest, and permission checks. There are no destructive commands, no data exfiltration, and no attempts to disable safety controls. Risk is limited to writing ordinary TypeScript files via Write/Edit.
- •No shell execution, network calls, or obfuscated payloads; tools limited to Read/Write/Edit
- •Contains an example placeholder API key ('tm_sk_abc123') used explicitly as a negative example, not a live credential
- •Encryption example stores the AES key in an environment variable rather than a KMS; acceptable but worth noting as a hardening opportunity
- •Timing-safe compare of hex HMACs can throw on length mismatch — a robustness bug, not a security hazard to the catalog
Exemples
My TwinMind API key is hardcoded in our TypeScript client. Refactor it to load from environment variables with a clear error when missing, and add a .env.example plus .gitignore entries.Add Express middleware that verifies the x-twinmind-signature header on our TwinMind webhooks using HMAC-SHA256 and a timing-safe comparison.Write AES-256-GCM encrypt and decrypt helpers in TypeScript so we can store TwinMind meeting transcripts encrypted at rest.name: twinmind-security-basics description: | Implement security best practices for TwinMind integrations. Use when securing API keys, configuring privacy settings, or implementing data protection for meeting recordings. Trigger with phrases like "twinmind security", "secure twinmind", "twinmind privacy", "protect twinmind data", "twinmind api key security". allowed-tools: Read, Write, Edit version: 1.0.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io
TwinMind Security Basics
Overview
Essential security practices for TwinMind integrations, focusing on API security, data privacy, and compliance.
Prerequisites
- TwinMind account configured
- Understanding of environment variables
- Basic security concepts
Instructions
Step 1: Secure API Key Management
// NEVER do this - hardcoded secrets
const TWINMIND_API_KEY = "tm_sk_abc123"; // BAD!
// DO this - use environment variables
const apiKey = process.env.TWINMIND_API_KEY;
if (!apiKey) {
throw new Error('TWINMIND_API_KEY environment variable required');
}
// Even better - use a secrets manager
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
async function getApiKey(): Promise<string> {
const client = new SecretManagerServiceClient();
const [version] = await client.accessSecretVersion({
name: 'projects/my-project/secrets/twinmind-api-key/versions/latest',
});
return version.payload?.data?.toString() || '';
}
Step 2: Environment Configuration
# .env file (gitignored)
TWINMIND_API_KEY=tm_sk_your_secret_key
TWINMIND_WEBHOOK_SECRET=whsec_your_webhook_secret
TWINMIND_ENCRYPTION_KEY=your_32_byte_encryption_key
# .env.example (committed to repo - no real values)
TWINMIND_API_KEY=tm_sk_xxx
TWINMIND_WEBHOOK_SECRET=whsec_xxx
TWINMIND_ENCRYPTION_KEY=xxx
# .gitignore
.env
.env.local
.env.*.local
*.pem
*.key
secrets/
Step 3: Validate Webhook Signatures
// src/twinmind/webhooks/verify.ts
import crypto from 'crypto';
export function verifyWebhookSignature(
payload: string | Buffer,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const providedSignature = signature.replace('sha256=', '');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(providedSignature)
);
}
// Express middleware
export function webhookMiddleware(req: Request, res: Response, next: NextFunction) {
const signature = req.headers['x-twinmind-signature'] as string;
const secret = process.env.TWINMIND_WEBHOOK_SECRET!;
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
const rawBody = (req as any).rawBody || JSON.stringify(req.body);
if (!verifyWebhookSignature(rawBody, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
next();
}
Step 4: Encrypt Sensitive Data at Rest
// src/twinmind/encryption.ts
import crypto from 'crypto';
const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 16;
const TAG_LENGTH = 16;
export function encrypt(text: string, key: string): string {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(
ALGORITHM,
Buffer.from(key, 'hex'),
iv
);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag();
// Return iv:tag:encrypted
return `${iv.toString('hex')}:${tag.toString('hex')}:${encrypted}`;
}
export function decrypt(encryptedData: string, key: string): string {
const [ivHex, tagHex, encrypted] = encryptedData.split(':');
const iv = Buffer.from(ivHex, 'hex');
const tag = Buffer.from(tagHex, 'hex');
const decipher = crypto.createDecipheriv(
ALGORITHM,
Buffer.from(key, 'hex'),
iv
);
decipher.setAuthTag(tag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Usage for storing transcripts
export function storeTranscript(transcript: string): string {
const key = process.env.TWINMIND_ENCRYPTION_KEY!;
return encrypt(transcript, key);
}
export function retrieveTranscript(encryptedTranscript: string): string {
const key = process.env.TWINMIND_ENCRYPTION_KEY!;
return decrypt(encryptedTranscript, key);
}
Step 5: Implement Access Control
// src/twinmind/auth/permissions.ts
export enum Permission {
READ_TRANSCRIPTS = 'transcripts:read',
WRITE_TRANSCRIPTS = 'transcripts:write',
DELETE_TRANSCRIPTS = 'transcripts:delete',
MANAGE_SETTINGS = 'settings:manage',
VIEW_ANALYTICS = 'analytics:view',
ADMIN = 'admin:*',
}
export interface User {
id: string;
email: string;
permissions: Permission[];
organizationId: string;
}
export function hasPermission(user: User, permission: Permission): boolean {
if (user.permissions.includes(Permission.ADMIN)) {
return true;
}
return user.permissions.includes(permission);
}
export function requirePermission(permission: Permission) {
return (req: Request, res: Response, next: NextFunction) => {
const user = req.user as User;
if (!user) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!hasPermission(user, permission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage in routes
app.get(
'/api/transcripts',
requirePermission(Permission.READ_TRANSCRIPTS),
getTranscripts
);
app.delete(
'/api/transcripts/:id',
requirePermission(Permission.DELETE_TRANSCRIPTS),
deleteTranscript
);
Step 6: Configure Privacy Settings
// src/twinmind/privacy.ts
export interface PrivacyConfig {
// Audio handling
storeAudio: boolean;
audioRetentionDays: number;
// Transcript handling
storeTranscripts: boolean;
transcriptRetentionDays: number;
encryptTranscripts: boolean;
// Processing
processLocally: boolean; // Use on-device processing when possible
// Sharing
allowSharing: boolean;
requireConsent: boolean;
// PII handling
redactPII: boolean;
piiPatterns: string[];
}
const defaultPrivacyConfig: PrivacyConfig = {
storeAudio: false, // TwinMind default - never store audio
audioRetentionDays: 0,
storeTranscripts: true,
transcriptRetentionDays: 90,
encryptTranscripts: true,
processLocally: true,
allowSharing: false,
requireConsent: true,
redactPII: true,
piiPatterns: [
'\\b\\d{3}-\\d{2}-\\d{4}\\b', // SSN
'\\b\\d{16}\\b', // Credit card
'\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b', // Email
],
};
export function redactPII(text: string, patterns: string[]): string {
let redacted = text;
for (const pattern of patterns) {
const regex = new RegExp(pattern, 'gi');
redacted = redacted.replace(regex, '[REDACTED]');
}
return redacted;
}
Step 7: Audit Logging
// src/twinmind/audit.ts
export interface AuditEvent {
timestamp: Date;
userId: string;
action: string;
resource: string;
resourceId?: string;
details?: Record<string, any>;
ipAddress?: string;
userAgent?: string;
}
export class AuditLogger {
private events: AuditEvent[] = [];
log(event: Omit<AuditEvent, 'timestamp'>): void {
const fullEvent: AuditEvent = {
...event,
timestamp: new Date(),
};
this.events.push(fullEvent);
this.persist(fullEvent);
// Log to console in development
if (process.env.NODE_ENV === 'development') {
console.log('[AUDIT]', JSON.stringify(fullEvent));
}
}
private async persist(event: AuditEvent): Promise<void> {
// Send to logging service (e.g., CloudWatch, Datadog)
// await logService.write(event);
}
async query(filters: Partial<AuditEvent>): Promise<AuditEvent[]> {
return this.events.filter(event => {
return Object.entries(filters).every(([key, value]) =>
event[key as keyof AuditEvent] === value
);
});
}
}
export const auditLogger = new AuditLogger();
// Usage
auditLogger.log({
userId: 'user_123',
action: 'TRANSCRIPT_ACCESSED',
resource: 'transcript',
resourceId: 'tr_abc123',
details: { method: 'GET', endpoint: '/api/transcripts/tr_abc123' },
});
Output
- Secure API key storage
- Webhook signature verification
- Data encryption at rest
- Access control implementation
- Privacy configuration
- Audit logging
Security Checklist
## Pre-Deployment Security Checklist
### API Security
- [ ] API keys stored in environment variables or secrets manager
- [ ] No hardcoded credentials in source code
- [ ] API key rotation process documented
- [ ] Rate limiting implemented
### Data Protection
- [ ] Transcripts encrypted at rest
- [ ] PII redaction enabled
- [ ] Data retention policies configured
- [ ] Backup encryption verified
### Authentication
- [ ] Webhook signatures verified
- [ ] JWT tokens validated properly
- [ ] Session timeout configured
- [ ] MFA enabled for admin accounts
### Access Control
- [ ] RBAC implemented
- [ ] Least privilege principle applied
- [ ] Regular permission audits scheduled
### Monitoring
- [ ] Audit logging enabled
- [ ] Failed auth attempts monitored
- [ ] Anomaly detection configured
- [ ] Incident response plan documented
Error Handling
| Scenario | Risk | Mitigation | |----------|------|------------| | API key exposed | Full account access | Rotate key immediately, audit logs | | Webhook unverified | Spoofed events | Always verify signatures | | PII leaked | Compliance violation | Enable redaction, encrypt data | | Unauthorized access | Data breach | Implement RBAC, audit logging |
TwinMind Privacy Features
TwinMind provides built-in privacy protection:
- No audio storage: Audio is processed in real-time and immediately deleted
- On-device processing: Transcription can run locally on device
- Encrypted backups: Optional cloud backups are encrypted
- Data residency: Choose data storage region (Enterprise)
- GDPR compliance: Data export and deletion support
Resources
Next Steps
For production deployment, see twinmind-prod-checklist.
Du fichier au premier essai
Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.
Consulter la source et ses fichiers annexesCe téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.
1. Télécharger le fichier
Télécharger SKILL.mdDepuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.
Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.
mkdir -p '.skill-downloads/twinmind-security-basics-mm5t8c8z' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/twinmind-security-basics-mm5t8c8z/download' --output '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md.part' && mv '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md.part' '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md'2. Vérifier les prérequis
Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.
Outils déclarés par l’auteur (à vérifier dans votre agent) : Read, Write, Edit
Extrait original : Prerequisites
- TwinMind account configured
- Understanding of environment variables
- Basic security concepts
Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.
3. Charger la skill
Après vérification, copiez le dossier complet dans le projet avec l’une des commandes ci-dessous, puis ouvrez une nouvelle session de l’outil. La commande s’arrête si un dossier du même nom existe déjà : comparez les fichiers avant de le remplacer.
Claude Code
test -f '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md' && test -s '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/twinmind-security-basics' && cp -R '.skill-downloads/twinmind-security-basics-mm5t8c8z' '.claude/skills/twinmind-security-basics'Cursor
test -f '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md' && test -s '.skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/twinmind-security-basics' && cp -R '.skill-downloads/twinmind-security-basics-mm5t8c8z' '.cursor/skills/twinmind-security-basics'OpenSkills
OpenSkills doit déjà être installé. La procédure locale install/read est contrôlée avec OpenSkills 1.5.0, qui exige Node.js ≥ 20.6.0. Aucun slug du catalogue n’est résolu automatiquement.
Installation officielle d’OpenSkillsopenskills install './.skill-downloads/twinmind-security-basics-mm5t8c8z'openskills read 'twinmind-security-basics-mm5t8c8z'La lecture doit afficher le contenu du fichier installé et son dossier de base. Elle ne lance ni la skill ni ses dépendances.
Pour un agent qui lit AGENTS.md, synchronisez ensuite l’index. Cette commande modifie AGENTS.md : relisez le diff avant de l’utiliser.
openskills syncMode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.
4. Faire un premier essai
Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :
Lis .skill-downloads/twinmind-security-basics-mm5t8c8z/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.
Sécurité AMM DeFi
Securite
Checklist de sécurité pour les contrats Solidity AMM, pools de liquidité et flux de swap. Couvre réentrance, attaques par donation, manipulation d'oracles, etc.
Créateur de règles Semgrep
Securite
Crée des règles Semgrep personnalisées pour détecter des vulnérabilités de sécurité, des motifs de bugs et des motifs de code. Inclut une méthodologie de test en premier, le support du mode taint et des étapes de validation. Utile pour construire des détections d'analyse statique ciblées.
Revue barre d'outils développement
Securite
Règles de sécurité pour la barre d'outils de développement, le client PostHog et les feature flags. À utiliser lors de la revue de modifications des composants dev-tools et connexes.