deepgram-enterprise-rbac

v2026.09.24

Configure enterprise role-based access control for Deepgram integrations. Use when implementing team permissions, managing API key scopes, or setting up organization-level access controls. Trigger: "deepgram RBAC", "deepgram permissions", "deepgram access control", "deepgram team roles", "deepgram enterprise", "deepgram key scopes".

GitHub
安装命令
npx skhub add jeremylongshore/deepgram-enterprise-rbac
Markdown
SKILL.md

Deepgram Enterprise RBAC

Prerequisites

  • A verified identity source, named project/role owners, least-privilege role map, and access-review schedule.
  • Authority to provision/revoke users and service credentials through approved identity/admin paths.

Examples

Grant a transcription service account only the development project scope it needs, validate its permitted action with a synthetic fixture, and record the owner and expiration/review date. Confirm that the same identity cannot access production; on role mismatch, revoke the broad assignment and correct the group mapping before further use.

Overview

Role-based access control for enterprise Deepgram deployments. Maps five application roles to Deepgram API key scopes, implements scoped key provisioning via the Deepgram Management API, Express permission middleware, team management with auto-provisioned keys, and automated key rotation.

Deepgram Scope Reference

ScopePermissionUsed By
memberFull access (all scopes)Admin only
listenSTT transcriptionDevelopers, Services
speakTTS synthesisDevelopers, Services
manageProject/key managementAdmin
usage:readView usage metricsAnalysts, Auditors
keys:readList API keysAuditors
keys:writeCreate/delete keysAdmin

Instructions

Step 1: Define Roles and Scope Mapping

interface Role {
  name: string;
  deepgramScopes: string[];
  keyExpiry: number;        // Days
  description: string;
}

const ROLES: Record<string, Role> = {
  admin: {
    name: 'Admin',
    deepgramScopes: ['member'],
    keyExpiry: 90,
    description: 'Full access — project and key management',
  },
  developer: {
    name: 'Developer',
    deepgramScopes: ['listen', 'speak'],
    keyExpiry: 90,
    description: 'STT and TTS — no management access',
  },
  analyst: {
    name: 'Analyst',
    deepgramScopes: ['usage:read'],
    keyExpiry: 365,
    description: 'Read-only usage metrics',
  },
  service: {
    name: 'Service Account',
    deepgramScopes: ['listen'],
    keyExpiry: 90,
    description: 'STT only — for automated systems',
  },
  auditor: {
    name: 'Auditor',
    deepgramScopes: ['usage:read', 'keys:read'],
    keyExpiry: 30,
    description: 'Read-only audit access',
  },
};

Step 2: Scoped Key Provisioning

import { createClient } from '@deepgram/sdk';

class DeepgramKeyManager {
  private admin: ReturnType<typeof createClient>;
  private projectId: string;

  constructor(adminKey: string, projectId: string) {
    this.admin = createClient(adminKey);
    this.projectId = projectId;
  }

  async createScopedKey(userId: string, roleName: string): Promise<{
    keyId: string;
    key: string;
    scopes: string[];
    expiresAt: string;
  }> {
    const role = ROLES[roleName];
    if (!role) throw new Error(`Unknown role: ${roleName}`);

    const expirationDate = new Date(Date.now() + role.keyExpiry * 86400000);

    const { result, error } = await this.admin.manage.createProjectKey(
      this.projectId,
      {
        comment: `${roleName}:${userId}:${new Date().toISOString().split('T')[0]}`,
        scopes: role.deepgramScopes,
        expiration_date: expirationDate.toISOString(),
      }
    );

    if (error) throw new Error(`Key creation failed: ${error.message}`);

    console.log(`Created ${roleName} key for ${userId} (expires ${expirationDate.toISOString().split('T')[0]})`);

    return {
      keyId: result.key_id,
      key: result.key,
      scopes: role.deepgramScopes,
      expiresAt: expirationDate.toISOString(),
    };
  }

  async revokeKey(keyId: string) {
    const { error } = await this.admin.manage.deleteProjectKey(
      this.projectId, keyId
    );
    if (error) throw new Error(`Key revocation failed: ${error.message}`);
    console.log(`Revoked key: ${keyId}`);
  }

  async listKeys() {
    const { result, error } = await this.admin.manage.getProjectKeys(this.projectId);
    if (error) throw error;

    return result.api_keys.map((k: any) => ({
      keyId: k.api_key_id,
      comment: k.comment,
      scopes: k.scopes,
      created: k.created,
      expiration: k.expiration_date,
    }));
  }
}

Step 3: Permission Middleware

import { Request, Response, NextFunction } from 'express';

interface AuthenticatedRequest extends Request {
  user?: { id: string; role: string; deepgramKeyId: string };
}

function requireRole(...allowedRoles: string[]) {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }

    if (!allowedRoles.includes(req.user.role)) {
      console.warn(`Access denied: user ${req.user.id} (${req.user.role}) tried to access ${req.path}`);
      return res.status(403).json({
        error: 'Insufficient permissions',
        required: allowedRoles,
        current: req.user.role,
      });
    }

    next();
  };
}

