Speak Security Basics

VerifiedSafe

Apply Speak security best practices for secrets, user data, and audio handling in language learning applications.

Sby Skills Guide Bot
SecurityIntermediate
7/26/2026
Claude CodeCursorWindsurfCopilotCodex
#speak#api-keys#audio-privacy#secrets-management#least-privilege

Recommended for

Automated summary

This skill gathers security best practices for Speak API keys, user data, and audio recordings in language learning applications.

Strengths

  • Covers secret management through environment variables and .gitignore rules
  • Provides a concrete API key rotation procedure verified with a health check
  • Includes a least-privilege scope matrix per environment (dev, staging, production)
  • Ships TypeScript code for AES-256-GCM encryption and user audio deletion with audit logging

Limitations

  • The TypeScript implementation is incomplete (the file cuts off mid-encrypt function)
  • Some references such as the Speak dashboard URL or scope names should be confirmed with the provider
  • Relies on external storage and audit services that the skill does not define
When to use it

Use it when setting up or auditing the security of a Speak integration, especially API key handling and audio recording privacy.

When not to use it

Avoid it for non-Speak security questions or detailed regulatory compliance requirements (GDPR, HIPAA) that fall outside its scope.

Security analysis

Safe
Quality score82/100

The skill is purely instructional security guidance. Its declared tools are limited to Read/Write/Grep and the content contains no destructive commands, no obfuscated payloads, and no exfiltration of secrets. The only network reference is a benign `curl` health-check example using a locally-configured env var, which is a standard rotation-verification pattern. Code samples are defensive (encryption, timing-safe webhook verification, GDPR data handling) rather than harmful. No meaningful execution risk.

No concerns found

How this analysis is made

Examples

Audit de sécurité Speak
Audite ma configuration Speak : mes clés API sont-elles correctement stockées et quelles scopes devrais-je utiliser en production ?
Rotation de clé API
Aide-moi à faire tourner ma clé API Speak en production sans casser le service.
Confidentialité audio
Implémente une gestion des enregistrements audio utilisateur chez Speak avec chiffrement et suppression sur demande.

name: speak-security-basics description: | Apply Speak security best practices for secrets, user data, and audio handling. Use when securing API keys, implementing user privacy controls, or auditing Speak security configuration. Trigger with phrases like "speak security", "speak secrets", "secure speak", "speak API key security", "speak privacy". allowed-tools: Read, Write, Grep version: 1.0.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io

Speak Security Basics

Overview

Security best practices for Speak API keys, user data, and audio content in language learning applications.

Prerequisites

  • Speak SDK installed
  • Understanding of environment variables
  • Access to Speak dashboard
  • Knowledge of audio privacy concerns

Instructions

Step 1: Configure Environment Variables

# .env (NEVER commit to git)
SPEAK_API_KEY=sk_live_***
SPEAK_APP_ID=app_***
SPEAK_WEBHOOK_SECRET=whsec_***

# .gitignore
.env
.env.local
.env.*.local
*.wav  # User audio recordings
*.mp3
recordings/

Step 2: Implement Secret Rotation

# 1. Generate new key in Speak developer dashboard
# https://developer.speak.com/dashboard/api-keys

# 2. Update environment variable
export SPEAK_API_KEY="new_key_here"

# 3. Verify new key works
curl -X POST https://api.speak.com/v1/health \
  -H "Authorization: Bearer ${SPEAK_API_KEY}" \
  -H "X-App-ID: ${SPEAK_APP_ID}"

# 4. Revoke old key in dashboard

Step 3: Apply Least Privilege Access

| Environment | Recommended Scopes | |-------------|-------------------| | Development | lessons:read, speech:analyze | | Staging | lessons:read, lessons:write, speech:analyze | | Production | Only required scopes for features |

// Use scoped API keys per feature
const lessonClient = new SpeakClient({
  apiKey: process.env.SPEAK_LESSON_API_KEY!, // lessons:* only
  appId: process.env.SPEAK_APP_ID!,
});

