Exa enterprise rbac
425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill exa-enterprise-rbacAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
'Manage Exa API key scoping, team access controls, and domain restrictions.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.7 KB, as published. Nobody here has run it
Exa Enterprise RBAC
Overview
Manage access to Exa search API through API key scoping and application-level controls. Exa is API-key-based (no built-in RBAC), so access control is implemented through multiple API keys per use case, application-layer permission enforcement, domain restrictions per team, and per-key usage monitoring.
Prerequisites
- Exa API account with team/enterprise plan
- Dashboard access at dashboard.exa.ai
- Multiple API keys for key isolation
Instructions
Step 1: Key-Per-Use-Case Architecture
// config/exa-keys.ts
import Exa from "exa-js";
// Create separate clients for each use case
const exaClients = {
// High-volume RAG pipeline — production key with higher limits
ragPipeline: new Exa(process.env.EXA_KEY_RAG!),
// Internal research tool — lower volume key
researchTool: new Exa(process.env.EXA_KEY_RESEARCH!),
// Customer-facing search — separate key for isolation
customerSearch: new Exa(process.env.EXA_KEY_CUSTOMER!),
};
export function getExaForUseCase(
useCase: keyof typeof exaClients
): Exa {
const client = exaClients[useCase];
if (!client) throw new Error(`No Exa client for use case: ${useCase}`);
return client;
}
Step 2: Application-Level Permission Enforcement
// middleware/exa-permissions.ts
interface ExaPermissions {
maxResults: number;
allowedTypes: ("auto" | "neural" | "keyword" | "fast" | "deep")[];
allowedCategories: string[];
includeDomains?: string[]; // restrict to these domains
dailySearchLimit: number;
}
const ROLE_PERMISSIONS: Record<string, ExaPermissions> = {
"rag-pipeline": {
maxResults: 10,
allowedTypes: ["neural", "auto"],
allowedCategories: [],
dailySearchLimit: 10000,
},
"research-analyst": {
maxResults: 25,
allowedTypes: ["neural", "keyword", "auto", "deep"],
allowedCategories: ["research paper", "news"],
dailySearchLimit: 500,
},
"marketing-team": {
maxResults: 5,
allowedTypes: ["keyword", "auto"],
allowedCategories: ["company", "news"],
dailySearchLimit: 100,
},
"compliance-team": {
maxResults: 10,
allowedTypes: ["keyword", "auto"],
allowedCategories: [],
includeDomains: ["nist.gov", "owasp.org", "sans.org", "sec.gov"],
dailySearchLimit: 200,
},
};
function validateSearchRequest(
role: string,
searchType: string,
numResults: number,
category?: string
): { allowed: boolean; reason?: string } {
const perms = ROLE_PERMISSIONS[role];
if (!perms) return { allowed: false, reason: "Unknown role" };
if (!perms.allowedTypes.includes(searchType as any)) {
return { allowed: false, reason: `Search type ${searchType} not allowed for ${role}` };
}
if (numResults > perms.maxResults) {
return { allowed: false, reason: `Max ${perms.maxResults} results for ${role}` };
}
if (category && perms.allowedCategories.length > 0 && !perms.allowedCategories.includes(category)) {
return { allowed: false, reason: `Category ${category} not allowed for ${role}` };
}
return { allowed: true };
}
Step 3: Domain Restrictions per Team
// Enforce domain restrictions so compliance-sensitive teams
// only see results from vetted sources
async function enforcedSearch(
exa: Exa,
role: string,
query: string,
opts: any = {}
) {
const perms = ROLE_PERMISSIONS[role];
if (!perms) throw new Error(`Unknown role: ${role}`);
const validation = validateSearchRequest(
role,
opts.type || "auto",
opts.numResults || 10,
opts.category
);
if (!validation.allowed) throw new Error(validation.reason);
return exa.searchAndContents(query, {
...opts,
numResults: Math.min(opts.numResults || 10, perms.maxResults),
type: opts.type || "auto",
// Merge domain restrictions from role permissions
includeDomains: perms.includeDomains || opts.includeDomains,
});
}
Step 4: Per-Key Usage Tracking
// Track usage per API key / role for budget enforcement
class KeyUsageTracker {
private usage = new Map<string, { count: number; resetAt: number }>();
checkAndIncrement(role: string): void {
const perms = ROLE_PERMISSIONS[role];
if (!perms) throw new Error(`Unknown role: ${role}`);
const now = Date.now();
const dayStart = new Date().setHours(0, 0, 0, 0);
let entry = this.usage.get(role);
if (!entry || entry.resetAt < now) {
entry = { count: 0, resetAt: dayStart + 24 * 60 * 60 * 1000 };
}
if (entry.count >= perms.dailySearchLimit) {
throw new Error(
`Daily search limit (${perms.dailySearchLimit}) exceeded for ${role}`
);
}
entry.count++;
this.usage.set(role, entry);
}
getUsage(role: string) {
const entry = this.usage.get(role);
const limit = ROLE_PERMISSIONS[role]?.dailySearchLimit || 0;
return {
used: entry?.count || 0,
limit,
remaining: limit - (entry?.count || 0),
};
}
}
Step 5: Key Rotation Procedure
set -euo pipefail
# 1. Create new key in Exa dashboard (dashboard.exa.ai)
# 2. Deploy new key alongside old key
# 3. Verify new key works
curl -s -o /dev/null -w "%{http_code}" \
-X POST https://api.exa.ai/search \
-H "x-api-key: $NEW_EXA_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"key rotation test","numResults":1}'
# 4. Switch traffic to new key
# 5. Monitor for errors
# 6. Revoke old key in dashboard after 24h
Error Handling
| Issue | Cause | Solution |
|---|---|---|
401 on search | Invalid or revoked API key | Regenerate in dashboard |
429 rate limited | Key-level rate limit exceeded | Distribute across keys |
| Daily limit hit | Search budget exhausted | Adjust limits or wait for reset |
| Wrong domain results | Missing domain filter | Apply includeDomains per role |
Resources
Next Steps
For policy enforcement, see exa-policy-guardrails. For multi-env setup, see exa-multi-env-setup.