Skip to main content
This page is the exhaustive technical reference for every security mechanism in Comis, organized by processing stage. Each section documents the exact rules, thresholds, pattern weights, and default values as implemented in the source code. For a user-friendly overview of how these layers work together, see Defense in Depth.

Threat Model

Comis operates under a single root assumption: the LLM is the attack surface. Every other design decision flows from that.

Trust Boundaries

SideWhat lives thereTrust
TrustedOperator config, the daemon process, secrets in the encrypted store, the action registry, the gateway token storeAuthoritative
Semi-trustedThe LLM (Anthropic / OpenAI / etc.)Verified outputs only
AdversarialUser messages on chat channels, fetched web pages, emails, MCP tool outputs, RAG memory entries fetched at runtimeTreated as untrusted by default
Every external input is wrapped, scanned, or filtered before it reaches the prompt. Every model output is scanned before it reaches the user. Every tool call is classified before it executes. The architecture never assumes any single check will catch everything.

STRIDE Coverage

ThreatCovered by
SpoofingBearer token + mTLS at gateway; email sender filter
TamperingAES-256-GCM auth tags on encrypted secrets; config redaction
RepudiationAudit aggregator + structured event schema with traceId
Information disclosureOutput guard, log sanitizer, config redaction, scoped secrets, canary tokens
Denial of serviceInjection rate limiter, budget guard, circuit breaker, audit aggregator dedup, ReDoS guard on log sanitizer
Elevation of privilegeAction classifier (fail-closed), approval gate, exec sandbox, exec security validator, safe path, SSRF guard, tool policy

Input Layer

The input layer validates and scans incoming messages before they reach the agent. It runs four mechanisms in sequence: structural validation, jailbreak detection, injection rate limiting, and external content wrapping.

Input Validation

Structural validation catches malformed and adversarial payloads before any semantic analysis. Pure function with no side effects. Checks performed:
CheckThresholdDetection
Message length100,000 characters maxMessages exceeding limit flagged as length_exceeded
Null bytesAny \0 characterDetected, sanitized (removed), flagged as null_bytes_detected
Whitespace ratioAbove 70%Ratio of whitespace to total characters, flagged as whitespace_ratio
Character repetition50+ consecutive identical charactersRegex (.)\1{49,}, flagged as excessive_repetition
The validation result contains:
  • valid: true if no structural issues found
  • reasons: Array of human-readable reason codes
  • sanitized: Copy of input with null bytes removed (original preserved for audit)
Source: validateInput() in packages/core/src/security/input-validator.ts

Input Guard (Jailbreak Detection)

The Input Guard performs semantic jailbreak detection using weighted compound phrase patterns, typoglycemia detection, and code block exclusion. It scores input text on a 0.0-1.0 scale.

Pattern Categories and Weights

Each category groups related regex patterns. If any pattern in a category matches, the category weight is added once (boolean per category — multiple matches within the same category do not multiply the weight).
CategoryWeightPatterns Detected
ignore_instructions0.6”Ignore previous instructions”, “ignore all instructions”
disregard_previous0.5”Disregard previous”, “disregard your instructions”
forget_instructions0.5”Forget everything”, “forget your instructions”
role_assumption0.4”You are now [role]”, “you are now a/an”
new_instructions0.5”New instructions”, “new instructions:“
important_override0.5”Important: override” and variants
override_safety0.6”Override safety” and variants
act_as_role0.4”Act as [role]” pattern
context_reset0.4Context reset manipulation
rule_replacement0.4Rule replacement attempts
system_markers0.3<system> tags, [system] brackets, system command markers
special_tokens0.3Special token delimiters (<|...|> patterns)
role_markers0.2Role boundary markers, assistant role markers
The 13 categories in the table above are evaluated each scan. Pattern constants are imported from injection-patterns.ts.

Risk Levels and Thresholds

Risk LevelScore RangeAction
LowBelow 0.4pass — no action taken
Medium0.4 to below 0.7reinforce — system prompt reinforcement injected
High0.7 and abovewarn (default) or block (if configured)

