@ciqol/resenv 0.1.1 → 0.1.3
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 +103 -161
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
# @ciqol/resenv
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**Doppler is the only built-in cloud provider.** A value such as
|
|
8
|
-
`doppler://my-project/dev/API_TOKEN` tells resenv which secret to fetch. The
|
|
9
|
-
application then reads the actual value from `process.env.API_TOKEN`.
|
|
10
|
-
|
|
11
|
-
```text
|
|
12
|
-
Nx or another loader sets process.env
|
|
13
|
-
|
|
|
14
|
-
resenv fetches the referenced values and replaces them in memory
|
|
15
|
-
|
|
|
16
|
-
Application starts with ordinary environment values
|
|
17
|
-
```
|
|
3
|
+
[](https://github.com/ciqol/resenv/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@ciqol/resenv)
|
|
5
|
+
[](https://www.npmjs.com/package/@ciqol/resenv)
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
resenv
|
|
7
|
+
**Resolve secret references already in `process.env` before application code
|
|
8
|
+
runs.** Your existing loader, such as Nx, chooses and loads dotenv files;
|
|
9
|
+
resenv replaces references in memory. It never rewrites those files.
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
Doppler is the only built-in cloud provider. For example,
|
|
12
|
+
`API_TOKEN=doppler://my-project/dev/API_TOKEN` becomes the fetched secret in
|
|
13
|
+
`process.env.API_TOKEN`. Resolution happens once at startup; restart the
|
|
14
|
+
application to fetch updated values.
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
Requires Node.js 20 or later. The package has no runtime npm dependencies and
|
|
17
|
+
is experimental; its API may change between 0.x releases.
|
|
18
|
+
|
|
19
|
+
## When to choose resenv
|
|
29
20
|
|
|
30
|
-
|
|
21
|
+
Choose resenv when your Node.js application already loads its environment through
|
|
22
|
+
Nx, dotenv or another loader, and you want to resolve individual Doppler secret
|
|
23
|
+
references before application code runs. Each reference selects its own project
|
|
24
|
+
and config, so one application can use secrets from multiple Doppler configs.
|
|
25
|
+
|
|
26
|
+
For Git worktrees, keep reviewed reference configuration and the startup command
|
|
27
|
+
with the project so each checkout receives them without copying plaintext secrets.
|
|
28
|
+
Each worktree still needs the application's usual setup and configured Doppler
|
|
29
|
+
access. Your existing loader remains responsible for selecting and loading files.
|
|
31
30
|
|
|
32
31
|
## Install
|
|
33
32
|
|
|
@@ -37,84 +36,60 @@ Install in your application project:
|
|
|
37
36
|
npm install --save-exact @ciqol/resenv
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
The CLI examples below use `npx --no-install` to run the copy installed in your
|
|
44
|
-
project. In an npm script, use `resenv` directly.
|
|
39
|
+
The CLI examples use `npx --no-install` to run that installed copy. In an npm
|
|
40
|
+
script, use `resenv` directly.
|
|
45
41
|
|
|
46
42
|
## Use with Doppler
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
[Doppler's introduction](https://docs.doppler.com/docs/start).
|
|
50
|
-
|
|
51
|
-
### 1. Authenticate locally
|
|
44
|
+
### 1. Authenticate
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
Install the [official Doppler CLI](https://docs.doppler.com/docs/cli#installation),
|
|
47
|
+
then log in from your application directory:
|
|
55
48
|
|
|
56
49
|
```sh
|
|
57
50
|
doppler login
|
|
58
51
|
```
|
|
59
52
|
|
|
60
|
-
|
|
53
|
+
resenv uses `DOPPLER_TOKEN` when supplied; otherwise it reuses the CLI login
|
|
54
|
+
selected for the application's working directory. Doppler owns login, logout
|
|
55
|
+
and credential storage. Missing credentials or access stop startup without
|
|
56
|
+
prompting for login.
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
> credential store directly.
|
|
58
|
+
For CI and other unattended environments, supply a suitably scoped
|
|
59
|
+
`DOPPLER_TOKEN` through your runner's secret facility; no CLI is needed.
|
|
60
|
+
See [authentication and platform details](https://github.com/ciqol/resenv/blob/main/docs/doppler.md)
|
|
61
|
+
for supported CLI locations and lookup rules.
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
existing Doppler CLI token for the application's working directory. It then
|
|
70
|
-
fetches only referenced secrets through the HTTPS API. Missing credentials or
|
|
71
|
-
access stop startup without opening a browser or prompting for login.
|
|
63
|
+
### 2. Declare references
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
runner's secret facility; the CLI is not needed in that mode. See
|
|
75
|
-
[Doppler authentication and platform details](https://github.com/ciqol/resenv/blob/main/docs/doppler.md), including
|
|
76
|
-
migration from the earlier resenv-managed login.
|
|
77
|
-
|
|
78
|
-
Need another secrets provider, operating system or CLI installation method?
|
|
79
|
-
[Request support](https://github.com/ciqol/resenv/issues/new) with your use case
|
|
80
|
-
and platform details.
|
|
81
|
-
|
|
82
|
-
### 2. Declare references in the environment your app already loads
|
|
83
|
-
|
|
84
|
-
For example, in an Nx application's `.env.serve`:
|
|
65
|
+
In the environment your application already loads, such as Nx's `.env.serve`:
|
|
85
66
|
|
|
86
67
|
```dotenv
|
|
87
68
|
API_TOKEN=doppler://my-project/dev/API_TOKEN
|
|
88
69
|
PORT=3000
|
|
89
70
|
```
|
|
90
71
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
The reference itself grants no access: the authenticated identity needs
|
|
94
|
-
permission to read that secret. Doppler references need no dotenvx private key.
|
|
72
|
+
Each reference names a project, config and secret. The authenticated identity
|
|
73
|
+
must have access to that secret. Plain values such as `PORT` pass through.
|
|
95
74
|
|
|
96
|
-
### 3. Preload
|
|
75
|
+
### 3. Preload on the application process
|
|
97
76
|
|
|
98
|
-
|
|
77
|
+
After your loader has populated the environment:
|
|
99
78
|
|
|
100
79
|
```sh
|
|
101
80
|
node --require @ciqol/resenv/resolve app.cjs
|
|
102
81
|
node --import @ciqol/resenv/resolve app.mjs
|
|
103
82
|
```
|
|
104
83
|
|
|
105
|
-
The preload replaces
|
|
106
|
-
imports execute. It infers Doppler from the value, so no provider flag is needed.
|
|
107
|
-
|
|
84
|
+
The preload replaces references before the application and its imports execute.
|
|
108
85
|
Alternatively, wrap a command whose environment is already populated:
|
|
109
86
|
|
|
110
87
|
```sh
|
|
111
88
|
npx --no-install resenv run -- node app.mjs
|
|
112
89
|
```
|
|
113
90
|
|
|
114
|
-
The wrapper
|
|
115
|
-
|
|
116
|
-
SIGHUP to that child. On POSIX, the child runs in its own session so terminal
|
|
117
|
-
signals reach it only through the wrapper.
|
|
91
|
+
The wrapper passes the resolved environment to its child, inherits terminal I/O,
|
|
92
|
+
preserves exit status and forwards termination signals.
|
|
118
93
|
|
|
119
94
|
## Nx integration
|
|
120
95
|
|
|
@@ -137,70 +112,70 @@ Put the preload on the application's target, after Nx loads its dotenv files:
|
|
|
137
112
|
}
|
|
138
113
|
```
|
|
139
114
|
|
|
140
|
-
Run `nx serve app
|
|
141
|
-
|
|
142
|
-
and target options. For an existing `@nx/js:node` target, add:
|
|
115
|
+
Run `nx serve app`, using your application's existing path and target options.
|
|
116
|
+
For an existing `@nx/js:node` target, add:
|
|
143
117
|
|
|
144
118
|
```json
|
|
145
119
|
"runtimeArgs": ["--require", "@ciqol/resenv/resolve"]
|
|
146
120
|
```
|
|
147
121
|
|
|
148
|
-
Preloading the outer `nx` process runs too early.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
its environment is loaded; values embedded in build output remain in that output.
|
|
152
|
-
|
|
153
|
-
## Resolution behavior
|
|
154
|
-
|
|
155
|
-
- Only the enabled reference forms are resolved. Ordinary values pass through.
|
|
156
|
-
- Doppler fetches only the named secrets; repeated references share one request
|
|
157
|
-
within a resolution pass. Values are not cached on disk.
|
|
158
|
-
- All lookups must succeed before the environment is changed or a child starts.
|
|
159
|
-
Missing access, malformed references and provider failures stop startup.
|
|
160
|
-
- Fetched values stay literal, even if they contain `$()` or another reference.
|
|
161
|
-
- The canonical preload leaves shell expressions literal. Executing commands
|
|
162
|
-
requires the separate opt-in described below.
|
|
163
|
-
- Application code can read its resolved environment. Access to the same
|
|
164
|
-
credentials also permits other code or sessions to retrieve those secrets.
|
|
165
|
-
|
|
166
|
-
See the current [Doppler limits](https://github.com/ciqol/resenv/blob/main/docs/doppler.md#resolution-and-security-boundaries)
|
|
167
|
-
and [security guidance](https://github.com/ciqol/resenv/blob/main/SECURITY.md).
|
|
122
|
+
Preloading the outer `nx` process runs too early. Keep the preload on each
|
|
123
|
+
application or build process that needs secrets, after its environment is loaded.
|
|
124
|
+
Secrets embedded in build output remain in that output.
|
|
168
125
|
|
|
169
126
|
## Library API
|
|
170
127
|
|
|
171
|
-
Call the
|
|
172
|
-
|
|
128
|
+
Call the synchronous resolver after loading the environment, before importing
|
|
129
|
+
modules that read configuration:
|
|
173
130
|
|
|
174
131
|
```js
|
|
175
132
|
import { resolve } from '@ciqol/resenv'
|
|
176
133
|
|
|
177
134
|
const childEnv = resolve()
|
|
178
|
-
|
|
179
|
-
// Also replace values in process.env; the returned copy can be ignored.
|
|
180
135
|
resolve(process.env, { replace: true })
|
|
181
136
|
```
|
|
182
137
|
|
|
183
|
-
`resolve(env = process.env, options = {})` always returns
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
138
|
+
`resolve(env = process.env, options = {})` always returns an independent
|
|
139
|
+
resolved copy. CommonJS uses `require('@ciqol/resenv')`.
|
|
140
|
+
|
|
141
|
+
| Option | Default | Behavior |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `replace` | `false` | Also replace input values after all lookups succeed. Requires `process.env` or writable data properties. |
|
|
144
|
+
| `commands` | `false` | Execute whole-value `$(command)` expressions through POSIX `/bin/sh`. |
|
|
145
|
+
| `commandTimeoutMs` | `30000` | Per-command timeout in milliseconds; a positive integer up to `2147483647`. |
|
|
146
|
+
|
|
147
|
+
Doppler is inferred from original values. Without references, the result is an
|
|
148
|
+
unchanged copy and no credentials are accessed. Library failures throw
|
|
149
|
+
`ResenvError`; its `code` identifies the failure. CLI and preload failures exit
|
|
150
|
+
with a safe message.
|
|
151
|
+
|
|
152
|
+
## Resolution and safety
|
|
153
|
+
|
|
154
|
+
- All lookups must succeed before the input environment changes or a child starts.
|
|
155
|
+
- Only named secrets are fetched; repeated references share a request within a
|
|
156
|
+
resolution pass. Values are not cached on disk.
|
|
157
|
+
- Fetched values stay literal, even if they contain another reference or `$()`.
|
|
158
|
+
- Shell commands require explicit opt-in and run with your permissions. Failed
|
|
159
|
+
resolution cannot undo their file or network effects.
|
|
160
|
+
- Use one preload mode per process. Combining modes or a preload with the CLI
|
|
161
|
+
fails with `CONFLICTING_PRELOADS`. Avoid additional resolvers in `NODE_OPTIONS`.
|
|
162
|
+
- Application code can read its secrets and any credentials supplied to it.
|
|
163
|
+
|
|
164
|
+
See [Doppler limits and compatibility](https://github.com/ciqol/resenv/blob/main/docs/doppler.md)
|
|
165
|
+
and [security guidance](https://github.com/ciqol/resenv/blob/main/SECURITY.md).
|
|
166
|
+
|
|
167
|
+
## Optional commands
|
|
187
168
|
|
|
188
|
-
|
|
189
|
-
`run`. Doppler is the only supported cloud provider today. No options are
|
|
190
|
-
required: without references, the result is an unchanged copy and no credentials
|
|
191
|
-
are accessed. An environment can contain several references, including different
|
|
192
|
-
Doppler projects/configs. Each value selects its own source; a command can supply
|
|
193
|
-
a Doppler token. Commands still require
|
|
194
|
-
`commands: true`. Returned secret values are never interpreted again.
|
|
169
|
+
Enable commands alongside Doppler references through the library:
|
|
195
170
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
171
|
+
```js
|
|
172
|
+
const env = resolve({ BUILD_LABEL: '$(printf local)' }, { commands: true })
|
|
173
|
+
```
|
|
199
174
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
175
|
+
Or use `resenv run --commands -- ...`, or preload
|
|
176
|
+
`@ciqol/resenv/resolve-commands`. The canonical `/resolve` preload leaves shell
|
|
177
|
+
expressions literal. Only use command mode with trusted configuration; see
|
|
178
|
+
[command limits](https://github.com/ciqol/resenv/blob/main/SECURITY.md#command-execution).
|
|
204
179
|
|
|
205
180
|
## Debug startup
|
|
206
181
|
|
|
@@ -208,55 +183,22 @@ preloads enable in-place replacement so application imports see resolved values.
|
|
|
208
183
|
NODE_DEBUG=ciqol:resenv node --require @ciqol/resenv/resolve app.mjs
|
|
209
184
|
```
|
|
210
185
|
|
|
211
|
-
For
|
|
212
|
-
|
|
213
|
-
`
|
|
186
|
+
For Nx, use `NODE_DEBUG=ciqol:resenv nx serve app`. Enable it when launching Node,
|
|
187
|
+
before dotenv loading. Diagnostics omit secrets, tokens, commands and provider
|
|
188
|
+
responses. `NODE_DEBUG=child_process` or `NODE_DEBUG=*` can expose credentials;
|
|
189
|
+
resenv blocks its subprocess operations in that mode. See
|
|
190
|
+
[diagnostic details](https://github.com/ciqol/resenv/blob/main/SECURITY.md#diagnostics).
|
|
214
191
|
|
|
215
|
-
|
|
216
|
-
CIQOL:RESENV 12345: Replaced secret value for "API_TOKEN".
|
|
217
|
-
CIQOL:RESENV 12345: Done: replaced 1 secret value(s).
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
With no matching references, the only message is `Nothing to handle.` Failures
|
|
221
|
-
include a safe error and completion summary. Logs omit values, tokens, command
|
|
222
|
-
text and provider responses; no log files are created.
|
|
223
|
-
|
|
224
|
-
`NODE_DEBUG=child_process` (including `NODE_DEBUG=*`) makes Node print spawn
|
|
225
|
-
options containing credentials. resenv blocks its subprocess operations with
|
|
226
|
-
`UNSAFE_DEBUG_LOGGING` in that mode. Use `NODE_DEBUG=ciqol:resenv` instead.
|
|
192
|
+
## Further reading
|
|
227
193
|
|
|
228
|
-
|
|
194
|
+
Each npm package includes this README for its installed version. The
|
|
195
|
+
[repository README](https://github.com/ciqol/resenv#readme) and linked online
|
|
196
|
+
guides follow current development and may describe newer versions. GitHub
|
|
197
|
+
[releases](https://github.com/ciqol/resenv/releases) contain version change notes.
|
|
229
198
|
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
is set in the environment. `resenv run --commands -- ...` enables commands
|
|
234
|
-
alongside inferred Doppler references, as does the command preload. Use only
|
|
235
|
-
trusted configuration; commands run with your permissions. A timeout limits
|
|
236
|
-
execution time, but cannot undo file changes, network requests or other effects.
|
|
237
|
-
Leave this option disabled when the environment can contain untrusted input.
|
|
238
|
-
Commands have closed stdin, a 30-second timeout and a 64 KiB combined output
|
|
239
|
-
limit. Trailing LF characters are removed; fetched output stays literal.
|
|
240
|
-
Embedded forms such as `prefix$(command)suffix` are unsupported.
|
|
241
|
-
|
|
242
|
-
Values beginning with `encrypted:` are left unchanged. resenv resolves cloud
|
|
243
|
-
references; dotenvx decryption is outside its scope. The package has no runtime
|
|
244
|
-
npm dependencies.
|
|
245
|
-
|
|
246
|
-
`/resolve-doppler` is an equivalent name for `/resolve`; `run --doppler` remains
|
|
247
|
-
accepted. New integrations should use the canonical entry points above.
|
|
248
|
-
|
|
249
|
-
Use one preload mode per process. Combining `/resolve` and `/resolve-commands`
|
|
250
|
-
or combining a preload with `resenv run` fails with `CONFLICTING_PRELOADS` to
|
|
251
|
-
prevent reinterpreting fetched values. Do not configure a second resolver in
|
|
252
|
-
the child process through `NODE_OPTIONS` or other launch hooks.
|
|
253
|
-
For both modes together, use `resenv run --commands -- ...` or one library call
|
|
254
|
-
with `{ commands: true }`.
|
|
255
|
-
|
|
256
|
-
## Contributing
|
|
257
|
-
|
|
258
|
-
For checkout setup, building, testing, trying local changes in another project
|
|
259
|
-
and submitting fixes, see [CONTRIBUTING.md](https://github.com/ciqol/resenv/blob/main/CONTRIBUTING.md).
|
|
199
|
+
- [Contributing, development and tests](https://github.com/ciqol/resenv/blob/main/CONTRIBUTING.md)
|
|
200
|
+
- [Request provider or platform support](https://github.com/ciqol/resenv/issues/new)
|
|
201
|
+
- [Background: A new Git worktree shouldn't mean another copy of your secrets](https://dev.to/titanron/a-new-git-worktree-shouldnt-mean-another-copy-of-your-secrets-424)
|
|
260
202
|
|
|
261
203
|
## License
|
|
262
204
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciqol/resenv",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Replace environment references with their resolved values at application startup",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,7 +20,12 @@
|
|
|
20
20
|
"environment",
|
|
21
21
|
"dotenv",
|
|
22
22
|
"secrets",
|
|
23
|
-
"nx"
|
|
23
|
+
"nx",
|
|
24
|
+
"doppler",
|
|
25
|
+
"secret-references",
|
|
26
|
+
"environment-variables",
|
|
27
|
+
"nodejs",
|
|
28
|
+
"git-worktree"
|
|
24
29
|
],
|
|
25
30
|
"main": "./index.cjs",
|
|
26
31
|
"module": "./index.mjs",
|