# Environment and properties

> Load dotenv files and Java properties, choose configuration files explicitly, control precedence, and decrypt dotenvx-compatible values with separately supplied keys.

Elide loads `.env` files as application environment defaults and `.properties` files as Java system-property defaults.
No application dotenv library is required. Both formats support dotenvx-compatible encrypted values.

> [!NOTE] Available in Elide 1.6.2
> This guide describes the run-context support in Elide 1.6.2.

## Load configuration

Create `.env` in the directory where you invoke Elide:

```dotenv
APP_NAME=greeter
APP_PORT=8080
APP_MESSAGE="Hello from ${APP_NAME}"
```

Create `app.ts`:

```typescript
console.log(process.env.APP_MESSAGE);
console.log(process.env.APP_PORT);
```

```sh
elide run app.ts
```

```text
Hello from greeter
8080
```

An inherited environment variable takes precedence, including an empty value:

```sh
APP_PORT=9090 elide run app.ts
APP_PORT= elide run app.ts
```

The second invocation receives an empty `APP_PORT`, rather than `8080`. Read environment variables through
[`process.env`](https://elide.help/docs/env-variables), Python's `os.environ`, or the environment interface for your language.
Child processes receive the effective environment too. When an environment policy filters inherited variables,
spawned Java processes, project tasks, and Node child processes inherit only the permitted snapshot. Explicit task
or JVM environment entries are applied afterward; an explicit Node child-process `env` map replaces inheritance.
File loading does not grant additional sandbox capabilities
or broaden a manifest's environment access; existing `--allow-env` controls still apply.

## Automatic file discovery

Within each selected directory, Elide reads these files in order:

| Configuration | First | Second, overriding the first |
| --- | --- | --- |
| Environment | `.env` | `.env.local` |
| Java properties | `.properties` | `.properties.local` |

Missing automatic files are ignored. Existing files that cannot be read or parsed cause an error.
The two formats are separate: an environment variable named `app.mode` does not set the Java property `app.mode`.

By default, the launcher checks only the invocation directory. The location of a script does not change that directory.
Running `elide run services/api/app.ts` does not automatically select `services/api/.env`.

With `--project PATH`, Elide selects that project directory, or the parent directory when `PATH` names its manifest.
It first loads enclosing directories that contain `elide.pkl`, from outermost to innermost, then the selected directory.
Within each directory, the local file follows the base file. Later directories override earlier ones.
Arbitrary ancestors, the home directory, and unrelated workspace members do not contribute launcher configuration.

```sh
elide --project services/api run app.ts
```

Project tasks also use their enclosing, workspace, and member environment layers. Select a project explicitly when its
enclosing configuration must be available at startup. See [Project setup](https://elide.help/docs/projects).

Elide does not select `.env.production`, `.env.test`, `.properties.production`, or similar names automatically.
`NODE_ENV`, a named execution profile, and dotenvx profile files do not select configuration files. Choose these files
explicitly.

## Choose files explicitly

`--env-file` replaces automatic dotenv discovery. `--properties-file` independently replaces automatic properties
discovery. Repeat either flag to layer files; later arguments win for duplicate names.
Paths are relative to the invocation directory, including when `--project` is supplied.

```sh
elide --env-file .env --env-file .env.production run app.ts
elide --properties-file config/base.properties --properties-file config/production.properties run Main.java
elide --env-file config/service.env --properties-file config/service.properties run Main.java
```

Explicit files are required: a missing file is an error. Selecting `.env.production` alone does not also load `.env`
or `.env.local`. Include each file you want.

| Flag | Effect |
| --- | --- |
| `--no-env` | Disable dotenv loading. Inherited environment variables remain available. |
| `--no-properties` | Disable properties-file loading. |
| `--no-env --no-properties` | Disable both kinds of file loading. |

```sh
elide --no-env --no-properties run app.ts
```

Combining `--no-env` with `--env-file`, or `--no-properties` with `--properties-file`, is a usage error.
You can disable one format while explicitly selecting files for the other.

Standalone Java source launches also honor explicit file selections, `--no-env`, and `--no-properties`
without rediscovering automatic files.

## Precedence

Environment precedence, from lowest to highest:

1. Selected dotenv files, in discovery or argument order.
2. Inherited process environment, including empty values.
3. Explicit environment assignments for a particular task or child process, within that execution scope.

Java property precedence, from lowest to highest:

1. Selected properties files, in discovery or argument order.
2. Operator-supplied JVM `-D` assignments when Elide is running on an existing JVM.
3. Explicit Elide CLI property assignments, such as `--vm app.mode=production`.

VM-provided property defaults do not count as operator assignments. Applications receive properties before application
initialization. Child Java launches receive application property defaults before their own explicit JVM arguments.
A task with distinct VM properties runs in a separate VM; its properties do not change the build driver's global state.

## Dotenv syntax

Dotenv files support comments, quoted values, multiline values, empty values, and `${NAME}` interpolation.
Single quotes preserve literal content. Double quotes allow interpolation.

```dotenv
# Application defaults
APP_NAME=greeter
APP_MESSAGE="Hello from ${APP_NAME}"
APP_LITERAL='${APP_NAME}'
APP_EMPTY=
APP_MULTILINE="first line
second line"
APP_COMMAND='$(echo hello)'
```

`APP_LITERAL` is the literal string `${APP_NAME}`. `APP_COMMAND` is the literal string `$(echo hello)`;
Elide does not execute shell command substitutions in configuration files.

Interpolation happens while each file is parsed. It can use inherited variables and earlier assignments in that file.
It is not rerun after later files override values. If `.env` defines `APP_NAME=greeter` and
`APP_MESSAGE="Hello from ${APP_NAME}"`, then `.env.local` changes `APP_NAME` to `api`, the message still reads
`Hello from greeter`. Do not rely on interpolation across separate files.

## Java properties

For Java or Kotlin applications, create `.properties`:

```properties
app.name=greeter
app.message=Hello world
app.empty=
app.multiline=first\
  second
app.unicode=\u263A
app.literal=${APP_NAME}
```

Read these with `System.getProperty("app.name")` in Java or Kotlin. For example, save `Main.java`:

```java
class Main {
  public static void main(String[] args) {
    System.out.println(System.getProperty("app.message"));
  }
}
```

```sh
elide run Main.java
elide --vm app.message="Hello from the CLI" run Main.java
```

The first invocation prints `Hello world`; the second prints `Hello from the CLI`.
`app.multiline` is `firstsecond`; `app.unicode` is `☺`; `app.literal` stays `${APP_NAME}`.

Properties use Java syntax: `#` and `!` comments, escaped separators, continuation lines, and `\uXXXX` escapes.
Quotes are part of the value, rather than dotenv quoting. Properties do not interpolate dotenv expressions or execute
commands. For portable non-ASCII content, use Unicode escapes; the properties reader uses Windows-1252 byte decoding.

Property names must be nonempty and contain neither NUL nor `=`. Values may contain `=`, but not NUL.
The `java.*`, `elide*`, and `org.graalvm*` property namespaces are restricted, except `elide.diag.*`.
Encryption does not bypass these restrictions. `polyglot.*` settings follow the engine-option path.
Use CLI VM options for settings such as heap size; a properties file is not a JVM argument file.

When Elide runs on an already-started JVM, file loading supplies environment overlays to applications and child
processes. It does not rewrite that JVM's `System.getenv()` or retroactively change its startup settings.

## Encrypted values

Elide decrypts dotenvx-compatible `encrypted:` values in dotenv files and Java properties. Plaintext and encrypted
values can appear in the same file and follow the same precedence rules. An encrypted value replaced by a later
plaintext value, or by an inherited environment value, does not need a decryption key.

Use a compatible encryption tool to author ciphertext. Elide supplies decryption when loading configuration;
it does not provide an encryption command. For example, with dotenvx installed:

```sh
dotenvx encrypt -f .env.production
elide --env-file .env.production run app.ts
```

Choose dotenvx's local `.env.keys` storage when prompted. Its
[encryption workflow](https://dotenvx.com/docs/custody/env-keys/) keeps ciphertext in the selected dotenv file and
writes the private key to `.env.keys`. For `.env.production`, the key is named `DOTENV_PRIVATE_KEY_PRODUCTION`.
Elide still requires `--env-file .env.production`; the key's suffix does not select the file.

### Supply private keys

Elide accepts inherited `DOTENV_PRIVATE_KEY` and suffixed `DOTENV_PRIVATE_KEY_*` variables, or keys in `.env.keys`
beside selected configuration sources. Environment and properties share the same key lookup.

1. Elide checks `.env.keys` beside selected dotenv sources, then selected properties sources, in source order.
   Each directory is checked once; later key-file assignments replace earlier assignments with the same name.
2. Inherited `DOTENV_PRIVATE_KEY*` assignments override matching names from key files.
3. The available keys are tried against effective encrypted values. A key suffix does not restrict decryption to a
   particular configuration filename.

Key files are read only when an effective encrypted value needs decryption. A plaintext-only run does not require
`.env.keys`. Key lookup does not search arbitrary ancestors or your home directory.

For CI, inject the matching private-key variable through the CI secret store, then run Elide normally:

```sh
# DOTENV_PRIVATE_KEY_PRODUCTION is supplied by the CI environment.
elide --env-file .env.production run app.ts
```

Keep `.env.keys` out of version control, back it up separately, and limit access to the file.
Review configuration before committing it: encryption of some values does not mean every value is encrypted.
Avoid printing application secrets or passing literal private keys in shell commands and logs.

Entries from `.env.keys` are used only for decryption; they are not added to application configuration.
`DOTENV_PUBLIC_KEY*` entries in configuration files are metadata and are omitted from the loaded application values.
Putting `DOTENV_PRIVATE_KEY*` assignments in an application dotenv or properties file is an error.
Inherited private-key variables remain subject to ordinary inherited-environment visibility.

### Encrypted Java properties

A Java property can contain a compatible ciphertext value without quotes:

```properties
app.secret=encrypted:...
```

The ellipsis is a placeholder for actual ciphertext, not a valid encrypted value. Elide parses Java properties first,
then decrypts the decoded value using the same keys as dotenv. Do not assume the dotenvx CLI can parse or rewrite
Java properties files. Decrypted text is used as a value: it is not reparsed as configuration, interpolated, or executed.

## Commands, tests, and errors

Automatic loading applies to `run`, `serve`, `dev`, `install`, `build`, `test`, `project`, `classpath`, `publish`,
`java`, `python`, `repl`, and direct file execution. Help, version, completions, and unrelated administrative commands
skip automatic configuration discovery. Tool commands such as `javac`, `kotlinc`, and `mvn` do not automatically load
these files. Explicit file flags request configuration loading on commands that reach the application launcher.

For reproducible tests, choose files explicitly or disable both formats:

```sh
elide --env-file .env.test --no-properties test
elide --no-env --no-properties test
```

These flags do not clear inherited environment variables. Set or remove those separately in the test runner.
A resolved invocation keeps its configuration snapshot; start a new invocation to pick up changed files or keys.

Unreadable selected files, malformed syntax, forbidden entries, invalid keys, and undecryptable effective values fail
before the relevant application or task executes. Diagnostics report an error category and source path without
printing source lines, values, ciphertext, or private keys. Fix the reported source or explicitly disable its format.
No partially resolved configuration from a failed format is published.

For variables that configure Elide itself, see [Runtime environment variables](https://elide.help/docs/runtime-env-variables).
For flag details, see [CLI reference](https://elide.help/docs/cli-references).
