openrouter-compliance-review
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
deepgram-enterprise-rbac
Configure enterprise role-based access control for Deepgram integrations.
Full skill instructions
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.
| Scope | Permission | Used By |
|---|---|---|
member | Full access (all scopes) | Admin only |
listen | STT transcription | Developers, Services |
speak | TTS synthesis | Developers, Services |
manage | Project/key management | Admin |
usage:read | View usage metrics | Analysts, Auditors |
keys:read | List API keys | Auditors |
keys:write | Create/delete keys | Admin |
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',
},
};
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,
}));
}
}
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);
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;
}
}
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;
}
| Action | Admin | Developer | Analyst | Service | Auditor |
|---|---|---|---|---|---|
| Transcribe (STT) | Yes | Yes | No | Yes | No |
| Text-to-Speech | Yes | Yes | No | No | No |
| View usage | Yes | No | Yes | No | Yes |
| Manage keys | Yes | No | No | No | No |
| View audit logs | Yes | No | No | No | Yes |
| Create projects | Yes | No | No | No | No |
| Issue | Cause | Solution |
|---|---|---|
| 403 Forbidden | Key lacks scope | Create new key with correct scopes |
| Key expired | No rotation configured | Enable automated rotation |
manage.createProjectKey fails | Admin key missing member scope | Use key with member scope |
| Team member can't transcribe | Wrong role assigned | Change role to developer or service |
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
Data synchronization, ETL patterns, and data management for MaintainX.
Execute Groq primary workflow: chat completions with tool use and JSON
Validate CSRF protection implementations for security gaps. Use when
Configure this skill should be used when the user asks about "API contract",
Execute Ideogram incident response with triage, mitigation, and postmortem.
Execute create, select, and transform features to improve machine learning
Reusable patterns for the Gamma REST API (no SDK exists).
Apply production-ready Deepgram SDK patterns for TypeScript and Python.
Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model
Diagnose and fix common OpenRouter API errors. Use when encountering
Optimize Linear API usage, reduce unnecessary calls, and maximize
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience