Agent Skills: Cohere Enterprise RBAC

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/cohere-enterprise-rbac

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/cohere-pack/skills/cohere-enterprise-rbac

Skill Files

Browse the full folder contents for cohere-enterprise-rbac.

Download Skill

Loading file tree…

plugins/saas-packs/cohere-pack/skills/cohere-enterprise-rbac/SKILL.md

Skill Metadata

Name
cohere-enterprise-rbac
Description
|

Cohere Enterprise RBAC

Overview

Configure enterprise-grade access control for Cohere API v2 with multi-team API key management, per-team model/budget restrictions, and audit trails.

Prerequisites

  • Cohere production API keys
  • Understanding of your team/service structure
  • Secret management infrastructure

Cohere Access Model

Cohere uses API key-based access control (no built-in RBAC or SSO). Enterprise patterns are implemented in your application layer.

| Cohere Feature | Availability | |----------------|-------------| | API key auth | All tiers | | Multiple API keys | Via dashboard | | Per-key rate limits | Production: 1000/min | | Usage dashboard | dashboard.cohere.com | | SSO/SAML | Not available (API key only) | | Per-key scoping | Not available |

Instructions

Step 1: Multi-Team Key Strategy

// Each team gets their own API key for tracking and revocation
interface TeamConfig {
  name: string;
  apiKeyEnvVar: string;
  allowedModels: string[];
  maxTokensPerCall: number;
  dailyBudgetUSD: number;
}

const teamConfigs: Record<string, TeamConfig> = {
  search: {
    name: 'Search Team',
    apiKeyEnvVar: 'CO_API_KEY_SEARCH',
    allowedModels: ['embed-v4.0', 'rerank-v3.5', 'command-r-08-2024'],
    maxTokensPerCall: 1000,
    dailyBudgetUSD: 50,
  },
  chatbot: {
    name: 'Chatbot Team',
    apiKeyEnvVar: 'CO_API_KEY_CHATBOT',
    allowedModels: ['command-a-03-2025', 'command-r7b-12-2024'],
    maxTokensPerCall: 4096,
    dailyBudgetUSD: 200,
  },
  ml: {
    name: 'ML Team',
    apiKeyEnvVar: 'CO_API_KEY_ML',
    allowedModels: ['embed-v4.0', 'embed-multilingual-v3.0'],
    maxTokensPerCall: 500,
    dailyBudgetUSD: 100,
  },
};

Step 2: Team-Scoped Client Factory

import { CohereClientV2 } from 'cohere-ai';

const clients = new Map<string, CohereClientV2>();

export function getCohereForTeam(teamId: string): CohereClientV2 {
  if (!clients.has(teamId)) {
    const config = teamConfigs[teamId];
    if (!config) throw new Error(`Unknown team: ${teamId}`);

    const apiKey = process.env[config.apiKeyEnvVar];
    if (!apiKey) throw new Error(`${config.apiKeyEnvVar} not set for team ${teamId}`);

    clients.set(teamId, new CohereClientV2({ token: apiKey }));
  }
  return clients.get(teamId)!;
}

Step 3: Model Access Enforcement

function enforceModelAccess(teamId: string, requestedModel: string): void {
  const config = teamConfigs[teamId];
  if (!config) throw new Error(`Unknown team: ${teamId}`);

  if (!config.allowedModels.includes(requestedModel)) {
    throw new Error(
      `Team ${config.name} is not authorized to use model ${requestedModel}. ` +
      `Allowed: ${config.allowedModels.join(', ')}`
    );
  }
}

// Enforced chat wrapper
export async function teamChat(
  teamId: string,
  message: string,
  model: string
): Promise<string> {
  enforceModelAccess(teamId, model);

  const config = teamConfigs[teamId];
  const cohere = getCohereForTeam(teamId);

  const response = await cohere.chat({
    model,
    messages: [{ role: 'user', content: message }],
    maxTokens: config.maxTokensPerCall,
  });

  return response.message?.content?.[0]?.text ?? '';
}

Step 4: Per-Team Budget Enforcement

class TeamBudgetTracker {
  private dailySpend = new Map<string, number>();
  private lastReset = new Date();

  track(teamId: string, tokens: { input: number; output: number }): void {
    // Reset daily at midnight
    const now = new Date();
    if (now.getDate() !== this.lastReset.getDate()) {
      this.dailySpend.clear();
      this.lastReset = now;
    }

    const current = this.dailySpend.get(teamId) ?? 0;
    // Rough cost estimate per 1M tokens (check cohere.com/pricing)
    const cost = (tokens.input / 1_000_000) * 0.5 + (tokens.output / 1_000_000) * 1.5;
    this.dailySpend.set(teamId, current + cost);
  }

  canProceed(teamId: string): boolean {
    const config = teamConfigs[teamId];
    if (!config) return false;
    const spent = this.dailySpend.get(teamId) ?? 0;
    return spent < config.dailyBudgetUSD;
  }

  getSpend(teamId: string): number {
    return this.dailySpend.get(teamId) ?? 0;
  }
}

const budgetTracker = new TeamBudgetTracker();

// Budget-enforced call
export async function budgetedChat(teamId: string, message: string, model: string): Promise<string> {
  if (!budgetTracker.canProceed(teamId)) {
    throw new Error(`Team ${teamId} has exceeded daily budget of $${teamConfigs[teamId].dailyBudgetUSD}`);
  }

  const response = await teamChat(teamId, message, model);

  // Track usage after successful call
  // Note: actual usage comes from response.usage.billedUnits
  return response;
}

Step 5: API Gateway Middleware

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

// Extract team from auth header or JWT
function extractTeamId(req: Request): string {
  const apiKey = req.headers['x-api-key'] as string;
  // Map API keys to teams (store in DB, not code)
  const teamMap = new Map<string, string>([
    ['search-api-key-hash', 'search'],
    ['chatbot-api-key-hash', 'chatbot'],
    ['ml-api-key-hash', 'ml'],
  ]);

  const teamId = teamMap.get(apiKey);
  if (!teamId) throw new Error('Invalid API key');
  return teamId;
}

function cohereAccessControl(allowedEndpoints: string[]) {
  return (req: Request, res: Response, next: NextFunction) => {
    try {
      const teamId = extractTeamId(req);

      // Check budget
      if (!budgetTracker.canProceed(teamId)) {
        return res.status(429).json({ error: 'Daily budget exceeded' });
      }

      // Attach team context
      (req as any).cohereTeam = teamId;
      next();
    } catch (err) {
      res.status(403).json({ error: (err as Error).message });
    }
  };
}

// Usage
app.post('/api/chat', cohereAccessControl(['chat']), chatHandler);
app.post('/api/embed', cohereAccessControl(['embed']), embedHandler);

Step 6: Audit Trail

interface CohereAccessLog {
  timestamp: Date;
  teamId: string;
  endpoint: string;
  model: string;
  tokensUsed: { input: number; output: number };
  costEstimate: number;
  success: boolean;
  errorCode?: number;
}

async function logAccess(entry: CohereAccessLog): Promise<void> {
  // Write to your audit database
  await db.cohereAccessLog.insert(entry);

  // Alert on suspicious patterns
  if (entry.costEstimate > 10) {
    console.warn(`High-cost call: team=${entry.teamId} cost=$${entry.costEstimate.toFixed(2)}`);
  }
}

// Usage reporting query
// SELECT team_id, SUM(cost_estimate), COUNT(*) as calls
// FROM cohere_access_log
// WHERE timestamp > NOW() - INTERVAL '24 hours'
// GROUP BY team_id
// ORDER BY SUM(cost_estimate) DESC;

Key Rotation Per Team

# Rotate a team's API key
# 1. Generate new key at dashboard.cohere.com
# 2. Update the team's secret
aws secretsmanager update-secret \
  --secret-id cohere/search-team/api-key \
  --secret-string "new-key-here"

# 3. Restart team's services
kubectl rollout restart deployment/search-service

# 4. Verify and revoke old key
# 5. Update audit log with rotation event

Output

  • Multi-team API key management with separate keys per team
  • Model access enforcement (search team cannot use chat models)
  • Per-team daily budget limits with automatic cutoff
  • Audit trail for all Cohere API calls with team attribution
  • API gateway middleware for access control

Error Handling

| Issue | Cause | Solution | |-------|-------|----------| | Team key missing | Env var not set | Check secret manager | | Model access denied | Not in allowedModels | Update team config | | Budget exceeded | High usage | Increase limit or optimize | | Key rotation gap | Old key revoked too early | Overlap keys during rotation |

Resources

Next Steps

For major migrations, see cohere-migration-deep-dive.