const speechClient = new SpeakClient({
  apiKey: process.env.SPEAK_SPEECH_API_KEY!, // speech:* only
  appId: process.env.SPEAK_APP_ID!,
});

Step 4: User Audio Privacy

// Audio data handling policies
interface AudioPrivacyConfig {
  retention: 'session_only' | '30_days' | 'permanent';
  shareWithSpeak: boolean; // For model improvement
  allowPlayback: boolean; // Let users hear their recordings
  encryption: 'at_rest' | 'in_transit' | 'both';
}

class SecureAudioHandler {
  private config: AudioPrivacyConfig;

  constructor(config: AudioPrivacyConfig) {
    this.config = config;
  }

  async storeAudio(userId: string, audioData: ArrayBuffer): Promise<string> {
    // Encrypt before storage
    const encrypted = await this.encrypt(audioData);

    // Generate non-guessable ID
    const audioId = crypto.randomUUID();

    // Store with expiration
    const expiry = this.config.retention === 'session_only'
      ? Date.now() + 60 * 60 * 1000 // 1 hour
      : this.config.retention === '30_days'
        ? Date.now() + 30 * 24 * 60 * 60 * 1000
        : null;

    await storage.put(audioId, encrypted, { expiry });

    // Audit log
    await auditLog({
      action: 'audio_stored',
      userId,
      audioId,
      retention: this.config.retention,
    });

    return audioId;
  }

  async deleteUserAudio(userId: string): Promise<void> {
    const audioIds = await storage.listByUser(userId);

    for (const id of audioIds) {
      await storage.delete(id);
    }

    await auditLog({
      action: 'audio_deleted',
      userId,
      count: audioIds.length,
    });
  }

  private async encrypt(data: ArrayBuffer): Promise<ArrayBuffer> {
    // Use AES-256-GCM encryption
    const key = await crypto.subtle.importKey(
      'raw',
      Buffer.from(process.env.AUDIO_ENCRYPTION_KEY!, 'base64'),
      'AES-GCM',
      false,
      ['encrypt']
    );

    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encrypted = await crypto.subtle.encrypt(
      { name: 'AES-GCM', iv },
      key,
      data
    );

    // Prepend IV to encrypted data
    const result = new Uint8Array(iv.length + encrypted.byteLength);
    result.set(iv);
    result.set(new Uint8Array(encrypted), iv.length);

    return result.buffer;
  }
}

Step 5: Webhook Signature Verification

import crypto from 'crypto';

function verifySpeakWebhook(
  payload: string,
  signature: string,
  timestamp: string
): boolean {
  const secret = process.env.SPEAK_WEBHOOK_SECRET!;

  // Reject old timestamps (replay attack protection)
  const timestampAge = Date.now() - parseInt(timestamp) * 1000;
  if (timestampAge > 300000) { // 5 minutes
    console.error('Webhook timestamp too old');
    return false;
  }

  // Compute expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expectedSignature}`)
  );
}

