├── README.md ├── env.abnf └── env.md /README.md: -------------------------------------------------------------------------------- 1 | # env 2 | 3 | [Specification for .env](env.md) 4 | 5 | ## Objectives 6 | 7 | The .env file format aims to be a minimal, unambiguous configuration file format primarily used for setting environment variables in development environments. While the format has existed for many years with various implementations, this specification aims to standardize the expected behavior and provide clear guidance for parser implementations. 8 | 9 | The env specification is a work in progress, you can view the document in its current state [here](env.md). 10 | 11 | ## Contributing 12 | 13 | You are invited to contribute to the development of this specification by submitting a [pull request](../../pulls), opening [an issue](../../issues), or joining us in [the discussions](../../discussions). 14 | 15 | All contributions are welcome! Here are some things we need: 16 | 17 | - [ ] Feedback on the specification 18 | - [ ] Examples of .env files 19 | - [ ] Examples of .env file parsing 20 | - [ ] Implementations in various languages 21 | - [ ] Test cases for .env file parsing 22 | - [ ] Documentation for .env file parsing 23 | - [ ] Use cases for .env files 24 | - [ ] Comparisons to other configuration file formats 25 | - [ ] Anything else you can think of! 26 | -------------------------------------------------------------------------------- /env.abnf: -------------------------------------------------------------------------------- 1 | ;; This document describes the .env file format syntax using ABNF (RFC 5234). 2 | ;; The grammar describes the Unicode code points after UTF-8 decoding. 3 | ;; All valid .env files will match this description. 4 | 5 | ;; Overall Structure 6 | 7 | env-file = *line 8 | 9 | line = empty-line / comment-line / keyval-line 10 | 11 | empty-line = ws newline 12 | comment-line = ws comment newline 13 | keyval-line = ws keyval ws [comment] newline 14 | 15 | ;; Basic Elements 16 | 17 | ws = *wschar 18 | wschar = %x20 ; Space 19 | wschar =/ %x09 ; Horizontal tab 20 | 21 | newline = %x0A ; LF 22 | newline =/ %x0D.0A ; CRLF 23 | 24 | ;; Comments 25 | 26 | comment = hash-comment / semicolon-comment / slash-comment 27 | 28 | hash-comment = %x23 *comment-char ; # comment 29 | semicolon-comment = %x3B *comment-char ; ; comment 30 | slash-comment = %x2F.2F *comment-char ; // comment 31 | 32 | comment-char = %x01-09 / %x0B-0C / %x0E-7F / non-ascii ; Any char except newline 33 | non-ascii = %x80-D7FF / %xE000-10FFFF ; Unicode excluding surrogate pairs 34 | 35 | ;; Key-Value Pairs 36 | 37 | keyval = key equals value 38 | equals = ws %x3D ws ; = with optional whitespace 39 | 40 | ;; Keys 41 | 42 | key = (ALPHA / %x5F) *(ALPHA / DIGIT / %x5F) ; Must start with letter/underscore, then letter/digit/underscore 43 | 44 | ;; Values 45 | 46 | value = unquoted-value / quoted-value / empty-value 47 | 48 | empty-value = "" ; Empty string 49 | 50 | unquoted-value = 1*(unquoted-char) 51 | unquoted-char = %x01-08 / %x0B-0C / %x0E-22 / %x24-7F / non-ascii ; Any char except newline, #, and quotes 52 | 53 | quoted-value = single-quoted / double-quoted / multiline-value 54 | 55 | single-quoted = %x27 *single-quoted-char %x27 ; 'value' 56 | single-quoted-char = %x01-08 / %x0B-0C / %x0E-26 / %x28-7F / non-ascii ; Any char except newline and single quote 57 | 58 | double-quoted = %x22 *double-quoted-char %x22 ; "value" 59 | double-quoted-char = %x01-08 / %x0B-0C / %x0E-21 / %x23-7F / non-ascii ; Any char except newline and double quote 60 | 61 | ;; Multi-line Values 62 | 63 | multiline-value = quoted-multiline / continuation-multiline 64 | 65 | quoted-multiline = double-quoted-multiline / single-quoted-multiline 66 | 67 | double-quoted-multiline = %x22 *multiline-content %x22 68 | single-quoted-multiline = %x27 *multiline-content %x27 69 | 70 | multiline-content = unquoted-char / newline 71 | 72 | continuation-multiline = unquoted-value backslash newline *continuation-line value 73 | continuation-line = ws unquoted-value backslash newline 74 | backslash = %x5C ; \ 75 | 76 | ;; Basic Character Classes (from RFC 5234) 77 | 78 | ALPHA = %x41-5A / %x61-7A ; A-Z / a-z 79 | DIGIT = %x30-39 ; 0-9 -------------------------------------------------------------------------------- /env.md: -------------------------------------------------------------------------------- 1 | # .ENV 2 | 3 | ### Environment Variables File Format Specification 4 | 5 | Authored by [Jon Schlinkert](https://github.com/jonschlinkert), et al. 6 | 7 | ## Objectives 8 | 9 | The .env file format aims to be a minimal, unambiguous configuration file format primarily used for setting environment variables in development environments. While the format has existed for many years with various implementations, this specification aims to standardize the expected behavior and provide clear guidance for parser implementations. 10 | 11 | ## Table of contents 12 | 13 | - [Spec](#spec) 14 | - [Comments](#comments) 15 | - [Key/Value Pair](#keyvalue-pair) 16 | - [Keys](#keys) 17 | - [Values](#values) 18 | - [String](#string) 19 | - [Data Types](#data-types) 20 | - [Multi-line Values](#multi-line-values) 21 | - [Error Handling](#error-handling) 22 | - [Parser Reliability Requirements](#parser-reliability-requirements) 23 | - [Variable Expansion](#variable-expansion) 24 | - [Filename Extension](#filename-extension) 25 | - [MIME Type](#mime-type) 26 | 27 | ## Spec 28 | 29 | - .env files MUST be valid UTF-8 encoded documents 30 | - Files SHOULD be named `.env` (see [Filename Extension](#filename-extension) for variations) 31 | - Whitespace means `tab` (`0x09`) or `space` (`0x20`) 32 | - Newline means `LF` (`0x0A`) or `CRLF` (`0x0D` `0x0A`) 33 | - Every non-empty line MUST be either: 34 | - A comment 35 | - A key/value pair 36 | - A line continuation of a previous value (value only, keys MUST NOT span multiple lines) 37 | - Empty lines are allowed and MUST be ignored 38 | - Parsers MUST preserve empty values (e.g., `FOO=`) 39 | - Parsers MUST preserve whitespace in values unless explicitly trimmed by quotes or other mechanisms 40 | - Leading and trailing whitespace on a line MUST be ignored 41 | 42 | ## Comments 43 | 44 | Comments allow humans to add notes to .env files without affecting their parsing. The following comment styles are supported, listed in order of prevalence: 45 | 46 | ### Primary Comment Format 47 | 48 | A hash symbol (`#`) marks the rest of the line as a comment: 49 | 50 | ```env 51 | # This is a full-line comment 52 | FOO=bar # This is an end-of-line comment 53 | ``` 54 | 55 | ### Alternative Comment Formats 56 | 57 | The following comment formats are also recognized by some parsers. Implementation of these formats is OPTIONAL: 58 | 59 | ```env 60 | ; This is a semicolon comment 61 | // This is a double-slash comment 62 | ``` 63 | 64 | Comments MUST NOT be recognized within values unless escaped: 65 | 66 | ```env 67 | SECRET="password#123" # The '#' is part of the value 68 | MESSAGE="Hello # World" # The '#' after Hello is part of the value 69 | ``` 70 | 71 | ## Key/Value Pair 72 | 73 | The primary building block of an .env file is the key/value pair. Each pair MUST be on its own line unless using a line continuation character (see [Multi-line Values](#multi-line-values)). Keys MUST NOT span multiple lines. 74 | 75 | ```env 76 | KEY=value 77 | ``` 78 | 79 | Key/value pairs MUST be separated by an equals sign (`=`). The first `=` character on a line serves as the separator. Additional `=` characters in the value are treated as part of the value: 80 | 81 | ```env 82 | URL=https://example.com/path?foo=bar&baz=qux # Valid, only first = is separator 83 | ``` 84 | 85 | ## Keys 86 | 87 | Keys are case-sensitive and MUST: 88 | 89 | - Begin with a letter (`A-Z`, `a-z`) or underscore (`_`) 90 | - Contain only letters, numbers, and underscores 91 | - Not be empty 92 | - Not span multiple lines 93 | 94 | ```env 95 | # Valid keys 96 | FOO=value 97 | foo=value 98 | FOO_BAR=value 99 | _FOO=value 100 | 101 | # Invalid keys 102 | 123FOO=value # Cannot start with number 103 | FOO-BAR=value # Cannot contain hyphens 104 | .FOO=value # Cannot start with period 105 | FOO\ 106 | BAR=value # Cannot span multiple lines 107 | ``` 108 | 109 | Traditional environment variable naming conventions RECOMMEND: 110 | 111 | - Using uppercase letters 112 | - Using underscores as separators 113 | - Avoiding special characters 114 | 115 | ## Values 116 | 117 | Values may contain any UTF-8 characters. Leading and trailing whitespace in values is significant unless the value is quoted: 118 | 119 | ```env 120 | FOO=bar # value is "bar" 121 | FOO= bar # value is " bar" 122 | FOO=bar baz # value is "bar baz" 123 | FOO="bar" # value is "bar" 124 | FOO=" bar " # value is " bar " 125 | ``` 126 | 127 | Empty values are valid and MUST be preserved: 128 | 129 | ```env 130 | EMPTY= 131 | EMPTY="" # Same as above 132 | ``` 133 | 134 | ## String 135 | 136 | Strings are the primary value type in .env files. They can be represented in two ways: 137 | 138 | ### Unquoted Strings 139 | 140 | Unquoted values extend from the first non-whitespace character after the `=` to the end of the line (or until a comment): 141 | 142 | ```env 143 | UNQUOTED=value with spaces 144 | PATH=/usr/local/bin:/usr/bin:/bin 145 | ``` 146 | 147 | ### Quoted Strings 148 | 149 | Values may be enclosed in single (`'`) or double (`"`) quotes. Quotes are required if the value: 150 | 151 | - Contains leading or trailing whitespace you want to preserve 152 | - Contains a comment character that should be treated as part of the value 153 | - Contains line breaks (see [Multi-line Values](#multi-line-values)) 154 | 155 | ```env 156 | MESSAGE='Hello World' 157 | PATH="C:\Program Files\App" 158 | HASH="my#password" # Without quotes, #password would be a comment 159 | ``` 160 | 161 | ## Data Types 162 | 163 | While all values in .env files are technically strings, parsers MAY implement optional type coercion for common data types. If type coercion is implemented: 164 | 165 | 1. It MUST be optional and easily disabled 166 | 2. The default behavior SHOULD be string-only unless type coercion is an established standard in the ecosystem 167 | 3. The implementation MUST document its coercion rules 168 | 4. Coercion MUST NOT affect the original string value 169 | 170 | When implemented, type coercion SHOULD follow these conventions: 171 | 172 | ### Boolean 173 | 174 | The following string values MAY be interpretable as booleans: 175 | 176 | ```env 177 | DEBUG=true # true 178 | DEBUG=false # false 179 | DEBUG=True # true 180 | DEBUG=False # false 181 | DEBUG=TRUE # true 182 | DEBUG=FALSE # false 183 | ``` 184 | 185 | ### Numbers 186 | 187 | String values that represent valid numbers MAY be coerced to numeric types: 188 | 189 | ```env 190 | PORT=8080 # Integer 191 | TIMEOUT=12.5 # Float 192 | SCIENTIFIC=1e-10 # Scientific notation 193 | ``` 194 | 195 | ### Null Values 196 | 197 | The following string values MAY be interpreted as null: 198 | 199 | ```env 200 | NULL=null 201 | NULL=NULL 202 | NULL= 203 | ``` 204 | 205 | ## Multi-line Values 206 | 207 | Long values can span multiple lines using one of two methods. Note that while values can span multiple lines, keys MUST NOT: 208 | 209 | ### Quoted Multi-line 210 | 211 | Values (but not keys) enclosed in quotes can contain newlines: 212 | 213 | ```env 214 | PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- 215 | MIIBOgIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzrw 216 | ... 217 | -----END RSA PRIVATE KEY-----" 218 | 219 | INVALID_KEY="MULTI 220 | LINE"=value # Invalid: keys cannot contain newlines 221 | ``` 222 | 223 | ### Line Continuation 224 | 225 | A backslash (`\`) at the end of a line indicates the value continues on the next line. This can only be used for values, not keys: 226 | 227 | ```env 228 | LONG_MESSAGE=first line \ 229 | second line \ 230 | third line 231 | 232 | INVALID_KEY=MULTI \ 233 | LINE=value # Invalid: keys cannot use line continuation 234 | ``` 235 | 236 | For line continuations: 237 | 238 | - The backslash MUST be the last character on the line (excluding whitespace) 239 | - Leading whitespace on continuation lines is included in the value 240 | - Empty continuation lines are preserved 241 | - Comments are not allowed on continuation lines 242 | 243 | Invalid continuation examples that MUST trigger ENV005: 244 | 245 | ```env 246 | SECRET=password\ # comment 247 | NEXT=value # Must trigger ENV005: comment after continuation 248 | 249 | VALUE=first\ # Must trigger ENV005: dangling continuation at EOF 250 | 251 | INVALID=test \ # Must trigger ENV005: whitespace before continuation 252 | next line 253 | ``` 254 | 255 | ## Error Handling 256 | 257 | Parsers MUST implement the following error handling: 258 | 259 | ### ENV001: Invalid Line Format 260 | 261 | - Description: A line that is not empty, not a comment, and does not contain a valid key/value pair 262 | - Example: `foo\nbar\nbaz=qux` 263 | - Required behavior: Parser MUST throw an error and MUST NOT combine invalid lines 264 | 265 | ### ENV002: Duplicate Key 266 | 267 | - Description: When the same key appears multiple times 268 | - Required behavior: Parser MUST either: 269 | a) Use the last value (most common) 270 | b) Throw an error 271 | c) Use the first value 272 | - Implementation MUST document which behavior it uses 273 | 274 | ### ENV003: Invalid Key Format 275 | 276 | - Description: Key contains invalid characters or starts with a number 277 | - Required behavior: Parser MUST throw an error 278 | 279 | ### ENV004: Unclosed Quote 280 | 281 | - Description: A quoted value missing its closing quote 282 | - Required behavior: Parser MUST throw an error 283 | 284 | ### ENV005: Invalid Line Continuation 285 | 286 | - Description: A backslash line continuation followed by a comment or EOF, or with preceding whitespace 287 | - Required behavior: Parser MUST throw an error 288 | 289 | ```env 290 | SECRET=password\ # comment 291 | NEXT=value # Should trigger ENV005 292 | ``` 293 | 294 | ### ENV006: Multi-line Key 295 | 296 | - Description: Attempt to define a key across multiple lines (either through quotes or line continuation) 297 | - Required behavior: Parser MUST throw an error 298 | - Example: 299 | 300 | ```env 301 | MULTI\ 302 | LINE_KEY=value 303 | 304 | "MULTI 305 | LINE"=value 306 | ``` 307 | 308 | ### ENV007: Invalid Encoding 309 | 310 | - Description: File contains non-UTF-8 characters 311 | - Required behavior: Parser MUST throw an error 312 | 313 | ## Parser Reliability Requirements 314 | 315 | The .env format is used for configuration that directly affects application behavior and security. Therefore, parsers MUST prioritize reliability and predictability over convenience. This section outlines common pitfalls and required parser behavior. 316 | 317 | ### Silent Failures 318 | 319 | Parsers MUST NOT silently ignore malformed input. The following examples show problematic input that MUST produce errors: 320 | 321 | ```env 322 | FOO # Invalid: no assignment 323 | BAR=value # This line is valid, but parser must still error due to FOO 324 | 325 | KEY VALUE # Invalid: missing assignment operator 326 | PORT=8080 # This line is valid, but parser must still error due to previous line 327 | 328 | USER=john\ # Invalid: dangling continuation 329 | PASSWORD= # This line is valid, but parser must still error due to previous line 330 | ``` 331 | 332 | ### Line Independence 333 | 334 | Each line MUST be validated independently before being combined with any other lines. Parsers MUST NOT: 335 | 336 | - Combine non-empty lines that lack assignment operators 337 | - Join lines unless explicitly continued with a backslash 338 | - Skip validation of any non-empty, non-comment line 339 | 340 | Examples of invalid line joining that MUST produce errors: 341 | 342 | ```env 343 | FOO 344 | BAR=value # Some parsers incorrectly return { BAR: 'value' } or { 'FOO\nBAR': 'value' } 345 | # Must error due to invalid FOO line 346 | 347 | A=1\n 348 | B # Some implementations might try to join B with previous line 349 | 350 | FOO=value\n 351 | BAR # Must not be joined with previous line 352 | 353 | KEY=a 354 | VALUE # Must error, not combine into KEY=aVALUE 355 | ``` 356 | 357 | ### Error Consistency 358 | 359 | Parsers MUST provide consistent error behavior: 360 | 361 | - Same input MUST produce same errors 362 | - Partial results MUST NOT be returned if any line is invalid 363 | - Error messages SHOULD identify the specific line and issue 364 | - Error codes MUST match those specified in Error Handling section 365 | 366 | Example of incorrect partial results that MUST be avoided: 367 | 368 | ```env 369 | VALID_KEY=value 370 | INVALID KEY=foo # Parser encounters invalid syntax here 371 | ANOTHER_VALID=bar 372 | # Parser must NOT return { VALID_KEY: 'value' } and then error 373 | # Must error immediately without returning any values 374 | ``` 375 | 376 | ### Implementation Requirements 377 | 378 | To ensure reliable behavior, implementations MUST: 379 | 380 | 1. Validate entire file before processing any assignments 381 | 2. Throw appropriate error (see Error Handling) for first encountered issue 382 | 3. Not attempt recovery from invalid syntax 383 | 4. Not provide configuration options that bypass these requirements 384 | 385 | The presence of valid lines in a file MUST NOT affect the parser's responsibility to error on invalid lines. This helps catch configuration issues early and prevents unpredictable behavior across different environments and implementations. 386 | 387 | ## Variable Expansion 388 | 389 | Variable expansion (e.g., `FOO=${BAR}`) is explicitly NOT part of this specification. Implementations MAY provide variable expansion as an optional feature, but if they do: 390 | 391 | 1. It MUST be disabled by default 392 | 2. It MUST be explicitly documented 393 | 3. It MUST define clear rules for: 394 | - Syntax (e.g., `${VAR}` vs `$VAR` vs `%VAR%`) 395 | - Scope (current file only, system environment, or both) 396 | - Error handling for undefined variables 397 | - Circular reference detection 398 | - Maximum expansion depth 399 | 400 | ## Filename Extension 401 | 402 | The canonical filename is `.env`. Common variations include: 403 | 404 | - `.env.local` 405 | - `.env.development` 406 | - `.env.test` 407 | - `.env.production` 408 | - `.env.example` 409 | 410 | ## MIME Type 411 | 412 | When transferring .env files over the internet, the appropriate MIME type is `application/env`. This designation is justified by the following characteristics that make .env files distinct from other formats: 413 | 414 | 1. **Distinct Format** 415 | 416 | - Specific syntax rules for key-value pairs 417 | - Defined comment styles 418 | - Special handling of quotes and whitespace 419 | - Line continuation mechanisms 420 | - Rules for multi-line values 421 | 422 | 2. **Specific Processing Model** 423 | 424 | - Strict parsing requirements 425 | - Defined error conditions and handling 426 | - Special security considerations for environment variables 427 | - Load-time behavior distinct from other configuration formats 428 | 429 | 3. **Unique Semantic Meaning** 430 | - Files represent environment variable declarations 431 | - Values have specific runtime implications 432 | - Format carries distinct security and operational considerations 433 | 434 | While .env files may appear superficially similar to plain text or other configuration formats, their unique combination of syntax, processing requirements, and semantic meaning warrants a distinct MIME type. This is analogous to how `application/json` exists despite JSON being representable as plain text, because the specific structure and processing model require unique identification. 435 | 436 | The `application/env` MIME type serves to: 437 | 438 | - Properly identify .env files in transport 439 | - Signal correct processing requirements to consuming systems 440 | - Enable appropriate security handling 441 | - Facilitate correct caching behavior 442 | - Allow proper content negotiation 443 | 444 | When transferring .env files over the internet, implementations MUST use the `application/env` MIME type. 445 | --------------------------------------------------------------------------------