@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 +23 -1
- package/README.md +6 -56
- 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/run.js +2 -4
- 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 +3 -5
- 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,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.
|
|
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
|
-
##
|
|
2600
|
+
## Ops 🛡️
|
|
2601
2601
|
|
|
2602
|
-
|
|
2603
|
-
<img src="https://dotenvx.com/assets/img/as2/9.jpg" alt="dotenvx as2" height="400" align="right">
|
|
2604
|
-
</a>
|
|
2602
|
+
[](https://dotenvx.com/ops)
|
|
2605
2603
|
|
|
2606
|
-
|
|
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
|
-
|
|
2632
|
-
|
|
2633
|
-
## Ops 🏰
|
|
2634
|
-
|
|
2635
|
-
[](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
|
|
2652
|
-
|
|
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
|
@@ -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/run.js
CHANGED
|
@@ -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
|
-
|
|
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) {
|
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
|
@@ -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
|
-
|
|
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
|
|
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
|