@dotenvx/dotenvx 1.55.1 → 1.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/package.json +3 -3
  3. package/src/cli/actions/encrypt.js +0 -3
  4. package/src/cli/actions/get.js +4 -2
  5. package/src/cli/actions/keypair.js +3 -2
  6. package/src/cli/actions/rotate.js +10 -5
  7. package/src/cli/actions/run.js +1 -14
  8. package/src/cli/actions/set.js +3 -2
  9. package/src/cli/commands/ext.js +0 -3
  10. package/src/cli/dotenvx.js +12 -9
  11. package/src/db/session.js +21 -0
  12. package/src/lib/extensions/ops.js +98 -0
  13. package/src/lib/helpers/buildEnvs.js +2 -15
  14. package/src/lib/helpers/{decryptKeyValue.js → cryptography/decryptKeyValue.js} +1 -1
  15. package/src/lib/helpers/cryptography/index.js +14 -0
  16. package/src/lib/helpers/{isPublicKey.js → cryptography/isPublicKey.js} +1 -1
  17. package/src/lib/helpers/{keypair.js → cryptography/localKeypair.js} +2 -2
  18. package/src/lib/helpers/cryptography/mutateKeysSrc.js +38 -0
  19. package/src/lib/helpers/cryptography/mutateSrc.js +24 -0
  20. package/src/lib/helpers/cryptography/opsKeypair.js +14 -0
  21. package/src/lib/helpers/cryptography/provision.js +47 -0
  22. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +26 -0
  23. package/src/lib/helpers/envResolution/determine.js +46 -0
  24. package/src/lib/helpers/{guessEnvironment.js → envResolution/environment.js} +4 -6
  25. package/src/lib/helpers/envResolution/index.js +8 -0
  26. package/src/lib/helpers/errors.js +13 -0
  27. package/src/lib/helpers/findEnvFiles.js +1 -1
  28. package/src/lib/helpers/isFullyEncrypted.js +3 -3
  29. package/src/lib/helpers/keyResolution/index.js +13 -0
  30. package/src/lib/helpers/keyResolution/keyNames.js +24 -0
  31. package/src/lib/helpers/keyResolution/keyValues.js +85 -0
  32. package/src/lib/helpers/keyResolution/readFileKey.js +15 -0
  33. package/src/lib/helpers/keyResolution/readProcessKey.js +7 -0
  34. package/src/lib/helpers/parse.js +1 -1
  35. package/src/lib/helpers/prependPublicKey.js +17 -0
  36. package/src/lib/helpers/preserveShebang.js +16 -0
  37. package/src/lib/main.d.ts +19 -3
  38. package/src/lib/main.js +9 -17
  39. package/src/lib/services/decrypt.js +23 -19
  40. package/src/lib/services/encrypt.js +41 -136
  41. package/src/lib/services/get.js +3 -3
  42. package/src/lib/services/keypair.js +12 -16
  43. package/src/lib/services/prebuild.js +2 -2
  44. package/src/lib/services/precommit.js +2 -2
  45. package/src/lib/services/rotate.js +59 -42
  46. package/src/lib/services/run.js +29 -112
  47. package/src/lib/services/sets.js +40 -124
  48. package/src/lib/helpers/decrypt.js +0 -31
  49. package/src/lib/helpers/deprecationNotice.js +0 -17
  50. package/src/lib/helpers/determineEnvs.js +0 -65
  51. package/src/lib/helpers/encrypt.js +0 -29
  52. package/src/lib/helpers/findPrivateKey.js +0 -25
  53. package/src/lib/helpers/findPublicKey.js +0 -15
  54. package/src/lib/helpers/guessPrivateKeyName.js +0 -18
  55. package/src/lib/helpers/guessPublicKeyName.js +0 -18
  56. package/src/lib/helpers/parseEncryptionKeyFromDotenvKey.js +0 -19
  57. package/src/lib/helpers/parseEnvironmentFromDotenvKey.js +0 -19
  58. package/src/lib/helpers/smartDotenvPrivateKey.js +0 -89
  59. package/src/lib/helpers/smartDotenvPublicKey.js +0 -42
  60. package/src/lib/services/ops.js +0 -136
  61. /package/src/lib/helpers/{encryptValue.js → cryptography/encryptValue.js} +0 -0
  62. /package/src/lib/helpers/{isEncrypted.js → cryptography/isEncrypted.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,7 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.55.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.56.0...main)
