yamlock 0.3.0 → 1.0.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/README.md +223 -38
- package/bin/yamlock +8 -0
- package/dist/cli/cli.js +457 -74
- package/dist/crypto/decrypt.js +112 -13
- package/dist/crypto/encrypt.js +107 -18
- package/dist/crypto/payload-v2.js +371 -0
- package/dist/crypto/utils.js +65 -14
- package/dist/errors.js +59 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +10 -0
- package/dist/utils/config.js +294 -54
- package/dist/utils/file.js +58 -0
- package/dist/utils/migrate.js +170 -0
- package/dist/utils/path.js +58 -9
- package/docs/api.md +56 -0
- package/docs/design/payload-v2.md +344 -0
- package/docs/errors.md +71 -0
- package/docs/yaml-behavior.md +51 -0
- package/examples/basic.js +31 -0
- package/examples/docs/ci-cd.md +58 -0
- package/examples/docs/key-rotation.md +65 -0
- package/package.json +19 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/yamlock)
|
|
8
|
-
[](https://github.com/phoenixweiss/yamlock/actions/workflows/ci.yml)
|
|
9
9
|
|
|
10
10
|
# yamlock
|
|
11
11
|
|
|
@@ -13,8 +13,10 @@ Value-level encryption for YAML and JSON configuration files. The name **yamlock
|
|
|
13
13
|
|
|
14
14
|
## Requirements
|
|
15
15
|
|
|
16
|
-
- Node.js 22
|
|
17
|
-
|
|
16
|
+
- Node.js 22 or newer
|
|
17
|
+
|
|
18
|
+
The package works with npm or another Node.js package manager. This repository
|
|
19
|
+
uses Yarn Classic 1.22.22 for development and release checks.
|
|
18
20
|
|
|
19
21
|
## Installation
|
|
20
22
|
|
|
@@ -34,8 +36,9 @@ yarn add yamlock # project dependency
|
|
|
34
36
|
|
|
35
37
|
## Features
|
|
36
38
|
|
|
37
|
-
- Encrypt/decrypt individual
|
|
39
|
+
- Encrypt/decrypt individual values with authenticated field-path metadata.
|
|
38
40
|
- CLI workflow that processes YAML or JSON files in place.
|
|
41
|
+
- Safe CLI migration from legacy payloads to authenticated v2 payloads.
|
|
39
42
|
- Recursively lock/unlock entire objects via `processConfig`.
|
|
40
43
|
- Public API exports that mirror CLI behavior for programmatic use.
|
|
41
44
|
- Focus on Node.js 22+, ESM modules, and a lightweight dependency set (`js-yaml`).
|
|
@@ -48,13 +51,14 @@ yarn add yamlock # project dependency
|
|
|
48
51
|
# Encrypt values in a YAML file
|
|
49
52
|
YAMLOCK_KEY="super-secret" yamlock encrypt config.yaml
|
|
50
53
|
|
|
51
|
-
# Decrypt values in place
|
|
52
|
-
yamlock decrypt settings.json --key "super-secret"
|
|
54
|
+
# Decrypt values in place; the payload determines its format and algorithm
|
|
55
|
+
yamlock decrypt settings.json --key "super-secret"
|
|
53
56
|
|
|
54
57
|
# Encrypt only selected fields into a new file
|
|
55
58
|
yamlock encrypt config.json --key "$YAMLOCK_KEY" --paths "db.password,api.token" --output config.secure.json
|
|
56
59
|
|
|
57
60
|
# Inspect CLI metadata
|
|
61
|
+
yamlock --help
|
|
58
62
|
yamlock version
|
|
59
63
|
yamlock algorithms
|
|
60
64
|
|
|
@@ -63,22 +67,81 @@ yamlock keygen --length 64 --format base64
|
|
|
63
67
|
|
|
64
68
|
# Preview changes without touching files
|
|
65
69
|
yamlock encrypt config.yml -o config.enc.yml -p db.password -k "my-secret-key" -d
|
|
70
|
+
|
|
71
|
+
# Preview a legacy-to-v2 migration without printing config contents
|
|
72
|
+
yamlock migrate config.yml -k "$YAMLOCK_KEY" -p db.password -d
|
|
73
|
+
|
|
74
|
+
# Explicitly write legacy v1 for a temporary compatibility requirement
|
|
75
|
+
yamlock encrypt config.yml -k "$YAMLOCK_KEY" --legacy --algorithm aes-256-cbc
|
|
66
76
|
```
|
|
67
77
|
|
|
68
|
-
The CLI detects YAML (`.yaml`/`.yml`) and JSON extensions automatically and
|
|
78
|
+
The CLI detects YAML (`.yaml`/`.yml`) and JSON extensions automatically and
|
|
79
|
+
writes the file back in the same format. Normal `encrypt` and `decrypt` writes
|
|
80
|
+
use an fsynced temporary file plus an atomic rename. In-place writes preserve
|
|
81
|
+
the source mode; a new `--output` inherits the source mode, while an existing
|
|
82
|
+
regular output preserves its own mode. Mutating operations reject symbolic-link
|
|
83
|
+
inputs and outputs rather than following or replacing their targets.
|
|
84
|
+
|
|
85
|
+
YAML writes preserve parsed values, not the original syntax tree. Comments,
|
|
86
|
+
anchor/alias syntax, merge keys, explicit tags, quoting, and formatting may be
|
|
87
|
+
removed or normalized. Review [the YAML rewrite contract](docs/yaml-behavior.md)
|
|
88
|
+
and use `--dry-run` or `--output` when presentation details matter.
|
|
69
89
|
|
|
70
90
|
Options of note:
|
|
71
91
|
- `--output <file>` writes the result to a separate file instead of overwriting the input.
|
|
72
|
-
- `--paths <path1,path2>` targets only the specified fields
|
|
73
|
-
- `--dry-run`
|
|
74
|
-
-
|
|
75
|
-
-
|
|
92
|
+
- `--paths <path1,path2>` targets only the specified fields using the [escaped path syntax](#field-path-syntax).
|
|
93
|
+
- `--dry-run` previews an operation without modifying files; encrypt/decrypt print content changes, while migrate prints only counts and target paths.
|
|
94
|
+
- `migrate` decrypts selected legacy payloads and re-encrypts them as authenticated v2 payloads.
|
|
95
|
+
- `migrate --allow-mixed` additionally authenticates and preserves selected values that are already v2.
|
|
96
|
+
- In-place migration creates `<file>.yamlock.bak` by default; `--no-backup` disables it explicitly.
|
|
97
|
+
- `encrypt --legacy` writes the legacy v1 format; `--algorithm` is accepted for encryption only together with `--legacy`.
|
|
98
|
+
- `encrypt --error-on-encrypted` fails when a selected value is already encrypted instead of preserving it.
|
|
99
|
+
- `encrypt --force-encrypt` explicitly treats selected `yl|...` strings as plaintext and adds another encryption layer.
|
|
100
|
+
- Command `keygen` produces a random key of 1–4096 whole bytes and shows how to store it (shell export or `.env`).
|
|
101
|
+
- Command `algorithms` prints the fixed v2 profile, tested legacy presets, and additional legacy ciphers available from the runtime.
|
|
76
102
|
- Command `version` prints the installed CLI version.
|
|
77
103
|
|
|
104
|
+
The CLI rejects unknown or duplicate options, missing option values, extra
|
|
105
|
+
positional arguments, and options that do not apply to the selected command.
|
|
106
|
+
Argument errors use structured `[yamlock:ERR_*]` codes and are reported before
|
|
107
|
+
the CLI reads or modifies a configuration file.
|
|
108
|
+
|
|
109
|
+
### Field path syntax
|
|
110
|
+
|
|
111
|
+
Default field paths use dots for object nesting and brackets for array indexes:
|
|
112
|
+
`db.password` and `users[0].token`. Inside an object key, backslashes, dots,
|
|
113
|
+
brackets, and commas are escaped with a backslash. For example,
|
|
114
|
+
`db\.primary.token` selects `token` below the literal key `db.primary`, while
|
|
115
|
+
`db.primary.token` selects three nested object keys. Quote escaped CLI paths so
|
|
116
|
+
the shell passes each backslash unchanged:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
yamlock encrypt config.json --key "$YAMLOCK_KEY" --paths 'db\.primary.token,labels\,primary'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Node.js callers can build the same canonical strings from unambiguous segments:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { processConfig, serializePath } from 'yamlock';
|
|
126
|
+
|
|
127
|
+
const selectedPath = serializePath(['db.primary', 'token']);
|
|
128
|
+
const encrypted = processConfig(config, {
|
|
129
|
+
mode: 'encrypt',
|
|
130
|
+
key: process.env.YAMLOCK_KEY,
|
|
131
|
+
paths: [selectedPath]
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
New payloads bind authentication to the escaped canonical path. Payloads written
|
|
136
|
+
by older yamlock versions for keys containing reserved characters remain
|
|
137
|
+
readable through the default serializer's compatibility path; selecting those
|
|
138
|
+
keys now requires the canonical escaped spelling. A custom `pathSerializer`
|
|
139
|
+
keeps its own contract and does not use the default compatibility fallback.
|
|
140
|
+
|
|
78
141
|
### Node.js API
|
|
79
142
|
|
|
80
143
|
```js
|
|
81
|
-
import { encryptValue, decryptValue, processConfig } from 'yamlock';
|
|
144
|
+
import { encryptValue, decryptValue, processConfig, serializePath } from 'yamlock';
|
|
82
145
|
|
|
83
146
|
const encrypted = encryptValue('swordfish', process.env.YAMLOCK_KEY, 'db.password');
|
|
84
147
|
const decrypted = decryptValue(encrypted, process.env.YAMLOCK_KEY, 'db.password');
|
|
@@ -88,14 +151,49 @@ const locked = processConfig(config, { mode: 'encrypt', key: process.env.YAMLOCK
|
|
|
88
151
|
const unlocked = processConfig(locked, { mode: 'decrypt', key: process.env.YAMLOCK_KEY });
|
|
89
152
|
```
|
|
90
153
|
|
|
154
|
+
Expected Node.js API failures extend `YamlockError` and expose stable `ERR_*`
|
|
155
|
+
codes. Specialized classes distinguish validation, payload, authentication,
|
|
156
|
+
legacy decryption, and config-processing failures; `YAMLOCK_ERROR_CODES` avoids
|
|
157
|
+
repeating code strings. See the [Node.js error contract](docs/errors.md).
|
|
158
|
+
|
|
159
|
+
The package includes TypeScript declarations. The supported exports, option
|
|
160
|
+
types, and `1.x` compatibility guarantees are listed in the
|
|
161
|
+
[public Node.js API contract](docs/api.md).
|
|
162
|
+
|
|
163
|
+
Repeated encryption is safe by default. `processConfig` authenticates selected
|
|
164
|
+
existing payloads with the supplied key and field path, preserves them
|
|
165
|
+
unchanged, and encrypts only selected plaintext values. Use
|
|
166
|
+
`existingPayloadPolicy: 'error'` when existing encrypted values should fail the
|
|
167
|
+
operation:
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
processConfig(config, {
|
|
171
|
+
mode: 'encrypt',
|
|
172
|
+
key: process.env.YAMLOCK_KEY,
|
|
173
|
+
existingPayloadPolicy: 'error'
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Malformed payloads, incorrect keys, and incorrect field paths are never silently
|
|
178
|
+
skipped. Re-running `yamlock encrypt` on a fully encrypted input does not rewrite
|
|
179
|
+
the source file. Use `yamlock migrate` rather than `encrypt` to convert preserved
|
|
180
|
+
legacy values to v2.
|
|
181
|
+
|
|
182
|
+
If plaintext intentionally begins with `yl|`, use
|
|
183
|
+
`existingPayloadPolicy: 'encrypt'` or CLI `--force-encrypt`. This also permits
|
|
184
|
+
deliberate nested encryption, so it should not be enabled in routine workflows;
|
|
185
|
+
each added layer requires a matching decrypt operation.
|
|
186
|
+
|
|
91
187
|
See `examples/basic.js` for a runnable end-to-end script (`node examples/basic.js`).
|
|
92
188
|
|
|
93
|
-
###
|
|
189
|
+
### Legacy algorithm customization
|
|
94
190
|
|
|
95
|
-
|
|
191
|
+
V2 deliberately has no free-form cipher settings. For temporary legacy
|
|
192
|
+
compatibility, select format version 1 and provide the old cipher options:
|
|
96
193
|
|
|
97
194
|
```js
|
|
98
195
|
const encrypted = encryptValue('swordfish', KEY, 'db.password', {
|
|
196
|
+
formatVersion: 1,
|
|
99
197
|
algorithm: 'chacha20-poly1305',
|
|
100
198
|
ivLength: 12 // override the IV size used during encryption
|
|
101
199
|
});
|
|
@@ -110,6 +208,7 @@ const processed = processConfig(
|
|
|
110
208
|
{
|
|
111
209
|
mode: 'encrypt',
|
|
112
210
|
key: KEY,
|
|
211
|
+
formatVersion: 1,
|
|
113
212
|
algorithm: { algorithm: 'aes-192-cbc', ivLength: 24 }
|
|
114
213
|
}
|
|
115
214
|
);
|
|
@@ -118,6 +217,7 @@ const processed = processConfig(
|
|
|
118
217
|
const restored = processConfig(processed, {
|
|
119
218
|
mode: 'decrypt',
|
|
120
219
|
key: KEY,
|
|
220
|
+
formatVersion: 1,
|
|
121
221
|
algorithm: { algorithm: 'aes-192-cbc', ivLength: 24 }
|
|
122
222
|
});
|
|
123
223
|
|
|
@@ -126,7 +226,7 @@ const mixedConfig = { db: { password: 'secret', retries: 3 } };
|
|
|
126
226
|
const lockedMixed = processConfig(mixedConfig, {
|
|
127
227
|
mode: 'encrypt',
|
|
128
228
|
key: KEY,
|
|
129
|
-
nonStringPolicy: 'stringify', // stringifies numbers
|
|
229
|
+
nonStringPolicy: 'stringify', // stringifies finite numbers, booleans, and null
|
|
130
230
|
pathSerializer: (segments) => segments.join('/') // custom path naming (db/password instead of dot notation)
|
|
131
231
|
});
|
|
132
232
|
|
|
@@ -146,49 +246,131 @@ const lockedUsers = processConfig(
|
|
|
146
246
|
);
|
|
147
247
|
```
|
|
148
248
|
|
|
249
|
+
`processConfig` recursively traverses arrays and plain objects. With the default
|
|
250
|
+
`nonStringPolicy: 'ignore'`, selected non-string and opaque values such as
|
|
251
|
+
`Date`, `undefined`, or `BigInt` are preserved unchanged. Policy `'error'`
|
|
252
|
+
rejects a selected non-string value. Policy `'stringify'` accepts only finite
|
|
253
|
+
numbers, booleans, and `null`; values that native JSON conversion could omit or
|
|
254
|
+
silently change are rejected instead of being encrypted with lost type
|
|
255
|
+
information. Decrypted stringified primitives remain strings.
|
|
256
|
+
|
|
257
|
+
Circular arrays/objects are rejected. A custom `pathSerializer` must return a
|
|
258
|
+
non-empty, unique string for every leaf and the same serializer must be used for
|
|
259
|
+
encryption and decryption. Policies apply only to values selected by `paths`;
|
|
260
|
+
unselected leaf values are preserved.
|
|
261
|
+
|
|
262
|
+
Direct `processConfig` calls preserve empty containers, sparse-array length and
|
|
263
|
+
holes, null-prototype objects, and own keys such as `__proto__`. JSON and YAML
|
|
264
|
+
files retain the values their parsers can represent, but those formats do not
|
|
265
|
+
encode JavaScript sparse-array holes as a distinct portable value.
|
|
266
|
+
|
|
267
|
+
### Authenticated payload v2 (default)
|
|
268
|
+
|
|
269
|
+
`encryptValue`, `processConfig`, and `yamlock encrypt` write authenticated v2
|
|
270
|
+
payloads by default. `decryptValue` and `processConfig` automatically read both
|
|
271
|
+
v1 and v2.
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
const encrypted = encryptValue('swordfish', KEY, 'db.password');
|
|
275
|
+
|
|
276
|
+
const locked = processConfig(
|
|
277
|
+
{ db: { password: 'swordfish' } },
|
|
278
|
+
{ mode: 'encrypt', key: KEY }
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// No format or algorithm option is required when decrypting.
|
|
282
|
+
const original = decryptValue(encrypted, KEY, 'db.password');
|
|
283
|
+
const unlocked = processConfig(locked, { mode: 'decrypt', key: KEY });
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
V2 uses a fixed AES-256-GCM profile, a random 12-byte nonce, a 16-byte
|
|
287
|
+
authentication tag, and scrypt with a separate random KDF salt. The field path
|
|
288
|
+
and security-critical metadata are authenticated. Free-form cipher and size
|
|
289
|
+
overrides are intentionally unavailable for v2.
|
|
290
|
+
|
|
291
|
+
Existing legacy files can be migrated safely with the CLI:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Preview counts and target paths; config contents are not printed.
|
|
295
|
+
yamlock migrate config.yaml --key "$YAMLOCK_KEY" --paths "db.password,api.token" --dry-run
|
|
296
|
+
|
|
297
|
+
# Migrate in place and create config.yaml.yamlock.bak.
|
|
298
|
+
yamlock migrate config.yaml --key "$YAMLOCK_KEY" --paths "db.password,api.token"
|
|
299
|
+
|
|
300
|
+
# Preserve the source and write a new file. Existing outputs are never replaced.
|
|
301
|
+
yamlock migrate config.yaml --key "$YAMLOCK_KEY" --paths "db.password,api.token" --output config.v2.yaml
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Migration validates every selected value and builds the complete result before
|
|
305
|
+
writing. Selected plaintext and non-string values are rejected, so use
|
|
306
|
+
`--paths` for partially encrypted configs. Selected v2 values are rejected
|
|
307
|
+
unless `--allow-mixed` is set; with that flag they are authenticated and kept
|
|
308
|
+
unchanged. In-place writes are atomic, preserve the source file mode, and do
|
|
309
|
+
not replace an existing backup. To roll back, verify the backup and then copy
|
|
310
|
+
`config.yaml.yamlock.bak` over `config.yaml`.
|
|
311
|
+
|
|
312
|
+
Legacy AES-CBC payloads have no authentication, so migration can only validate
|
|
313
|
+
their structure, field path, and successful decryption. Backups use the source
|
|
314
|
+
file permissions and match `*.yamlock.bak` in the repository `.gitignore`.
|
|
315
|
+
See [the payload v2 design](docs/design/payload-v2.md) for the format, threat
|
|
316
|
+
model, limits, and staged migration plan. This design and implementation have
|
|
317
|
+
not received a third-party security audit.
|
|
318
|
+
|
|
149
319
|
## Advanced usage
|
|
150
320
|
|
|
151
|
-
- **Selective encryption**: combine `--paths` on the CLI or `paths: []` in `processConfig` to encrypt only sensitive
|
|
152
|
-
- **
|
|
321
|
+
- **Selective encryption**: combine `--paths` on the CLI or a non-empty `paths: ['db.password']` array in `processConfig` to encrypt only sensitive fields.
|
|
322
|
+
- **Repeated encryption**: valid selected payloads are authenticated and preserved; add `--error-on-encrypted` or `existingPayloadPolicy: 'error'` for strict workflows.
|
|
323
|
+
- **Non-string handling**: use `nonStringPolicy: 'ignore' | 'stringify' | 'error'` to preserve opaque leaves, stringify finite JSON primitives, or reject selected non-string values; use `pathSerializer` to change path representation (e.g., `db/password` instead of dot notation).
|
|
153
324
|
- **CI/CD flows**: see [examples/docs/ci-cd.md](examples/docs/ci-cd.md) for a GitHub Actions job that decrypts configs for builds and re-encrypts them before publishing artifacts.
|
|
154
325
|
- **Key rotation**: follow [examples/docs/key-rotation.md](examples/docs/key-rotation.md) for a step-by-step process, including scripting tips for large repos.
|
|
155
326
|
|
|
156
327
|
### Supported algorithms
|
|
157
328
|
|
|
158
|
-
| Algorithm |
|
|
159
|
-
|
|
160
|
-
| `aes-
|
|
161
|
-
| `aes-
|
|
162
|
-
| `aes-
|
|
163
|
-
| `
|
|
329
|
+
| Algorithm | Format | Notes |
|
|
330
|
+
|-----------|--------|-------|
|
|
331
|
+
| `aes-256-gcm` | v2 default | Fixed authenticated profile with scrypt, a 12-byte nonce, and a 16-byte tag. |
|
|
332
|
+
| `aes-128-cbc` | legacy v1 | Compatibility only; ciphertext and metadata are not authenticated. |
|
|
333
|
+
| `aes-192-cbc` | legacy v1 | Compatibility only; ciphertext and metadata are not authenticated. |
|
|
334
|
+
| `aes-256-cbc` | legacy v1 | Compatibility default when `--legacy` is used without `--algorithm`. |
|
|
335
|
+
| `chacha20-poly1305` | legacy v1 | Authenticates ciphertext, but not all serialized metadata protected by v2. |
|
|
164
336
|
|
|
165
|
-
|
|
337
|
+
Additional algorithms exposed by `crypto.getCiphers()` are available only in
|
|
338
|
+
explicit legacy mode and are not part of the supported v2 profile. Prefer the
|
|
339
|
+
default v2 writer for new data.
|
|
166
340
|
|
|
167
341
|
## Release information
|
|
168
342
|
|
|
169
|
-
- The badges at the top show the latest npm version and the status of the
|
|
343
|
+
- The badges at the top show the latest npm version and the status of the full CI matrix.
|
|
170
344
|
- See [CHANGELOG.md](CHANGELOG.md) for detailed release notes; install a specific tag via `npm install yamlock@<version>`.
|
|
345
|
+
- yamlock versions are bumped with my own release utility,
|
|
346
|
+
[Bumpster](https://github.com/phoenixweiss/Bumpster). It keeps the tracked
|
|
347
|
+
`VERSION` file and `package.json` synchronized while publishing the `dev`,
|
|
348
|
+
`main`, and `vX.Y.Z` Git refs atomically.
|
|
171
349
|
|
|
172
|
-
### Encrypted value
|
|
350
|
+
### Encrypted value formats
|
|
173
351
|
|
|
174
|
-
|
|
352
|
+
New values use the authenticated v2 envelope:
|
|
353
|
+
|
|
354
|
+
```txt
|
|
355
|
+
yl|2|aes-256-gcm|scrypt|32768|8|1|<kdf_salt>|<nonce>|<path>|<ciphertext>|<tag>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Legacy values remain readable and can still be written explicitly:
|
|
175
359
|
|
|
176
360
|
```txt
|
|
177
361
|
yl|<algorithm>|<salt_base64>|<iv_base64>|<data_base64>
|
|
178
362
|
```
|
|
179
363
|
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
- `<algorithm>` - algorithm name (e.g., aes-256-cbc)
|
|
183
|
-
- `<salt_base64>` - Base64-encoded field path
|
|
184
|
-
- `<iv_base64>` - Base64-encoded initialization vector
|
|
185
|
-
- `<data_base64>` - Base64-encoded encrypted data
|
|
364
|
+
The legacy name `<salt_base64>` is historical: that segment is only the
|
|
365
|
+
Base64-encoded field path and is not a random salt or KDF input.
|
|
186
366
|
|
|
187
|
-
|
|
367
|
+
See [the payload v2 design](docs/design/payload-v2.md) for the canonical field
|
|
368
|
+
definitions, limits, compatibility rules, and legacy security limitations.
|
|
188
369
|
|
|
189
370
|
### Key rotation
|
|
190
371
|
|
|
191
|
-
See [
|
|
372
|
+
See [the key rotation guide](examples/docs/key-rotation.md) for a step-by-step
|
|
373
|
+
workflow that re-encrypts values with v2 and a new `YAMLOCK_KEY`.
|
|
192
374
|
|
|
193
375
|
## Inspiration and motivation
|
|
194
376
|
|
|
@@ -211,9 +393,12 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow, available scrip
|
|
|
211
393
|
|
|
212
394
|
## Future work
|
|
213
395
|
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
|
|
396
|
+
- An async encryption API with bounded scrypt concurrency for large configs.
|
|
397
|
+
- Stricter file-format validation and preservation rules for advanced YAML features.
|
|
398
|
+
|
|
399
|
+
## Author
|
|
400
|
+
|
|
401
|
+
Created and maintained by [Pavel Tkachev (@phoenixweiss)](https://github.com/phoenixweiss).
|
|
217
402
|
|
|
218
403
|
## License
|
|
219
404
|
|
package/bin/yamlock
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
async function loadCliEntry() {
|
|
4
|
+
if (process.env.YAMLOCK_TEST_SOURCE === '1') {
|
|
5
|
+
const sourceModule = await import('../src/cli/cli.js');
|
|
6
|
+
if (sourceModule.runCli) {
|
|
7
|
+
return sourceModule.runCli;
|
|
8
|
+
}
|
|
9
|
+
throw new Error('Source CLI entry does not export runCli.');
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
try {
|
|
5
13
|
const distModule = await import('../dist/cli/cli.js');
|
|
6
14
|
if (distModule.runCli) {
|