Typoglycemia Detection

The guard detects scrambled-middle variants of 8 key jailbreak terms. Each match adds 0.3 to the score. A word is a typoglycemia variant if it has the same length, same first and last characters, same sorted middle characters, but is not an exact match.
TermExample Variant
ignoreignroe
previouspreivous
instructionsinsrtuctinos
systemsysetm
bypassbyapss
overrideovrreide
forgetfroget
deletedleete

Code Block Exclusion

Content inside fenced code blocks (triple backticks) and inline code (single backticks) is stripped before pattern matching. This minimizes false positives on technical content that legitimately discusses prompt injection or system commands.

InputSecurityGuardConfig Interface

FieldTypeDefaultDescription
mediumThresholdnumber0.4Score threshold for medium risk
highThresholdnumber0.7Score threshold for high risk
action"warn" | "block""warn"Action for high-risk detections
Source: createInputSecurityGuard() and PATTERN_WEIGHTS in packages/core/src/security/input-security-guard.ts

Injection Rate Limiter

Tracks repeated high-score injection detections per user with a sliding window approach. Each user is tracked independently by tenantId:userId key.
SettingDefaultDescription
windowMs300000 (5 min)Time window for counting detections
warnThreshold3Detection count that triggers warn level
auditThreshold5Detection count that triggers audit level
entryTtlMs300000 (5 min)TTL for inactive entries
maxEntries10000Maximum tracked users (prevents memory leaks)
Threshold behavior:
CountLevelEvent
Below 3noneNo action
3 (exactly)warnthresholdCrossed: true on the 3rd detection
4warnthresholdCrossed: false (already warned)
5 (exactly)auditthresholdCrossed: true on the 5th detection
Above 5auditthresholdCrossed: false (already audited)
When maxEntries is reached, the oldest entry (by most recent timestamp) is evicted. Timers use unref() for clean daemon shutdown.
Source: createInjectionRateLimiter() in packages/core/src/security/injection-rate-limiter.ts

External Content Wrapping

External content from untrusted sources (emails, webhooks, APIs, web tools) is wrapped with random delimiters and security warnings before passing to the LLM. Delimiter generation: Each session gets a deterministic random delimiter from AsyncLocalStorage context (or a fresh 24-hex-character delimiter from randomBytes(12)). The wrapping format is:
<<<UNTRUSTED_{delimiter}>>>
Source: Email
From: sender@example.com
Subject: Help request
---
[content here]
<<<END_UNTRUSTED_{delimiter}>>>
Source types: email, webhook, api, channel_metadata, web_search, web_fetch, document, unknown. Marker sanitization: If the content itself contains delimiter patterns (static <<<EXTERNAL_UNTRUSTED_CONTENT>>> or dynamic <<<UNTRUSTED_hex>>>), they are replaced with [[MARKER_SANITIZED]] before wrapping. This also handles fullwidth Unicode equivalents to prevent bypass via character substitution. Suspicious pattern detection: Content is scanned against 17 injection patterns from injection-patterns.ts. When patterns are found, an onSuspiciousContent callback fires with the source, matched patterns, content length, and sender.
Source: wrapExternalContent() in packages/core/src/security/external-content.ts

Skill Layer

The skill layer scans and sanitizes prompt skill content at load time, before it can influence agent behavior.

Content Scanner

Scans skill body content for dangerous patterns across 6 categories. Pure function with no side effects — callers handle audit events and blocking decisions.
CategoryRulesSeverityDescription
exec_injection4 rulesCRITICALSubshell injection $(cmd), backtick injection, eval(), pipe to shell
env_harvesting3 rulesWARNprintenv, /proc/self/environ, env dump piped to encoding/exfiltration
crypto_mining3 rulesCRITICAL/WARNstratum:// protocol, known miner binaries (CRITICAL); pool domains (WARN)
network_exfiltration3 rulesWARN/CRITICALcurl | bash (WARN), wget -O- (WARN); reverse shell patterns (CRITICAL)
obfuscated_encoding3 rulesWARN/CRITICALLong base64 80+ chars (WARN), long hex 20+ sequences (WARN); base64 decode piped (CRITICAL)
xml_breakout2 rulesCRITICALClosing skill XML tags, system-level message tags (breakout attempts)
Total: 18 scan rules across 6 categories in the skill scanner. (The broader injection-pattern library aggregates 65 patterns across 8 categories — see the table below.)
Source: scanSkillContent() and CONTENT_SCAN_RULES in packages/skills/src/prompt/content-scanner.ts

