@dotenvx/dotenvx 2.2.1 → 2.3.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,27 @@
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/v2.2.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.3.0...main)
6
+
7
+ ## [2.3.0](https://github.com/dotenvx/dotenvx/compare/v2.2.2...v2.3.0) (2026-07-08)
8
+
9
+ ### Added
10
+
11
+ * Add `lock up` and `lock down` commands for password protecting private keys ([#875](https://github.com/dotenvx/dotenvx/pull/875))
12
+
13
+ ### Changed
14
+
15
+ * BREAKING: Rename `keychain` commands to `native` to make way for windows and linux. ([#875](https://github.com/dotenvx/dotenvx/pull/875))
16
+
17
+ ### Removed
18
+
19
+ * BREAKING: Remove `--no-ops`. Use `--no-armor`. ([#875](https://github.com/dotenvx/dotenvx/pull/875))
20
+
21
+ ## [2.2.2](https://github.com/dotenvx/dotenvx/compare/v2.2.1...v2.2.2) (2026-07-08)
22
+
23
+ ### Changed
24
+
25
+ * Move `login` and `logout` under armor ([#874](https://github.com/dotenvx/dotenvx/pull/874))
6
26
 
7
27
  ## [2.2.1](https://github.com/dotenvx/dotenvx/compare/v2.2.0...v2.2.1) (2026-07-08)
8
28
 
package/README.md CHANGED
@@ -1385,12 +1385,12 @@ $ dotenvx run --token "$DOTENVX_ARMOR_TOKEN" -- yourcommand
1385
1385
  ```
1386
1386
 
1387
1387
  </details>
1388
- <details><summary>`run --no-keychain`</summary><br>
1388
+ <details><summary>`run --no-native`</summary><br>
1389
1389
 
1390
- Turn off macOS Keychain lookups.
1390
+ Turn off OS secret store lookups.
1391
1391
 
1392
1392
  ```sh
1393
- $ dotenvx run --no-keychain -- yourcommand
1393
+ $ dotenvx run --no-native -- yourcommand
1394
1394
  ```
1395
1395
 
1396
1396
  </details>
@@ -1415,12 +1415,12 @@ World
1415
1415
  ```
1416
1416
 
1417
1417
  </details>
1418
- <details><summary>`get KEY --no-keychain`</summary><br>
1418
+ <details><summary>`get KEY --no-native`</summary><br>
1419
1419
 
1420
- Turn off macOS Keychain lookups for get.
1420
+ Turn off OS secret store lookups for get.
1421
1421
 
1422
1422
  ```sh
1423
- $ dotenvx get HELLO --no-keychain
1423
+ $ dotenvx get HELLO --no-native
1424
1424
  ```
1425
1425
 
1426
1426
  </details>
@@ -1739,12 +1739,12 @@ set HELLO_PLAIN (.env)
1739
1739
  Keys ending in `_PLAIN` are not encrypted by `dotenvx set` or `dotenvx encrypt`.
1740
1740
 
1741
1741
  </details>
1742
- <details><summary>`set KEY value --no-keychain`</summary><br>
1742
+ <details><summary>`set KEY value --no-native`</summary><br>
1743
1743
 
1744
- Turn off macOS Keychain lookups for set.
1744
+ Turn off OS secret store lookups for set.
1745
1745
 
1746
1746
  ```sh
1747
- $ dotenvx set HELLO Dotenvx --no-keychain
1747
+ $ dotenvx set HELLO Dotenvx --no-native
1748
1748
  ```
1749
1749
 
1750
1750
  </details>
@@ -1779,12 +1779,12 @@ $ dotenvx encrypt -f .env.production
1779
1779
  ```
1780
1780
 
1781
1781
  </details>
1782
- <details><summary>`encrypt --no-keychain`</summary><br>
1782
+ <details><summary>`encrypt --no-native`</summary><br>
1783
1783
 
1784
- Turn off macOS Keychain lookups for encrypt.
1784
+ Turn off OS secret store lookups for encrypt.
1785
1785
 
1786
1786
  ```sh
1787
- $ dotenvx encrypt --no-keychain
1787
+ $ dotenvx encrypt --no-native
1788
1788
  ◈ encrypted (.env)
1789
1789
  ```
1790
1790
 
@@ -1918,12 +1918,12 @@ $ dotenvx decrypt
1918
1918
  ```
1919
1919
 
1920
1920
  </details>
1921
- <details><summary>`decrypt --no-keychain`</summary><br>
1921
+ <details><summary>`decrypt --no-native`</summary><br>
1922
1922
 
1923
- Turn off macOS Keychain lookups for decrypt.
1923
+ Turn off OS secret store lookups for decrypt.
1924
1924
 
1925
1925
  ```sh
1926
- $ dotenvx decrypt --no-keychain
1926
+ $ dotenvx decrypt --no-native
1927
1927
  ◇ decrypted (.env)
1928
1928
  ```
1929
1929
 
@@ -2039,12 +2039,12 @@ $ dotenvx keypair
2039
2039
  ```
2040
2040
 
2041
2041
  </details>
2042
- <details><summary>`keypair --no-keychain`</summary><br>
2042
+ <details><summary>`keypair --no-native`</summary><br>
2043
2043
 
2044
- Turn off macOS Keychain lookups for keypair.
2044
+ Turn off OS secret store lookups for keypair.
2045
2045
 
2046
2046
  ```sh
2047
- $ dotenvx keypair --no-keychain
2047
+ $ dotenvx keypair --no-native
2048
2048
  {"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}
2049
2049
  ```
2050
2050
 
@@ -2323,52 +2323,93 @@ CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "apps/backend/index.js"]
2323
2323
  ```
2324
2324
 
2325
2325
  </details>
2326
- <details><summary>`keychain`</summary><br>
2326
+ <details><summary>`lock`</summary><br>
2327
2327
 
2328
- Store private keys in macOS Keychain to keep them out of `.env.keys`.
2328
+ Lock private keys with a local passphrase to keep them protected inside `.env.keys`.
2329
+
2330
+ ```
2331
+ # example
2332
+ DOTENV_PRIVATE_KEY=locked:02f5b97ad58b49ae324cd4e7937bc19b251d006b31cacf46f789eeaf03f923cedc:AZIPDxKqjPLiGl5b4CqVGbR3CIBDUcqHthGaoeWLoUvxbTHJkj3jGoGWGaxFSDUJGQUmWDaExRzxKpVydYF_7qiWr1ecqksOFho5t3EMwKbqX2-y-LZO9K3a4SJaYAjDJXpn3NwG4vAt1oLmGA
2333
+ ```
2334
+
2335
+ </details>
2336
+ <details><summary>`lock up`</summary><br>
2337
+
2338
+ Lock a private key in `.env.keys` with a local passphrase.
2339
+
2340
+ ```sh
2341
+ $ dotenvx lock up
2342
+ ```
2343
+
2344
+ Here's what a locked key looks like:
2345
+
2346
+ ```ini
2347
+ # .env.keys
2348
+ DOTENV_PRIVATE_KEY=locked:02f5b97ad58b49ae324cd4e7937bc19b251d006b31cacf46f789eeaf03f923cedc:AZIPDxKqjPLiGl5b4CqVGbR3CIBDUcqHthGaoeWLoUvxbTHJkj3jGoGWGaxFSDUJGQUmWDaExRzxKpVydYF_7qiWr1ecqksOFho5t3EMwKbqX2-y-LZO9K3a4SJaYAjDJXpn3NwG4vAt1oLmGA
2349
+ ```
2350
+
2351
+ Specify files with `-f` and `-fk`.
2352
+
2353
+ ```sh
2354
+ $ dotenvx lock up -f .env.production -fk .env.keys
2355
+ ```
2356
+
2357
+ </details>
2358
+ <details><summary>`lock down`</summary><br>
2359
+
2360
+ Unlock a private key in `.env.keys` with its local passphrase.
2361
+
2362
+ ```sh
2363
+ $ dotenvx lock down
2364
+ ```
2365
+
2366
+ </details>
2367
+ <details><summary>`native`</summary><br>
2368
+
2369
+ Move private keys into your OS secret store (macOS Keychain supported).
2329
2370
 
2330
2371
  Currently supported on macOS.
2331
2372
 
2332
2373
  </details>
2333
- <details><summary>`keychain up`</summary><br>
2374
+ <details><summary>`native up`</summary><br>
2334
2375
 
2335
- Move a private key from `.env.keys` into macOS Keychain.
2376
+ Move a private key from `.env.keys` into your OS secret store.
2336
2377
 
2337
2378
  ```sh
2338
- $ dotenvx keychain up
2379
+ $ dotenvx native up
2339
2380
  ```
2340
2381
 
2341
2382
  Specify files with `-f` and `-fk`.
2342
2383
 
2343
2384
  ```sh
2344
- $ dotenvx keychain up -f .env.production -fk .env.keys
2385
+ $ dotenvx native up -f .env.production -fk .env.keys
2345
2386
  ```
2346
2387
 
2347
2388
  </details>
2348
- <details><summary>`keychain down`</summary><br>
2389
+ <details><summary>`native down`</summary><br>
2349
2390
 
2350
- Move a private key from macOS Keychain back into `.env.keys`.
2391
+ Move a private key from your OS secret store back into `.env.keys`.
2351
2392
 
2352
2393
  ```sh
2353
- $ dotenvx keychain down
2394
+ $ dotenvx native down
2354
2395
  ```
2355
2396
 
2356
2397
  </details>
2357
- <details><summary>`keychain push`</summary><br>
2398
+ <details><summary>`native push`</summary><br>
2358
2399
 
2359
- Copy a private key from `.env.keys` into macOS Keychain.
2400
+ Copy a private key from `.env.keys` into your OS secret store.
2360
2401
 
2361
2402
  ```sh
2362
- $ dotenvx keychain push
2403
+ $ dotenvx native push
2363
2404
  ```
2364
2405
 
2365
2406
  </details>
2366
- <details><summary>`keychain pull`</summary><br>
2407
+ <details><summary>`native pull`</summary><br>
2367
2408
 
2368
- Copy a private key from macOS Keychain into `.env.keys`.
2409
+ Copy a private key from your OS secret store into `.env.keys`.
2369
2410
 
2370
2411
  ```sh
2371
- $ dotenvx keychain pull
2412
+ $ dotenvx native pull
2372
2413
  ```
2373
2414
 
2374
2415
  </details>
@@ -2382,7 +2423,7 @@ Move private keys into [Dotenvx Armor ⛨](https://dotenvx.com/armor) for off-de
2382
2423
  Move a private key from `.env.keys` into Dotenvx Armor.
2383
2424
 
2384
2425
  ```sh
2385
- $ dotenvx login
2426
+ $ dotenvx armor login
2386
2427
  $ dotenvx armor up
2387
2428
  ```
2388
2429
 
@@ -2434,6 +2475,24 @@ Move an armored key to another team.
2434
2475
  $ dotenvx armor move --team acme
2435
2476
  ```
2436
2477
 
2478
+ </details>
2479
+ <details><summary>`armor login`</summary><br>
2480
+
2481
+ Log in to Dotenvx Armor.
2482
+
2483
+ ```sh
2484
+ $ dotenvx armor login
2485
+ ```
2486
+
2487
+ </details>
2488
+ <details><summary>`armor logout`</summary><br>
2489
+
2490
+ Log out of Dotenvx Armor.
2491
+
2492
+ ```sh
2493
+ $ dotenvx armor logout
2494
+ ```
2495
+
2437
2496
  </details>
2438
2497
  <details><summary>`help`</summary><br>
2439
2498
 
@@ -2470,9 +2529,9 @@ Commands:
2470
2529
  prevent including .env files in docker
2471
2530
 
2472
2531
  Professional Security:
2473
- login log in to move keys off-device, share with your team, and audit access
2474
- logout log out of connected security features
2475
- armor ⛨ move private keys off-device [www.dotenvx.com/armor]
2532
+ lock ⊡ lock private keys with a local passphrase
2533
+ native move private keys into your OS secret store (macOS Keychain supported)
2534
+ armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]
2476
2535
  ```
2477
2536
 
2478
2537
  You can get more detailed help per command with `dotenvx help COMMAND`.
@@ -2520,24 +2579,6 @@ X.X.X
2520
2579
 
2521
2580
  </details>
2522
2581
 
2523
- ### Utilities
2524
-
2525
- Additional workflow helpers.
2526
-
2527
- <details><summary>`ext scan`</summary><br>
2528
-
2529
- Scan for leaked secrets.
2530
-
2531
- ```sh
2532
- $ dotenvx ext scan
2533
- 100 commits scanned.
2534
- no leaks found
2535
- ```
2536
-
2537
- Uses [gitleaks](https://gitleaks.io) under the hood.
2538
-
2539
- </details>
2540
-
2541
2582
  ### Library 📦
2542
2583
 
2543
2584
  Use dotenvx directly in code.
@@ -2888,18 +2929,6 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
2888
2929
 
2889
2930
  &nbsp;
2890
2931
 
2891
- ## Whitepaper
2892
-
2893
- > **Dotenvx: Reducing Secrets Risk with Cryptographic Separation**
2894
- >
2895
- > Abstract. An ideal secrets solution would not only centralize secrets but also contain the fallout of a breach. While secrets managers offer centralized storage and distribution, their design creates a large blast radius, risking exposure of thousands or even millions of secrets. We propose a solution that reduces the blast radius by splitting secrets management into two distinct components: an encrypted secrets file and a separate decryption key.
2896
- >
2897
- > ...
2898
- >
2899
- > [Read the whitepaper](https://dotenvx.com/dotenvx.pdf)
2900
-
2901
- &nbsp;
2902
-
2903
2932
  ## Guides
2904
2933
 
2905
2934
  > Go deeper with `dotenvx` – detailed framework and platform guides.
@@ -2931,6 +2960,9 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
2931
2960
  * [Render](https://dotenvx.com/docs/platforms/render)
2932
2961
  * [CI/CDs](https://dotenvx.com/docs#cis)
2933
2962
  * [GitHub Actions](https://dotenvx.com/docs/cis/github-actions)
2963
+ * [Password Managers](https://dotenvx.com/docs#password-managers)
2964
+ * [1Password](https://dotenvx.com/docs/guides/1password)
2965
+ * [Bitwarden](https://dotenvx.com/docs/guides/bitwarden)
2934
2966
  * [Background Jobs](https://dotenvx.com/docs#background-jobs)
2935
2967
  * [Trigger.dev](https://dotenvx.com/docs/background-jobs/triggerdotdev)
2936
2968
  * [Package Managers](https://dotenvx.com/docs#package-managers)
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.2.1",
2
+ "version": "2.3.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -36,7 +36,7 @@
36
36
  "standard": "standard",
37
37
  "standard:fix": "standard --fix",
38
38
  "test": "tap run --test-env=DOTENVX_NO_ARMOR=true --allow-empty-coverage --disable-coverage --timeout=60000",
39
- "testshell": "bash shellspec",
39
+ "testshell": "shellspec --shell /bin/bash",
40
40
  "release:check": "npm test && npm run testshell",
41
41
  "prerelease": "npm run release:check",
42
42
  "release": "npm run release:check && standard-version"
@@ -4,12 +4,11 @@ const { logger } = require('./../../shared/logger')
4
4
  const catchAndLog = require('../../lib/helpers/catchAndLog')
5
5
  const createSpinner = require('../../lib/helpers/createSpinner')
6
6
  const Session = require('../../db/session')
7
- const normalizeArmorAliases = require('./normalizeArmorAliases')
8
7
 
9
8
  const decryptTransform = require('./../../lib/transforms/decrypt')
10
9
 
11
10
  async function decrypt () {
12
- const options = normalizeArmorAliases(this.opts())
11
+ const options = this.opts()
13
12
  const spinner = await createSpinner({ ...options, text: 'decrypting' })
14
13
 
15
14
  logger.debug(`options: ${JSON.stringify(options)}`)
@@ -17,7 +16,7 @@ async function decrypt () {
17
16
  const envs = this.envs
18
17
  const sesh = new Session()
19
18
  const noArmor = options.armor === false || (await sesh.noArmor())
20
- const noKeychain = options.keychain === false || options.noKeychain === true
19
+ const noKeychain = options.native === false || options.noNative === true
21
20
 
22
21
  let errorCount = 0
23
22
 
@@ -6,10 +6,9 @@ const encryptTransform = require('./../../lib/transforms/encrypt')
6
6
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
7
  const createSpinner = require('../../lib/helpers/createSpinner')
8
8
  const Session = require('../../db/session')
9
- const normalizeArmorAliases = require('./normalizeArmorAliases')
10
9
 
11
10
  async function encryptAction () {
12
- const options = normalizeArmorAliases(this.opts())
11
+ const options = this.opts()
13
12
  const spinner = await createSpinner({ ...options, text: 'encrypting' })
14
13
  const sesh = new Session()
15
14
 
@@ -21,7 +20,7 @@ async function encryptAction () {
21
20
  const fk = options.envKeysFile || '.env.keys'
22
21
  const noCreate = options.create === false
23
22
  const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
24
- const noKeychain = options.keychain === false || options.noKeychain === true
23
+ const noKeychain = options.native === false || options.noNative === true
25
24
 
26
25
  let errorCount = 0
27
26
 
@@ -6,11 +6,10 @@ const catchAndLog = require('./../../lib/helpers/catchAndLog')
6
6
  const createSpinner = require('../../lib/helpers/createSpinner')
7
7
  const Session = require('../../db/session')
8
8
  const getResolver = require('./../../lib/resolvers/get')
9
- const normalizeArmorAliases = require('./normalizeArmorAliases')
10
9
  const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
11
10
 
12
11
  async function get (key) {
13
- const options = normalizeDotenvConfigConvention(normalizeArmorAliases(this.opts()))
12
+ const options = normalizeDotenvConfigConvention(this.opts())
14
13
  const spinner = await createSpinner({ ...options, text: 'decrypting' })
15
14
 
16
15
  logger.debug(`options: ${JSON.stringify(options)}`)
@@ -33,7 +32,7 @@ async function get (key) {
33
32
  try {
34
33
  const sesh = new Session()
35
34
  const noArmor = options.armor === false || (await sesh.noArmor())
36
- const noKeychain = options.keychain === false || options.noKeychain === true
35
+ const noKeychain = options.native === false || options.noNative === true
37
36
  const { parsed, errors } = await getResolver({
38
37
  key,
39
38
  envs,
@@ -5,7 +5,7 @@ const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
5
5
 
6
6
  async function up () {
7
7
  const options = this.opts()
8
- const spinner = await createSpinner({ ...options, text: 'keychaining' })
8
+ const spinner = await createSpinner({ ...options, text: 'storing' })
9
9
 
10
10
  logger.debug(`options: ${JSON.stringify(options)}`)
11
11
 
@@ -15,7 +15,7 @@ async function up () {
15
15
 
16
16
  if (spinner) spinner.stop()
17
17
  if (changed) {
18
- logger.success(`⌥ keychained (${keyDisplay})`)
18
+ logger.success(`⌥ stored (${keyDisplay})`)
19
19
  } else {
20
20
  logger.info(`○ no change (${keyDisplay})`)
21
21
  }
@@ -3,10 +3,9 @@ const { logger } = require('./../../shared/logger')
3
3
  const keypairResolver = require('./../../lib/resolvers/keypair')
4
4
  const catchAndLog = require('./../../lib/helpers/catchAndLog')
5
5
  const createSpinner = require('../../lib/helpers/createSpinner')
6
- const normalizeArmorAliases = require('./normalizeArmorAliases')
7
6
 
8
7
  async function keypair (key) {
9
- const options = normalizeArmorAliases(this.opts())
8
+ const options = this.opts()
10
9
  const spinner = await createSpinner({ ...options, text: 'retrieving' })
11
10
 
12
11
  logger.debug(`options: ${JSON.stringify(options)}`)
@@ -21,7 +20,7 @@ async function keypair (key) {
21
20
  envFile: options.envFile,
22
21
  envKeysFile: options.envKeysFile,
23
22
  armor: options.armor,
24
- noKeychain: options.keychain === false || options.noKeychain === true,
23
+ noKeychain: options.native === false || options.noNative === true,
25
24
  token: options.token,
26
25
  command: process.argv.slice(2),
27
26
  onStatus: (text) => {
@@ -0,0 +1,74 @@
1
+ const crypto = require('crypto')
2
+
3
+ const { logger } = require('../../../shared/logger')
4
+ const LockDown = require('./../../../lib/services/lockDown')
5
+ const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
6
+ const prompts = require('../../../lib/helpers/prompts')
7
+
8
+ function unlockedValue (lockedPrivateKey, passphrase) {
9
+ const parts = lockedPrivateKey.split(':')
10
+ const payload = Buffer.from(parts.slice(2).join(':'), 'base64url')
11
+ const version = payload.subarray(0, 1)[0]
12
+ const salt = payload.subarray(1, 17)
13
+ const iv = payload.subarray(17, 29)
14
+ const tag = payload.subarray(29, 45)
15
+ const ciphertext = payload.subarray(45)
16
+ const key = crypto.scryptSync(passphrase, salt, 32)
17
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
18
+
19
+ if (version !== 1) {
20
+ throw new Error('unsupported locked private key version')
21
+ }
22
+
23
+ decipher.setAuthTag(tag)
24
+
25
+ return Buffer.concat([
26
+ decipher.update(ciphertext),
27
+ decipher.final()
28
+ ]).toString('utf8')
29
+ }
30
+
31
+ async function down () {
32
+ const options = this.opts()
33
+ logger.debug(`options: ${JSON.stringify(options)}`)
34
+
35
+ try {
36
+ const lockDown = new LockDown(options.envFile, options.envKeysFile)
37
+ const plan = lockDown.plan()
38
+ let results = plan.alreadyUnlocked
39
+
40
+ if (plan.locked.length > 0) {
41
+ const passphrase = await prompts.password({
42
+ message: 'passphrase',
43
+ prefix: '⊡',
44
+ separator: '='
45
+ }, {
46
+ input: process.stdin,
47
+ output: process.stderr
48
+ })
49
+
50
+ results = lockDown.run(lockedPrivateKey => unlockedValue(lockedPrivateKey, passphrase)).results
51
+ }
52
+
53
+ for (const result of results) {
54
+ const keyDisplay = armoredKeyDisplay(result.publicKeyValue) || result.privateKeyName
55
+
56
+ if (result.changed) {
57
+ logger.success(`⊡ unlocked (${keyDisplay})`)
58
+ } else {
59
+ logger.info(`○ no change (${keyDisplay})`)
60
+ }
61
+ }
62
+ } catch (error) {
63
+ if (error.code === 'PROMPT_CANCELLED') {
64
+ process.exit(130)
65
+ return
66
+ }
67
+
68
+ logger.error(error.message)
69
+ process.exit(1)
70
+ }
71
+ }
72
+
73
+ module.exports = down
74
+ module.exports.unlockedValue = unlockedValue
@@ -0,0 +1,80 @@
1
+ const crypto = require('crypto')
2
+
3
+ const { logger } = require('../../../shared/logger')
4
+ const LockUp = require('./../../../lib/services/lockUp')
5
+ const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
6
+ const prompts = require('../../../lib/helpers/prompts')
7
+
8
+ function lockedValue (privateKey, passphrase, publicKey) {
9
+ const salt = crypto.randomBytes(16)
10
+ const iv = crypto.randomBytes(12)
11
+ const key = crypto.scryptSync(passphrase, salt, 32)
12
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
13
+ const ciphertext = Buffer.concat([
14
+ cipher.update(privateKey, 'utf8'),
15
+ cipher.final()
16
+ ])
17
+ const tag = cipher.getAuthTag()
18
+ const payload = Buffer.concat([
19
+ Buffer.from([1]),
20
+ salt,
21
+ iv,
22
+ tag,
23
+ ciphertext
24
+ ]).toString('base64url')
25
+
26
+ return `locked:${publicKey}:${payload}`
27
+ }
28
+
29
+ async function up () {
30
+ const options = this.opts()
31
+ logger.debug(`options: ${JSON.stringify(options)}`)
32
+
33
+ try {
34
+ const lockUp = new LockUp(options.envFile, options.envKeysFile)
35
+
36
+ const plan = lockUp.plan()
37
+ let results = plan.alreadyLocked.map(lockedPrivateKey => ({
38
+ changed: false,
39
+ privateKeyName: lockedPrivateKey.privateKeyName,
40
+ privateKeyValue: undefined,
41
+ lockedPrivateKeyValue: lockedPrivateKey.lockedPrivateKeyValue,
42
+ publicKeyValue: lockedPrivateKey.publicKeyValue,
43
+ alreadyLocked: true
44
+ }))
45
+
46
+ if (plan.matches.length > 0) {
47
+ const passphrase = await prompts.password({
48
+ message: 'passphrase',
49
+ prefix: '⊡',
50
+ separator: '='
51
+ }, {
52
+ input: process.stdin,
53
+ output: process.stderr
54
+ })
55
+
56
+ results = lockUp.run((privateKey, publicKey) => lockedValue(privateKey, passphrase, publicKey)).results
57
+ }
58
+
59
+ for (const result of results) {
60
+ const keyDisplay = armoredKeyDisplay(result.publicKeyValue) || result.privateKeyName
61
+
62
+ if (result.changed) {
63
+ logger.success(`⊡ locked (${keyDisplay})`)
64
+ } else {
65
+ logger.info(`○ no change (${keyDisplay})`)
66
+ }
67
+ }
68
+ } catch (error) {
69
+ if (error.code === 'PROMPT_CANCELLED') {
70
+ process.exit(130)
71
+ return
72
+ }
73
+
74
+ logger.error(error.message)
75
+ process.exit(1)
76
+ }
77
+ }
78
+
79
+ module.exports = up
80
+ module.exports.lockedValue = lockedValue
@@ -6,7 +6,6 @@ const envsResolver = require('./../../lib/resolvers/envs')
6
6
  const catchAndLog = require('./../../lib/helpers/catchAndLog')
7
7
  const createSpinner = require('../../lib/helpers/createSpinner')
8
8
  const Session = require('../../db/session')
9
- const normalizeArmorAliases = require('./normalizeArmorAliases')
10
9
  const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
11
10
  const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
12
11
 
@@ -47,7 +46,7 @@ function uniqueInjectedKeys (processedEnvs) {
47
46
  }
48
47
 
49
48
  async function run () {
50
- const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(normalizeArmorAliases(this.opts())))
49
+ const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
51
50
 
52
51
  let commandArgs = this.args
53
52
  if (commandArgs.length < 1) {
@@ -63,7 +62,7 @@ async function run () {
63
62
 
64
63
  const sesh = new Session()
65
64
  const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
66
- const noKeychain = options.keychain === false || options.noKeychain === true
65
+ const noKeychain = options.native === false || options.noNative === true
67
66
 
68
67
  if (commandArgs.length < 1) {
69
68
  if (spinner) spinner.stop()
@@ -8,10 +8,9 @@ const createSpinner = require('../../lib/helpers/createSpinner')
8
8
  const Errors = require('../../lib/helpers/errors')
9
9
  const prompts = require('../../lib/helpers/prompts')
10
10
  const Session = require('../../db/session')
11
- const normalizeArmorAliases = require('./normalizeArmorAliases')
12
11
 
13
12
  async function set (key, value) {
14
- const options = normalizeArmorAliases(this.opts())
13
+ const options = this.opts()
15
14
 
16
15
  let encrypt = true
17
16
  let settingMessage = 'encrypting'
@@ -65,7 +64,7 @@ async function set (key, value) {
65
64
  const fk = options.envKeysFile || '.env.keys'
66
65
  const noCreate = options.create === false
67
66
  const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
68
- const noKeychain = options.keychain === false || options.noKeychain === true
67
+ const noKeychain = options.native === false || options.noNative === true
69
68
 
70
69
  let errorCount = 0
71
70