@dotenvx/dotenvx 1.53.0 → 1.55.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.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,29 @@
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.53.0...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.55.0...main)
6
+
7
+ ## [1.55.0](https://github.com/dotenvx/dotenvx/compare/v1.54.1...v1.55.0) (2026-03-13)
8
+
9
+ ### Added
10
+
11
+ * Add 'KEYS OFF COMPUTER' security feature when [dotenvx-ops](https://dotenvx.com/ops) installed ([#746](https://github.com/dotenvx/dotenvx/pull/746))
12
+
13
+ ### Removed
14
+
15
+ * Remove `ProKeypair` logic
16
+
17
+ ## [1.54.1](https://github.com/dotenvx/dotenvx/compare/v1.54.0...v1.54.1) (2026-03-06)
18
+
19
+ ### Changed
20
+
21
+ * Fix npm publish
22
+
23
+ ## [1.54.0](https://github.com/dotenvx/dotenvx/compare/v1.53.0...v1.54.0) (2026-03-06)
24
+
25
+ ### Removed
26
+
27
+ * Remove `ops observe` (radar feature). If needed download [dotenvx-ops](https://dotenvx.com/ops) and reach out to me directly at mot@dotenvx.com. ([#745](https://github.com/dotenvx/dotenvx/pull/745))
6
28
 
7
29
  ## [1.53.0](https://github.com/dotenvx/dotenvx/compare/v1.52.0...v1.53.0) (2026-03-05)
8
30
 
package/README.md CHANGED
@@ -2597,50 +2597,11 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
2597
2597
 
2598
2598
   
2599
2599
 
2600
- ## AS2 🔐
2600
+ ## Ops 🛡️
2601
2601
 
2602
- <a href="https://dotenvx.com/as2">
2603
- <img src="https://dotenvx.com/assets/img/as2/9.jpg" alt="dotenvx as2" height="400" align="right">
2604
- </a>
2602
+ [![dotenvx-ops](https://dotenvx.com/dotenvx-ops-banner.png?v=3)](https://dotenvx.com/ops)
2605
2603
 
2606
- *agentic secret storage*.
2607
-
2608
- > Secrets designed for agents. No logins. No consoles. Pure cryptography.
2609
-
2610
- ### Quickstart
2611
-
2612
- Install [`vestauth`](https://github.com/vestauth/vestauth) and initialize your agent. (AS2 uses [vestauth](https://vestauth.com) to authenticate agents.)
2613
-
2614
- ```sh
2615
- npm i -g vestauth
2616
- vestauth agent init
2617
- ```
2618
-
2619
- Your agent can `set` secrets.
2620
-
2621
- ```
2622
- vestauth agent curl -X POST https://as2.dotenvx.com/set '{"KEY": "value"}'
2623
- ```
2624
-
2625
- Your agent can `get` secrets.
2626
-
2627
- ```
2628
- vestauth agent curl https://as2.dotenvx.com/get?key=KEY
2629
- ```
2630
-
2631
- &nbsp;
2632
-
2633
- ## Ops 🏰
2634
-
2635
- [![dotenvx-ops](https://dotenvx.com/dotenvx-ops-banner.png?v=2)](https://dotenvx.com/ops)
2636
-
2637
- *production grade dotenvx*–with operational primitives.
2638
-
2639
- > As dotenvx spreads inside companies, we're learning—through enterprise engagements—that dotenvx is missing an operations layer.
2640
- >
2641
- > Dotenvx Ops is our answer.
2642
- >
2643
- > It's production grade dotenvx–with operational primitives for teams, infrastructure, and agents. Private key management, access controls, and more.
2604
+ > KEYS OFF COMPUTER
2644
2605
 
2645
2606
  ### Quickstart
2646
2607
 
@@ -2648,23 +2609,12 @@ Install it and gain `ops` commands.
2648
2609
 
2649
2610
  ```sh
2650
2611
  $ curl -sfS https://dotenvx.sh/ops | sh
2651
- $ dotenvx ops backup
2652
- backed up [username/project]
2653
- ⮕ next run [dotenvx-ops open] to view
2612
+ $ dotenvx ops login
2613
+ $ dotenvx encrypt
2654
2614
  ```
2655
2615
 
2656
2616
  ### CLI
2657
2617
 
2658
- <details><summary>`ops backup`</summary><br>
2659
-
2660
- Back up .env.keys.
2661
-
2662
- ```sh
2663
- $ dotenvx-ops backup
2664
- ✔ backed up [username/project]
2665
- ```
2666
-
2667
- </details>
2668
2618
  <details><summary>`ops login`</summary><br>
2669
2619
 
2670
2620
  Log in.
@@ -2687,7 +2637,7 @@ $ dotenvx ops logout
2687
2637
  ```
2688
2638
 
2689
2639
  </details>
2690
- <details><summary>`settings`</summary><br>
2640
+ <details><summary>`ops settings`</summary><br>
2691
2641
 
2692
2642
  Check and configure various settings for [Ops](https://dotenvx.com/ops) - `username`, `token`, and more.
2693
2643
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.53.0",
2
+ "version": "1.55.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -10,6 +10,7 @@ function decrypt () {
10
10
  logger.debug(`options: ${JSON.stringify(options)}`)
11
11
 
12
12
  const envs = this.envs
13
+ const opsOn = options.opsOff !== true
13
14
 
14
15
  let errorCount = 0
15
16
 
@@ -17,7 +18,7 @@ function decrypt () {
17
18
  if (options.stdout) {
18
19
  const {
19
20
  processedEnvs
20
- } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
21
+ } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
21
22
 
22
23
  for (const processedEnv of processedEnvs) {
23
24
  if (processedEnv.error) {
@@ -42,7 +43,7 @@ function decrypt () {
42
43
  processedEnvs,
43
44
  changedFilepaths,
44
45
  unchangedFilepaths
45
- } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
46
+ } = new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
46
47
 
47
48
  for (const processedEnv of processedEnvs) {
48
49
  logger.verbose(`decrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
@@ -11,12 +11,13 @@ function encrypt () {
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 Encrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
20
+ } = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
20
21
 
21
22
  for (const processedEnv of processedEnvs) {
22
23
  console.log(processedEnv.envSrc)
@@ -28,7 +29,7 @@ function encrypt () {
28
29
  processedEnvs,
29
30
  changedFilepaths,
30
31
  unchangedFilepaths
31
- } = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile).run()
32
+ } = new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, opsOn).run()
32
33
 
33
34
  for (const processedEnv of processedEnvs) {
34
35
  logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
@@ -61,8 +62,11 @@ function encrypt () {
61
62
 
62
63
  for (const processedEnv of processedEnvs) {
63
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).
64
68
  logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
65
- logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
69
+ // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
66
70
 
67
71
  if (!isIgnoringDotenvKeys()) {
68
72
  logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
@@ -65,7 +65,7 @@ function rotate () {
65
65
  for (const processedEnv of processedEnvs) {
66
66
  if (processedEnv.privateKeyAdded) {
67
67
  logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
68
- logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
68
+ // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
69
69
 
70
70
  if (!isIgnoringDotenvKeys()) {
71
71
  logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
@@ -3,7 +3,6 @@ const { logger } = require('./../../shared/logger')
3
3
 
4
4
  const executeCommand = require('./../../lib/helpers/executeCommand')
5
5
  const Run = require('./../../lib/services/run')
6
- const Ops = require('./../../lib/services/ops')
7
6
 
8
7
  const conventions = require('./../../lib/helpers/conventions')
9
8
  const DeprecationNotice = require('./../../lib/helpers/deprecationNotice')
@@ -45,8 +44,6 @@ async function run () {
45
44
  new DeprecationNotice().dotenvKey() // DEPRECATION NOTICE
46
45
 
47
46
  const {
48
- beforeEnv,
49
- afterEnv,
50
47
  processedEnvs,
51
48
  readableStrings,
52
49
  readableFilepaths,
@@ -54,7 +51,8 @@ async function run () {
54
51
  } = new Run(envs, options.overload, process.env.DOTENV_KEY, process.env, options.envKeysFile, opsOn).run()
55
52
 
56
53
  if (opsOn) {
57
- try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
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 {}
58
56
  }
59
57
 
60
58
  for (const processedEnv of processedEnvs) {
@@ -67,7 +67,7 @@ function set (key, value) {
67
67
  for (const processedEnv of processedEnvs) {
68
68
  if (processedEnv.privateKeyAdded) {
69
69
  logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
70
- logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
70
+ // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
71
71
 
72
72
  if (!isIgnoringDotenvKeys()) {
73
73
  logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
@@ -96,6 +96,7 @@ program.command('get')
96
96
  .option('-a, --all', 'include all machine envs as well')
97
97
  .option('-pp, --pretty-print', 'pretty print output')
98
98
  .option('--format <type>', 'format of the output (json, shell, eval)', 'json')
99
+ .option('--ops-off', 'disable dotenvx-ops features', false)
99
100
  .action(function (...args) {
100
101
  this.envs = envs
101
102
  getAction.apply(this, args)
@@ -114,6 +115,7 @@ program.command('set')
114
115
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
115
116
  .option('-c, --encrypt', 'encrypt value', true)
116
117
  .option('-p, --plain', 'store value as plain text', false)
118
+ .option('--ops-off', 'disable dotenvx-ops features', false)
117
119
  .action(function (...args) {
118
120
  this.envs = envs
119
121
  setAction.apply(this, args)
@@ -127,6 +129,7 @@ program.command('encrypt')
127
129
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
128
130
  .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
129
131
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
132
+ .option('--ops-off', 'disable dotenvx-ops features', false)
130
133
  .option('--stdout', 'send to stdout')
131
134
  .action(function (...args) {
132
135
  this.envs = envs
@@ -141,6 +144,7 @@ program.command('decrypt')
141
144
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
142
145
  .option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
143
146
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
147
+ .option('--ops-off', 'disable dotenvx-ops features', false)
144
148
  .option('--stdout', 'send to stdout')
145
149
  .action(function (...args) {
146
150
  this.envs = envs
@@ -155,6 +159,7 @@ program.command('keypair')
155
159
  .argument('[KEY]', 'environment variable key name')
156
160
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
157
161
  .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)
158
163
  .option('-pp, --pretty-print', 'pretty print output')
159
164
  .option('--format <type>', 'format of the output (json, shell)', 'json')
160
165
  .action(keypairAction)
@@ -176,6 +181,7 @@ program.command('rotate')
176
181
  .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
177
182
  .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
178
183
  .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
184
+ .option('--ops-off', 'disable dotenvx-ops features', false)
179
185
  .option('--stdout', 'send to stdout')
180
186
  .action(function (...args) {
181
187
  this.envs = envs
@@ -201,7 +207,7 @@ program.command('help [command]')
201
207
  // dotenvx pro
202
208
  program.addHelpText('after', ' ')
203
209
  program.addHelpText('after', 'Advanced: ')
204
- program.addHelpText('after', ' ops 🏰 ops')
210
+ program.addHelpText('after', ' ops 🛡️ ops')
205
211
  program.addHelpText('after', ' ext 🔌 extensions')
206
212
 
207
213
  // dotenvx ext
@@ -40,7 +40,7 @@ function executeDynamic (program, command, rawArgs) {
40
40
 
41
41
  console.log(ops)
42
42
  console.log('')
43
- logger.warn(`[INSTALLATION_NEEDED] install dotenvx-${command} to use [dotenvx ${command}] 🏰`)
43
+ logger.warn(`[INSTALLATION_NEEDED] install dotenvx-${command} to use [dotenvx ${command}] 🛡️`)
44
44
  logger.help('⮕ next run: [curl -sfS https://dotenvx.sh/ops | sh]')
45
45
  logger.help('⮕ see more: [https://dotenvx.com/ops]')
46
46
  } else {
@@ -1,22 +1,25 @@
1
1
  // helpers
2
2
  const guessPrivateKeyName = require('./guessPrivateKeyName')
3
- const ProKeypair = require('./proKeypair')
3
+ const findPublicKey = require('./findPublicKey')
4
4
 
5
5
  // services
6
6
  const Keypair = require('./../services/keypair')
7
+ const Ops = require('./../services/ops')
7
8
 
8
- function findPrivateKey (envFilepath, envKeysFilepath = null, opsOn = true) {
9
+ function findPrivateKey (envFilepath, envKeysFilepath = null, opsOn = false, publicKey = null) {
9
10
  // use path/to/.env.${environment} to generate privateKeyName
10
11
  const privateKeyName = guessPrivateKeyName(envFilepath)
11
12
 
12
- let proKeypairs = {}
13
13
  if (opsOn) {
14
- proKeypairs = new ProKeypair(envFilepath).run() // TODO: implement custom envKeysFilepath
14
+ const resolvedPublicKey = publicKey || findPublicKey(envFilepath)
15
+ const opsPrivateKey = new Ops().keypair(resolvedPublicKey)
16
+ if (opsPrivateKey) {
17
+ return opsPrivateKey
18
+ }
15
19
  }
16
20
 
17
21
  const keypairs = new Keypair(envFilepath, envKeysFilepath).run()
18
-
19
- return proKeypairs[privateKeyName] || keypairs[privateKeyName]
22
+ return keypairs[privateKeyName]
20
23
  }
21
24
 
22
25
  module.exports = { findPrivateKey }
@@ -1,21 +1,15 @@
1
1
  // helpers
2
2
  const guessPublicKeyName = require('./guessPublicKeyName')
3
- const ProKeypair = require('./proKeypair')
4
3
 
5
4
  // services
6
5
  const Keypair = require('./../services/keypair')
7
6
 
8
- function findPublicKey (envFilepath, opsOn = true) {
7
+ function findPublicKey (envFilepath) {
9
8
  const publicKeyName = guessPublicKeyName(envFilepath)
10
9
 
11
- let proKeypairs = {}
12
- if (opsOn) {
13
- proKeypairs = new ProKeypair(envFilepath).run()
14
- }
15
-
16
10
  const keypairs = new Keypair(envFilepath).run()
17
11
 
18
- return proKeypairs[publicKeyName] || keypairs[publicKeyName]
12
+ return keypairs[publicKeyName]
19
13
  }
20
14
 
21
15
  module.exports = findPublicKey
package/src/lib/main.js CHANGED
@@ -12,7 +12,6 @@ const Sets = require('./services/sets')
12
12
  const Get = require('./services/get')
13
13
  const Keypair = require('./services/keypair')
14
14
  const Genexample = require('./services/genexample')
15
- const Ops = require('./services/ops')
16
15
 
17
16
  // helpers
18
17
  const buildEnvs = require('./helpers/buildEnvs')
@@ -58,15 +57,14 @@ const config = function (options = {}) {
58
57
  try {
59
58
  const envs = buildEnvs(options, DOTENV_KEY)
60
59
  const {
61
- beforeEnv,
62
- afterEnv,
63
60
  processedEnvs,
64
61
  readableFilepaths,
65
62
  uniqueInjectedKeys
66
63
  } = new Run(envs, overload, DOTENV_KEY, processEnv, envKeysFile, opsOn).run()
67
64
 
68
65
  if (opsOn) {
69
- try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
66
+ // removed radar feature for now. contact me at mot@dotenvx.com if still needed for your organization.
67
+ // try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
70
68
  }
71
69
 
72
70
  let lastError
@@ -239,7 +237,7 @@ const set = function (key, value, options = {}) {
239
237
  for (const processedEnv of processedEnvs) {
240
238
  if (processedEnv.privateKeyAdded) {
241
239
  logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
242
- logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
240
+ // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
243
241
 
244
242
  if (!isIgnoringDotenvKeys()) {
245
243
  logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
@@ -7,6 +7,7 @@ const TYPE_ENV_FILE = 'envFile'
7
7
  const Errors = require('./../helpers/errors')
8
8
  const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
9
9
  const { findPrivateKey } = require('./../helpers/findPrivateKey')
10
+ const findPublicKey = require('./../helpers/findPublicKey')
10
11
  const decryptKeyValue = require('./../helpers/decryptKeyValue')
11
12
  const isEncrypted = require('./../helpers/isEncrypted')
12
13
  const dotenvParse = require('./../helpers/dotenvParse')
@@ -15,11 +16,12 @@ const detectEncoding = require('./../helpers/detectEncoding')
15
16
  const determineEnvs = require('./../helpers/determineEnvs')
16
17
 
17
18
  class Decrypt {
18
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null) {
19
+ constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = true) {
19
20
  this.envs = determineEnvs(envs, process.env)
20
21
  this.key = key
21
22
  this.excludeKey = excludeKey
22
23
  this.envKeysFilepath = envKeysFilepath
24
+ this.opsOn = opsOn
23
25
 
24
26
  this.processedEnvs = []
25
27
  this.changedFilepaths = new Set()
@@ -65,7 +67,8 @@ class Decrypt {
65
67
  let envSrc = fsx.readFileX(filepath, { encoding })
66
68
  const envParsed = dotenvParse(envSrc)
67
69
 
68
- const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
70
+ const publicKey = findPublicKey(envFilepath)
71
+ const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn, publicKey)
69
72
  const privateKeyName = guessPrivateKeyName(envFilepath)
70
73
 
71
74
  row.privateKey = privateKey
@@ -20,11 +20,12 @@ const truncate = require('./../helpers/truncate')
20
20
  const isPublicKey = require('./../helpers/isPublicKey')
21
21
 
22
22
  class Encrypt {
23
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null) {
23
+ constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = true) {
24
24
  this.envs = determineEnvs(envs, process.env)
25
25
  this.key = key
26
26
  this.excludeKey = excludeKey
27
27
  this.envKeysFilepath = envKeysFilepath
28
+ this.opsOn = opsOn
28
29
 
29
30
  this.processedEnvs = []
30
31
  this.changedFilepaths = new Set()
@@ -76,8 +77,8 @@ class Encrypt {
76
77
 
77
78
  const publicKeyName = guessPublicKeyName(envFilepath)
78
79
  const privateKeyName = guessPrivateKeyName(envFilepath)
79
- const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
80
80
  const existingPublicKey = findPublicKey(envFilepath)
81
+ const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn, existingPublicKey)
81
82
 
82
83
  let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
83
84
  if (this.envKeysFilepath) {
@@ -86,6 +87,7 @@ class Encrypt {
86
87
  const relativeFilepath = path.relative(path.dirname(filepath), envKeysFilepath)
87
88
 
88
89
  if (existingPrivateKey) {
90
+ // throw new Error('implement for remote Ops existingPrivateKey')
89
91
  const kp = keypair(existingPrivateKey)
90
92
  publicKey = kp.publicKey
91
93
  privateKey = kp.privateKey
@@ -109,6 +111,7 @@ class Encrypt {
109
111
  envSrc = `${firstLinePreserved}${prependPublicKey}\n${envSrc}`
110
112
  }
111
113
  } else if (existingPublicKey) {
114
+ // throw new Error('implement for remote Ops existingPrivateKey')
112
115
  publicKey = existingPublicKey
113
116
  } else {
114
117
  // .env.keys
@@ -122,9 +125,13 @@ class Encrypt {
122
125
  const firstLinePreserved = ps.firstLinePreserved
123
126
  envSrc = ps.envSrc
124
127
 
128
+ // TODO: instead get this from API
125
129
  const kp = keypair() // generates a fresh keypair in memory
126
130
  publicKey = kp.publicKey
127
131
  privateKey = kp.privateKey
132
+ // Ops hook point (first-time key generation):
133
+ // if Ops is installed and opsOff is not set, send privateKey/privateKeyName/envFilepath
134
+ // to your Ops service before persisting or immediately after writing below.
128
135
 
129
136
  const prependPublicKey = this._prependPublicKey(publicKeyName, publicKey, filename, relativeFilepath)
130
137
 
@@ -133,7 +140,7 @@ class Encrypt {
133
140
  '#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
134
141
  '#/ private decryption keys. DO NOT commit to source control /',
135
142
  '#/ [how it works](https://dotenvx.com/encryption) /',
136
- '#/ backup with: `dotenvx ops backup` /',
143
+ // '#/ backup with: `dotenvx ops backup` /',
137
144
  '#/----------------------------------------------------------/'
138
145
  ].join('\n')
139
146
  const appendPrivateKey = [
@@ -148,6 +155,9 @@ class Encrypt {
148
155
 
149
156
  // write to .env.keys
150
157
  fsx.writeFileX(envKeysFilepath, keysSrc)
158
+ // Ops hook point (after persistence):
159
+ // if Ops is installed and opsOff is not set, trigger backup/registration now that
160
+ // .env.keys has been written and row.privateKeyAdded will be true for callers.
151
161
 
152
162
  row.privateKeyAdded = true
153
163
  row.envKeysFilepath = this.envKeysFilepath || path.join(path.dirname(envFilepath), path.basename(envKeysFilepath))
@@ -10,12 +10,12 @@ class Ops {
10
10
  // check npm lib
11
11
  try {
12
12
  this.opsLib = this._opsNpm()
13
- logger.successv(`📡 radar: ${this.opsLib.status}`)
13
+ logger.successv(`🛡️ ops: ${this.opsLib.status}`)
14
14
  } catch (e) {
15
15
  // check binary cli
16
16
  try {
17
17
  this.opsLib = this._opsCli()
18
- logger.successv(`📡 radar: ${this.opsLib.status}`)
18
+ logger.successv(`🛡️ ops: ${this.opsLib.status}`)
19
19
  } catch (_e2) {
20
20
  // noop
21
21
  }
@@ -30,6 +30,38 @@ class Ops {
30
30
  }
31
31
  }
32
32
 
33
+ keypair (publicKey) {
34
+ const args = ['keypair']
35
+ if (publicKey) {
36
+ args.push(publicKey)
37
+ }
38
+
39
+ const options = { stdio: ['pipe', 'pipe', 'ignore'] }
40
+ const fallbackBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
41
+
42
+ try {
43
+ const output = childProcess.execFileSync(fallbackBin, args, options)
44
+ const parsed = JSON.parse(output.toString())
45
+ if (parsed && parsed.private_key) {
46
+ return parsed.private_key
47
+ }
48
+ } catch (e) {
49
+ // noop
50
+ }
51
+
52
+ try {
53
+ const output = childProcess.execFileSync('dotenvx-ops', args, options)
54
+ const parsed = JSON.parse(output.toString())
55
+ if (parsed && parsed.private_key) {
56
+ return parsed.private_key
57
+ }
58
+ } catch (e) {
59
+ // noop
60
+ }
61
+
62
+ return null
63
+ }
64
+
33
65
  encode (payload) {
34
66
  return Buffer.from(JSON.stringify(payload)).toString('base64')
35
67
  }
@@ -15,6 +15,7 @@ const append = require('./../helpers/append')
15
15
  const detectEncoding = require('./../helpers/detectEncoding')
16
16
  const determineEnvs = require('./../helpers/determineEnvs')
17
17
  const { findPrivateKey } = require('./../helpers/findPrivateKey')
18
+ const findPublicKey = require('./../helpers/findPublicKey')
18
19
  const decryptKeyValue = require('./../helpers/decryptKeyValue')
19
20
  const keypair = require('./../helpers/keypair')
20
21
 
@@ -73,7 +74,8 @@ class Rotate {
73
74
 
74
75
  const publicKeyName = guessPublicKeyName(envFilepath)
75
76
  const privateKeyName = guessPrivateKeyName(envFilepath)
76
- const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
77
+ const existingPublicKey = findPublicKey(envFilepath)
78
+ const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath, false, existingPublicKey)
77
79
 
78
80
  let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
79
81
  if (this.envKeysFilepath) {
@@ -12,6 +12,7 @@ const dotenvParse = require('./../helpers/dotenvParse')
12
12
  const parseEnvironmentFromDotenvKey = require('./../helpers/parseEnvironmentFromDotenvKey')
13
13
  const detectEncoding = require('./../helpers/detectEncoding')
14
14
  const { findPrivateKey } = require('./../helpers/findPrivateKey')
15
+ const findPublicKey = require('./../helpers/findPublicKey')
15
16
  const guessPrivateKeyName = require('./../helpers/guessPrivateKeyName')
16
17
  const determineEnvs = require('./../helpers/determineEnvs')
17
18
 
@@ -97,7 +98,8 @@ class Run {
97
98
  const src = fsx.readFileX(filepath, { encoding })
98
99
  this.readableFilepaths.add(envFilepath)
99
100
 
100
- const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn)
101
+ const publicKey = findPublicKey(envFilepath)
102
+ const privateKey = findPrivateKey(envFilepath, this.envKeysFilepath, this.opsOn, publicKey)
101
103
  const privateKeyName = guessPrivateKeyName(envFilepath)
102
104
  const { parsed, errors, injected, preExisted } = new Parse(src, privateKey, this.processEnv, this.overload, privateKeyName).run()
103
105
 
@@ -77,8 +77,8 @@ class Sets {
77
77
 
78
78
  const publicKeyName = guessPublicKeyName(envFilepath)
79
79
  const privateKeyName = guessPrivateKeyName(envFilepath)
80
- const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath)
81
80
  const existingPublicKey = findPublicKey(envFilepath)
81
+ const existingPrivateKey = findPrivateKey(envFilepath, this.envKeysFilepath, false, existingPublicKey)
82
82
 
83
83
  let envKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
84
84
  if (this.envKeysFilepath) {
@@ -137,7 +137,7 @@ class Sets {
137
137
  '#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
138
138
  '#/ private decryption keys. DO NOT commit to source control /',
139
139
  '#/ [how it works](https://dotenvx.com/encryption) /',
140
- '#/ backup with: `dotenvx ops backup` /',
140
+ // '#/ backup with: `dotenvx ops backup` /',
141
141
  '#/----------------------------------------------------------/'
142
142
  ].join('\n')
143
143
  const appendPrivateKey = [
@@ -1,39 +0,0 @@
1
- const path = require('path')
2
- const childProcess = require('child_process')
3
-
4
- const guessPrivateKeyName = require('./guessPrivateKeyName')
5
- const guessPublicKeyName = require('./guessPublicKeyName')
6
-
7
- class ProKeypair {
8
- constructor (envFilepath) {
9
- this.envFilepath = envFilepath
10
- }
11
-
12
- run () {
13
- let result = {}
14
-
15
- try {
16
- const fallbackBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-pro')
17
- const output = childProcess.execSync(`${fallbackBin} keypair -f ${this.envFilepath}`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
18
- result = JSON.parse(output)
19
- } catch (_e) {
20
- try {
21
- // if installed as binary cli
22
- const output = childProcess.execSync(`dotenvx-pro keypair -f ${this.envFilepath}`, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
23
-
24
- result = JSON.parse(output)
25
- } catch (_e) {
26
- const privateKeyName = guessPrivateKeyName(this.envFilepath)
27
- const publicKeyName = guessPublicKeyName(this.envFilepath)
28
-
29
- // match format of dotenvx-pro
30
- result[privateKeyName] = null
31
- result[publicKeyName] = null
32
- }
33
- }
34
-
35
- return result
36
- }
37
- }
38
-
39
- module.exports = ProKeypair