Security
Dakera is built with enterprise-grade security from the ground up. Every request passes through authentication, rate limiting, input validation, and injection detection before reaching the engine. All memory content can be encrypted at rest with AES-256-GCM, and comprehensive audit logging provides full operational visibility.
Security layers
| Layer | Protection | Configuration |
|---|---|---|
| Encryption at Rest | AES-256-GCM authenticated encryption | DAKERA_ENCRYPTION_KEY |
| API Authentication | Hierarchical key scopes with namespace restriction | DAKERA_ROOT_API_KEY |
| Input Validation | 7 injection patterns blocked, field limits, depth limits | Built-in — always active |
| Security Headers | CORS, CSP, HSTS, X-Frame-Options, and more | 3 presets (default, permissive, strict) |
| Rate Limiting | Global token bucket + per-namespace sliding-window | RATE_LIMIT_RPS, RATE_LIMIT_BURST |
| Audit Logging | HTTP request audit + business event audit | DAKERA_AUDIT_ENABLED=true |
Input validation
Dakera's input validation layer is always active and cannot be disabled. It detects and blocks 7 categories of injection attacks before any data reaches the engine:
| # | Pattern | What it blocks |
|---|---|---|
| 1 | SQL injection | Classic SQL payloads ('; DROP TABLE, UNION SELECT, tautologies) |
| 2 | NoSQL injection | MongoDB-style operators in unexpected fields ($gt, $ne outside filter context) |
| 3 | Command injection | Shell metacharacters and command chaining (; rm -rf, $(cmd), backticks) |
| 4 | Path traversal | Directory escape sequences (../../etc/passwd, null bytes, encoded slashes) |
| 5 | XSS | Script tags, event handlers, data URIs, encoded HTML entities |
| 6 | SSRF | Internal network addresses, cloud metadata endpoints (169.254.169.254) |
| 7 | Header injection | CRLF sequences, HTTP response splitting attempts |
Field limits
| Field | Limit |
|---|---|
| Memory content | Max body size (DAKERA_MAX_BODY_SIZE, default 10 MB) |
| Tags per memory | 50 tags maximum |
| Tag length | 256 characters per tag |
| Agent ID | 256 characters |
| Namespace name | 128 characters, alphanumeric + hyphens |
| API key name | 128 characters |
| JSON nesting depth | 32 levels maximum |
CORS configuration
Cross-Origin Resource Sharing controls which domains can access Dakera's API from a browser context.
# Allow specific origins (production — recommended)
DAKERA_CORS_ORIGINS=https://app.example.com,https://dashboard.example.com
DAKERA_CORS_CREDENTIALS=true
# Allow all origins (development only)
DAKERA_CORS_ORIGINS=*
DAKERA_CORS_CREDENTIALS=false
CORS_ORIGINS=* with CORS_CREDENTIALS=true — this is a security misconfiguration. In production, always list specific trusted origins.Content Security Policy
Dakera sends CSP headers to prevent XSS and data injection attacks. Three presets are available:
| Preset | Directives | Use case |
|---|---|---|
| Default | default-src 'none'; frame-ancestors 'none' | API-only deployments (no browser UI) |
| Permissive | CSP and HSTS disabled | Development and testing |
| Strict | Restrictive CSP + HSTS with preload | Production with compliance requirements |
# Custom CSP directives
DAKERA_CSP_ENABLED=true
DAKERA_CSP_DIRECTIVES="default-src 'none'; frame-ancestors 'none'; base-uri 'self'"
Security headers
Dakera automatically sets defense-in-depth HTTP headers on every response:
| Header | Value | Configuration |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains | DAKERA_HSTS_ENABLED (default: true), DAKERA_HSTS_MAX_AGE |
X-Frame-Options | DENY | DAKERA_FRAME_OPTIONS (DENY, SAMEORIGIN, or custom) |
X-Content-Type-Options | nosniff | Always on |
X-XSS-Protection | 1; mode=block | Always on |
Referrer-Policy | strict-origin-when-cross-origin | Always on |
Content-Security-Policy | Per preset (see above) | DAKERA_CSP_ENABLED, DAKERA_CSP_DIRECTIVES |
TLS & HTTPS
Dakera does not terminate TLS natively — this is intentional. Running a single binary without TLS complexity keeps the attack surface minimal. TLS termination belongs at the reverse proxy layer (Nginx, Caddy, Traefik, or a cloud load balancer).
See Deployment → Reverse proxy & TLS termination for complete Nginx and Caddy configurations. For gRPC, configure mTLS at the proxy level when client certificate authentication is required.
Encryption at rest
# Enable encryption via environment variable
export DAKERA_ENCRYPTION_KEY="your-passphrase-min-8-chars"
# Or use a raw 256-bit key (64 hex chars)
export DAKERA_ENCRYPTION_KEY=$(openssl rand -hex 32)
# Rotate keys (re-encrypts all memories atomically)
curl -X POST http://localhost:3300/admin/encryption/rotate \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"old_key":"current-passphrase","new_key":"new-passphrase"}'
API key authentication
Scope hierarchy
| Scope | Permissions | Use case |
|---|---|---|
read | Query vectors, recall memories, get namespace info | Read-only clients |
write | Upsert/delete vectors, store/forget memories (includes read) | Applications writing data |
admin | Namespace management, policy config (includes write) | Administrative tools |
super_admin | Key management, cluster ops, encryption rotation (includes admin) | Operators only |
API key lifecycle
The complete workflow from bootstrap to rotation:
# 1. Bootstrap root key (set at server start)
export DAKERA_ROOT_API_KEY=$(openssl rand -hex 32)
# 2. Create a scoped application key
curl -X POST http://localhost:3300/admin/keys \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"query-service","scope":"read","expires_in_days":90}'
# {"key":"dk_abc123...","name":"query-service","scope":"read","expires_at":"2026-08-13T..."}
# 3. Restrict key to a namespace (multi-tenant isolation)
curl -X POST http://localhost:3300/admin/keys \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"tenant-a","scope":"write","namespace":"tenant-a","expires_in_days":90}'
# 4. List all keys
curl http://localhost:3300/admin/keys \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY"
# 5. Monitor key usage
curl http://localhost:3300/admin/keys/dk_abc123/usage \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY"
# 6. Revoke a compromised key
curl -X DELETE http://localhost:3300/admin/keys/dk_abc123 \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY"
Namespace isolation
Namespaces provide hard security boundaries in multi-tenant deployments. A key scoped to namespace tenant-a can only access data within that namespace — it cannot read, write, or even discover other namespaces.
| Isolation property | Guarantee |
|---|---|
| Data isolation | Memories, vectors, entities, and KG nodes are namespace-scoped |
| Key isolation | Namespace-scoped keys cannot access other namespaces |
| Index isolation | Each namespace has independent HNSW/BM25 indexes |
| Policy isolation | Decay, AutoPilot, and retention policies are per-namespace |
# Create an isolated namespace for a tenant
curl -X POST http://localhost:3300/admin/namespaces \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"tenant-a","config":{"vector_index":"hnsw","encryption":true}}'
# Create a key scoped to that namespace
curl -X POST http://localhost:3300/admin/keys \
-H "Authorization: Bearer $DAKERA_ROOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"tenant-a-app","scope":"write","namespace":"tenant-a"}'
Rate limiting
Dakera enforces rate limits at two levels: a global limit (token bucket) and per-namespace sliding window. When Redis is available, rate limiting is distributed across the cluster.
# Global rate limit
RATE_LIMIT_RPS=100 # requests per second (default)
RATE_LIMIT_BURST=50 # burst capacity
When rate limited, clients receive a 429 Too Many Requests response with Retry-After header indicating when to retry.
# Example rate-limited response
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json
{"error":"rate_limited","message":"Too many requests. Retry after 1 second."}
Audit logging
When enabled, Dakera logs every API request with authentication context, timing, and optionally the request/response body. Audit logs provide a complete trail for compliance and debugging.
# Enable full audit logging
DAKERA_AUDIT_ENABLED=true
DAKERA_AUDIT_REQUEST_BODY=true # log request bodies (careful: increases log volume)
DAKERA_AUDIT_RESPONSE_BODY=false # log response bodies
DAKERA_AUDIT_MAX_BODY_SIZE=4096 # truncate bodies larger than 4KB
DAKERA_AUDIT_EXCLUDE_PATHS=/health,/metrics # skip noisy endpoints
Example audit log entry
{
"timestamp": "2026-05-15T14:30:22.456Z",
"method": "POST",
"path": "/v1/memories/store",
"status": 200,
"latency_ms": 12,
"key_name": "query-service",
"key_scope": "write",
"namespace": "production",
"agent_id": "assistant-v2",
"ip": "10.0.1.42",
"user_agent": "dakera-py/0.11.46"
}
Container security
| Practice | Implementation |
|---|---|
| Non-root user | The official image runs as UID 1000 (dakera user) — never as root |
| Read-only filesystem | Mount /data as the only writable volume. All other paths are read-only |
| No shell | Distroless base image — no shell, no package manager, reduced attack surface |
| K8s security context | Set runAsNonRoot: true, readOnlyRootFilesystem: true, drop all capabilities |
| Image signing | GHCR images are signed with Sigstore cosign — verify before deploying |
# Kubernetes security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
Network security
| Port | Service | Exposure |
|---|---|---|
3300 | REST API | Via reverse proxy only — never expose directly to the internet |
50051 | gRPC API | Internal services only — or via gRPC-aware proxy with mTLS |
7946 | Gossip (SWIM) | Cluster nodes only — firewall from external access |
# UFW firewall rules for a Dakera node
sudo ufw allow from any to any port 443 proto tcp # HTTPS via reverse proxy
sudo ufw allow from 10.0.0.0/8 to any port 3300 # REST API from internal network
sudo ufw allow from 10.0.0.0/8 to any port 50051 # gRPC from internal network
sudo ufw allow from 10.0.0.0/8 to any port 7946 # Gossip between cluster nodes
sudo ufw deny 3300 # Block external REST access
sudo ufw deny 50051 # Block external gRPC access
sudo ufw deny 7946 # Block external gossip access
Production security checklist
| Item | Priority |
|---|---|
| Root API key with 256-bit+ entropy | Required |
| Scoped application keys (least privilege) | Required |
| TLS on all external endpoints (reverse proxy) | Required |
| Server behind reverse proxy (never expose port 3300 publicly) | Required |
| Firewall: only expose 443 externally | Required |
Encryption at rest (DAKERA_ENCRYPTION_KEY) | Recommended |
| CORS restricted to application domains | Recommended |
| Rate limiting tuned for workload | Recommended |
| Audit logging enabled | Recommended |
| Key rotation schedule (90 days) | Recommended |
| Namespace-scoped keys for multi-tenant deployments | Recommended |
| Container runs as non-root with read-only filesystem | Recommended |
| Image signature verification before deploy | Recommended |