Security¶
Secure Hector deployments with authentication, authorization, and tool sandboxing.
Authentication¶
JWT Authentication¶
Enable JWT with JWKS (JSON Web Key Set):
server:
auth:
enabled: true
jwks_url: https://auth.yourdomain.com/.well-known/jwks.json
issuer: https://auth.yourdomain.com/
audience: your-api-identifier
require_auth: true # Default: true when enabled
excluded_paths: # Paths that don't require auth
- /health
- /.well-known/agent-card.json
Required Fields:
jwks_url: URL to fetch JSON Web Key Set (public keys for JWT verification)issuer: Expected token issuer (issclaim)audience: Expected token audience (audclaim)
All three fields are required for JWT authentication to work. Incomplete configuration will be rejected.
Optional Fields:
require_auth(default:true): When true, returns 401 for missing/invalid tokensexcluded_paths: List of paths accessible without authenticationrefresh_interval(default:15m): How often to refresh the JWKS
Hector validates JWT tokens from the Authorization: Bearer <token> header using the public keys from the JWKS endpoint.
Zero-Config Mode (CLI)¶
Enable JWT auth via command-line flags:
hector serve \
--auth-jwks-url https://auth.yourdomain.com/.well-known/jwks.json \
--auth-issuer https://auth.yourdomain.com/ \
--auth-audience your-api-identifier \
--provider anthropic \
--model claude-sonnet-4
All three auth flags are required. Providing only one or two will result in a warning and auth will be disabled:
# ❌ Incomplete - will warn and disable auth
hector serve --auth-jwks-url https://... --provider anthropic
# ✅ Complete - auth enabled
hector serve \
--auth-jwks-url https://... \
--auth-issuer https://... \
--auth-audience your-api \
--provider anthropic
To make auth optional (allow unauthenticated requests):
hector serve \
--auth-jwks-url https://... \
--auth-issuer https://... \
--auth-audience your-api \
--no-auth-required \
--provider anthropic
Integration Examples¶
Auth0:
server:
auth:
enabled: true
jwks_url: https://${AUTH0_DOMAIN}/.well-known/jwks.json
issuer: https://${AUTH0_DOMAIN}/
audience: ${AUTH0_AUDIENCE}
Keycloak:
server:
auth:
enabled: true
jwks_url: https://keycloak.yourdomain.com/realms/your-realm/protocol/openid-connect/certs
issuer: https://keycloak.yourdomain.com/realms/your-realm
audience: hector-api
AWS Cognito:
server:
auth:
enabled: true
jwks_url: https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
issuer: https://cognito-idp.{region}.amazonaws.com/{userPoolId}
audience: {clientId}
API Key Authentication¶
Use static API keys:
server:
auth:
enabled: true
api_keys:
- ${API_KEY_1}
- ${API_KEY_2}
Environment variables:
export API_KEY_1="key-abc123..."
export API_KEY_2="key-def456..."
Clients send: Authorization: Bearer key-abc123...
Combined Authentication¶
Support both JWT and API keys:
server:
auth:
enabled: true
jwks_url: https://auth.yourdomain.com/.well-known/jwks.json
api_keys:
- ${SERVICE_API_KEY}
Hector accepts either JWT tokens or API keys.
Agent Visibility¶
Control agent discovery and access with visibility levels. Note: Visibility controls discovery, not access. Access control is managed by server.auth.require_auth.
agents:
# Public agent (default) - visible to all, auth controlled by server.auth.require_auth
public_assistant:
visibility: public
# Visible in discovery to everyone
# Access requires auth if server.auth.require_auth is true
# Internal agent - visible only when authenticated
internal_analyst:
visibility: internal
# Only visible in discovery when authenticated
# Access requires authentication
# Private agent - not exposed via HTTP
private_helper:
visibility: private
# Hidden from discovery
# Not accessible via HTTP (internal use only)
Visibility Levels¶
public (default):
- Visible in agent discovery (
/agents) to all users (authenticated or not) - Accessible via HTTP endpoints
- Auth enforcement: Controlled by
server.auth.require_authsetting - If
require_auth: true→ requires authentication - If
require_auth: false→ accessible without authentication
internal:
- Visible in discovery only when authenticated
- Hidden from unauthenticated users in discovery
- Auth enforcement: Always requires authentication regardless of
require_authsetting
private:
- Hidden from discovery endpoint
- Not accessible via HTTP endpoints
- Only callable internally by other agents (sub-agents, agent tools)
Visibility vs. Authentication¶
Important distinction:
- Visibility controls who can see the agent in discovery
- Authentication controls who can access the agent
Example with require_auth: true:
server:
auth:
enabled: true
require_auth: true # All agents require auth by default
agents:
assistant:
visibility: public # Visible in discovery to everyone
# BUT: Still requires authentication to access (due to require_auth: true)
admin:
visibility: internal # Only visible in discovery when authenticated
# AND: Requires authentication to access
Example with require_auth: false:
server:
auth:
enabled: true
require_auth: false # Auth is optional
agents:
assistant:
visibility: public # Visible in discovery to everyone
# AND: Accessible without authentication (require_auth: false)
admin:
visibility: internal # Only visible when authenticated
# AND: Always requires authentication (internal visibility enforces this)
Example¶
server:
auth:
enabled: true
jwks_url: https://auth.company.com/.well-known/jwks.json
issuer: https://auth.company.com/
audience: company-api
require_auth: true # Require auth for all endpoints except excluded
excluded_paths:
- /health
- /.well-known/agent-card.json
agents:
# Customer-facing agent - visible to all, but requires auth to access
customer_support:
visibility: public # Visible in discovery without auth
instruction: Help customers with basic questions
# Access requires authentication (due to require_auth: true)
# Internal admin agent - visible and accessible only when authenticated
admin_assistant:
visibility: internal # Only visible when authenticated
tools: [execute_command, write_file]
instruction: Administrative tasks
# Always requires authentication (internal visibility)
# Backend helper - not exposed via HTTP
data_processor:
visibility: private # Not accessible via HTTP
instruction: Process data internally
# Only callable by other agents internally
Tool Security¶
Tool Approval (HITL)¶
Require human approval for sensitive tools:
tools:
write_file:
type: function
handler: write_file
require_approval: true
approval_prompt: "Allow writing to {file}?"
execute_command:
type: command
require_approval: true
approval_prompt: "Execute: {command}?"
When an agent calls an approval-required tool: 1. Execution pauses 2. User receives approval request 3. User approves or denies 4. Tool executes or returns error
Command Sandboxing¶
Restrict command execution:
tools:
execute_command:
type: command
working_directory: ./workspace
max_execution_time: 30s
allowed_commands:
- git
- npm
- python
- pytest
denied_commands:
- rm
- dd
- sudo
deny_by_default: false
Whitelist Mode (recommended):
tools:
execute_command:
type: command
deny_by_default: true # Deny all except allowed
allowed_commands:
- ls
- cat
- grep
Only whitelisted commands can execute.
Working Directory Restriction¶
Limit command scope:
tools:
execute_command:
type: command
working_directory: ./safe-workspace
# Commands execute only in this directory
Execution Timeout¶
Prevent long-running commands:
tools:
execute_command:
type: command
max_execution_time: 30s # Kill after 30 seconds
Secret Management¶
Environment Variables¶
Never commit secrets to configuration:
# ✅ Good - Environment variable
llms:
default:
api_key: ${OPENAI_API_KEY}
# ❌ Bad - Hardcoded secret
llms:
default:
api_key: sk-proj-abc123...
.env Files¶
Store secrets in .env (add to .gitignore):
# .env
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
DATABASE_PASSWORD=secret
Hector automatically loads .env files.
Kubernetes Secrets¶
apiVersion: v1
kind: Secret
metadata:
name: hector-secrets
namespace: hector
type: Opaque
stringData:
OPENAI_API_KEY: sk-...
ANTHROPIC_API_KEY: sk-ant-...
DATABASE_PASSWORD: secret
Reference in deployment:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: hector
envFrom:
- secretRef:
name: hector-secrets
HashiCorp Vault¶
Use Vault for secret management:
# Retrieve secrets from Vault
export OPENAI_API_KEY=$(vault kv get -field=api_key secret/hector/openai)
Or use Vault Agent for injection.
Network Security¶
TLS/HTTPS¶
Terminate TLS at reverse proxy:
# nginx.conf
server {
listen 443 ssl http2;
server_name agents.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/agents.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/agents.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
Kubernetes Network Policies¶
Restrict pod communication:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: hector
namespace: hector
spec:
podSelector:
matchLabels:
app: hector
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
# Allow database
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# Allow HTTPS for LLM APIs
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
CORS Configuration¶
Control allowed origins:
server:
cors:
allowed_origins:
- https://app.yourdomain.com
- https://dashboard.yourdomain.com
allowed_methods:
- GET
- POST
- OPTIONS
allowed_headers:
- Authorization
- Content-Type
max_age: 86400
Wildcard (development only):
server:
cors:
allowed_origins:
- "*"
Rate Limiting¶
Prevent abuse with rate limiting:
server:
rate_limiting:
enabled: true
requests_per_minute: 60
burst: 10
Per-user rate limiting (with auth):
server:
rate_limiting:
enabled: true
requests_per_minute: 100
burst: 20
per_user: true # Separate limits per authenticated user
Audit Logging¶
Enable structured logging for auditing:
logger:
level: info
format: json # Structured logs for parsing
Logs include:
- Authentication attempts
- Agent requests
- Tool executions
- Errors
Example log:
{
"timestamp": "2025-01-15T10:30:00Z",
"level": "info",
"component": "auth",
"action": "authenticate",
"user": "[email protected]",
"success": true
}
Security Best Practices¶
Principle of Least Privilege¶
Grant minimal permissions:
# ✅ Good - Minimal tools
agents:
reader:
tools: [read_file, grep_search]
# ❌ Bad - Excessive permissions
agents:
reader:
tools: [read_file, write_file, execute_command, web_request]
Agent Isolation¶
Separate agents by trust level:
agents:
# Untrusted: public-facing, limited tools
public_assistant:
visibility: public
tools: [search]
# Trusted: internal, more tools
internal_assistant:
visibility: internal
tools: [search, read_file, write_file]
# Privileged: admin only, all tools
admin_assistant:
visibility: internal
tools: [execute_command, write_file, web_request]
Tool Whitelisting¶
Use explicit whitelists:
# ✅ Good - Explicit whitelist
tools:
execute_command:
deny_by_default: true
allowed_commands: [ls, cat, grep]
# ❌ Bad - Blacklist (incomplete)
tools:
execute_command:
deny_by_default: false
denied_commands: [rm] # Many dangerous commands not listed
Secrets Rotation¶
Rotate secrets regularly:
# Generate new API key
new_key=$(generate_api_key)
# Update Kubernetes secret
kubectl create secret generic hector-secrets \
--from-literal=API_KEY=$new_key \
--dry-run=client -o yaml | kubectl apply -f -
# Restart pods to pick up new secret
kubectl rollout restart deployment/hector -n hector
Input Validation¶
Validate inputs in custom tools:
func ValidatePath(path string) error {
// Prevent path traversal
if strings.Contains(path, "..") {
return errors.New("path traversal not allowed")
}
// Restrict to workspace
if !strings.HasPrefix(path, "/workspace/") {
return errors.New("path must be within workspace")
}
return nil
}
Production Security Checklist¶
- Enable authentication (JWT or API keys)
- Use agent visibility controls
- Require approval for destructive tools
- Enable command sandboxing with whitelists
- Store secrets in environment variables or vault
- Use TLS/HTTPS (via reverse proxy)
- Configure network policies (Kubernetes)
- Set up CORS restrictions
- Enable rate limiting
- Use structured logging for auditing
- Implement secrets rotation
- Regular security updates
- Monitor for suspicious activity
Example: Secure Production Setup¶
# config.yaml
version: "2"
llms:
default:
provider: openai
model: gpt-4o
api_key: ${OPENAI_API_KEY} # From Kubernetes secret
tools:
execute_command:
type: command
working_directory: ./workspace
max_execution_time: 30s
deny_by_default: true
allowed_commands: [git, npm, python, pytest]
require_approval: true
write_file:
type: function
handler: write_file
require_approval: true
read_file:
type: function
handler: read_file
# No approval needed for read-only
agents:
# Public agent: minimal tools
public_assistant:
visibility: public
llm: default
tools: [search]
# Internal agent: more tools
internal_assistant:
visibility: internal
llm: default
tools: [search, read_file, write_file]
document_stores: [internal_docs]
# Admin agent: full access
admin_assistant:
visibility: internal
llm: default
tools: [execute_command, write_file, read_file]
server:
port: 8080
auth:
enabled: true
jwks_url: https://auth.company.com/.well-known/jwks.json
issuer: https://auth.company.com/
audience: company-api
require_auth: true
excluded_paths:
- /health
- /.well-known/agent-card.json
cors:
allowed_origins:
- https://app.company.com
rate_limiting:
enabled: true
requests_per_minute: 100
per_user: true
observability:
metrics:
enabled: true
tracing:
enabled: true
endpoint: jaeger-collector:4317
logger:
level: info
format: json
Next Steps¶
- Deployment Guide - Deploy securely
- Observability Guide - Monitor security events
- Tools Guide - Configure tool security