Apollo Enterprise RbacSAFE
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
Overview
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
4f83675ca38aOBSERVED · 2026-10-08Host compatibility
What the documentation claims. We have not run a compatibility test.
| Host | Status | Notes |
|---|---|---|
| claude-code | mentioned |
What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
--- name: apollo-enterprise-rbac description: 'Enterprise role-based access control for Apollo.io. Use when implementing team permissions, restricting data access, or setting up enterprise security controls. Trigger with phrases like "apollo rbac", "apollo permissions", "apollo roles", "apollo team access", "apollo enterprise security". ' allowed-tools: Read, Write, Edit, Bash(kubectl:*), Bash(curl:*) version: 1.13.0 license: MIT author: Jeremy Longshore <[email protected]> tags: - saas - apollo - security - rbac compatibility: Designed for Claude Code --- # Apollo Enterprise RBAC ## Overview Role-based access control for Apollo.io API integrations. Apollo API keys are all-or-nothing (standard vs master), so RBAC must be implemented in your application layer as a proxy between users and the Apollo API. This skill builds a permission matrix, scoped API key system, Express middleware, and admin audit endpoints. ## Prerequisites - Apollo master API key - Node.js 18+ with Express ## Instructions ### Step 1: Define Roles and Permission Matrix Map Apollo API operations to team roles. Apollo's API has two main categories: - **Read-only**: search (free), enrichment (credits) - **Write**: contacts CRUD, sequences, deals, tasks ```typescript // src/rbac/roles.ts export type Role = 'viewer' | 'analyst' | 'sales_rep' | 'sales_manager' | 'admin'; export interface Permission { searchPeople: boolean; // /mixed_people/api_search (free) searchOrganizations: boolean; // /mixed_companies/search (free) enrichPerson: boolean; // /people/match (1 credit) bulkEnrich: boolean; // /people/bulk_match (credits) enrichOrg: boolean; // /organizations/enrich (1 credit) manageContacts: boolean; // /contacts CRUD (master key) manageSequences: boolean; // /emailer_campaigns/* (master key) manageDeals: boolean; // /opportunities/* (master key) exportPII: boolean; // download contacts with email/phone viewAnalytics: boolean; // sequence stats, usage manageTeam: boolean; // create/revoke scoped keys } export const PERMISSIONS: Record<Role, Permission> = { viewer: { searchPeople: true, searchOrganizations: true, enrichPerson: false, bulkEnrich: false, enrichOrg: false, manageContacts: false, manageSequences: false, manageDeals: false, exportPII: false, viewAnalytics: true, manageTeam: false, }, analyst: { searchPeople: true, searchOrganizations: true, enrichPerson: true, bulkEnrich: false, enrichOrg: true, manageContacts: false, manageSequences: false, manageDeals: false, exportPII: false, viewAnalytics: true, manageTeam: false, }, sales_rep: { searchPeople: true, searchOrganizations: true, enrichPerson: true, bulkEnrich: false, enrichOrg: true, manageContacts: true, manageSequences: true, manageDeals: true, exportPII: false, viewAnalytics: false, manageTeam: false, }, sales_manager: { searchPeople: true, searchOrganizations: true, enrichPerson: true, bulkEnrich: true, enrichOrg: true, manageContacts: true, manageSequences: true, manageDeals: true, exportPII: true, viewAnalytics: true, manageTeam: true, }, admin: { searchPeople: true, searchOrganizations: true, enrichPerson: true, bulkEnrich: true, enrichOrg: true, manageContacts: true, manageSequences: true, manageDeals: true, exportPII: true, viewAnalytics: true, manageTeam: true, }, }; ``` ### Step 2: Scoped API Key System ```typescript // src/rbac/api-keys.ts import crypto from 'crypto'; interface ScopedKey { keyHash: string; teamId: string; role: Role; createdBy: string; createdAt: string; expiresAt: string; } interface IssuedScopedKey { key: string; // return once; never persist or log this value teamId: string; role: Role; expiresAt: string; } // In production: store the hash in a durable database, never the raw key. const keys = new Map<string, ScopedKey>(); function hashKey(value: string): string { return crypto.createHash('sha256').update(value).digest('hex'); } export function createScopedKey(teamId: string, role: Role, createdBy: string, ttlDays: number = 90): IssuedScopedKey { const plaintextKey = `ak_${teamId}_${crypto.randomBytes(16).toString('hex')}`; const entry: ScopedKey = { keyHash: hashKey(plaintextKey), teamId, role, createdBy, createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + ttlDays * 86400000).toISOString(), }; keys.set(entry.keyHash, entry); return { key: plaintextKey, teamId, role, expiresAt: entry.expiresAt }; } export function resolveKey(apiKey: string): ScopedKey | null { const entry = keys.get(hashKey(apiKey)); if (!entry) return null; if (new Date(entry.expiresAt) < new Date()) { keys.delete(entry.keyHash); return null; } return entry; } export function revokeKey(apiKey: string) { keys.delete(hashKey(apiKey)); } ``` ### Step 3: Permission Middleware ```typescript // src/rbac/middleware.ts import { Request, Response, NextFunction } from 'express'; import { PERMISSIONS, Permission } from './roles'; import { resolveKey } from './api-keys'; // Map Apollo API paths to required permissions const ENDPOINT_PERMISSIONS: Record<string, keyof Permission> = { '/mixed_people/api_search': 'searchPeople', '/mixed_companies/search': 'searchOrganizations', '/people/match': 'enrichPerson', '/people/bulk_match': 'bulkEnrich', '/organizations/enrich': 'enrichOrg', '/contacts': 'manageContacts', '/emailer_campaigns': 'manageSequences', '/opportunities': 'manageDeals', }; export function requirePermission(action: keyof Permission) { return (req: Request, res: Response, next: NextFunction) => { const apiKey = req.headers['x-api-key'] as string; if (!apiKey) return res.status(401).json({ error: 'x-api-key header required' }); const key = resolveKey(apiKey); if (!key) return res.status(401).json({ error: 'Invalid or expired API
Trust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | PASS |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (0)
No findings outside the package's declared scope.
Gates applied: no_behavioural_pass.
4f83675ca38afull audit observations/trust-audit/skill/jeremylongshore__apollo-enterprise-rbac.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-08 | 4f83675ca38a | SAFE | B | 89 | first audit |
Questions
What does the Apollo Enterprise Rbac skill do?
Model-agnostic agent-skills platform with a harness-free canonical layer, verified adapters, and the ccpi package manager. Explore at tonsofskills.com.
Is Apollo Enterprise Rbac safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Apollo Enterprise Rbac access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
Which assistants does Apollo Enterprise Rbac work with?
Its documentation mentions claude-code. That is what the text claims, not a compatibility test we ran.
How current is this page?
The grade is for one exact copy of the source (4f83675ca38a), read on 2026-10-08. The repository is watched, and a new audit runs when it changes — this is the first audit.