Injection Pattern Library (cross-cutting)

The injection-pattern library powers multiple layers (input guard, external content wrapper, memory write validator, output guard, log sanitizer). It totals 65 distinct patterns across 8 categories:
CategoryCountModuleUsed by
Jailbreak17patterns/jailbreak.tsInput guard, external content, memory write
Role markers6patterns/role-markers.tsInput guard
Dangerous commands4patterns/dangerous-commands.tsMemory write (critical), external content
Secret formats5patterns/secret-formats.tsOutput guard
Prompt extraction2patterns/prompt-extraction.tsOutput guard
Credential logging6patterns/credential-log.tsLog sanitizer
Invisible chars2patterns/invisible-chars.tsSanitization pipeline
Content scanner23patterns/content-scanner.tsSkill content scanner, workspace scanner

Sanitization Pipeline

Skill body content passes through a 4-step pipeline before reaching the system prompt:
StepOperationPurpose
1Strip HTML commentsRemove <!-- hidden content --> that could contain injection
2NFKC normalizationDecompose fullwidth/ligature characters to canonical form
3Strip invisible charactersRemove zero-width characters including Unicode tag block bypass
4Enforce size limitTruncate to maxBodyLength with [TRUNCATED] marker
Size enforcement applies to the final sanitized output, not the raw input. This prevents unnecessary truncation when HTML comments inflate the raw size.
Source: sanitizeSkillBody() in packages/skills/src/prompt/sanitizer.ts

Execution Layer

The execution layer classifies actions, enforces approval gates, prevents SSRF, and validates file paths before any side effects occur.

Action Classifier

Every action in the system is classified by risk level. Unknown actions default to "destructive" (fail-closed principle).
ClassificationDescriptionExample Actions
readNo side effects, safe to auto-approvefile.read, memory.search, config.read
mutateModifiable side effects, loggedfile.write, memory.store, message.send
destructiveIrreversible or high-risk, may require confirmationfile.delete, memory.clear, system.shutdown
The registry contains 178 registered actions across 21 categories. After bootstrap, the registry is locked via lockRegistry() to prevent runtime classification downgrades by malicious plugins. For the complete action registry, see Action Classifier.
Source: classifyAction() and ACTION_REGISTRY in packages/core/src/security/action-classifier.ts

Approval Gate

The approval system controls whether agent-initiated actions proceed automatically, require human confirmation, or are denied. Three modes:
ModeBehavior
autoAction proceeds without human confirmation
requireAction pauses until human approves or denies
denyAction is rejected immediately
Configuration layers: Comis has two distinct configuration sections that interact:
  1. security.actionConfirmation — Quick toggle for destructive/sensitive action confirmation
  2. approvals — Full rule-based approval workflow with pattern matching
The approvals system evaluates rules in order (first match wins). Each rule matches action types by pattern and specifies a mode, timeout, and minimum trust level. Approval rule fields:
FieldTypeDefaultDescription
actionPatternstring(required)Pattern matching action types
mode"auto" | "require" | "deny""auto"Approval behavior
timeoutMsnumber300000 (5 min)Timeout for human approval (0 = no timeout)
minTrustLevel"untrusted" | "basic" | "verified" | "admin""verified"Trust level required to auto-approve
Source: ApprovalRuleSchema and ApprovalsConfigSchema in packages/core/src/config/schema-approvals.ts

SSRF Guard

Validates URLs before any outbound HTTP request to prevent Server-Side Request Forgery. Uses DNS-pinned validation: the URL is resolved to an IP address and checked against blocked ranges before the actual fetch. Blocked IP ranges:
RangeDescription
privateRFC 1918 addresses (10.x, 172.16-31.x, 192.168.x)
loopback127.0.0.0/8 and ::1
linkLocal169.254.0.0/16 and fe80::/10
uniqueLocalfc00::/7 (IPv6 private)
unspecified0.0.0.0 and ::
reservedIANA reserved ranges
Cloud metadata IPs (explicitly blocked):
IPService
169.254.169.254AWS, GCP, Azure instance metadata
169.254.170.2AWS ECS task metadata
100.100.100.200Alibaba Cloud metadata
Protocol check: Only http: and https: protocols are allowed. Validation sequence:
  1. Parse URL (reject invalid)
  2. Check protocol (reject non-HTTP/HTTPS)
  3. DNS resolution (reject unresolvable hostnames)
  4. Cloud metadata IP check (reject explicit metadata IPs)
  5. IP range classification (reject private/loopback/link-local/reserved)
Source: validateUrl(), BLOCKED_RANGES, and CLOUD_METADATA_IPS in packages/core/src/security/ssrf-guard.ts

Exec Security Validator

Pre-sandbox shell-substitution check applied to every system.exec command string. Implemented as a quote-aware state machine (ShellQuoteTracker) that tracks normal / single / double / backtick context.
PatternBehavior
Command substitution $(...)Rejected outside single quotes
Backtick substitution `...`Rejected outside single quotes
Process substitution <(...) / >(...)Rejected
Zsh process substitution =(...)Rejected
Zsh equals expansion =cmd at word boundaryRejected
Returns null if safe, or an error message describing the dangerous pattern. The validator runs before the OS sandbox so it never even reaches bubblewrap/sandbox-exec. Quote-awareness avoids false positives on legitimate strings like printf '$(echo)' where the shell does not interpret the substitution.
Source: validateExecCommand() and ShellQuoteTracker in packages/skills/src/builtin/exec-security.ts

Email Sender Filter

Allowlist gating for the email channel.
ModeBehavior
allowlist (default)Reject every sender not in allowFrom (case-insensitive)
openAccept any sender (not recommended for production)
Independent of the allowlist, isAutomatedSender() rejects bulk and automated mail by inspecting:
  • RFC 3834 Auto-Submitted header
  • Precedence: bulk | junk | list
  • List-Unsubscribe header
  • X-Auto-Response-Suppress header
  • noreply / no-reply address patterns
Source: isAllowedSender() and isAutomatedSender() in packages/channels/src/email/sender-filter.ts

Safe Path

Validates file paths to prevent directory traversal attacks. Returns a resolved, validated absolute path that is guaranteed to stay within the base directory. Attack vectors defended:
VectorDefense
Basic traversal (../)Path resolution + prefix check
URL-encoded traversal (%2e%2e%2f)decodeURIComponent before resolution
Prefix attacks (/uploads vs /uploads-evil)Trailing separator in prefix check
Null byte injectionExplicit \0 check in all segments
Symlink escapesWalk each path component, check lstat for symlinks resolving outside base
For the complete safe path API, see Safe Path.
Source: safePath() in packages/core/src/security/safe-path.ts

Credential Broker (Network Layer)

What it does. For API-key CLIs driven from the exec sandbox, the credential broker acts as an in-process HTTPS MITM proxy. The real key never enters the sandbox — the broker resolves it per-request from SecretManager and injects it at the TLS boundary. On Linux, the credentialed sandbox runs in broker-only network mode, where --unshare-net is applied and the broker unix socket is the only bind-mounted network path. This kernel-enforcement is validated on the Linux production host class: direct egress from inside the namespace fails (network unreachable), while the bound broker socket stays reachable. The broker is fail-closed: a missing binding returns 403, a missing secret returns 502, and a forged proxy token returns 407. No path forwards the request without a valid credential. All broker activity is audited via broker:* events carrying agentId and traceId. Network modes (SandboxOptions.network):
Modebwrap argsDescription
open (default)--unshare-all --share-netStandard exec sandbox; full network access
broker-only--unshare-all --unshare-net --bind <socketPath> <socketPath>Driven-CLI sandbox; only broker unix socket reachable (Linux only)
Source: packages/infra/src/credential-broker/mitm-broker.ts — fail-closed gates; packages/skills/src/tools/builtin/sandbox/bwrap-provider.tsbroker-only branch. See Credential Broker for the full deep-dive.

Output Layer

The output layer scans agent responses before they reach the user, catching leaked secrets, canary tokens, and prompt extraction attempts.

Output Guard

Scans LLM responses for secret patterns, canary token leakage, and prompt extraction attempts. Critical findings are blocked and redacted; warning-level findings are reported but not redacted.

Secret Patterns

Critical patterns (blocked and redacted):
Pattern NameDescriptionRedaction
aws_keyAWS access key IDs (AKIA...)[REDACTED:aws_key]
hex_secret_3232+ character hex secrets[REDACTED:hex_secret_32]
base64_secretBase64-encoded secrets[REDACTED:base64_secret]
private_key_header-----BEGIN ... PRIVATE KEY-----[REDACTED:private_key_header]
github_tokenGitHub tokens (ghp_, gho_, etc.)[REDACTED:github_token]
slack_tokenSlack tokens (xapp-, etc.)[REDACTED:slack_token]
anthropic_keyAnthropic API keys (sk-ant-)[REDACTED:anthropic_key]
openai_project_keyOpenAI project keys (sk-proj-)[REDACTED:openai_project_key]
telegram_bot_tokenTelegram bot tokens (digits:alphanumeric)[REDACTED:telegram_bot_token]
discord_bot_tokenDiscord bot tokens (dot-separated segments)[REDACTED:discord_bot_token]
google_api_keyGoogle API keys (AIzaSy...)[REDACTED:google_api_key]
db_connection_stringDatabase connection strings (postgres://, etc.)[REDACTED:db_connection_string]
generic_api_keyGeneric API key assignments[REDACTED:generic_api_key]
Warning patterns (detected but not redacted):
Pattern NameDescription
bearer_tokenBearer token patterns
jwt_tokenJWT tokens (three dot-separated base64url segments)

Prompt Extraction Patterns

Prompt extraction patterns (warning severity, detect-only):
Pattern NameDescription
system_prompt_labelLabels like “System prompt:”, “My instructions:“
instructions_labelLabels indicating system instruction disclosure

Canary Token Check

If a canaryToken is provided in the scan context and found in the response, it is redacted as [REDACTED:canary] with critical severity. This indicates the agent leaked its canary token, suggesting prompt extraction succeeded.

Redaction Format

Critical findings are redacted in the sanitized output using the pattern:
[REDACTED:{pattern_name}]
For example, an AWS key becomes [REDACTED:aws_key].
Source: createOutputGuard(), SECRET_PATTERNS, and PROMPT_EXTRACTION_PATTERNS_LOCAL in packages/core/src/security/output-guard.ts

Canary Tokens

Canary tokens are injected into system prompts to detect prompt extraction attacks. If the token appears in the agent’s output, it means the system prompt was leaked. Generation: HMAC-SHA256 of "canary:{sessionKey}" using a configured secret. The first 16 hex characters are used, prefixed with CTKN_.
CTKN_a1b2c3d4e5f67890
Properties:
  • Deterministic per session: Same session key and secret always produce the same canary
  • Format: CTKN_ prefix followed by 16 hex characters
  • Detection: The output guard checks if the canary token appears anywhere in the response
Source: generateCanaryToken() and detectCanaryLeakage() in packages/core/src/security/canary-token.ts

Log Sanitizer

Defense-in-depth regex-based credential scrubbing for log strings. This is a second line of defense after Pino’s structured field redaction — it catches credentials embedded in free-text log messages. Pino’s structured-field redactor (the first line of defense) auto-redacts the canonical credential field names: apiKey, token, password, secret, authorization, botToken, privateKey, cookie, webhookSecret. The Log Sanitizer below covers credentials that escape into free-text log messages. 18 credential patterns processed in order (more specific patterns first):
PatternReplacementDescription
Anthropic API keyssk-ant-[REDACTED]sk-ant-... keys
OpenAI project keyssk-proj-[REDACTED]sk-proj-... keys
Generic sk- keyssk-[REDACTED]Any sk- prefixed key (20+ chars)
Bearer tokensBearer [REDACTED]Bearer token in text
Telegram bot tokens[REDACTED_BOT_TOKEN]digits:alphanumeric format
AWS access keysAKIA[REDACTED]AKIA followed by 16 chars
AWS secret keys$1[REDACTED_AWS_SECRET]40-char base64-like after common prefixes
Stripe keyssk_[REDACTED]sk_live_ or sk_test_ keys
Google API keysAIza[REDACTED]AIzaSy... keys
Slack app tokensxapp-[REDACTED]xapp- prefixed tokens
SendGrid keysSG.[REDACTED]SG. prefixed keys
JWTs[REDACTED_JWT]Three dot-separated base64url segments
DB connection strings[REDACTED_CONN_STRING]postgres://, mysql://, etc.
URL passwords://$1:[REDACTED]@Passwords in URL credentials
Discord bot tokens[REDACTED_DISCORD_TOKEN]Dot-separated token segments
Hex secrets[REDACTED_HEX]40+ character hex strings
GitHub tokensgh[REDACTED]ghp_, gho_, etc. (36+ chars)
Size limit: Inputs exceeding 1 MB are returned as-is to prevent ReDoS on oversized strings.
Source: sanitizeLogString() and CREDENTIAL_PATTERNS in packages/core/src/security/log-sanitizer.ts

Data Layer

The data layer protects stored data through memory write validation, encrypted secrets storage, and configuration redaction.

Memory Write Validator

Pre-storage security scan for memory content. Prevents memory poisoning attacks where adversaries store prompt injection payloads for later retrieval via RAG.
ClassificationTriggerStorage Behavior
CLEANNo suspicious patterns detectedStored normally
WARNJailbreak/role patterns detected (from injection-patterns.ts)Trust downgraded to "external"
CRITICALDangerous command patterns detected (exec, elevated: true, rm -rf, delete all)Storage blocked entirely
The validator uses the same detectSuspiciousPatterns() function from external-content.ts for pattern detection. Critical patterns are a subset: execution-oriented patterns that are dangerous when stored for later RAG retrieval.
Source: validateMemoryWrite() in packages/core/src/security/memory-write-validator.ts

Encrypted Secrets Store

Secrets are encrypted using AES-256-GCM with HKDF-SHA256 key derivation. Each encryption operation generates unique cryptographic material. Algorithm chain:
  1. Generate 32-byte random salt
  2. Derive encryption key via HKDF-SHA256 from master key + salt
  3. Generate 12-byte random IV (AES-GCM standard nonce)
  4. Encrypt with AES-256-GCM
  5. Output: ciphertext, IV, 16-byte authentication tag, salt
EncryptedSecret fields:
FieldTypeSizeDescription
ciphertextBufferVariableAES-256-GCM encrypted data
ivBuffer12 bytesInitialization vector (AES-GCM standard nonce)
authTagBuffer16 bytesGCM authentication tag
saltBuffer32 bytesRandom salt for HKDF key derivation
Master key requirements: At least 32 bytes, provided as hex (64+ chars) or base64 (44+ chars). Only the first 32 bytes are used.
Source: createSecretsCrypto() and EncryptedSecret in packages/core/src/security/secret-crypto.ts

Secret Reference Resolver (SecretRefResolver)

Multi-source resolver for SecretRef values in YAML config. Three providers, all routed through SecretManager for the env case so every credential read is auditable.
ProviderSyntaxBehavior
envenv:provider/KEY_NAME or ${KEY_NAME}Reads via SecretManager
filefile:provider/abs/path (with optional #/json/pointer)Absolute paths only, regular file check, no symlinks to restricted paths, max 1MB
execexec:provider/idJSON-RPC credential helper, default 10s timeout, validates { protocolVersion: 1, values: { [id]: string } }
resolveConfigSecretRefs() walks a config recursively over a structuredClone (never mutates the input) and fails fast on the first resolution error.
SettingDefaultDescription
fileMaxBytes1_048_576Max bytes a file provider will read
execTimeoutMs10_000Max ms an exec provider may run
Source: resolveSecretRef(), resolveConfigSecretRefs() in packages/core/src/security/secret-ref-resolver.ts

Config Redaction

Before config objects are exposed via RPC (config.read), secret-bearing fields are replaced with "[REDACTED]". Secret field pattern (case-insensitive):
/^(.*token|.*secret|.*password|.*apiKey|.*api_key|.*credential|.*private_key|botToken|appSecret|hmacSecret|webhookSecret)$/i
The redaction function:
  1. Deep-clones the config via structuredClone (input is never mutated)
  2. Walks all nested objects recursively
  3. Replaces string values whose keys match SECRET_FIELD_PATTERN with "[REDACTED]"
Source: redactConfigSecrets() and SECRET_FIELD_PATTERN in packages/core/src/security/config-redaction.ts

Gateway / Transport Layer

The gateway layer authenticates clients before any request enters the application.

Bearer Token Authentication

Maps bearer tokens to client identities and scopes. Uses crypto.timingSafeEqual() for constant-time comparison and compares every entry in the token store even after a match, to prevent timing-based enumeration of valid tokens.
ComponentBehavior
Comparisoncrypto.timingSafeEqual(), constant-time
IterationAll entries scanned even on hit
Wildcard scope"*" grants all scopes
Scope checkcheckScope(token, requested) returns boolean
Source: createTokenStore(), checkScope(), extractBearerToken() in packages/gateway/src/auth/token-auth.ts

mTLS Verifier

Validates server certificate, key, and CA at startup; verifies clients against the configured CA at connection time. All validation happens via Node’s built-in X509Certificate class — no third-party crypto.
CheckWhen
Server cert PEM format and expiryDaemon startup (fail-fast)
Server key PEM formatDaemon startup
CA cert PEM format and expiryDaemon startup
Client cert validationPer connection (Hono TLS layer)
Client CN extractionPer connection (extractClientCN)
Source: validateCertificates(), extractClientCN() in packages/gateway/src/auth/mtls-verifier.ts

Audit

The audit subsystem produces structured security events and deduplicates rapid detections.

Audit Event Schema

Every significant action produces an audit event validated against AuditEventSchema.
FieldTypeRequiredDescription
idstring (UUID v4)AutoUnique event identifier
timestampstring (ISO 8601)AutoWhen the event occurred
tenantIdstringYesTenant identifier for multi-tenant isolation
agentIdstringYesAgent that performed the action
userIdstringYesUser who triggered the action
actionTypestringYesAction identifier (e.g., "file.delete")
classification"read" | "mutate" | "destructive"YesRisk classification
outcome"success" | "failure" | "denied"YesAction result
metadataRecord<string, unknown>NoArbitrary event metadata (default: {})
traceIdstringNoDistributed tracing identifier
durationnumberNoAction duration in milliseconds
Source: AuditEventSchema and createAuditEvent() in packages/core/src/security/audit.ts

Audit Aggregator

Deduplicates rapid security events within configurable time windows, emitting summary events instead of flooding the event bus.
SettingDefaultDescription
windowMs60000 (60 seconds)Deduplication window per event source
maxPatternsPerSummary10Maximum representative patterns in summary
Behavior:
  • Events are bucketed by source type (user_input, tool_output, external_content, memory_write)
  • First event in a window creates a new bucket with a setTimeout timer
  • Subsequent events in the same window increment the count and add patterns
  • When the timer fires, a summary security:injection_detected event is emitted with the accumulated count and unique patterns
  • Timers use unref() for clean daemon shutdown
  • flush() emits all pending summaries immediately
  • destroy() clears all timers without emitting
Source: createAuditAggregator() in packages/core/src/security/audit-aggregator.ts

Configuration Reference

SecurityConfigSchema

Top-level security configuration.
SettingTypeDefaultDescription
security.logRedactionbooleantrueEnable structured log redaction of sensitive fields
security.auditLogbooleantrueEnable audit event logging
security.permissionPermissionConfig(see below)Node.js permission model settings
security.actionConfirmationActionConfirmationConfig(see below)Action confirmation requirements
security.agentToAgentAgentToAgentConfig(see below)Agent-to-agent messaging policy
security.secretsSecretsConfig(see below)Encrypted secrets store configuration

PermissionConfig

SettingTypeDefaultDescription
permission.enableNodePermissionsbooleanfalseEnable Node.js --permission flag enforcement
permission.allowedFsPathsstring[][]Allowed filesystem read/write paths
permission.allowedNetHostsstring[][]Allowed network hosts for outbound connections

ActionConfirmationConfig

SettingTypeDefaultDescription
actionConfirmation.requireForDestructivebooleantrueRequire confirmation for destructive actions
actionConfirmation.requireForSensitivebooleanfalseRequire confirmation for sensitive actions
actionConfirmation.autoApprovestring[][]Actions that bypass confirmation

AgentToAgentConfig

SettingTypeDefaultDescription
agentToAgent.enabledbooleantrueEnable cross-agent session messaging
agentToAgent.maxPingPongTurnsnumber3Maximum reply-back loop turns (0-5)
agentToAgent.allowAgentsstring[][]Allowed agent IDs for sub-agent spawning (empty = allow all)
agentToAgent.subAgentRetentionMsnumber3600000 (1 hour)Retention period for completed sub-agent sessions
agentToAgent.waitTimeoutMsnumber60000 (60s)Default timeout for wait mode
agentToAgent.subAgentMaxStepsnumber50Default max steps for sub-agent execution
agentToAgent.subAgentToolGroupsstring[]["coding"]Default tool profile groups for sub-agents
agentToAgent.subAgentMcpTools"inherit" | "none""inherit"MCP tool inheritance policy for sub-agents
Source: SecurityConfigSchema, PermissionConfigSchema, ActionConfirmationConfigSchema, AgentToAgentConfigSchema in packages/core/src/config/schema-security.ts

ApprovalsConfigSchema

Top-level approvals configuration.
SettingTypeDefaultDescription
approvals.enabledbooleanfalseEnable the approval workflow
approvals.defaultMode"auto" | "require" | "deny""auto"Default mode for unmatched actions
approvals.rulesApprovalRule[][]Ordered list of approval rules (first match wins)
approvals.defaultTimeoutMsnumber300000 (5 min)Default approval request timeout
If approvals.enabled is false but approvals.rules has entries, the rules are not evaluated. Comis logs a configuration warning in this case.
Source: ApprovalsConfigSchema and checkApprovalsConfig() in packages/core/src/config/schema-approvals.ts

Secrets storage

The credential store backend is selected globally via security.storage.
SettingTypeDefaultDescription
security.storage"encrypted" | "file" | "env""encrypted"Credential storage backend: encrypted (AES-256-GCM SQLite), file (plaintext JSON at 0600), or env (read-only, reads .env/process.env). The database path is fixed at <dataDir>/secrets.db and is not configurable.
Source: SecurityConfigSchema in packages/core/src/config/schema-security.ts

AgentSecretsConfigSchema

Per-agent secret access control (configured per agent, not globally).
SettingTypeDefaultDescription
secrets.allowstring[][]Glob patterns for allowed secret names (empty = unrestricted)
Source: AgentSecretsConfigSchema in packages/core/src/config/schema-secrets.ts

Action Classifier

Complete action registry with all 100+ classifications

Tool Security

Tool policy profiles and content scanning

Safe Path

Path traversal prevention API

Sandbox

Execution environment isolation

Secret Manager

Encrypted secrets store operations

Node Permissions

Node.js permission model integration

Defense in Depth

User-friendly security overview

Hardening

Production hardening checklist