6
+
7
+ ## [1.56.0](https://github.com/dotenvx/dotenvx/compare/v1.55.1...v1.56.0) (2026-03-19)
8
+
9
+ ### Changed
10
+
11
+ - `ops off` flag — now respected by `get`, `keypair`, `rotate`, and `encrypt` ([#750](https://github.com/dotenvx/dotenvx/pull/750))
12
+ - `--pp` alias — added as shorthand for `--pretty-print`; toward sunsetting `-pp` ([#750](https://github.com/dotenvx/dotenvx/pull/750))
13
+
14
+ ### Removed
15
+
16
+ * Remove support for `.env.vault` files ([#750](https://github.com/dotenvx/dotenvx/pull/750))
6
17
 
7
18
  ## [1.55.1](https://github.com/dotenvx/dotenvx/compare/v1.55.0...v1.55.1) (2026-03-13)
8
19
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.55.1",
2
+ "version": "1.56.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -35,8 +35,8 @@
35
35
  "scripts": {
36
36
  "standard": "standard",
37
37
  "standard:fix": "standard --fix",
38
- "test": "tap run --allow-empty-coverage --disable-coverage --timeout=60000",
39
- "test-coverage": "tap run --show-full-coverage --timeout=60000",
38
+ "test": "tap run --test-env=DOTENVX_OPS_OFF=true --allow-empty-coverage --disable-coverage --timeout=60000",
39
+ "test-coverage": "tap run --test-env=DOTENVX_OPS_OFF=true --show-full-coverage --timeout=60000",
40
40
  "testshell": "bash shellspec",
41
41
  "prerelease": "npm test && npm run testshell",
42
42
  "release": "standard-version"
@@ -62,9 +62,6 @@ function encrypt () {
62
62
 
63
63
  for (const processedEnv of processedEnvs) {
64
64
  if (processedEnv.privateKeyAdded) {
65
- // Ops hook point (first-time key created for this env file):
66
- // gate with `opsOn` and an Ops-installed check before calling your
67
- // Ops service (for example: backup/register processedEnv.privateKey).
68
65
  logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
69
66
  // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
70
67
 
@@ -12,6 +12,7 @@ function get (key) {
12
12
 
13
13
  const options = this.opts()
14
14
  logger.debug(`options: ${JSON.stringify(options)}`)
15
+ const prettyPrint = options.prettyPrint || options.pp
15
16
 
16
17
  const ignore = options.ignore || []
17
18
 
@@ -24,7 +25,8 @@ function get (key) {
24
25
  }
25
26
 
26
27
  try {
27
- const { parsed, errors } = new Get(key, envs, options.overload, process.env.DOTENV_KEY, options.all, options.envKeysFile).run()
28
+ const opsOn = options.opsOff !== true
29
+ const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, opsOn).run()
28
30
 
29
31
  for (const error of errors || []) {
30
32
  if (options.strict) throw error // throw immediately if strict
@@ -65,7 +67,7 @@ function get (key) {
65
67
  console.log(inline)
66
68
  } else {
67
69
  let space = 0
68
- if (options.prettyPrint) {
70
+ if (prettyPrint) {
69
71
  space = 2
70
72
  }
71
73
 
@@ -9,8 +9,9 @@ function keypair (key) {
9
9
 
10
10
  const options = this.opts()
11
11
  logger.debug(`options: ${JSON.stringify(options)}`)
12
+ const prettyPrint = options.prettyPrint || options.pp
12
13
 
13
- const results = main.keypair(options.envFile, key, options.envKeysFile)
14
+ const results = main.keypair(options.envFile, key, options.envKeysFile, options.opsOff)
14
15
 
15
16
  if (typeof results === 'object' && results !== null) {
16
17
  // inline shell format - env $(dotenvx keypair --format=shell) your-command
@@ -25,7 +26,7 @@ function keypair (key) {
25
26
  // json format
26
27
  } else {
27
28
  let space = 0
28
- if (options.prettyPrint) {
29
+ if (prettyPrint) {
29
30
  space = 2
30
31
  }
31
32
 
@@ -11,17 +11,20 @@ function rotate () {
11
11
  logger.debug(`options: ${JSON.stringify(options)}`)
12
12
 
13
13
  const envs = this.envs
14
+ const opsOn = options.opsOff !== true
14
15
 
15
16
  // stdout - should not have a try so that exit codes can surface to stdout
16
17
  if (options.stdout) {
17
18
  const {
18
19
  processedEnvs
19
- } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile).run()
20
+ } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
20
21
 
21
22
  for (const processedEnv of processedEnvs) {
22
23
  console.log(processedEnv.envSrc)
23
- console.log('')
24
- console.log(processedEnv.envKeysSrc)
24
+ if (processedEnv.privateKeyAdded) {
25
+ console.log('')
26
+ console.log(processedEnv.envKeysSrc)
27
+ }
25
28
  }
26
29
  process.exit(0) // exit early
27
30
  } else {
@@ -30,7 +33,7 @@ function rotate () {
30
33
  processedEnvs,
31
34
  changedFilepaths,
32
35
  unchangedFilepaths
33
- } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile).run()
36
+ } = new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
34
37
 
35
38
  for (const processedEnv of processedEnvs) {
36
39
  logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
@@ -46,7 +49,9 @@ function rotate () {
46
49
  }
47
50
  } else if (processedEnv.changed) {
48
51
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
49
- fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
52
+ if (processedEnv.privateKeyAdded) {
53
+ fsx.writeFileX(processedEnv.envKeysFilepath, processedEnv.envKeysSrc)
54
+ }
50
55
 
51
56
  logger.verbose(`rotated ${processedEnv.envFilepath} (${processedEnv.filepath})`)
52
57
  } else {
@@ -5,7 +5,6 @@ const executeCommand = require('./../../lib/helpers/executeCommand')
5
5
  const Run = require('./../../lib/services/run')
6
6
 
7
7
  const conventions = require('./../../lib/helpers/conventions')
8
- const DeprecationNotice = require('./../../lib/helpers/deprecationNotice')
9
8
 
10
9
  async function run () {
11
10
  const commandArgs = this.args
@@ -41,26 +40,14 @@ async function run () {
41
40
  envs = this.envs
42
41
  }
43
42
 
44
- new DeprecationNotice().dotenvKey() // DEPRECATION NOTICE
45
-
46
43
  const {
47
44
  processedEnvs,
48
45
  readableStrings,
49
46
  readableFilepaths,
50
47
  uniqueInjectedKeys
51
- } = new Run(envs, options.overload, process.env.DOTENV_KEY, process.env, options.envKeysFile, opsOn).run()
52
-
53
- if (opsOn) {
54
- // removed radar feature for now. contact me at mot@dotenvx.com if still needed for your organization.
55
- // try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
56
- }
48
+ } = new Run(envs, options.overload, process.env, options.envKeysFile, opsOn).run()
57
49
 
58
50
  for (const processedEnv of processedEnvs) {
59
- if (processedEnv.type === 'envVaultFile') {
60
- logger.verbose(`loading env from encrypted ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
61
- logger.debug(`decrypting encrypted env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
62
- }
63
-
64
51
  if (processedEnv.type === 'envFile') {
65
52
  logger.verbose(`loading env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
66
53
  }
@@ -22,12 +22,13 @@ function set (key, value) {
22
22
  try {
23
23
  const envs = this.envs
24
24
  const envKeysFilepath = options.envKeysFile
25
+ const opsOn = options.opsOff !== true
25
26
 
26
27
  const {
27
28
  processedEnvs,
28
29
  changedFilepaths,
29
30
  unchangedFilepaths
30
- } = new Sets(key, value, envs, encrypt, envKeysFilepath).run()
31
+ } = new Sets(key, value, envs, encrypt, envKeysFilepath, opsOn).run()
31
32
 
32
33
  let withEncryption = ''
33
34
 
@@ -65,7 +66,7 @@ function set (key, value) {
65
66
  }
66
67
 
67
68
  for (const processedEnv of processedEnvs) {
68
- if (processedEnv.privateKeyAdded) {
69
+ if (processedEnv.privateKeyAdded) { // TODO: change to localPrivateKeyAdded
69
70
  logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
70
71
  // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
71
72
 
@@ -10,9 +10,6 @@ ext
10
10
  .description('🔌 extensions')
11
11
  .allowUnknownOption()
12
12
 
13
- // list known extensions here you want to display
14
- ext.addHelpText('after', ' vault 🔐 manage .env.vault files')
15
-
16
13
  ext
17
14
  .argument('[command]', 'dynamic ext command')
18
15
  .argument('[args...]', 'dynamic ext command arguments')
@@ -13,6 +13,9 @@ const executeDynamic = require('./../lib/helpers/executeDynamic')
13
13
  const removeDynamicHelpSection = require('./../lib/helpers/removeDynamicHelpSection')
14
14
  const removeOptionsHelpParts = require('./../lib/helpers/removeOptionsHelpParts')
15
15
 
16
+ const Session = require('./../db/session')
17
+ const sesh = new Session()
18
+
16
19
  // for use with run
17
20
  const envs = []
18
21
  function collectEnvs (type) {
@@ -68,12 +71,11 @@ program.command('run')
68
71
  .option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
69
72
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
70
73
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
71
- .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
72
74
  .option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
73
75
  .option('--strict', 'process.exit(1) on any errors', false)
74
76
  .option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
75
77
  .option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
76
- .option('--ops-off', 'disable dotenvx-ops features', false)
78
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
77
79
  .action(function (...args) {
78
80
  this.envs = envs
79
81
  runAction.apply(this, args)
@@ -88,15 +90,15 @@ program.command('get')
88
90
  .option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
89
91
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
90
92
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
91
- .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
92
93
  .option('-o, --overload', 'override existing env variables (by default, existing env vars take precedence over .env files)')
93
94
  .option('--strict', 'process.exit(1) on any errors', false)
94
95
  .option('--convention <name>', 'load a .env convention (available conventions: [\'nextjs\', \'flow\'])')
95
96
  .option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
96
97
  .option('-a, --all', 'include all machine envs as well')
97
98
  .option('-pp, --pretty-print', 'pretty print output')
99
+ .option('--pp', 'pretty print output (alias)')
98
100
  .option('--format <type>', 'format of the output (json, shell, eval)', 'json')
99
- .option('--ops-off', 'disable dotenvx-ops features', false)
101
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
100
102
  .action(function (...args) {
101
103
  this.envs = envs
102
104
  getAction.apply(this, args)
@@ -115,7 +117,7 @@ program.command('set')
115
117
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
116
118
  .option('-c, --encrypt', 'encrypt value', true)
117
119
  .option('-p, --plain', 'store value as plain text', false)
118
- .option('--ops-off', 'disable dotenvx-ops features', false)
120
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
119
121
  .action(function (...args) {
120
122
  this.envs = envs
121
123
  setAction.apply(this, args)
@@ -129,7 +131,7 @@ program.command('encrypt')
129
131
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
130
132
  .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
131
133
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
132
- .option('--ops-off', 'disable dotenvx-ops features', false)
134
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
133
135
  .option('--stdout', 'send to stdout')
134
136
  .action(function (...args) {
135
137
  this.envs = envs
@@ -144,7 +146,7 @@ program.command('decrypt')
144
146
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
145
147
  .option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
146
148
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
147
- .option('--ops-off', 'disable dotenvx-ops features', false)
149
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
148
150
  .option('--stdout', 'send to stdout')
149
151
  .action(function (...args) {
150
152
  this.envs = envs
@@ -159,8 +161,9 @@ program.command('keypair')
159
161
  .argument('[KEY]', 'environment variable key name')
160
162
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
161
163
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
162
- .option('--ops-off', 'disable dotenvx-ops features', false)
164
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
163
165
  .option('-pp, --pretty-print', 'pretty print output')
166
+ .option('--pp', 'pretty print output (alias)')
164
167
  .option('--format <type>', 'format of the output (json, shell)', 'json')
165
168
  .action(keypairAction)
166
169
 
@@ -181,7 +184,7 @@ program.command('rotate')
181
184
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
182
185
  .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
183
186
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
184
- .option('--ops-off', 'disable dotenvx-ops features', false)
187
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
185
188
  .option('--stdout', 'send to stdout')
186
189
  .action(function (...args) {
187
190
  this.envs = envs
@@ -0,0 +1,21 @@
1
+ const Ops = require('./../lib/extensions/ops')
2
+
3
+ class Session {
4
+ constructor () {
5
+ this.ops = new Ops()
6
+ this.opsStatus = this.ops.status()
7
+ }
8
+
9
+ //
10
+ // opsOff/On
11
+ //
12
+ opsOn () {
13
+ return this.opsStatus === 'on'
14
+ }
15
+
16
+ opsOff () {
17
+ return !this.opsOn()
18
+ }
19
+ }
20
+
21
+ module.exports = Session
@@ -0,0 +1,98 @@
1
+ const path = require('path')
2
+ const childProcess = require('child_process')
3
+
4
+ // const { logger } = require('./../../shared/logger')
5
+
6
+ class Ops {
7
+ constructor () {
8
+ this.opsLib = null
9
+
10
+ if (this._isForcedOff()) {
11
+ return
12
+ }
13
+
14
+ // check npm lib
15
+ try { this.opsLib = this._opsNpm() } catch (_e) {}
16
+
17
+ // check binary cli
18
+ if (!this.opsLib) {
19
+ try { this.opsLib = this._opsCli() } catch (_e) {}
20
+ }
21
+
22
+ if (this.opsLib) {
23
+ // logger.successv(`🛡️ ops: ${this.opsLib.status()}`)
24
+ }
25
+ }
26
+
27
+ status () {
28
+ if (this._isForcedOff() || !this.opsLib) {
29
+ return 'off'
30
+ }
31
+
32
+ return this.opsLib.status()
33
+ }
34
+
35
+ keypair (publicKey) {
36
+ if (this._isForcedOff() || !this.opsLib) {
37
+ return {}
38
+ }
39
+
40
+ return this.opsLib.keypair(publicKey)
41
+ }
42
+
43
+ observe (payload) {
44
+ if (!this._isForcedOff() && this.opsLib && this.opsLib.status() !== 'off') {
45
+ const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
46
+ this.opsLib.observe(encoded)
47
+ }
48
+ }
49
+
50
+ //
51
+ // private
52
+ //
53
+ _opsNpm () {
54
+ const npmBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
55
+ return this._opsLib(npmBin)
56
+ }
57
+
58
+ _opsCli () {
59
+ return this._opsLib('dotenvx-ops')
60
+ }
61
+
62
+ _opsLib (binary) {
63
+ childProcess.execFileSync(binary, ['--version'], { stdio: ['pipe', 'pipe', 'ignore'] })
64
+
65
+ return {
66
+ status: () => {
67
+ return childProcess.execFileSync(binary, ['status'], { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
68
+ },
69
+ keypair: (publicKey) => {
70
+ const args = ['keypair']
71
+ if (publicKey) {
72
+ args.push(publicKey)
73
+ }
74
+ const output = childProcess.execFileSync(binary, args, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
75
+ const parsed = JSON.parse(output.toString())
76
+ return parsed
77
+ },
78
+ observe: (encoded) => {
79
+ try {
80
+ const subprocess = childProcess.spawn(binary, ['observe', encoded], {
81
+ stdio: 'ignore',
82
+ detached: true
83
+ })
84
+
85
+ subprocess.unref() // let it run independently
86
+ } catch (e) {
87
+ // noop
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ _isForcedOff () {
94
+ return process.env.DOTENVX_OPS_OFF === 'true'
95
+ }
96
+ }
97
+
98
+ module.exports = Ops
@@ -1,10 +1,7 @@
1
- const path = require('path')
2
-
3
1
  const conventions = require('./conventions')
4
2
  const dotenvOptionPaths = require('./dotenvOptionPaths')
5
- const DeprecationNotice = require('./deprecationNotice')
6
3
 
7
- function buildEnvs (options, DOTENV_KEY = undefined) {
4
+ function buildEnvs (options) {
8
5
  // build envs using user set option.path
9
6
  const optionPaths = dotenvOptionPaths(options) // [ '.env' ]
10
7
 
@@ -13,18 +10,8 @@ function buildEnvs (options, DOTENV_KEY = undefined) {
13
10
  envs = conventions(options.convention).concat(envs)
14
11
  }
15
12
 
16
- new DeprecationNotice({ DOTENV_KEY }).dotenvKey() // DEPRECATION NOTICE
17
-
18
13
  for (const optionPath of optionPaths) {
19
- // if DOTENV_KEY is set then assume we are checking envVaultFile
20
- if (DOTENV_KEY) {
21
- envs.push({
22
- type: 'envVaultFile',
23
- value: path.join(path.dirname(optionPath), '.env.vault')
24
- })
25
- } else {
26
- envs.push({ type: 'envFile', value: optionPath })
27
- }
14
+ envs.push({ type: 'envFile', value: optionPath })
28
15
  }
29
16
 
30
17
  return envs
@@ -1,6 +1,6 @@
1
1
  const { decrypt } = require('eciesjs')
2
2
 
3
- const Errors = require('./errors')
3
+ const Errors = require('./../errors')
4
4
 
5
5
  const PREFIX = 'encrypted:'
6
6
 
@@ -0,0 +1,14 @@
1
+ module.exports = {
2
+ opsKeypair: require('./opsKeypair'),
3
+ localKeypair: require('./localKeypair'),
4
+ encryptValue: require('./encryptValue'),
5
+ decryptKeyValue: require('./decryptKeyValue'),
6
+ isEncrypted: require('./isEncrypted'),
7
+ isPublicKey: require('./isPublicKey'),
8
+ provision: require('./provision'),
9
+ provisionWithPrivateKey: require('./provisionWithPrivateKey'),
10
+ mutateSrc: require('./mutateSrc'),
11
+
12
+ // other
13
+ isFullyEncrypted: require('./../isFullyEncrypted')
14
+ }
@@ -1,6 +1,6 @@
1
1
  const PUBLIC_KEY_PATTERN = /^DOTENV_PUBLIC_KEY/
2
2
 
3
- function isPublicKey (key, value) {
3
+ function isPublicKey (key) {
4
4
  return PUBLIC_KEY_PATTERN.test(key)
5
5
  }
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { PrivateKey } = require('eciesjs')
2
2
 
3
- function keypair (existingPrivateKey) {
3
+ function localKeypair (existingPrivateKey) {
4
4
  let kp
5
5
 
6
6
  if (existingPrivateKey) {
@@ -18,4 +18,4 @@ function keypair (existingPrivateKey) {
18
18
  }
19
19
  }
20
20
 
21
- module.exports = keypair
21
+ module.exports = localKeypair
@@ -0,0 +1,38 @@
1
+ const FIRST_TIME_KEYS_SRC = [
2
+ '#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
3
+ '#/ private decryption keys. DO NOT commit to source control /',
4
+ '#/ [how it works](https://dotenvx.com/encryption) /',
5
+ // '#/ backup with: `dotenvx ops backup` /',
6
+ '#/----------------------------------------------------------/'
7
+ ].join('\n')
8
+
9
+ const path = require('path')
10
+ const fsx = require('./../fsx')
11
+
12
+ function mutateKeysSrc ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
13
+ const filename = path.basename(envFilepath)
14
+ const filepath = path.resolve(envFilepath)
15
+ let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
16
+ if (keysFilepath) {
17
+ resolvedKeysFilepath = path.resolve(keysFilepath)
18
+ }
19
+ const appendPrivateKey = [`# ${filename}`, `${privateKeyName}=${privateKeyValue}`, ''].join('\n')
20
+
21
+ let keysSrc = ''
22
+ if (fsx.existsSync(resolvedKeysFilepath)) {
23
+ keysSrc = fsx.readFileX(resolvedKeysFilepath)
24
+ }
25
+ keysSrc = keysSrc.length > 1 ? keysSrc : `${FIRST_TIME_KEYS_SRC}\n`
26
+ keysSrc = `${keysSrc}\n${appendPrivateKey}`
27
+
28
+ fsx.writeFileX(resolvedKeysFilepath, keysSrc) // TODO: don't write if ops
29
+
30
+ const envKeysFilepath = keysFilepath || path.join(path.dirname(envFilepath), path.basename(resolvedKeysFilepath))
31
+
32
+ return {
33
+ keysSrc,
34
+ envKeysFilepath
35
+ }
36
+ }
37
+
38
+ module.exports = mutateKeysSrc
@@ -0,0 +1,24 @@
1
+ const path = require('path')
2
+ const preserveShebang = require('./../preserveShebang')
3
+ const prependPublicKey = require('./../prependPublicKey')
4
+
5
+ function mutateSrc ({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue }) {
6
+ const filename = path.basename(envFilepath)
7
+ const filepath = path.resolve(envFilepath)
8
+ let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
9
+ if (keysFilepath) {
10
+ resolvedKeysFilepath = path.resolve(keysFilepath)
11
+ }
12
+ const relativeFilepath = path.relative(path.dirname(filepath), resolvedKeysFilepath)
13
+
14
+ const ps = preserveShebang(envSrc)
15
+ const prependedPublicKey = prependPublicKey(publicKeyName, publicKeyValue, filename, relativeFilepath)
16
+
17
+ envSrc = `${ps.firstLinePreserved}${prependedPublicKey}\n${ps.envSrc}`
18
+
19
+ return {
20
+ envSrc
21
+ }
22
+ }
23
+
24
+ module.exports = mutateSrc
@@ -0,0 +1,14 @@
1
+ const Ops = require('../../extensions/ops')
2
+
3
+ function opsKeypair (existingPublicKey) {
4
+ const kp = new Ops().keypair(existingPublicKey)
5
+ const publicKey = kp.public_key
6
+ const privateKey = kp.private_key
7
+
8
+ return {
9
+ publicKey,
10
+ privateKey
11
+ }
12
+ }
13
+
14
+ module.exports = opsKeypair
@@ -0,0 +1,47 @@
1
+ const mutateSrc = require('./mutateSrc')
2
+ const mutateKeysSrc = require('./mutateKeysSrc')
3
+ const opsKeypair = require('./opsKeypair')
4
+ const localKeypair = require('./localKeypair')
5
+ const { keyNames } = require('../keyResolution')
6
+
7
+ function provision ({ envSrc, envFilepath, keysFilepath, opsOn }) {
8
+ opsOn = opsOn === true
9
+ const { publicKeyName, privateKeyName } = keyNames(envFilepath)
10
+
11
+ let publicKey
12
+ let privateKey
13
+ let keysSrc
14
+ let envKeysFilepath
15
+ let privateKeyAdded = false
16
+
17
+ if (opsOn) {
18
+ const kp = opsKeypair()
19
+ publicKey = kp.publicKey
20
+ privateKey = kp.privateKey
21
+ } else {
22
+ const kp = localKeypair()
23
+ publicKey = kp.publicKey
24
+ privateKey = kp.privateKey
25
+ }
26
+
27
+ const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
28
+ envSrc = mutated.envSrc
29
+
30
+ if (!opsOn) {
31
+ const mutated = mutateKeysSrc({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
32
+ keysSrc = mutated.keysSrc
33
+ envKeysFilepath = mutated.envKeysFilepath
34
+ privateKeyAdded = true
35
+ }
36
+
37
+ return {
38
+ envSrc,
39
+ keysSrc,
40
+ publicKey,
41
+ privateKey,
42
+ privateKeyAdded,
43
+ envKeysFilepath
44
+ }
45
+ }
46
+
47
+ module.exports = provision
@@ -0,0 +1,26 @@
1
+ const Errors = require('./../errors')
2
+ const mutateSrc = require('./mutateSrc')
3
+ const localKeypair = require('./localKeypair')
4
+
5
+ function provisionWithPrivateKey ({ envSrc, envFilepath, keysFilepath, privateKeyValue, publicKeyValue, publicKeyName }) {
6
+ const { publicKey, privateKey } = localKeypair(privateKeyValue) // opsOn doesn't matter here since privateKeyValue was already discovered prior (via ops and local) and passed as privateKeyValue
7
+
8
+ // if derivation doesn't match what's in the file (or preset in env)
9
+ if (publicKeyValue && publicKeyValue !== publicKey) {
10
+ throw new Errors({ publicKey, publicKeyExisting: publicKeyValue }).mispairedPrivateKey()
11
+ }
12
+
13
+ // scenario when encrypting a monorepo second .env file from a prior generated -fk .env.keys file
14
+ if (!publicKeyValue) {
15
+ const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
16
+ envSrc = mutated.envSrc
17
+ }
18
+
19
+ return {
20
+ envSrc,
21
+ publicKey,
22
+ privateKey
23
+ }
24
+ }
25
+
26
+ module.exports = provisionWithPrivateKey