API Key Permissions
Control what API keys can access and do within your Enclava instance.
Permission System Overview
Enclava uses a hierarchical permission system:
- Global Permissions - Platform-wide capabilities
- Module Permissions - Access to specific features (RAG, Agents, etc.)
- Resource Permissions - Access to specific resources (collections, chatbots)
- Model Access - Which LLM models can be used
Global Permissions
Available Permissions
| Permission | Description | Default for New Keys |
|---|---|---|
api.read | Read access to API endpoints | ✓ |
api.write | Write access to API endpoints | ✓ |
chat_completions | Use chat completion endpoints | ✓ |
embeddings | Generate embeddings | ✓ |
rag.read | Read RAG collections and documents | ✓ |
rag.write | Upload and modify RAG documents | ✓ |
rag.search | Search RAG collections | ✓ |
chatbot.chat | Use chatbot endpoints | ✓ |
chatbot.manage | Create and manage chatbots | ✓ |
agent.chat | Use agent endpoints | ✓ |
agent.manage | Create and manage agents | ✓ |
tools.execute | Use tool endpoints | ✓ |
Checking Key Permissions
import requests
response = requests.get(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}
)
key_info = response.json()
print("Permissions:")
for perm in key_info["permissions"]:
print(f" - {perm}")
Model Access Permissions
Restricting Models
Create keys with specific model access:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "GPT-3.5 Only",
"allowed_models": ["gpt-3.5-turbo"]
}
)
Allowing Multiple Models
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Production Models",
"allowed_models": ["gpt-4", "gpt-3.5-turbo", "text-embedding-ada-002"]
}
)
Full Model Access
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Full Access",
"allowed_models": [] # Empty = all models
}
)
Resource-Level Permissions
RAG Collection Access
Restrict a key to specific RAG collections:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Documentation Reader",
"permissions": ["rag.read", "rag.search"],
"rag_collections": ["docs_collection", "policy_docs"]
}
)
Chatbot Access
Restrict a key to specific chatbots:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Support Bot Key",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["chatbot-123", "chatbot-456"]
}
)
Agent Access
Restrict a key to specific agents:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Research Agent Key",
"permissions": ["agent.chat"],
"agent_ids": ["agent-research", "agent-analysis"]
}
)
Permission Use Cases
Use Case 1: Read-Only Documentation Key
For applications that only need to search documentation:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Doc Search Only",
"permissions": ["rag.search"],
"rag_collections": ["public_docs"],
"allowed_models": ["text-embedding-ada-002"] # Only embeddings
}
)
Use Case 2: Production Chatbot Key
For a live chatbot widget:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Chat Widget Production",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["support-bot-prod"],
"allowed_models": ["gpt-3.5-turbo"], # Cheaper model for chat
"daily_spend_limit": 50.00,
"monthly_spend_limit": 1500.00
}
)
Use Case 3: Internal Analysis Tool
For internal data analysis:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Internal Analysis",
"permissions": [
"chat_completions",
"rag.read",
"rag.search",
"tools.execute"
],
"rag_collections": ["internal_docs", "financial_data"],
"allowed_models": ["gpt-4"],
"no_budget_limit": True # Internal tool, no spend limit
}
)
Use Case 4: External API Key
For exposing limited functionality to external partners:
response = requests.post(
"http://localhost/api-internal/v1/api-keys",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"name": "Partner API Key",
"permissions": ["chatbot.chat"],
"chatbot_ids": ["public-info-bot"],
"allowed_models": ["gpt-3.5-turbo"],
"rag_collections": ["public_content"], # Only public data
"daily_request_limit": 1000, # Rate limit
"daily_spend_limit": 10.00
}
)
Updating Permissions
Add Permissions to Existing Key
response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat", "rag.search"] # Add RAG search
}
)
Remove Permissions
response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat"] # Remove RAG search
}
)
Change Model Access
response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"allowed_models": ["gpt-4"] # Change from multiple to single model
}
)
Permission Errors
Common Error Messages
403 Forbidden - Permission Denied
{
"error": "PERMISSION_DENIED",
"message": "API key does not have permission to access this resource"
}
Solution: Add the required permission to the key.
403 Forbidden - Model Not Allowed
{
"error": "MODEL_NOT_ALLOWED",
"message": "API key does not have access to model 'gpt-4'"
}
Solution: Add the model to allowed_models or use an allowed model.
403 Forbidden - Collection Not Allowed
{
"error": "COLLECTION_NOT_ALLOWED",
"message": "API key does not have access to collection 'internal_docs'"
}
Solution: Add the collection to the key's allowed RAG collections.
Debugging Permissions
import requests
# Try to request
response = requests.post(
"http://localhost/api/v1/rag/search",
headers={"Authorization": "Bearer en_xxxxxxxx"},
json={"collection_name": "internal_docs", "query": "test"}
)
# Check if it's a permission error
if response.status_code == 403:
error = response.json()
print(f"Error: {error['error']}")
print(f"Message: {error['message']}")
# Get key details to see current permissions
key_response = requests.get(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}
)
print("\nCurrent permissions:")
print(key_response.json()["permissions"])
Best Practices
Principle of Least Privilege
Always grant the minimum permissions needed:
| Application Type | Recommended Permissions |
|---|---|
| Chat widget | chatbot.chat only |
| Documentation search | rag.read, rag.search only |
| Data analysis | chat_completions, rag.search |
| Full application | All needed permissions |
Separate Keys for Separate Environments
| Environment | Key Permissions |
|---|---|
| Development | Full access, no limits |
| Staging | Production-like permissions, higher limits |
| Production | Minimal permissions, strict limits |
Document Permission Changes
Keep track of why permissions were changed:
# When updating permissions, add a note
response = requests.put(
f"http://localhost/api-internal/v1/api-keys/{key_id}",
headers={"Authorization": "Bearer YOUR_JWT_TOKEN"},
json={
"permissions": ["chatbot.chat", "rag.search"],
"note": "Added RAG search for improved support bot accuracy"
}
)
Next Steps
- API Key Creation - Create keys with permissions
- API Key Management - Update and rotate keys
- OpenAI API - Start using your keys