@dotenvx/dotenvx 1.54.1 → 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 +11 -1
- package/README.md +24 -22
- package/package.json +1 -1
- package/src/cli/actions/decrypt.js +3 -2
- package/src/cli/actions/encrypt.js +7 -3
- package/src/cli/actions/rotate.js +1 -1
- package/src/cli/actions/set.js +1 -1
- package/src/cli/dotenvx.js +7 -1
- package/src/lib/helpers/executeDynamic.js +1 -1
- package/src/lib/helpers/findPrivateKey.js +9 -6
- package/src/lib/helpers/findPublicKey.js +2 -8
- package/src/lib/main.js +1 -1
- package/src/lib/services/decrypt.js +5 -2
- package/src/lib/services/encrypt.js +13 -3
- package/src/lib/services/ops.js +34 -2
- package/src/lib/services/rotate.js +3 -1
- package/src/lib/services/run.js +3 -1
- package/src/lib/services/sets.js +2 -2
- package/src/lib/helpers/proKeypair.js +0 -39
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
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.
|
|
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
|
|
6
16
|
|
|
7
17
|
## [1.54.1](https://github.com/dotenvx/dotenvx/compare/v1.54.0...v1.54.1) (2026-03-06)
|
|
8
18
|
|
package/README.md
CHANGED
|
@@ -1247,6 +1247,15 @@ $ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env
|
|
|
1247
1247
|
$ dotenvx run -fk .env.keys -f apps/app1/.env -- yourcommand
|
|
1248
1248
|
```
|
|
1249
1249
|
|
|
1250
|
+
</details>
|
|
1251
|
+
<details><summary>`run --ops-off`</summary><br>
|
|
1252
|
+
|
|
1253
|
+
Turn off [Dotenvx Ops](https://dotenvx.com/ops) features.
|
|
1254
|
+
|
|
1255
|
+
```sh
|
|
1256
|
+
$ dotenvx run --ops-off -- yourcommand
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1250
1259
|
</details>
|
|
1251
1260
|
<details><summary>`get KEY`</summary><br>
|
|
1252
1261
|
|
|
@@ -2485,6 +2494,16 @@ This is equivalent to using `--convention=nextjs` with the CLI:
|
|
|
2485
2494
|
$ dotenvx run --convention=nextjs -- node index.js
|
|
2486
2495
|
```
|
|
2487
2496
|
|
|
2497
|
+
</details>
|
|
2498
|
+
<details><summary>`config(opsOff:)` - opsOff</summary><br>
|
|
2499
|
+
|
|
2500
|
+
Turn off [Dotenvx Ops](https://dotenvx.com/ops) features.
|
|
2501
|
+
|
|
2502
|
+
```js
|
|
2503
|
+
// index.js
|
|
2504
|
+
require('@dotenvx/dotenvx').config({opsOff: true})
|
|
2505
|
+
```
|
|
2506
|
+
|
|
2488
2507
|
</details>
|
|
2489
2508
|
<details><summary>`parse(src)`</summary><br>
|
|
2490
2509
|
|
|
@@ -2578,17 +2597,11 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
|
|
|
2578
2597
|
|
|
2579
2598
|
|
|
2580
2599
|
|
|
2581
|
-
## Ops
|
|
2582
|
-
|
|
2583
|
-
[](https://dotenvx.com/ops)
|
|
2600
|
+
## Ops 🛡️
|
|
2584
2601
|
|
|
2585
|
-
|
|
2602
|
+
[](https://dotenvx.com/ops)
|
|
2586
2603
|
|
|
2587
|
-
>
|
|
2588
|
-
>
|
|
2589
|
-
> Dotenvx Ops is our answer.
|
|
2590
|
-
>
|
|
2591
|
-
> It's production grade dotenvx–with operational primitives for teams, infrastructure, and agents. Private key management, access controls, and more.
|
|
2604
|
+
> KEYS OFF COMPUTER
|
|
2592
2605
|
|
|
2593
2606
|
### Quickstart
|
|
2594
2607
|
|
|
@@ -2596,23 +2609,12 @@ Install it and gain `ops` commands.
|
|
|
2596
2609
|
|
|
2597
2610
|
```sh
|
|
2598
2611
|
$ curl -sfS https://dotenvx.sh/ops | sh
|
|
2599
|
-
$ dotenvx ops
|
|
2600
|
-
|
|
2601
|
-
⮕ next run [dotenvx-ops open] to view
|
|
2612
|
+
$ dotenvx ops login
|
|
2613
|
+
$ dotenvx encrypt
|
|
2602
2614
|
```
|
|
2603
2615
|
|
|
2604
2616
|
### CLI
|
|
2605
2617
|
|
|
2606
|
-
<details><summary>`ops backup`</summary><br>
|
|
2607
|
-
|
|
2608
|
-
Back up .env.keys.
|
|
2609
|
-
|
|
2610
|
-
```sh
|
|
2611
|
-
$ dotenvx-ops backup
|
|
2612
|
-
✔ backed up [username/project]
|
|
2613
|
-
```
|
|
2614
|
-
|
|
2615
|
-
</details>
|
|
2616
2618
|
<details><summary>`ops login`</summary><br>
|
|
2617
2619
|
|
|
2618
2620
|
Log in.
|
package/package.json
CHANGED
|
@@ -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')
|
package/src/cli/actions/set.js
CHANGED
|
@@ -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')
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
12
|
+
return keypairs[publicKeyName]
|
|
19
13
|
}
|
|
20
14
|
|
|
21
15
|
module.exports = findPublicKey
|
package/src/lib/main.js
CHANGED
|
@@ -237,7 +237,7 @@ const set = function (key, value, options = {}) {
|
|
|
237
237
|
for (const processedEnv of processedEnvs) {
|
|
238
238
|
if (processedEnv.privateKeyAdded) {
|
|
239
239
|
logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
|
|
240
|
-
logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
|
|
240
|
+
// logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
|
|
241
241
|
|
|
242
242
|
if (!isIgnoringDotenvKeys()) {
|
|
243
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
|
|
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))
|
package/src/lib/services/ops.js
CHANGED
|
@@ -10,12 +10,12 @@ class Ops {
|
|
|
10
10
|
// check npm lib
|
|
11
11
|
try {
|
|
12
12
|
this.opsLib = this._opsNpm()
|
|
13
|
-
logger.successv(
|
|
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(
|
|
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
|
|
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) {
|
package/src/lib/services/run.js
CHANGED
|
@@ -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
|
|
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
|
|
package/src/lib/services/sets.js
CHANGED
|
@@ -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
|