Loki Mode v5.51.0 -- Enterprise Security
February 23, 2026 ยท View on GitHub
Overview
Loki Mode's security model follows a defense-in-depth approach. All security features are opt-in via environment variables, with sensible defaults that prioritize safety. When enterprise auth is not configured, the system operates in local-only mode with no network exposure.
Authentication
Token Authentication
The primary authentication method uses bearer tokens prefixed with loki_.
Configuration:
# Enable token auth for the dashboard/control plane API
export LOKI_ENTERPRISE_AUTH=true
Token Lifecycle:
# Create a new API key
curl -X POST http://localhost:57374/api/keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "ci-pipeline", "role": "operator"}'
# Rotate a key with 24-hour grace period
curl -X POST http://localhost:57374/api/keys/ci-pipeline/rotate \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"grace_period_hours": 24}'
# Revoke a key
curl -X DELETE http://localhost:57374/api/keys/ci-pipeline \
-H "Authorization: Bearer $ADMIN_TOKEN"
SDK Usage:
Python:
from loki_mode_sdk import AutonomiClient
client = AutonomiClient(
base_url="http://localhost:57374",
token="loki_your_token_here"
)
TypeScript:
import { AutonomiClient } from 'loki-mode-sdk';
const client = new AutonomiClient({
baseUrl: 'http://localhost:57374',
token: 'loki_your_token_here'
});
OIDC / SSO Integration
For organizations using identity providers (Okta, Auth0, Azure AD), Loki Mode supports OIDC-based authentication.
Configuration:
export LOKI_OIDC_ISSUER="https://your-idp.okta.com/oauth2/default"
export LOKI_OIDC_CLIENT_ID="your-client-id"
export LOKI_OIDC_CLIENT_SECRET="your-client-secret"
export LOKI_OIDC_REDIRECT_URI="http://localhost:57374/auth/callback"
The system validates OIDC tokens against the issuer's JWKS endpoint and maps claims to Loki Mode roles.
Authorization
Role-Based Access Control
Four built-in roles provide scoped access:
| Role | Scope | Permissions |
|---|---|---|
admin | Full control | Create/delete tenants, manage API keys, configure policies, all operator/viewer/auditor permissions |
operator | Execution | Start/cancel runs, create/update projects and tasks, read all resources |
viewer | Read-only | Read projects, runs, tasks, status. No write or delete operations |
auditor | Audit access | Read audit logs, verify chain integrity, generate compliance reports. No execution permissions |
Scope Hierarchy:
admin > operator > viewer
admin > auditor
Admin inherits all permissions. Operator inherits viewer permissions. Auditor is a separate branch with read-only access to audit data.
API Key Scopes:
API keys are created with a role assignment:
# Create a viewer key for monitoring dashboards
curl -X POST http://localhost:57374/api/keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"name": "grafana-reader", "role": "viewer"}'
# Create an operator key for CI pipelines
curl -X POST http://localhost:57374/api/keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"name": "github-actions", "role": "operator"}'
API Security
TLS Configuration
Enable HTTPS for the dashboard and API:
export LOKI_TLS_CERT="/path/to/cert.pem"
export LOKI_TLS_KEY="/path/to/key.pem"
When both variables are set, the dashboard server binds on HTTPS only. Self-signed certificates are supported for development.
Rate Limiting
The API enforces rate limits per token/IP:
| Endpoint Group | Default Limit |
|---|---|
| Read operations | 100 req/min |
| Write operations | 30 req/min |
| Auth operations | 10 req/min |
Rate limit headers are included in every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1708520460
CORS
CORS is configured via environment variables:
export LOKI_CORS_ORIGINS="https://dashboard.company.com,https://admin.company.com"
Default behavior: localhost origins are allowed for development. In production, explicitly set allowed origins.
Webhook Security
HMAC-SHA256 Verification
All outbound webhooks and inbound webhook handlers use HMAC-SHA256 for request authentication.
Slack Webhooks:
export LOKI_SLACK_SIGNING_SECRET="your-slack-signing-secret"
Inbound Slack requests are verified using the X-Slack-Signature and X-Slack-Request-Timestamp headers. The system:
- Checks timestamp is within 5 minutes (replay attack prevention)
- Computes
HMAC-SHA256(signing_secret, "v0:" + timestamp + ":" + body) - Compares against the
X-Slack-Signatureheader using constant-time comparison
Microsoft Teams Webhooks:
export LOKI_TEAMS_WEBHOOK_SECRET="your-shared-secret"
Inbound Teams requests are verified using the shared secret HMAC.
Fail-Closed Behavior:
If a webhook signature cannot be verified:
- The request is rejected with HTTP 401
- An audit entry is recorded with
success: false - The event is not processed
This is a deliberate fail-closed design. Invalid or missing signatures always result in rejection.
Audit Logging
Hash-Chained Tamper-Evident Logs
Audit logs use SHA-256 hash chains for tamper evidence. Each entry's hash depends on the previous entry's hash, creating an immutable chain from the genesis record.
JavaScript Audit (Agent Actions):
.loki/audit/audit.jsonl
Each entry:
{
"seq": 42,
"timestamp": "2026-02-21T10:00:00.000Z",
"who": "agent-1",
"what": "file_write",
"where": "src/app.js",
"why": "implement feature",
"previousHash": "abc123...",
"hash": "def456..."
}
Python Audit (Dashboard API):
~/.loki/dashboard/audit/audit-2026-02-21.jsonl
Each entry:
{
"timestamp": "2026-02-21T10:00:00.000Z",
"action": "create",
"resource_type": "project",
"resource_id": "proj-1",
"user_id": "admin",
"success": true,
"_integrity_hash": "abc123..."
}
Chain Verification
JavaScript:
const audit = require('./src/audit');
audit.init('/path/to/project');
const result = audit.verifyChain();
// { valid: true, entries: 1542, brokenAt: null, error: null }
Python:
from dashboard.audit import verify_log_integrity
result = verify_log_integrity("/path/to/audit-2026-02-21.jsonl")
# {"valid": True, "entries_checked": 1542, "first_tampered_line": None}
API:
curl http://localhost:57374/api/audit/verify \
-H "Authorization: Bearer $TOKEN"
Syslog Forwarding
Forward audit events to a centralized syslog server for SIEM integration:
export LOKI_AUDIT_SYSLOG_HOST="syslog.company.com"
export LOKI_AUDIT_SYSLOG_PORT="514" # default
export LOKI_AUDIT_SYSLOG_PROTO="udp" # or "tcp"
Security-relevant actions (delete, kill, stop, login, logout, create_token, revoke_token) are logged at WARNING level. All other actions are logged at INFO level.
Syslog forwarding is fire-and-forget -- failures never block the main audit write path.
Log Rotation
export LOKI_AUDIT_MAX_SIZE_MB="10" # Rotate when file exceeds this size
export LOKI_AUDIT_MAX_FILES="10" # Keep this many rotated files
Date-based log files (audit-YYYY-MM-DD.jsonl) are automatically rotated when they exceed the size limit. Old files are removed when the count exceeds the maximum.
Data Residency
Local Data Storage
All Loki Mode data is stored locally on the machine running the system:
| Data Type | Location |
|---|---|
| Project state | .loki/ (project directory) |
| Audit logs (agent) | .loki/audit/ |
| Audit logs (dashboard) | ~/.loki/dashboard/audit/ |
| Event bus | .loki/events/ |
| Memory system | .loki/memory/ |
| Metrics | .loki/metrics/ |
| Policy files | .loki/policies.yaml or .loki/policies.json |
| Configuration | .loki/config.yaml or .loki/config.json |
No data is transmitted to external services unless explicitly configured (OTEL endpoint, integration webhooks, syslog forwarding).
Provider Restrictions
The data residency controller restricts which AI providers can be used based on region:
const audit = require('./src/audit');
audit.init('/path/to/project');
// Check if a provider is allowed in a region
const allowed = audit.checkProvider('anthropic', 'us'); // true/false
// Check if air-gapped mode is enabled
const airGapped = audit.isAirGapped();
Configure via .loki/config.yaml:
residency:
allowed_providers:
- anthropic
- openai
allowed_regions:
- us
- eu
air_gapped: false
Policy Engine Security
Pre-Execution Policy Checks
The policy engine evaluates rules before any agent action executes:
const policy = require('./src/policies');
policy.init('/path/to/project');
const result = policy.evaluate('pre_execution', {
file_path: '/tmp/malicious.sh',
project_dir: '/home/user/project',
active_agents: 3,
});
if (!result.allowed) {
// result.decision === 'DENY' or 'REQUIRE_APPROVAL'
// result.reason describes the violation
// result.violations array contains details
process.exit(1);
}
Path Traversal Protection
The file_path must start with project_dir rule uses path.resolve() to normalize paths before comparison, preventing traversal attacks like:
/project/../etc/passwd -> resolves to /etc/passwd -> DENIED
/project-evil/secret -> prefix check with path.sep -> DENIED
Secret Detection
The data policy scanner detects secrets in content before it leaves the system:
- API keys and tokens
- AWS access keys
- Private keys (RSA, EC)
- GitHub personal access tokens
- OpenAI API keys
- Slack bot tokens
When a secret is detected, the policy engine returns DENY and the content is not transmitted.
PII Scanning
Optional PII scanning detects:
- Email addresses
- Social Security Numbers
- Phone numbers
- Credit card numbers
Security Best Practices
- Always enable audit logging in production -- It is on by default; do not set
LOKI_AUDIT_DISABLED=truein production. - Use TLS for the dashboard -- Set
LOKI_TLS_CERTandLOKI_TLS_KEYwhen the dashboard is network-accessible. - Rotate API keys regularly -- Use the
/api/keys/{id}/rotateendpoint with grace periods. - Configure syslog forwarding -- Forward audit events to your SIEM for centralized monitoring.
- Set token budgets -- Use resource policies to prevent runaway token consumption.
- Enable data scanning -- Configure
datapolicies forsecret_detectionto catch accidental credential exposure. - Restrict providers by region -- Use the data residency controller to enforce compliance with data sovereignty requirements.
- Verify audit chain integrity -- Periodically run
verifyChain()or call/api/audit/verifyto detect tampering.