--permission flag to provide OS-level sandboxing for child processes spawned during skill execution. When enabled, child processes run with restricted file system and network access.
Source:
packages/core/src/config/schema-security.ts — PermissionConfigSchema Zod schema.Overview
The Node.js permission model (available in Node.js 22+) restricts what a child process can access at the operating system level. This provides a defense layer beneath application-level controls like tool policies and content scanning. Key distinction: Node.js’s permission model is a process-level boundary. To enforce restrictions on child processes spawned during tool execution, the daemon process itself must be launched with--permission (children inherit via NODE_OPTIONS). Comis does not auto-inject these flags from config — they must be supplied on the daemon command line. See “How It Works” below for the operator-driven flow and the MCP exception that strips NODE_OPTIONS for compatibility.
Configuration
ThePermissionConfigSchema has 3 fields:
| Field | Type | Default | Description |
|---|---|---|---|
enableNodePermissions | boolean | false | Enable Node.js --permission flag enforcement for child processes |
allowedFsPaths | string[] | [] | Allowed filesystem read/write paths (passed as --allow-fs-read and --allow-fs-write) |
allowedNetHosts | string[] | [] | Allowed network hosts for outbound connections (passed as --allow-net) |
YAML Configuration Example
How It Works
When the daemon process is launched with--permission:
-
File system access:
allowedFsPathsshould mirror the--allow-fs-read/--allow-fs-writeflags supplied to the daemon. Paths not in this list are inaccessible to the daemon and any child processes that inheritNODE_OPTIONS. -
Network access:
allowedNetHostsshould mirror--allow-net. Only connections to listed hosts are permitted. -
Child process spawning: Child Node processes inherit the daemon’s permission flags via
NODE_OPTIONSunless explicitly stripped (as the MCP launcher does).
Permission Flag Mapping
| Config Field | Node.js Flag | Effect |
|---|---|---|
allowedFsPaths | --allow-fs-read, --allow-fs-write | Restricts file system access to listed paths (operator must pass the flags at daemon launch) |
allowedNetHosts | --allow-net | Restricts outbound network to listed hosts (operator must pass the flag at daemon launch) |
| (implicit) | --permission | Must be set on the daemon launch command; not auto-injected by Comis |
Limitations
- Node.js 22+ required: The
--permissionflag is not available in older Node.js versions. Comis requires Node.js 22+, so this is satisfied by default. - Third-party module compatibility: Some npm packages that perform unrestricted file system or network operations will fail when permissions are enabled. Test thoroughly before enabling in production.
- No per-tool granularity: Permissions apply to the entire child process. You cannot grant different file system paths to different tools within the same execution.
- Path resolution: Paths in
allowedFsPathsshould be absolute. Relative paths may not resolve as expected in the child process context. - No wildcard support: The Node.js permission model does not support glob patterns in path specifications. Each directory must be listed explicitly.
Error Behavior
When a child process attempts an operation that violates its permissions, Node.js throws aERR_ACCESS_DENIED error. This error propagates through the tool execution system and is reported to the agent as a tool failure.
Example error when a child process attempts to read a file outside allowedFsPaths:
Default Behavior
WhenenableNodePermissions is false (the default), child processes inherit the full permissions of the daemon process. This is the recommended setting for most deployments where the daemon runs as a dedicated system user with appropriate OS-level access controls.
Enable Node.js permissions only when you need an additional sandboxing layer — for example, when running untrusted skills that may attempt to access files or network resources outside their intended scope.
Recommended Paths
When enabling permissions, include at minimum:| Path | Purpose |
|---|---|
Data directory (~/.comis) | Database, config, logs |
| Agent workspace directory | File operations from tools |
/tmp (or a subdirectory) | Temporary files for media processing |
| Node.js installation directory | Module resolution |
Interaction with Other Security Layers
Node.js permissions are one layer in Comis’s defense-in-depth approach:| Layer | Scope | Mechanism |
|---|---|---|
| Tool Policy | Application-level | Controls which tools an agent can use |
| Content Scanner | Skill-level | Detects malicious patterns in skill bodies |
| SSRF Guard | Network-level | Blocks requests to internal/metadata IPs |
| Safe Path | File-level | Prevents path traversal within allowed directories |
| Node Permissions | OS-level | Restricts child process file system and network access |
| Budget / Circuit Breaker | Resource-level | Limits token spend and failure cascades |
SecurityConfigSchema Context
ThePermissionConfigSchema is nested within the broader SecurityConfigSchema:
SecurityConfigSchema is defined in packages/core/src/config/schema-security.ts and parsed via Zod with defaults for all fields.
Production fd-API Disablement (Linux)
When Comis runs undernode --permission, Node.js categorically disables the file-descriptor-based fs API family at the V8/libuv level — this applies to the daemon process itself, not just child processes. This is distinct from the path-restriction behavior described above and affects how the daemon writes credential files and sets file permissions.
Understanding this behavior matters if you enable security.permission.enableNodePermissions: true or if you run the daemon via the reference systemd unit (which passes --permission in ExecStart).
Disabled APIs
Undernode --permission, the following APIs are refused with ERR_ACCESS_DENIED:
| API | Impact |
|---|---|
fs.fsyncSync / fs.fdatasyncSync / FileHandle.sync() | Credential file writes (secrets, OAuth profiles) skip the fsync durability barrier |
fs.fchmodSync | Trace files and session metadata cannot have permissions set after write |
fs.fchownSync | Ownership changes on daemon-managed files are skipped |
Error [ERR_ACCESS_DENIED]: ... is disabled when permission model is enabled
Historical production impact
Observed on a production VPS (2026-06-02): the data-dir lock’sfsync killed every boot attempt — every daemon start threw ERR_ACCESS_DENIED before the first log line. Later, fchmod refusals broke MCP OAuth discovery (no OAuth-protected MCP server could connect) and session-metadata writes. (Source: packages/shared/src/fsync-permission.ts:15-22)
Both issues were root-caused to the fd-API categorical disablement rather than missing path grants.
How Comis guards against this
Comis guards all fd-API call sites viaisFsyncDisabledByPermissionModel (defined in packages/shared/src/fsync-permission.ts:33). This function detects ERR_ACCESS_DENIED by error code or the categorical error message and returns true, allowing callers to skip the failing call gracefully rather than crashing.
Guarded call sites:
packages/daemon/src/wiring/data-dir-lock.ts—fsyncSyncon the lock file and parent directory (boot-critical)packages/core/src/oauth/oauth-credential-store-file.ts—fsyncSyncon OAuth profile atomic writespackages/memory/src/file-secret-store.ts—fsyncSyncon the secrets file atomic writespackages/cli/src/sync-tooling/atomic-write.ts—fsyncSyncpackages/observability/src/shared/fs-safe.ts—fchmodSyncon trace files and session metadatapackages/skills/src/tools/builtin/terminal-driver/terminal-worker-entry.ts— fd-based API
Operator implications
- Best-effort durability: Under
--permission, encrypted-mode credential file writes (secrets, OAuth profiles) aretmp → renameatomic but not fsync-durable. A kernel panic or power failure betweenrenameand the next journal flush could corrupt the credential file. This risk is identical to writes on any filesystem without journaling guarantees — acceptable for most production deployments, but notable for high-availability setups with unreliable power. - Best-effort permissions: Trace files and session metadata may not have the expected
0o600mode under--permission. Files are still written correctly; only the post-writechmodis skipped. - Linux-only: This behavior only occurs on production systemd deployments that pass
--permission. macOS default setups and development installs (without--permission) are unaffected. - No action required by default:
security.permission.enableNodePermissionsdefaults tofalse. You must explicitly opt in. If you do not enable it, none of this applies.
If you see
ERR_ACCESS_DENIED errors in daemon logs that are NOT related to child-process file access, check whether the operation involves one of the fd-based APIs above. The isFsyncDisabledByPermissionModel guard handles the known call sites, but third-party Node.js libraries loaded by the daemon may also use fd-based APIs.Security Model
Defense-in-depth security architecture
Sandbox
Skill sandboxing implementation
Safe Path
Path traversal prevention
