Security Model
How Velyr limits what scripts can do, what the threat model does and does not promise, and how the code base is kept auditable.
Velyr's first principle is secure by default (ADR-003): a script receives only the host authority it is explicitly given. This page summarizes the model. The authoritative sources are docs/security/ and docs/SECURITY.md in the repository.
Default deny and capabilities#
A fresh velyr run can import modules from the project root and nothing else. File, network, environment, subprocess, native and inspector access are denied until granted. All host operations pass through one capability check at the host-op boundary.
| Capability | Flag (allow / deny) |
|---|---|
| Filesystem read / write | --allow-read, --allow-write / --deny-read, --deny-write |
| Network connect | --allow-net / --deny-net |
| Network listen | --allow-listen / --deny-listen |
| Environment | --allow-env / --deny-env |
| Subprocess | --allow-run / --deny-run |
| Remote imports | --allow-import / --deny-import |
| Native code | --allow-native / --deny-native |
| Inspector | --inspect / --deny-inspect |
-A / --allow-all grants everything except native loading and the inspector, and prints a warning. Precedence is explicit deny, then explicit scoped allow, then default deny. There is no interactive prompt in headless mode. Scoping, config-file syntax and examples are on Permissions and Permissions configuration.
Some capabilities have no operation behind them yet: for example no subprocess API, native loader or inspector is exposed by the CLI today. The flags and the capability model exist so the policy is fixed before the features arrive.
Scripts can probe their own authority with Velyr.permissions.query(...), which never prompts and emits no denial event. Denials surface as errors with the code VELYR_PERMISSION_DENIED.
Workers#
A worker's authority is parent ∩ requested ∩ deployment_policy, so a worker can never widen what its parent holds. See Workers.
Resource quotas#
Per-class counters (files, sockets, listeners, workers, timers, in-flight body bytes) stop a script from exhausting the host. Operations past the limit fail with a quota error, and bounded message queues fail rather than grow. Limits are set in the quotas block of velyr.json; defaults are listed on Workers. Quotas bound counts and body bytes; they are not a CPU or total-memory sandbox.
Threat model summary#
The assets include the host filesystem, network identity, environment secrets, subprocess execution, other isolates, the package cache and lockfile integrity, and native memory. The adversaries considered are malicious or compromised npm dependencies, malicious input to the parser or engine, untrusted user scripts, hostile network peers, malicious native addons, package substitution, denial-of-service workloads and cross-isolate attackers.
| Risk | Planned control | Status |
|---|---|---|
| Information disclosure | Deny by default, scoped grants | Available |
| Module identity confusion | Canonical module IDs (file URL of real path) | Available |
| Worker privilege escalation | Permission intersection | Available |
| Resource exhaustion | Quotas, request and body limits | Available |
| Package substitution | Lockfile, integrity verification | Experimental |
| Audit trail | Structured permission and host-op events (tracing) | Experimental |
| Engine hardening | Optional --jitless deployment mode (ADR-009) | Planned |
Known gap, documented in the module host: resolution checks that a target exists before consulting permissions, so the existence of files outside the grant can be observed. See Modules.
Native extensions#
Rust crates compiled into Velyr are the only code that runs in-process by default. Node-API addons, FFI libraries and native plugins are classed as restricted: they can read process memory, bypass runtime permissions, crash the process and call OS APIs directly. So Node-API compatibility does not imply sandbox safety.
The policy is that native loading is off by default, requires an explicit grant, is audited in telemetry, and can be disabled entirely by a deployment profile. Out-of-process addon hosts are under investigation. For new plugins the recommended direction is WebAssembly components with injected capabilities. None of these layers exists yet (compat-napi is a reserved name). See Node and npm compatibility and Plugins.
Supply chain#
For Velyr's own Rust dependencies:
- The workspace sets
unsafe_code = "forbid". A crate that needsunsafemust be listed inunsafe-allowlist.txt(todayengine-v8,engine-gc,engine-vmand the reservedcompat-napi). Theengine-v8crate downgrades the lint to a warning because it wraps V8. CI runsscripts/ci/check-unsafe.sh, which fails when any other crate does not inherit the workspace lints. deny.tomlis checked bycargo denyin CI: yanked crates are denied, licenses must be on an allow-list (MIT, Apache-2.0, BSD-2/3-Clause, ISC, Unicode-3.0, Zlib, MPL-2.0 for dependency use only, CC0-1.0), and only crates.io is an accepted registry (unknown registries and git sources are denied). One advisory is ignored with a written reason (RUSTSEC-2024-0436, an unmaintained crate pulled in by thev8crate).- A scheduled security workflow runs
cargo denyandcargo auditon pushes, pull requests and daily. - Versions of the HTTP and TLS stack are pinned exactly in
crates/io-http/Cargo.toml, and CI also checks crate dependency boundaries. - The
fuzz/workspace has targets for error codes, permission path checks, module resolution, packageexportsresolution and tarball extraction.
For packages you run:
- Package installation (not yet shipped) is designed around integrity verification, a deterministic lockfile, a content-addressed cache and lifecycle scripts denied by default. See Node and npm compatibility.
- Release provenance, SBOMs and signed releases are listed as goals for before a stable release, not as present features.
Reporting a vulnerability#
The repository does not yet contain a root SECURITY.md; docs/SECURITY.md lists it, a private disclosure channel and a security advisories process as required before a public beta. Until a channel is published, do not post exploit details in a public issue. Check the repository for a SECURITY.md or the project's security advisories page, and use the private channel it names.
Severity guidance from the program:
- Critical: native code execution from ordinary JS without a native or process grant, permission bypass exposing the filesystem, environment or network, cross-isolate memory corruption.
- High: escape of a scoped permission, crafted package or source causing reliable memory corruption, an inspector exposed remotely without authorization.
- Medium and low: assessed by exploitability, authority gained, impact and deployment prerequisites.