Security & safety
Kopilot's premise is that an AI agent may propose anything but execute only what a policy — and where it matters, a human — allows. This page describes each layer, and just as importantly, what the layers do not guarantee.
Risk assessment
Every command a sub-agent wants to run is classified before execution:
low (read-only), medium (mutating but not
destructive, e.g. kubectl apply), high
(destructive), or critical (blocked). A task's response
reports the highest risk seen across all commands it attempted.
The approval gate
Commands classified as destructive are not executed. The set includes:
kubectl delete,drain,cordon,taint,evict,replace,patch,scale, andkubectl … --prunehelm uninstall,helm delete,helm rollback- Obfuscated kubectl/helm invocations — command substitution (
$(…), backticks),eval,xargs kubectl/xargs helm— which are gated because they cannot be safely pattern-checked - Destructive commands with
-A/--all-namespaces
Instead of running, the executor registers a pending approval request and
the agent reports the approval id in its answer. A human lists the queue
(GET /approvals) and decides
(POST /approvals/{id}/approve or /deny); an
optional X-Kopilot-Operator header records the decider.
Approvals:
- apply to the exact command (whitespace-normalized),
- are single-use — consumed on execution,
- expire after 10 minutes, and
- are in-memory — per replica, not persisted across restarts.
Protected namespaces
Destructive commands targeting kube-system,
kube-public, or kube-node-lease (configurable
via SAFETY_PROTECTED_NAMESPACES) are refused outright. They
never enter the approval queue — there is no override through the API.
Hard-blocked patterns
Some commands are always refused, in any namespace, with no approval path:
rm -rf / variants, dd of=/dev/…,
mkfs, fork bombs, shutdown/reboot/halt,
and similar host-destructive patterns.
Execution limits
- Rate limit: 50 tool calls per 5 minutes per task.
- Subprocess timeout: 90 seconds; processes are killed as a group.
- Output caps: at most 2 MB read from a process, at most 12,000 characters returned to the model.
- Read-only commands are retried once on timeout; mutating commands are never auto-retried (a timed-out
kubectl applymay have taken effect server-side). read_resourcefile reads are restricted toSAFETY_READ_PATHS(default/etc/kubedevaiops); URL and ConfigMap reads are validated.
API security
- Bearer auth (
API_AUTH_TOKEN) on task submission, history, metrics, and approvals. Unset means open dev mode — the server logs a warning. - Webhook HMAC:
POST /webhookrequires an HMAC-SHA256 signature over the raw body (X-Kopilot-Signature: sha256=<hexdigest>) with theAPI_WEBHOOK_SECRETshared secret; the endpoint is disabled when the secret is unset. - CORS off by default:
API_CORS_ORIGINSis an explicit allowlist, empty by default. - Slack allowlist:
SLACK_ALLOWED_USERSrestricts who can run tasks from Slack.
Audit log
Every significant action is written as a structured JSON event: task submitted, command executed, command blocked, approval requested, approval approved/denied/consumed, webhook received. Ship stdout to your log pipeline and you have a reviewable trail of everything the agent did and who authorized it.
Honest limitations
Read this section before granting the agent write access to anything.
- The gate is pattern-based. It is deliberately conservative — unparseable or indirect commands are treated as risky rather than decoded — but a regex layer can never be a complete security boundary. It is defense in depth.
- RBAC is the real boundary. Commands run under the pod's service account. Scope that service account to the least privilege your use case needs; see the RBAC notes.
- Approvals are not durable. The queue is in-memory, single-replica, and cleared on restart.
- Conversation memory is not durable either — task history and agent state are process-local.
- The LLM can be wrong. The safety layer constrains what runs, not what the model concludes. Treat recommendations as informed drafts and review the evidence the agent cites.