// Express middleware
function speakWebhookMiddleware(req: Request, res: Response, next: NextFunction) {
  const signature = req.headers['x-speak-signature'] as string;
  const timestamp = req.headers['x-speak-timestamp'] as string;

  if (!verifySpeakWebhook(req.body.toString(), signature, timestamp)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  next();
}

Step 6: User Data Protection

// GDPR/CCPA compliance for language learning data
interface UserLearningData {
  lessonHistory: LessonRecord[];
  pronunciationScores: PronunciationRecord[];
  vocabularyProgress: VocabularyProgress[];
  audioRecordings: AudioReference[];
}

class UserDataManager {
  async exportUserData(userId: string): Promise<UserLearningData> {
    // Gather all user data for GDPR export
    const [lessons, scores, vocab, audio] = await Promise.all([
      db.lessons.findByUser(userId),
      db.pronunciation.findByUser(userId),
      db.vocabulary.findByUser(userId),
      storage.listAudioByUser(userId),
    ]);

    await auditLog({
      action: 'user_data_export',
      userId,
      timestamp: new Date(),
    });

    return {
      lessonHistory: lessons,
      pronunciationScores: scores,
      vocabularyProgress: vocab,
      audioRecordings: audio,
    };
  }

  async deleteUserData(userId: string): Promise<DeletionResult> {
    // Complete data deletion for GDPR
    const results = await Promise.allSettled([
      db.lessons.deleteByUser(userId),
      db.pronunciation.deleteByUser(userId),
      db.vocabulary.deleteByUser(userId),
      this.deleteUserAudio(userId),
      speakClient.users.delete(userId), // Delete from Speak's servers
    ]);

    await auditLog({
      action: 'user_data_deleted',
      userId,
      timestamp: new Date(),
      results: results.map(r => r.status),
    });

    return {
      success: results.every(r => r.status === 'fulfilled'),
      timestamp: new Date(),
    };
  }
}

Security Checklist

API Security

  • [ ] API keys in environment variables
  • [ ] .env files in .gitignore
  • [ ] Different keys for dev/staging/prod
  • [ ] Minimal scopes per environment
  • [ ] Webhook signatures validated
  • [ ] Key rotation schedule documented

Audio Security

  • [ ] Audio encrypted at rest
  • [ ] Audio encrypted in transit (HTTPS)
  • [ ] Clear retention policy
  • [ ] User consent obtained
  • [ ] Deletion mechanism implemented

User Data

  • [ ] GDPR export implemented
  • [ ] CCPA compliance verified
  • [ ] Audit logging enabled
  • [ ] Data minimization practiced
  • [ ] Privacy policy updated

Output

  • Secure API key storage
  • Environment-specific access controls
  • Audio privacy protection
  • Webhook security enabled
  • User data protection compliance

Error Handling

| Security Issue | Detection | Mitigation | |----------------|-----------|------------| | Exposed API key | Git scanning, audit logs | Rotate immediately | | Excessive scopes | Audit logs review | Reduce permissions | | Missing rotation | Key age check | Schedule rotation | | Audio leak | Access logs | Encrypt and restrict | | Missing consent | Compliance audit | Update consent flow |

Examples

Service Account Pattern

const clients = {
  lessons: new SpeakClient({
    apiKey: process.env.SPEAK_LESSON_KEY,
    appId: process.env.SPEAK_APP_ID,
  }),
  speech: new SpeakClient({
    apiKey: process.env.SPEAK_SPEECH_KEY,
    appId: process.env.SPEAK_APP_ID,
  }),
};

Audit Logging

interface AuditEntry {
  timestamp: Date;
  action: string;
  userId: string;
  resource: string;
  result: 'success' | 'failure';
  metadata?: Record<string, any>;
}

async function auditLog(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {
  const log: AuditEntry = { ...entry, timestamp: new Date() };

  // Store in audit database
  await auditDb.insert(log);

  // Also log for compliance
  console.log('[AUDIT]', JSON.stringify(log));
}

Resources

Next Steps

For production deployment, see speak-prod-checklist.

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/speak-security-basics-ms1aw5nc' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/speak-security-basics-ms1aw5nc/download' --output '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md.part' && mv '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md.part' '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Tools declared by the author (check availability in your agent): Read, Write, Grep

Original excerpt: Prerequisites
  • Speak SDK installed
  • Understanding of environment variables
  • Access to Speak dashboard
  • Knowledge of audio privacy concerns

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && test -s '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/speak-security-basics' && cp -R '.skill-downloads/speak-security-basics-ms1aw5nc' '.claude/skills/speak-security-basics'

Cursor
test -f '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && test -s '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/speak-security-basics' && cp -R '.skill-downloads/speak-security-basics-ms1aw5nc' '.cursor/skills/speak-security-basics'

Codex
test -f '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && test -s '.skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md' && mkdir -p '.agents/skills' && test ! -e '.agents/skills/speak-security-basics' && cp -R '.skill-downloads/speak-security-basics-ms1aw5nc' '.agents/skills/speak-security-basics'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/speak-security-basics-ms1aw5nc'

openskills read 'speak-security-basics-ms1aw5nc'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/speak-security-basics-ms1aw5nc/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills