Environment Variables

Guest code reads environment variables through familiar APIs -- process.env in JavaScript and the equivalents in other languages. Underneath, every host-environment read flows through a single Elide-controlled access point. This page documents what guest code sees, how that set is decided, and the limits of the model as it stands today.

process.env in JavaScript

Elide installs the Node-style process global, and process.env is a plain JavaScript object:

javascript
console.log(process.env.HOME);
process.env.MY_FLAG = "1";          // observable to later reads in this realm
process.env is materialised once per realm, on first access, and cached. The accessor (ProcessGlobal.envForRealm) iterates HostEnvironment.snapshot() and installs each entry as a writable, configurable, enumerable own data property. Consequences:
  • Guest writes are visible to later guest reads in the same realm, because they mutate the cached object.
  • Guest writes do not reach the OS environment. Elide does not propagate them back; a future child_process.spawn would inherit the pre-mutation host environment unless given an explicit env.
  • process.env cannot be reassigned wholesale (process.env = {...}); it is an own data property of process. Individual entries remain writable.

For the rest of the process surface (argv, execArgv, exitCode, platform, ...), see the process global reference.

The host-environment exposure model

All host-side reads of OS environment variables performed on Elide's behalf -- both engine-startup queries and the data backing guest-visible surfaces like process.env and os.userInfo().shell -- route through dev.elide.cfg.HostEnvironment. This class exists to be the single chokepoint at which two policy layers can be enforced:

  • Sandbox policy -- restricts which host names a realm or invocation can see (deny-list, allow-list, or namespace gate).
  • Secrets provider -- shadows or supplies values from Elide-managed secret stores instead of the OS environment, so e.g. process.env.DATABASE_URL can resolve to a managed secret never visible to the OS.

Both layers are realised as alternative HostEnvironment.Provider implementations installed via HostEnvironment.install(...). A provider's get(name) returns null for unknown or denied names, and its snapshot() returns only the visible entries -- so guest code **cannot distinguish an absent name from a denied one**, nor an OS-backed value from an Elide-managed secret.

Snapshots are fully realised, stable copies. A process.env object built from one snapshot keeps its values even if a different provider is later installed. A sandbox or secrets provider must therefore be installed before the first guest realm boots; post-boot rotation requires invalidating and re-evaluating the affected realm.

The default: full passthrough

The default provider is HostEnvironment.SYSTEM -- an unfiltered passthrough of System.getenv(). The shipped CLI installs no other provider, so in a normal elide run / elide serve invocation **process.env reflects the complete OS environment of the Elide process**, matching Node. Sandbox- or secrets-mediated runs (which install their own provider) are the mechanism by which that set is narrowed or overlaid; that wiring is internal and not yet surfaced as a user-facing option.

Relationship to the polyglot environment knob

The runtime builds each guest context with allowEnvironmentAccess(EnvironmentAccess.NONE). That context setting governs the polyglot engine's own environment-inheritance behavior; it does not gate process.env. Elide's process.env is populated from HostEnvironment.snapshot() independently, which is why the default OS-passthrough is observable even though the polyglot EnvironmentAccess is set to NONE. (When the debugger is active, DebuggerComponent switches the context to EnvironmentAccess.INHERIT as part of its broad trusted-principal grants.)

What is NOT injected

To be precise about behavior you might expect from other runtimes:

  • NODE_ENV is not injected. Elide does not set or default NODE_ENV for any language. If it is present in your environment it shows through the normal passthrough like any other variable; if it is not set, it is simply absent. There is no development/production default.
  • No synthetic ELIDE_* variables are injected into guest environments (e.g. there is no ELIDE_ROOT or ELIDE_INTERNAL env var added for guests). The __Elide_ token is an internal guest-symbol prefix, not an environment variable.

Flags

There are no CLI flags that control host-environment exposure today -- specifically, there is no --allow:host-env, no --env=allowlist, and no per-name allow/deny flag. The allow-list and secrets concepts described above are realised through HostEnvironment.Provider installation in the embedding/host layer, not through command-line options. Selection of the guest language is by file extension, not by flags, so there is likewise no language-selecting REPL flag in this area.

Summary

  • process.env is a per-realm snapshot taken on first access, sourced from HostEnvironment.snapshot(); guest mutations are realm-local and never reach the OS.
  • The default HostEnvironment provider is an unfiltered System.getenv() passthrough, so the CLI exposes the full OS environment by default (Node-compatible).
  • Sandbox/secrets filtering is a provider-swap mechanism (HostEnvironment.install), not yet a user-facing flag; denied names are indistinguishable from absent ones.
  • NODE_ENV and synthetic ELIDE_* env vars are not injected.
  • EnvironmentAccess.NONE on the polyglot context does not gate process.env.

What's next