Security Overview
The table below summarizes all active protections across every layer of the stack.| Layer | Protection | Status |
|---|---|---|
| Authentication | JWT tokens with RS256/HS256 + RBAC role enforcement | Active |
| Authentication | Legacy bearer token with constant-time comparison (crypto/subtle) | Active |
| Authentication | OAuth 2.0 + PKCE for Anthropic, OpenAI, GitHub Copilot | Active |
| Authorization | Role-based access control (viewer / operator / admin) | Active |
| Encryption | AES-256-GCM encryption for stored credentials | Active |
| Encryption | Session data encrypted at rest with per-profile keys | Active |
| Transport | TLS 1.3 support for gRPC and REST endpoints | Active |
| Transport | mTLS support with client certificate verification | Active |
| Keychain | OS keychain integration (macOS Keychain, Linux secret-service, Windows Credential Manager) | Active |
| Shell | POSIX quoting to prevent shell injection in arguments | Active |
| Editors | EDITOR validation against allowlist of known editors | Active |
| Agent Commands | 150+ command allowlist (strict mode) or 50+ denylist patterns (permissive mode) | Active |
| Agent Paths | Read path blocking outside workspace directory | Active |
| Agent Shell | Shell config sourcing disabled by default | Active |
| Agent Output | Output sanitizer strips ANSI escape sequences and truncates large output | Active |
| Policies | Word-boundary matching to prevent permission escalation | Active |
| Plugins | Ed25519 signature verification for plugin binaries | Active |
| Plugins | Quarantine period for newly installed unsigned plugins | Active |
| Plugins | Per-plugin permission manifest (network, filesystem, exec) | Active |
| gRPC | SSRF prevention with private IP blocking on outbound requests | Active |
| gRPC | Rate limiting (token bucket) per client IP | Active |
| gRPC | Maximum message size limits (send/receive) | Active |
| gRPC | Maximum concurrent stream limits | Active |
| gRPC | Input validation on all RPC fields | Active |
| gRPC | Reflection disabled by default (hides service schema) | Active |
| Audit | Structured JSON audit logging for all sensitive operations | Active |
| Binaries | stty resolved via exec.LookPath (prevents PATH injection) | Active |
| Containers | Read-only filesystem, no-new-privileges, drop ALL capabilities | Active |
| Kubernetes | Fail-closed authentication for operator webhooks | Active |
| Kubernetes | Resource type allowlist for operator actions | Active |
| Kubernetes | Log scrubbing of secrets and tokens | Active |
| Kubernetes | CORS policy with configurable allowed origins | Active |
| Kubernetes | RBAC namespace-scoped by default, restrictive SecurityContext | Active |
| Kubernetes | NetworkPolicy for pod-level network segmentation | Active |
| Network | TLS optional with warning when disabled | Active |
| Environment | Env variable redaction in logs and error messages | Active |
| History | Disable history recording for sensitive sessions | Active |
| Session | Configurable session TTL with automatic expiration | Active |
| CI/CD | govulncheck, gosec, Dependabot, Cosign image signing | Active |
| Errors | All critical-path errors (io.ReadAll, json.Marshal) handled | Active |
| Proto | Meta field preserved in proto conversion (round-trip SessionData) | Active |
Authentication and Authorization
JWT Authentication (Recommended)
The gRPC and REST servers support JWT-based authentication with configurable issuer, audience, and secret. JWTs carry role claims that map to RBAC policies.- HS256 (Symmetric)
- Via Helm
RBAC Roles
Three built-in roles control access to API endpoints and operations:| Role | Permissions | Typical Use |
|---|---|---|
viewer | Read-only access to incidents, SLOs, analytics, audit logs | Dashboards, monitoring |
operator | Viewer + acknowledge/resolve incidents, approve remediations, manage runbooks | On-call engineers |
admin | Full access including configuration, user management, destructive operations | Platform admins |
The
/Health endpoint is always accessible without authentication to support load balancer and orchestrator health checks.Legacy Bearer Token
For simpler deployments, the server supports static bearer token authentication with constant-time comparison (crypto/subtle.ConstantTimeCompare), preventing timing attacks.
- Via flag
- Via environment variable
OAuth 2.0 + PKCE
ChatCLI supports OAuth 2.0 with PKCE for the following providers:| Provider | Flow | Token Storage |
|---|---|---|
| Anthropic | Authorization Code + PKCE | AES-256-GCM encrypted file |
| OpenAI | Authorization Code + PKCE | AES-256-GCM encrypted file |
| GitHub Copilot | Device Code Flow | AES-256-GCM encrypted file |
Encryption and Data Protection
AES-256-GCM Credential Encryption
All OAuth credentials are encrypted at rest using AES-256-GCM in~/.chatcli/auth-profiles.json. The encryption key is automatically generated and stored with strict permissions.
| File | Permission | Content |
|---|---|---|
~/.chatcli/auth-profiles.json | 0600 | AES-256-GCM encrypted OAuth credentials |
~/.chatcli/.auth-key | 0600 | AES-256-GCM encryption key |
~/.chatcli/coder_policy.json | 0600 | Coder policy rules |
Session Encryption
Session data can be encrypted at rest when a custom encryption key is provided:TLS 1.3 Transport Security
- Server TLS
- Mutual TLS (mTLS)
- Development (no TLS)
If TLS certificate loading fails, the error is written to both stderr and the structured log, including the cert and key paths. In containers, this ensures the error is visible via
kubectl logs even if the structured logger cannot flush before the crash.Environment Variable Redaction
Sensitive environment variables are automatically redacted in logs and error messages. You can control the redaction behavior:OS Keychain Integration
ChatCLI can store encryption keys in the OS keychain instead of the filesystem:| Backend | macOS | Linux | Windows |
|---|---|---|---|
keychain | Keychain.app | secret-service (GNOME Keyring) | Credential Manager |
file | ~/.chatcli/.auth-key | ~/.chatcli/.auth-key | %APPDATA%\chatcli\.auth-key |
auto | Keychain if available, else file | secret-service if available, else file | Credential Manager if available, else file |
Agent Mode Security
Command Allowlist (Strict Mode)
In strict mode, only commands from the allowlist can be executed. The default allowlist includes 150+ safe commands organized by category:File Operations (30+ commands)
File Operations (30+ commands)
Development Tools (40+ commands)
Development Tools (40+ commands)
System Utilities (30+ commands)
System Utilities (30+ commands)
Network and Cloud (30+ commands)
Network and Cloud (30+ commands)
Text Processing (20+ commands)
Text Processing (20+ commands)
Custom Allowlist
Extend the allowlist with your own commands:Denylist Patterns (Permissive Mode)
In permissive mode, 50+ regex patterns detect and block dangerous commands:| Category | Examples |
|---|---|
| Data destruction | rm -rf /, dd if=, mkfs, drop database |
| Remote execution | curl | bash, wget | sh, base64 | bash |
| Code injection | python -c, perl -e, ruby -e, node -e, php -r, eval |
| Command substitution | $(curl ...), `wget ...` |
| Process substitution | <(cmd), >(cmd) |
| Privilege escalation | sudo, chmod 777 /, chown -R / |
| Network manipulation | nc -l, iptables -F, /dev/tcp/ |
| Kernel | insmod, modprobe, rmmod, sysctl -w |
| Evasion | ${IFS;cmd}, VAR=x; bash, export PATH= |
Read Path Blocking
In strict workspace mode, the agent can only read files within the current workspace directory:Shell Configuration Sourcing
By default, shell configuration files (~/.bashrc, ~/.zshrc) are not sourced during agent command execution to prevent malicious aliases and functions:
Input guard — typeahead protection in security prompts
When a security box appears (coder/agent mode), three layers defend against accidental typing being consumed as a y/n response:- Flush kernel TTY —
TCIFLUSH(Linux) /TIOCFLUSH(BSD/Darwin) /FlushConsoleInputBuffer(Windows) discards bytes in the kernel queue before the box renders. - Drain channel — empties the centralized non-blocking stdin channel (the 10-line buffer the reader goroutine uses).
- Intent debounce — discards any input that arrives in the first 250ms after the box is drawn (minimum human reaction window).
stty sane on the controlling /dev/tty to recover from a prior go-prompt teardown that may have left the terminal in raw mode (echo off). Without this reset, you type and don’t see characters on screen — even though the kernel is capturing them.
Output Sanitizer
Agent command output is sanitized before being sent to the LLM:- ANSI escape sequences are stripped
- Output is truncated to prevent context overflow
EDITOR Validation
When the user edits commands in agent mode, theEDITOR variable is validated against an allowlist of known editors:
Kubeconfig Access Control
Control whether agent commands can access kubeconfig:Shell Injection Protection
All code paths where dynamic values are interpolated into shell commands use theutils.ShellQuote() function, which applies POSIX quoting with single quotes:
- Quote injection:
'; rm -rf /; echo ' - Command substitution:
$(malicious)or`malicious` - Variable expansion:
$HOME,${PATH} - Pipe/redirection:
| cat /etc/passwd,> /etc/crontab
Binary Resolution via LookPath
Thestty binary (used to restore the terminal) is resolved once at startup via exec.LookPath("stty"), returning the absolute path. This prevents an attacker from placing a malicious stty in the PATH.
Plugin Security
Ed25519 Signature Verification
ChatCLI plugins are verified using Ed25519 digital signatures. Each plugin binary must be signed with the developer’s private key, and the corresponding public key must be registered.Quarantine for Unsigned Plugins
Unsigned plugins enter a quarantine period before they can execute:Plugin Permission Manifest
Each plugin declares its required permissions in a manifest:| Permission | Description | Default |
|---|---|---|
network | Allow outbound network access | false |
filesystem.read | Allowed read paths | Workspace only |
filesystem.write | Allowed write paths | Workspace only |
exec | Allowed executables | None |
gRPC Server Security
SSRF Prevention
The server blocks outbound requests to private IP ranges, preventing Server-Side Request Forgery attacks:10.0.0.0/8,172.16.0.0/12,192.168.0.0/16(RFC 1918)127.0.0.0/8(loopback)169.254.0.0/16(link-local, including cloud metadata endpoints)::1/128,fc00::/7(IPv6 private)
Rate Limiting
Token-bucket rate limiting protects against abuse and DoS:Message Size Limits
Prevent memory exhaustion from oversized messages:Input Validation
All RPC fields are validated before processing:- String length limits on all text fields
- Enum value validation for severity, status, etc.
- Namespace and resource name format validation (Kubernetes naming rules)
- Timestamp range validation
Audit Logging
All sensitive operations are recorded in structured JSON audit logs:Bind Address
Control which network interface the server listens on:Interceptor Chain
All requests pass through a chain of gRPC interceptors:gRPC Reflection (Disabled by Default)
gRPC reflection exposes the full service schema, allowing tools likegrpcurl and grpcui to discover and call all RPCs. In production, this can facilitate reconnaissance by attackers.
Kubernetes Operator Security
Fail-Closed Authentication
The operator webhook uses fail-closed authentication: if the authentication service is unavailable, all requests are denied. This prevents unauthorized access during outages. API keys are hot-reloaded every 30 seconds with the following priority order:- Secret
chatcli-operator-secrets(priority) —api-keysfield containing a YAML list of{key, role, description}entries - ConfigMap
chatcli-operator-config(fallback) — sameapi-keysfield - Reject the request (or accept in dev-mode if
CHATCLI_OPERATOR_DEV_MODE=true)
Resource Type Allowlist
The operator can only manage resources from a configurable allowlist:Log Scrubbing
Secrets, tokens, and sensitive data are automatically scrubbed from operator logs:CORS Policy
The REST API enforces a configurable CORS policy:RBAC and NetworkPolicy
- Namespace-Scoped RBAC (Default)
- Cluster-Wide RBAC
Pod SecurityContext
The Helm chart defines a restrictive SecurityContext by default:When
securityContext.readOnlyRootFilesystem is true, the chart automatically mounts an emptyDir volume at /tmp (limited to 100Mi) so the application can write temporary files.Operator Dev Mode
For local development, the operator can run in dev mode with relaxed security:Operator TLS
Container Security (Docker)
Thedocker-compose.yml includes the following hardening measures:
| Measure | Protection |
|---|---|
read_only: true | Prevents malware from writing files to the container filesystem |
tmpfs | Provides an in-memory /tmp directory with limited size |
no-new-privileges | Prevents child processes from gaining more privileges than the parent |
| Resource limits | Prevents excessive CPU/memory consumption (DoS) |
CI/CD Security
ChatCLI’s CI/CD pipeline includes multiple security checks:Dependabot
Automated dependency updates with security alerts for vulnerable packages. Configured via
.github/dependabot.yml.Cosign Image Signing
Container images are signed using Sigstore Cosign for supply chain verification.
Coder Mode Governance (Policy Manager)
Word Boundary Matching
The policy system uses word boundary matching to prevent permission escalation by prefix. Example:| Rule | Command | Result |
|---|---|---|
@coder read = allow | @coder read file.txt | Allowed |
@coder read = allow | @coder readlink /tmp | Blocked (ask) |
@coder read --file /etc = deny | @coder read --file /etc/passwd | Blocked (deny) |
/, =, etc.) and not a word continuation (letter, digit, -, _). This ensures that read does not match readlink.
Default Rules
Read commands are allowed by default:Security Environment Variables Reference
Complete reference of all security-related environment variables:Server Security
| Variable | Description | Default |
|---|---|---|
CHATCLI_JWT_SECRET | JWT signing secret (HS256) or path to RSA key (RS256) | "" |
CHATCLI_JWT_ISSUER | Expected JWT issuer claim | "" |
CHATCLI_JWT_AUDIENCE | Expected JWT audience claim | "" |
CHATCLI_RATE_LIMIT_RPS | Rate limit: sustained requests per second | 10 |
CHATCLI_RATE_LIMIT_BURST | Rate limit: burst capacity | 20 |
CHATCLI_MAX_RECV_MSG_SIZE | Maximum gRPC receive message size (bytes) | 4194304 |
CHATCLI_MAX_SEND_MSG_SIZE | Maximum gRPC send message size (bytes) | 4194304 |
CHATCLI_MAX_CONCURRENT_STREAMS | Maximum concurrent gRPC streams per connection | 100 |
CHATCLI_BIND_ADDRESS | Network interface to bind to. Auto-detects 0.0.0.0 in Kubernetes. | 127.0.0.1 / 0.0.0.0 (K8s) |
CHATCLI_AUDIT_LOG_PATH | Path for structured audit log file | "" |
CHATCLI_LOG_FILE | Application log file path | ~/.chatcli/app.log |
CHATCLI_LOG_MAX_SIZE_MB | Max log file size before rotation (MB) | 100 |
CHATCLI_LOG_MAX_BACKUPS | Number of rotated log files to keep | 3 |
CHATCLI_LOG_MAX_AGE_DAYS | Maximum age of rotated log files (days) | 30 |
CHATCLI_DEBUG | Enable debug mode with verbose logging | false |
CHATCLI_ALLOW_HTTP_PROVIDERS | Allow HTTP (non-TLS) connections to LLM providers | false |
CHATCLI_GRPC_REFLECTION | Enable gRPC reflection (use only in dev) | false |
CHATCLI_SERVER_TOKEN | Legacy bearer token for gRPC authentication | "" |
CHATCLI_SERVER_TLS_CERT | Server TLS certificate path | "" |
CHATCLI_SERVER_TLS_KEY | Server TLS key path | "" |
Agent Security
| Variable | Description | Default |
|---|---|---|
CHATCLI_AGENT_SECURITY_MODE | Security mode: strict (allowlist) or permissive (denylist) | permissive |
CHATCLI_AGENT_ALLOWLIST | Extra allowed commands in strict mode (semicolon-separated) | "" |
CHATCLI_AGENT_DENYLIST | Extra denied patterns in permissive mode (semicolon-separated regex) | "" |
CHATCLI_AGENT_WORKSPACE_STRICT | Restrict file access to workspace directory only | false |
CHATCLI_AGENT_ALLOW_KUBECONFIG | Allow agent commands to access kubeconfig | false |
CHATCLI_AGENT_EXTRA_READ_PATHS | Additional allowed read paths (semicolon-separated) | "" |
CHATCLI_AGENT_SOURCE_SHELL_CONFIG | Source shell config (~/.bashrc, etc.) in agent commands | false |
CHATCLI_AGENT_ALLOW_SUDO | Allow sudo without automatic blocking | false |
CHATCLI_AGENT_CMD_TIMEOUT | Timeout per executed command | 10m |
CHATCLI_MAX_COMMAND_OUTPUT | Maximum command output size (bytes) | 65536 |
Plugin and Auth Security
| Variable | Description | Default |
|---|---|---|
CHATCLI_ALLOW_UNSIGNED_PLUGINS | Allow unsigned plugins to execute | false |
CHATCLI_ALLOW_INSECURE | Allow insecure (non-TLS) connections | false |
CHATCLI_TLS_CLIENT_CERT | Client TLS certificate for mTLS | "" |
CHATCLI_TLS_CLIENT_KEY | Client TLS key for mTLS | "" |
CHATCLI_ENCRYPTION_KEY | Custom encryption key for session data | Auto-generated |
CHATCLI_KEYCHAIN_BACKEND | Keychain backend: auto, file, keychain | auto |
CHATCLI_DISABLE_HISTORY | Disable conversation history recording | false |
CHATCLI_SESSION_TTL | Session time-to-live before expiration | 24h |
CHATCLI_ENV_REDACT_MODE | Env redaction: full, partial, none | full |
CHATCLI_REDACT_PATTERNS | Custom redaction patterns (semicolon-separated regex) | "" |
Operator Security
| Variable | Description | Default |
|---|---|---|
CHATCLI_OPERATOR_DEV_MODE | Enable operator dev mode (relaxed security) | false |
CHATCLI_AIOPS_TLS_CERT | AIOps REST API TLS certificate | "" |
CHATCLI_AIOPS_TLS_KEY | AIOps REST API TLS key | "" |
CHATCLI_GRPC_TLS_CERT | gRPC TLS certificate | "" |
CHATCLI_GRPC_TLS_KEY | gRPC TLS key | "" |
CHATCLI_GRPC_TLS_CA | gRPC CA certificate for mTLS verification | "" |
CHATCLI_ALLOWED_RESOURCE_TYPES | Allowed Kubernetes resource types (comma-separated) | deployments,statefulsets,daemonsets |
CHATCLI_LOG_SCRUB_PATTERNS | Patterns to scrub from logs (semicolon-separated regex) | Built-in patterns |
Version Check
ChatCLI automatically checks for newer versions on GitHub. To disable (e.g., air-gapped environments or CI/CD):Production Best Practices
Require plugin signatures
Keep
CHATCLI_ALLOW_UNSIGNED_PLUGINS as false (default). Only install plugins with valid Ed25519 signatures.Keep gRPC reflection disabled
Do not set
CHATCLI_GRPC_REFLECTION=true in production. Use only for local debugging.Use namespace-scoped RBAC
Keep
rbac.clusterWide: false (default) unless you need to monitor multiple namespaces.Next Steps
Coder Mode Governance
Policy rules to control what the Coder can execute.
Configure the Server
Deploy and configure the gRPC server.
Deploy with Docker and Helm
Complete containerized deployment guide.
Environment Variables
Complete environment variables reference.
K8s Operator
AIOps autonomous remediation platform.
Plugin System
Extend ChatCLI with custom plugins.