Security
Account lockout, session hardening, CSP, and audit events.
Koda is shipped to run agents in production. Its security posture is built around three ideas: fail closed on required infrastructure, log everything that could matter later, and don't allow accidental downgrades from hardened defaults. This page summarises the controls in place.
Authentication
- Single-user owner account created on first boot. No self-service registration beyond that.
- Password policy — 12+ characters, 3 of 4 character classes, no identifier substring, not in the top-500 common passwords list, Shannon entropy ≥ 2.0 bits/char.
- Sessions — 32-byte URL-safe random token, SHA-256 hashed in storage. Default TTL 7 days. Transport is the encrypted
koda_operator_sessioncookie withHttpOnly; Secure; SameSite=Strict. - Recovery codes — ten Argon2-hashed single-use codes, shown once on registration. All remaining codes invalidate when any one is used.
Session revocation
- Logout — revokes the current session.
- Password change — revokes every other session; keeps the initiating session active.
- Password reset via recovery code — revokes all sessions.
Rate limits and lockout
Per-IP buckets back every auth-adjacent route. Response latency is floored at ~300 ms on failure paths to blunt timing-based enumeration.
POST /auth/login— 5 req / 5 min / IP.POST /auth/password/recover— 5 req / hour / IP.POST /auth/password/change— 10 req / hour / user.POST /auth/register-owner— 3 req / hour / IP.- General operator bucket — 120 req / min (via
CONTROL_PLANE_RATE_LIMIT). - Account lockout — after
CONTROL_PLANE_OPERATOR_LOGIN_MAX_FAILURES(default 5) the account locks forCONTROL_PLANE_OPERATOR_LOGIN_LOCKOUT_SECONDS(default 15 min).
Production refusals
Several combinations are refused at boot when KODA_ENV=production. Koda exits before accepting traffic.
CONTROL_PLANE_AUTH_MODE=developmentoropen.ALLOW_LOOPBACK_BOOTSTRAP=true.ALLOW_INSECURE_COOKIES=true.ALLOW_INSECURE_WEB_OPERATOR_SESSION_SECRET=true.
Content Security Policy
/login, /setup, and /forgot-password enforce a strict CSP with no 'unsafe-inline' or 'unsafe-eval'. Every script is served with a nonce. Patching 'unsafe-inline' back in to work around a third-party script would break the hardening that keeps credential-entry pages resistant to XSS.
Bootstrap flow
- Code generation. Short-lived bootstrap codes are written to
$${STATE_ROOT_DIR}/control_plane/bootstrap.txtwith mode0600, printed once to the container log, and deleted after successful registration. - Loopback trust. On development hosts with
ALLOW_LOOPBACK_BOOTSTRAP=true, requests originating from127.0.0.1skip the code challenge. Refused in production. - Regeneration.
koda auth issue-codeprints a new code without restarting the stack. Rate limited to prevent flooding.
Secrets at rest
Every secret (provider credentials, bot tokens, integration passwords, per-agent secrets) is encrypted before it touches Postgres using the master key pointed at by CONTROL_PLANE_MASTER_KEY_FILE. The key file itself sits outside the database, owned by the service user with mode 0600, and is the single item you need to protect most carefully on the host.
Runtime validation
The security gRPC service (:50065) validates every risky runtime operation before it executes:
- Shell commands — checked for injection patterns and blocked-command lists.
- Environment variables — sanitised to remove sensitive keys before handoff to child processes.
- Runtime paths — validated for traversal attempts and resolved to absolute paths.
- S3 object keys — validated against the bucket's permitted prefix.
- Log values — redacted before emission (credentials, tokens, long opaque strings).
- Filesystem policy — reads and writes are checked against the agent's declared filesystem scope.
Audit trail
Every security-sensitive event writes a structured security.* record through the emit_security() helper. Events are queryable from the dashboard's Operations view and from /api/control-plane/audit (when audit export is enabled).
Representative event names:
security.auth.login_success/security.auth.login_failuresecurity.auth.password_changed/security.auth.password_resetsecurity.auth.recovery_code_usedsecurity.session.created/security.session.revokedsecurity.telegram.rejectedsecurity.runtime.shell_blocked
Hardening checklist
KODA_ENV=production,ALLOW_LOOPBACK_BOOTSTRAP=false.- HTTPS everywhere with
Secure,HttpOnly,SameSite=Strictcookies. - Reverse proxy fronting
127.0.0.1bindings; Postgres and SeaweedFS never exposed publicly. .env, master key, and bootstrap files owned by the service user, mode0600.WEB_OPERATOR_SESSION_SECRETset to 32+ random bytes and rotated on a schedule.CONTROL_PLANE_API_TOKENblank unless needed; rotate when set.- Audit events exported to your SIEM of choice.
Find something concerning? The repo's SECURITY.md is the authoritative disclosure path. Do not file public issues for vulnerabilities.
Next steps
- Monitoring — health probes and observability surfaces.
- Troubleshooting — what to check first when something looks wrong.