function requireScope(...requiredScopes: string[]) {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }

    const role = ROLES[req.user.role];
    const hasScopes = requiredScopes.every(
      s => role.deepgramScopes.includes(s) || role.deepgramScopes.includes('member')
    );

    if (!hasScopes) {
      return res.status(403).json({
        error: 'Missing required Deepgram scopes',
        required: requiredScopes,
        current: role.deepgramScopes,
      });
    }

    next();
  };
}

// Route examples:
app.post('/api/transcribe', requireScope('listen'), transcribeHandler);
app.post('/api/tts', requireScope('speak'), ttsHandler);
app.get('/api/usage', requireScope('usage:read'), usageHandler);
app.post('/api/keys', requireRole('admin'), createKeyHandler);
app.get('/api/audit', requireRole('admin', 'auditor'), auditHandler);

Step 4: Team Management

interface Team {
  id: string;
  name: string;
  projectId: string;       // Deepgram project ID
  members: Array<{
    userId: string;
    role: string;
    keyId: string;
    joinedAt: string;
  }>;
}

class TeamManager {
  private keyManager: DeepgramKeyManager;

  constructor(adminKey: string, projectId: string) {
    this.keyManager = new DeepgramKeyManager(adminKey, projectId);
  }

  async addMember(team: Team, userId: string, role: string) {
    // Provision Deepgram key with role scopes
    const key = await this.keyManager.createScopedKey(userId, role);

    team.members.push({
      userId,
      role,
      keyId: key.keyId,
      joinedAt: new Date().toISOString(),
    });

    console.log(`Added ${userId} to ${team.name} as ${role}`);
    return key;
  }

  async removeMember(team: Team, userId: string) {
    const member = team.members.find(m => m.userId === userId);
    if (!member) throw new Error(`User ${userId} not in team`);

    // Revoke Deepgram key
    await this.keyManager.revokeKey(member.keyId);

    team.members = team.members.filter(m => m.userId !== userId);
    console.log(`Removed ${userId} from ${team.name}, key revoked`);
  }

  async changeRole(team: Team, userId: string, newRole: string) {
    const member = team.members.find(m => m.userId === userId);
    if (!member) throw new Error(`User ${userId} not in team`);

    // Revoke old key, create new key with new role scopes
    await this.keyManager.revokeKey(member.keyId);
    const newKey = await this.keyManager.createScopedKey(userId, newRole);

    member.role = newRole;
    member.keyId = newKey.keyId;

    console.log(`Changed ${userId} role to ${newRole}`);
    return newKey;
  }
}

Step 5: Automated Key Rotation

async function rotateExpiringKeys(
  keyManager: DeepgramKeyManager,
  db: any,
  daysBeforeExpiry = 7
) {
  const keys = await keyManager.listKeys();
  const now = Date.now();
  const threshold = now + daysBeforeExpiry * 86400000;
  let rotated = 0;

  for (const key of keys) {
    if (!key.expiration) continue;
    const expiresAt = new Date(key.expiration).getTime();

    if (expiresAt < threshold) {
      // Parse role from comment (format: "role:userId:date")
      const [role, userId] = (key.comment ?? '').split(':');
      if (!role || !userId) {
        console.warn(`Cannot rotate key ${key.keyId} — unknown format: ${key.comment}`);
        continue;
      }

      console.log(`Rotating key for ${userId} (${role}), expires ${key.expiration}`);

      // Create new key
      const newKey = await keyManager.createScopedKey(userId, role);

      // Update database with new key ID
      await db.query(
        'UPDATE team_members SET key_id = $1 WHERE user_id = $2',
        [newKey.keyId, userId]
      );

      // Revoke old key (after a grace period, or immediately)
      await keyManager.revokeKey(key.keyId);
      rotated++;
    }
  }

  console.log(`Rotated ${rotated} keys expiring within ${daysBeforeExpiry} days`);
  return rotated;
}

Step 6: Access Control Matrix

ActionAdminDeveloperAnalystServiceAuditor
Transcribe (STT)YesYesNoYesNo
Text-to-SpeechYesYesNoNoNo
View usageYesNoYesNoYes
Manage keysYesNoNoNoNo
View audit logsYesNoNoNoYes
Create projectsYesNoNoNoNo

Output

  • Five-role permission model with Deepgram scope mapping
  • Scoped API key provisioning via Management API
  • Express middleware (role-based and scope-based)
  • Team management with auto-provisioned/revoked keys
  • Automated key rotation for expiring keys

Error Handling

IssueCauseSolution
403 ForbiddenKey lacks scopeCreate new key with correct scopes
Key expiredNo rotation configuredEnable automated rotation
manage.createProjectKey failsAdmin key missing member scopeUse key with member scope
Team member can't transcribeWrong role assignedChange role to developer or service

Resources

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/.curated/deepgram-enterprise-rbac

默认分支

main

最新提交

e5a6c3b

Tree SHA

c2dc8e8