@dotenvx/dotenvx 1.71.2 → 1.72.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 +15 -1
- package/README.md +2 -2
- package/package.json +8 -3
- package/src/cli/actions/decrypt.js +7 -3
- package/src/cli/actions/encrypt.js +9 -3
- package/src/cli/actions/get.js +3 -1
- package/src/cli/actions/keypair.js +3 -1
- package/src/cli/actions/login.js +63 -0
- package/src/cli/actions/logout.js +36 -0
- package/src/cli/actions/normalizeArmorOptions.js +1 -2
- package/src/cli/actions/rotate.js +6 -2
- package/src/cli/actions/run.js +2 -1
- package/src/cli/actions/set.js +4 -2
- package/src/cli/dotenvx.js +14 -19
- package/src/db/device.js +73 -0
- package/src/db/session.js +186 -0
- package/src/lib/api/postLogout.js +34 -0
- package/src/lib/api/postOauthDeviceCode.js +38 -0
- package/src/lib/api/postOauthToken.js +35 -0
- package/src/lib/extensions/armor.js +13 -0
- package/src/lib/helpers/buildApiError.js +16 -0
- package/src/lib/helpers/buildOauthError.js +14 -0
- package/src/lib/helpers/createSpinner.js +1 -1
- package/src/lib/helpers/cryptography/armorKeypair.js +2 -1
- package/src/lib/helpers/cryptography/provision.js +2 -2
- package/src/lib/helpers/cryptography/provisionSync.js +2 -2
- package/src/lib/helpers/decryptDeviceValue.js +10 -0
- package/src/lib/helpers/encryptDeviceValue.js +9 -0
- package/src/lib/helpers/formatCode.js +11 -0
- package/src/lib/helpers/http.js +7 -0
- package/src/lib/helpers/jsonToEnv.js +7 -0
- package/src/lib/helpers/keyResolution/keyValues.js +3 -0
- package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +3 -0
- package/src/lib/helpers/keyResolution/keyValuesSync.js +3 -0
- package/src/lib/helpers/listenForOpenKey.js +46 -0
- package/src/lib/helpers/normalizeToken.js +5 -0
- package/src/lib/helpers/openUrl.js +7 -0
- package/src/lib/main.d.ts +0 -24
- package/src/lib/main.js +1 -1
- package/src/lib/services/decrypt.js +7 -2
- package/src/lib/services/encrypt.js +5 -2
- package/src/lib/services/get.js +8 -3
- package/src/lib/services/keypair.js +12 -3
- package/src/lib/services/login.js +26 -0
- package/src/lib/services/loginPoll.js +35 -0
- package/src/lib/services/logout.js +26 -0
- package/src/lib/services/rotate.js +10 -3
- package/src/lib/services/run.js +12 -3
- package/src/lib/services/sets.js +26 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
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.72.0...main)
|
|
6
|
+
|
|
7
|
+
## [1.72.0](https://github.com/dotenvx/dotenvx/compare/v1.71.3...v1.72.0) (2026-06-15)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Move `armor login` command to first class `dotenvx login` command. ([#836](https://github.com/dotenvx/dotenvx/pull/836))
|
|
12
|
+
* `dotenvx` remains local-first: login is optional and only needed when you want Armor features like moving keys off-device, team sharing, and audit access.
|
|
13
|
+
* Patch 2 high severity dependencies ([#837](https://github.com/dotenvx/dotenvx/pull/837))
|
|
14
|
+
|
|
15
|
+
## [1.71.3](https://github.com/dotenvx/dotenvx/compare/v1.71.2...v1.71.3) (2026-06-11)
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
* Forward command to Dotenvx Armor Key Guard approvals ([#835](https://github.com/dotenvx/dotenvx/pull/835))
|
|
6
20
|
|
|
7
21
|
## [1.71.2](https://github.com/dotenvx/dotenvx/compare/v1.71.1...v1.71.2) (2026-06-08)
|
|
8
22
|
|
package/README.md
CHANGED
|
@@ -2119,8 +2119,8 @@ Commands:
|
|
|
2119
2119
|
run inject env at runtime [dotenvx run -- yourcommand]
|
|
2120
2120
|
get [KEY] return a single environment variable
|
|
2121
2121
|
set <KEY> <value> set a single environment variable
|
|
2122
|
-
encrypt
|
|
2123
|
-
decrypt
|
|
2122
|
+
encrypt encrypt .env file(s)
|
|
2123
|
+
decrypt decrypt .env file(s)
|
|
2124
2124
|
keypair [KEY] print public/private keys for .env file(s)
|
|
2125
2125
|
ls [directory] print all .env files in a tree structure
|
|
2126
2126
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.72.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -45,21 +45,26 @@
|
|
|
45
45
|
"funding": "https://dotenvx.com",
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"commander": "^11.1.0",
|
|
48
|
+
"conf": "^10.2.0",
|
|
48
49
|
"dotenv": "^17.2.1",
|
|
49
50
|
"eciesjs": "^0.4.10",
|
|
50
51
|
"enquirer": "^2.4.1",
|
|
52
|
+
"env-paths": "^2.2.1",
|
|
51
53
|
"execa": "^5.1.1",
|
|
52
54
|
"fdir": "^6.2.0",
|
|
53
55
|
"ignore": "^5.3.0",
|
|
54
56
|
"object-treeify": "1.1.33",
|
|
57
|
+
"open": "^8.4.2",
|
|
55
58
|
"picomatch": "^4.0.4",
|
|
59
|
+
"systeminformation": "^5.22.11",
|
|
60
|
+
"undici": "^7.11.0",
|
|
56
61
|
"which": "^4.0.0",
|
|
57
62
|
"yocto-spinner": "^1.1.0"
|
|
58
63
|
},
|
|
59
64
|
"devDependencies": {
|
|
60
|
-
"@yao-pkg/pkg": "^6.
|
|
65
|
+
"@yao-pkg/pkg": "^6.12.0",
|
|
61
66
|
"capture-console": "^1.0.2",
|
|
62
|
-
"esbuild": "^0.
|
|
67
|
+
"esbuild": "^0.28.1",
|
|
63
68
|
"proxyquire": "^2.1.3",
|
|
64
69
|
"sinon": "^14.0.1",
|
|
65
70
|
"standard": "^17.1.0",
|
|
@@ -13,8 +13,8 @@ async function decrypt () {
|
|
|
13
13
|
|
|
14
14
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
15
15
|
|
|
16
|
-
const sesh = new Session()
|
|
17
16
|
const envs = this.envs
|
|
17
|
+
const sesh = new Session()
|
|
18
18
|
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
19
19
|
|
|
20
20
|
let errorCount = 0
|
|
@@ -24,7 +24,9 @@ async function decrypt () {
|
|
|
24
24
|
if (spinner) spinner.stop()
|
|
25
25
|
const {
|
|
26
26
|
processedEnvs
|
|
27
|
-
} = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor
|
|
27
|
+
} = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
28
|
+
command: process.argv.slice(2)
|
|
29
|
+
}).run()
|
|
28
30
|
if (spinner) spinner.stop()
|
|
29
31
|
for (const processedEnv of processedEnvs) {
|
|
30
32
|
if (processedEnv.error) {
|
|
@@ -47,7 +49,9 @@ async function decrypt () {
|
|
|
47
49
|
processedEnvs,
|
|
48
50
|
changedFilepaths,
|
|
49
51
|
unchangedFilepaths
|
|
50
|
-
} = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor
|
|
52
|
+
} = await new Decrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
53
|
+
command: process.argv.slice(2)
|
|
54
|
+
}).run()
|
|
51
55
|
|
|
52
56
|
for (const processedEnv of processedEnvs) {
|
|
53
57
|
logger.verbose(`decrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
|
@@ -46,8 +46,8 @@ async function encrypt () {
|
|
|
46
46
|
|
|
47
47
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
48
48
|
|
|
49
|
-
const sesh = new Session()
|
|
50
49
|
const envs = this.envs
|
|
50
|
+
const sesh = new Session()
|
|
51
51
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
52
52
|
const noCreate = options.create === false
|
|
53
53
|
|
|
@@ -56,7 +56,10 @@ async function encrypt () {
|
|
|
56
56
|
if (spinner) spinner.stop()
|
|
57
57
|
const {
|
|
58
58
|
processedEnvs
|
|
59
|
-
} = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, noCreate, options.token,
|
|
59
|
+
} = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, noCreate, options.token, {
|
|
60
|
+
...encryptOptions(noArmor),
|
|
61
|
+
command: process.argv.slice(2)
|
|
62
|
+
}).run()
|
|
60
63
|
if (spinner) spinner.stop()
|
|
61
64
|
for (const processedEnv of processedEnvs) {
|
|
62
65
|
console.log(processedEnv.envSrc)
|
|
@@ -69,7 +72,10 @@ async function encrypt () {
|
|
|
69
72
|
processedEnvs,
|
|
70
73
|
changedFilepaths,
|
|
71
74
|
unchangedFilepaths
|
|
72
|
-
} = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, noCreate, options.token,
|
|
75
|
+
} = await new Encrypt(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, noCreate, options.token, {
|
|
76
|
+
...encryptOptions(noArmor),
|
|
77
|
+
command: process.argv.slice(2)
|
|
78
|
+
}).run()
|
|
73
79
|
|
|
74
80
|
for (const processedEnv of processedEnvs) {
|
|
75
81
|
logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
package/src/cli/actions/get.js
CHANGED
|
@@ -32,7 +32,9 @@ async function get (key) {
|
|
|
32
32
|
const sesh = new Session()
|
|
33
33
|
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
34
34
|
if (spinner) spinner.stop()
|
|
35
|
-
const { parsed, errors } = await new Get(key, envs, options.overload, options.all, options.envKeysFile, noArmor
|
|
35
|
+
const { parsed, errors } = await new Get(key, envs, options.overload, options.all, options.envKeysFile, noArmor, {
|
|
36
|
+
command: process.argv.slice(2)
|
|
37
|
+
}).run()
|
|
36
38
|
|
|
37
39
|
for (const error of errors || []) {
|
|
38
40
|
if (options.strict) throw error // throw immediately if strict
|
|
@@ -19,7 +19,9 @@ async function keypair (key) {
|
|
|
19
19
|
const sesh = new Session()
|
|
20
20
|
const noArmor = options.armor === false || await sesh.noArmor()
|
|
21
21
|
if (spinner) spinner.stop()
|
|
22
|
-
const keypairs = await new Keypair(options.envFile, options.envKeysFile, noArmor
|
|
22
|
+
const keypairs = await new Keypair(options.envFile, options.envKeysFile, noArmor, {
|
|
23
|
+
command: process.argv.slice(2)
|
|
24
|
+
}).run()
|
|
23
25
|
const results = key ? keypairs[key] : keypairs
|
|
24
26
|
|
|
25
27
|
if (spinner) spinner.stop()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { logger } = require('../../shared/logger')
|
|
2
|
+
|
|
3
|
+
const Session = require('../../db/session')
|
|
4
|
+
const Login = require('../../lib/services/login')
|
|
5
|
+
const LoginPoll = require('../../lib/services/loginPoll')
|
|
6
|
+
|
|
7
|
+
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
|
+
const formatCode = require('../../lib/helpers/formatCode')
|
|
9
|
+
const listenForOpenKey = require('../../lib/helpers/listenForOpenKey')
|
|
10
|
+
const openUrl = require('../../lib/helpers/openUrl')
|
|
11
|
+
|
|
12
|
+
const FRAMES = ['◐', '◓', '◑', '◒']
|
|
13
|
+
|
|
14
|
+
async function login () {
|
|
15
|
+
const options = this.opts()
|
|
16
|
+
const spinner = await createSpinner({ ...options, text: 'logging in', frames: FRAMES })
|
|
17
|
+
|
|
18
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
19
|
+
|
|
20
|
+
const sesh = new Session()
|
|
21
|
+
await sesh.notifyUpdate()
|
|
22
|
+
const hostname = options.hostname || sesh.hostname()
|
|
23
|
+
let cleanupOpenKeyListener = () => {}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const {
|
|
27
|
+
deviceCode,
|
|
28
|
+
userCode,
|
|
29
|
+
verificationUri,
|
|
30
|
+
verificationUriComplete,
|
|
31
|
+
interval
|
|
32
|
+
} = await new Login(hostname).run()
|
|
33
|
+
|
|
34
|
+
const promptMessage = `◌ press Enter to open [${verificationUri}] and enter code [${formatCode(userCode)}]...`
|
|
35
|
+
|
|
36
|
+
logger.debug(`POST ${hostname} with deviceCode ${deviceCode} at interval ${interval}`)
|
|
37
|
+
logger.info(promptMessage)
|
|
38
|
+
|
|
39
|
+
// begin polling
|
|
40
|
+
const pollPromise = new LoginPoll(hostname, deviceCode, interval).run()
|
|
41
|
+
cleanupOpenKeyListener = listenForOpenKey(() => openUrl(verificationUriComplete))
|
|
42
|
+
const data = await pollPromise
|
|
43
|
+
|
|
44
|
+
cleanupOpenKeyListener()
|
|
45
|
+
if (spinner) spinner.stop()
|
|
46
|
+
logger.success(`◉ logged in (${data.username})`)
|
|
47
|
+
process.exit(0)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
cleanupOpenKeyListener()
|
|
50
|
+
if (spinner) spinner.stop()
|
|
51
|
+
if (error.message) {
|
|
52
|
+
logger.error(error.message)
|
|
53
|
+
} else {
|
|
54
|
+
logger.error(error)
|
|
55
|
+
}
|
|
56
|
+
if (error.stack) {
|
|
57
|
+
logger.debug(error.stack)
|
|
58
|
+
}
|
|
59
|
+
process.exit(1)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = login
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { logger } = require('../../shared/logger')
|
|
2
|
+
|
|
3
|
+
const Session = require('../../db/session')
|
|
4
|
+
const Logout = require('../../lib/services/logout')
|
|
5
|
+
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
6
|
+
|
|
7
|
+
const FRAMES = ['◐', '◓', '◑', '◒']
|
|
8
|
+
|
|
9
|
+
async function logout () {
|
|
10
|
+
const options = this.opts()
|
|
11
|
+
const spinner = await createSpinner({ ...options, text: 'logging out', frames: FRAMES })
|
|
12
|
+
|
|
13
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
14
|
+
|
|
15
|
+
const sesh = new Session()
|
|
16
|
+
const hostname = options.hostname || sesh.hostname()
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const data = await new Logout(hostname).run()
|
|
20
|
+
if (spinner) spinner.stop()
|
|
21
|
+
logger.success(`◌ logged out (${data.username})`)
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (spinner) spinner.stop()
|
|
24
|
+
if (error.message) {
|
|
25
|
+
logger.error(error.message)
|
|
26
|
+
} else {
|
|
27
|
+
logger.error(error)
|
|
28
|
+
}
|
|
29
|
+
if (error.stack) {
|
|
30
|
+
logger.debug(error.stack)
|
|
31
|
+
}
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = logout
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
function normalizeArmorOptions (options) {
|
|
2
|
-
if (options.armor === false || options.ops === false
|
|
2
|
+
if (options.armor === false || options.ops === false) {
|
|
3
3
|
options.armor = false
|
|
4
4
|
options.ops = false
|
|
5
|
-
options.vlt = false
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
return options
|
|
@@ -23,7 +23,9 @@ async function rotate () {
|
|
|
23
23
|
if (spinner) spinner.stop()
|
|
24
24
|
const {
|
|
25
25
|
processedEnvs
|
|
26
|
-
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor
|
|
26
|
+
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
27
|
+
command: process.argv.slice(2)
|
|
28
|
+
}).run()
|
|
27
29
|
if (spinner) spinner.stop()
|
|
28
30
|
for (const processedEnv of processedEnvs) {
|
|
29
31
|
console.log(processedEnv.envSrc)
|
|
@@ -40,7 +42,9 @@ async function rotate () {
|
|
|
40
42
|
processedEnvs,
|
|
41
43
|
changedFilepaths,
|
|
42
44
|
unchangedFilepaths
|
|
43
|
-
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor
|
|
45
|
+
} = await new Rotate(envs, options.key, options.excludeKey, options.envKeysFile, noArmor, {
|
|
46
|
+
command: process.argv.slice(2)
|
|
47
|
+
}).run()
|
|
44
48
|
|
|
45
49
|
for (const processedEnv of processedEnvs) {
|
|
46
50
|
logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
|
package/src/cli/actions/run.js
CHANGED
|
@@ -56,7 +56,8 @@ async function run () {
|
|
|
56
56
|
readableFilepaths,
|
|
57
57
|
uniqueInjectedKeys
|
|
58
58
|
} = await new Run(envs, options.overload, process.env, options.envKeysFile, noArmor, {
|
|
59
|
-
token: options.token
|
|
59
|
+
token: options.token,
|
|
60
|
+
command: commandArgs
|
|
60
61
|
}).run()
|
|
61
62
|
|
|
62
63
|
for (const processedEnv of processedEnvs) {
|
package/src/cli/actions/set.js
CHANGED
|
@@ -24,9 +24,9 @@ async function set (key, value) {
|
|
|
24
24
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
const sesh = new Session()
|
|
28
27
|
const envs = this.envs
|
|
29
28
|
const envKeysFilepath = options.envKeysFile
|
|
29
|
+
const sesh = new Session()
|
|
30
30
|
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
31
31
|
const noCreate = options.create === false
|
|
32
32
|
|
|
@@ -35,7 +35,9 @@ async function set (key, value) {
|
|
|
35
35
|
processedEnvs,
|
|
36
36
|
changedFilepaths,
|
|
37
37
|
unchangedFilepaths
|
|
38
|
-
} = await new Sets(key, value, envs, encrypt, envKeysFilepath, noArmor, noCreate
|
|
38
|
+
} = await new Sets(key, value, envs, encrypt, envKeysFilepath, noArmor, noCreate, {
|
|
39
|
+
command: process.argv.slice(2)
|
|
40
|
+
}).run()
|
|
39
41
|
|
|
40
42
|
let withEncryption = ''
|
|
41
43
|
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -72,7 +72,6 @@ program.command('run')
|
|
|
72
72
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
73
73
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
74
74
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
75
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
76
75
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
77
76
|
.action(function (...args) {
|
|
78
77
|
this.envs = envs
|
|
@@ -96,7 +95,6 @@ program.command('get')
|
|
|
96
95
|
.option('--pp', 'pretty print output (alias)')
|
|
97
96
|
.option('--format <type>', 'format of the output (json, shell, colon, eval)', 'json')
|
|
98
97
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
99
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
100
98
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
101
99
|
.action(function (...args) {
|
|
102
100
|
this.envs = envs
|
|
@@ -117,7 +115,6 @@ program.command('set')
|
|
|
117
115
|
.option('-p, --plain', 'store value as plain text', false)
|
|
118
116
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
119
117
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
120
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
121
118
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
122
119
|
.action(function (...args) {
|
|
123
120
|
this.envs = envs
|
|
@@ -126,7 +123,7 @@ program.command('set')
|
|
|
126
123
|
|
|
127
124
|
// dotenvx encrypt
|
|
128
125
|
program.command('encrypt')
|
|
129
|
-
.description('
|
|
126
|
+
.description('encrypt .env file(s)')
|
|
130
127
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
131
128
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
132
129
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
@@ -135,7 +132,6 @@ program.command('encrypt')
|
|
|
135
132
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
136
133
|
.option('--no-create', 'do not create .env file(s) when missing')
|
|
137
134
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
138
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
139
135
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
140
136
|
.action(function (...args) {
|
|
141
137
|
this.envs = envs
|
|
@@ -144,13 +140,12 @@ program.command('encrypt')
|
|
|
144
140
|
|
|
145
141
|
// dotenvx decrypt
|
|
146
142
|
program.command('decrypt')
|
|
147
|
-
.description('
|
|
143
|
+
.description('decrypt .env file(s)')
|
|
148
144
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
|
|
149
145
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
150
146
|
.option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
|
|
151
147
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
|
|
152
148
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
153
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
154
149
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
155
150
|
.option('--stdout', 'send to stdout')
|
|
156
151
|
.action(function (...args) {
|
|
@@ -166,7 +161,6 @@ program.command('rotate')
|
|
|
166
161
|
.option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
|
|
167
162
|
.option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
|
|
168
163
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
169
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
170
164
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
171
165
|
.option('--stdout', 'send to stdout')
|
|
172
166
|
.action(function (...args) {
|
|
@@ -182,7 +176,6 @@ program.command('keypair')
|
|
|
182
176
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
|
|
183
177
|
.option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
|
|
184
178
|
.option('--no-armor', 'disable dotenvx-armor features')
|
|
185
|
-
.addOption(new Option('--no-vlt', 'disable dotenvx-vlt features (deprecated. use --no-armor)').hideHelp())
|
|
186
179
|
.addOption(new Option('--no-ops', 'disable dotenvx-ops features (deprecated. use --no-armor)').hideHelp())
|
|
187
180
|
.option('-pp, --pretty-print', 'pretty print output')
|
|
188
181
|
.option('--pp', 'pretty print output (alias)')
|
|
@@ -211,20 +204,20 @@ program.command('doctor', { hidden: true })
|
|
|
211
204
|
|
|
212
205
|
// dotenvx login
|
|
213
206
|
program.command('login', { hidden: true })
|
|
214
|
-
.description('')
|
|
207
|
+
.description('log in to move keys off-device, share with your team, and audit access')
|
|
215
208
|
.allowUnknownOption()
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
|
|
209
|
+
.option('--hostname <hostname>', 'set Armor ⛨ hostname')
|
|
210
|
+
.action(function (...args) {
|
|
211
|
+
return require('./actions/login').apply(this, args)
|
|
219
212
|
})
|
|
220
213
|
|
|
221
214
|
// dotenvx logout
|
|
222
215
|
program.command('logout', { hidden: true })
|
|
223
|
-
.description('log out of
|
|
216
|
+
.description('log out of connected security features')
|
|
224
217
|
.allowUnknownOption()
|
|
225
|
-
.
|
|
226
|
-
|
|
227
|
-
|
|
218
|
+
.option('--hostname <hostname>', 'set Armor ⛨ hostname')
|
|
219
|
+
.action(function (...args) {
|
|
220
|
+
return require('./actions/logout').apply(this, args)
|
|
228
221
|
})
|
|
229
222
|
|
|
230
223
|
// dotenvx help
|
|
@@ -245,8 +238,10 @@ program.command('help [command]')
|
|
|
245
238
|
|
|
246
239
|
// dotenvx armor
|
|
247
240
|
program.addHelpText('after', ' ')
|
|
248
|
-
program.addHelpText('after', '
|
|
249
|
-
program.addHelpText('after', '
|
|
241
|
+
program.addHelpText('after', 'Professional Security: ')
|
|
242
|
+
program.addHelpText('after', ' login log in to move keys off-device, share with your team, and audit access')
|
|
243
|
+
program.addHelpText('after', ' logout log out of connected security features')
|
|
244
|
+
program.addHelpText('after', ' armor ⛨ move private keys off-device [www.dotenvx.com/armor]')
|
|
250
245
|
program.addHelpText('after', ' ext ⊕ extensions')
|
|
251
246
|
|
|
252
247
|
// dotenvx ext
|
package/src/db/device.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const Conf = require('conf')
|
|
2
|
+
const { PrivateKey } = require('eciesjs')
|
|
3
|
+
|
|
4
|
+
const encryptDeviceValue = require('./../lib/helpers/encryptDeviceValue')
|
|
5
|
+
const decryptDeviceValue = require('./../lib/helpers/decryptDeviceValue')
|
|
6
|
+
|
|
7
|
+
class Device {
|
|
8
|
+
constructor () {
|
|
9
|
+
this.store = new Conf({
|
|
10
|
+
cwd: process.env.DOTENVX_CONFIG || undefined,
|
|
11
|
+
projectName: 'dotenvx',
|
|
12
|
+
configName: '.device-key',
|
|
13
|
+
projectSuffix: '',
|
|
14
|
+
fileExtension: '',
|
|
15
|
+
encryptionKey: 'dotenvxpro dotenvxpro dotenvxpro' // backwards compatible
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
touch () {
|
|
20
|
+
const _privateKey = this.privateKey()
|
|
21
|
+
const _publicKey = this.publicKey()
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
privateKey: _privateKey,
|
|
25
|
+
publicKey: _publicKey
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
configPath () {
|
|
30
|
+
return this.store.path
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
privateKey () {
|
|
34
|
+
const currentPrivateKey = this.store.get('private_key/1')
|
|
35
|
+
if (currentPrivateKey && currentPrivateKey.length > 0) {
|
|
36
|
+
this.store.set('private_key/1', currentPrivateKey)
|
|
37
|
+
|
|
38
|
+
return currentPrivateKey
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// generate privateKey for the first time
|
|
42
|
+
const kp = new PrivateKey()
|
|
43
|
+
const _privateKey = kp.secret.toString('hex')
|
|
44
|
+
|
|
45
|
+
this.store.set('private_key/1', _privateKey)
|
|
46
|
+
|
|
47
|
+
return _privateKey
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
publicKey () {
|
|
51
|
+
// must have private key to try and get public key
|
|
52
|
+
const privateKeyHex = this.privateKey()
|
|
53
|
+
if (!privateKeyHex || privateKeyHex.length < 1) {
|
|
54
|
+
return ''
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// create keyPair object from hex string
|
|
58
|
+
const _privateKey = new PrivateKey(Buffer.from(privateKeyHex, 'hex'))
|
|
59
|
+
|
|
60
|
+
// compute publicKey from privateKey
|
|
61
|
+
return _privateKey.publicKey.toHex()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
encrypt (value) {
|
|
65
|
+
return encryptDeviceValue(value, this.publicKey())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
decrypt (value) {
|
|
69
|
+
return decryptDeviceValue(value, this.privateKey())
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = Device
|