@ciqol/resenv 0.1.1 → 0.1.2

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.
Files changed (2) hide show
  1. package/README.md +92 -162
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,33 +1,20 @@
1
1
  # @ciqol/resenv
2
2
 
3
- **Replace references already in `process.env` with their resolved values at
4
- application startup.** Your existing loader, such as Nx, chooses the dotenv
5
- files. resenv resolves the resulting environment before application code runs.
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
- ```
18
-
19
- Resolution happens once at startup. It does not watch the provider for changes;
20
- restart the application to fetch updated values. No filename is passed to
21
- resenv, and dotenv files are never rewritten.
3
+ [![🏗️ CI](https://github.com/ciqol/resenv/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ciqol/resenv/actions/workflows/ci.yml)
4
+ [![NPM Version](https://img.shields.io/npm/v/@ciqol/resenv)](https://www.npmjs.com/package/@ciqol/resenv)
5
+ [![NPM Downloads](https://img.shields.io/npm/dm/@ciqol/resenv)](https://www.npmjs.com/package/@ciqol/resenv)
22
6
 
23
- Requires Node.js 20 or later. The package is experimental and remains in initial
24
- development; its API may change between 0.x releases.
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.
25
10
 
26
- Each npm package includes this README for the version you installed. The
27
- [repository README](https://github.com/ciqol/resenv#readme) and linked online
28
- guides follow current development and may describe newer versions.
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.
29
15
 
30
- 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).
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.
31
18
 
32
19
  ## Install
33
20
 
@@ -37,84 +24,60 @@ Install in your application project:
37
24
  npm install --save-exact @ciqol/resenv
38
25
  ```
39
26
 
40
- This records the version selected by npm's `latest` tag in your project's
41
- manifest and lockfile.
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.
27
+ The CLI examples use `npx --no-install` to run that installed copy. In an npm
28
+ script, use `resenv` directly.
45
29
 
46
30
  ## Use with Doppler
47
31
 
48
- Doppler manages application secrets. If you're new to it, start with
49
- [Doppler's introduction](https://docs.doppler.com/docs/start).
50
-
51
- ### 1. Authenticate locally
32
+ ### 1. Authenticate
52
33
 
53
- Follow the [official Doppler CLI installation guide](https://docs.doppler.com/docs/cli#installation)
54
- for your operating system, then log in from your application directory:
34
+ Install the [official Doppler CLI](https://docs.doppler.com/docs/cli#installation),
35
+ then log in from your application directory:
55
36
 
56
37
  ```sh
57
38
  doppler login
58
39
  ```
59
40
 
60
- Each developer authenticates with their own Doppler access.
61
-
62
- > [!WARNING]
63
- > Authentication and credential storage depend on the Doppler CLI's implementation
64
- > and may vary by platform or CLI version. resenv only reuses its existing token;
65
- > it does not manage login, logout or credential storage, or access the OS
66
- > credential store directly.
41
+ resenv uses `DOPPLER_TOKEN` when supplied; otherwise it reuses the CLI login
42
+ selected for the application's working directory. Doppler owns login, logout
43
+ and credential storage. Missing credentials or access stop startup without
44
+ prompting for login.
67
45
 
68
- At startup, resenv uses `DOPPLER_TOKEN` when supplied; otherwise it reads the
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.
46
+ For CI and other unattended environments, supply a suitably scoped
47
+ `DOPPLER_TOKEN` through your runner's secret facility; no CLI is needed.
48
+ See [authentication and platform details](https://github.com/ciqol/resenv/blob/main/docs/doppler.md)
49
+ for supported CLI locations and lookup rules.
72
50
 
73
- For CI and other unattended environments, supply `DOPPLER_TOKEN` through your
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.
51
+ ### 2. Declare references
77
52
 
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`:
53
+ In the environment your application already loads, such as Nx's `.env.serve`:
85
54
 
86
55
  ```dotenv
87
56
  API_TOKEN=doppler://my-project/dev/API_TOKEN
88
57
  PORT=3000
89
58
  ```
90
59
 
91
- The reference contains the Doppler project, config and secret name. This is
92
- resenv's reference syntax. Plain values such as `PORT` pass through unchanged.
93
- The reference itself grants no access: the authenticated identity needs
94
- permission to read that secret. Doppler references need no dotenvx private key.
60
+ Each reference names a project, config and secret. The authenticated identity
61
+ must have access to that secret. Plain values such as `PORT` pass through.
95
62
 
96
- ### 3. Preload resenv on the application process
63
+ ### 3. Preload on the application process
97
64
 
98
- Once the environment has been loaded:
65
+ After your loader has populated the environment:
99
66
 
100
67
  ```sh
101
68
  node --require @ciqol/resenv/resolve app.cjs
102
69
  node --import @ciqol/resenv/resolve app.mjs
103
70
  ```
104
71
 
105
- The preload replaces `process.env.API_TOKEN` before the application and its
106
- imports execute. It infers Doppler from the value, so no provider flag is needed.
107
-
72
+ The preload replaces references before the application and its imports execute.
108
73
  Alternatively, wrap a command whose environment is already populated:
109
74
 
110
75
  ```sh
111
76
  npx --no-install resenv run -- node app.mjs
112
77
  ```
113
78
 
114
- The wrapper supplies resolved values to the child process. It inherits
115
- stdin/stdout/stderr, preserves exit status and forwards SIGINT, SIGTERM and
116
- SIGHUP to that child. On POSIX, the child runs in its own session so terminal
117
- signals reach it only through the wrapper.
79
+ The wrapper passes the resolved environment to its child, inherits terminal I/O,
80
+ preserves exit status and forwards termination signals.
118
81
 
119
82
  ## Nx integration
120
83
 
@@ -137,70 +100,70 @@ Put the preload on the application's target, after Nx loads its dotenv files:
137
100
  }
138
101
  ```
139
102
 
140
- Run `nx serve app`. Nx selects `.env.serve` and any applicable overrides,
141
- then starts the application with resenv. Use your application's existing path
142
- and target options. For an existing `@nx/js:node` target, add:
103
+ Run `nx serve app`, using your application's existing path and target options.
104
+ For an existing `@nx/js:node` target, add:
143
105
 
144
106
  ```json
145
107
  "runtimeArgs": ["--require", "@ciqol/resenv/resolve"]
146
108
  ```
147
109
 
148
- Preloading the outer `nx` process runs too early. A shell-wide `NODE_OPTIONS`
149
- setting also affects unrelated processes. Keep the preload on the application
150
- target. If a build needs secrets, its process needs its own integration after
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).
110
+ Preloading the outer `nx` process runs too early. Keep the preload on each
111
+ application or build process that needs secrets, after its environment is loaded.
112
+ Secrets embedded in build output remain in that output.
168
113
 
169
114
  ## Library API
170
115
 
171
- Call the library after your loader has populated the environment, before
172
- importing modules that read configuration:
116
+ Call the synchronous resolver after loading the environment, before importing
117
+ modules that read configuration:
173
118
 
174
119
  ```js
175
120
  import { resolve } from '@ciqol/resenv'
176
121
 
177
122
  const childEnv = resolve()
178
-
179
- // Also replace values in process.env; the returned copy can be ignored.
180
123
  resolve(process.env, { replace: true })
181
124
  ```
182
125
 
183
- `resolve(env = process.env, options = {})` always returns a new resolved
184
- environment. `replace` defaults to `false`, leaving the input unchanged. With
185
- `replace: true`, it also replaces values in the input after every lookup succeeds;
186
- the return value is still an independent copy.
126
+ `resolve(env = process.env, options = {})` always returns an independent
127
+ resolved copy. CommonJS uses `require('@ciqol/resenv')`.
128
+
129
+ | Option | Default | Behavior |
130
+ | --- | --- | --- |
131
+ | `replace` | `false` | Also replace input values after all lookups succeed. Requires `process.env` or writable data properties. |
132
+ | `commands` | `false` | Execute whole-value `$(command)` expressions through POSIX `/bin/sh`. |
133
+ | `commandTimeoutMs` | `30000` | Per-command timeout in milliseconds; a positive integer up to `2147483647`. |
134
+
135
+ Doppler is inferred from original values. Without references, the result is an
136
+ unchanged copy and no credentials are accessed. Library failures throw
137
+ `ResenvError`; its `code` identifies the failure. CLI and preload failures exit
138
+ with a safe message.
139
+
140
+ ## Resolution and safety
141
+
142
+ - All lookups must succeed before the input environment changes or a child starts.
143
+ - Only named secrets are fetched; repeated references share a request within a
144
+ resolution pass. Values are not cached on disk.
145
+ - Fetched values stay literal, even if they contain another reference or `$()`.
146
+ - Shell commands require explicit opt-in and run with your permissions. Failed
147
+ resolution cannot undo their file or network effects.
148
+ - Use one preload mode per process. Combining modes or a preload with the CLI
149
+ fails with `CONFLICTING_PRELOADS`. Avoid additional resolvers in `NODE_OPTIONS`.
150
+ - Application code can read its secrets and any credentials supplied to it.
151
+
152
+ See [Doppler limits and compatibility](https://github.com/ciqol/resenv/blob/main/docs/doppler.md)
153
+ and [security guidance](https://github.com/ciqol/resenv/blob/main/SECURITY.md).
154
+
155
+ ## Optional commands
187
156
 
188
- Providers are inferred from each original value, just as with `/resolve` and
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.
157
+ Enable commands alongside Doppler references through the library:
195
158
 
196
- CommonJS uses the same API through
197
- `require('@ciqol/resenv')`. Both formats share the `ResenvError` class, whose
198
- `code` identifies the failure. CLI and preload failures exit with a safe message.
159
+ ```js
160
+ const env = resolve({ BUILD_LABEL: '$(printf local)' }, { commands: true })
161
+ ```
199
162
 
200
- Using `replace: true` requires `process.env` or writable data properties. A read-only
201
- property, setter or Proxy target fails before any replacement is committed.
202
- Keep the default copy mode for inputs that should remain unchanged. The Node
203
- preloads enable in-place replacement so application imports see resolved values.
163
+ Or use `resenv run --commands -- ...`, or preload
164
+ `@ciqol/resenv/resolve-commands`. The canonical `/resolve` preload leaves shell
165
+ expressions literal. Only use command mode with trusted configuration; see
166
+ [command limits](https://github.com/ciqol/resenv/blob/main/SECURITY.md#command-execution).
204
167
 
205
168
  ## Debug startup
206
169
 
@@ -208,55 +171,22 @@ preloads enable in-place replacement so application imports see resolved values.
208
171
  NODE_DEBUG=ciqol:resenv node --require @ciqol/resenv/resolve app.mjs
209
172
  ```
210
173
 
211
- For the Nx target above, use `NODE_DEBUG=ciqol:resenv nx serve app`. Set `NODE_DEBUG`
212
- when launching Node, before dotenv loading. Diagnostics use `node:util`'s
213
- `debuglog` and are off by default:
174
+ For Nx, use `NODE_DEBUG=ciqol:resenv nx serve app`. Enable it when launching Node,
175
+ before dotenv loading. Diagnostics omit secrets, tokens, commands and provider
176
+ responses. `NODE_DEBUG=child_process` or `NODE_DEBUG=*` can expose credentials;
177
+ resenv blocks its subprocess operations in that mode. See
178
+ [diagnostic details](https://github.com/ciqol/resenv/blob/main/SECURITY.md#diagnostics).
214
179
 
215
- ```text
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.
180
+ ## Further reading
227
181
 
228
- ## Optional commands
182
+ Each npm package includes this README for its installed version. The
183
+ [repository README](https://github.com/ciqol/resenv#readme) and linked online
184
+ guides follow current development and may describe newer versions. GitHub
185
+ [releases](https://github.com/ciqol/resenv/releases) contain version change notes.
229
186
 
230
- - **Opt-in shell commands:** `/resolve-commands` resolves complete `$(command)`
231
- values through `/bin/sh`. The canonical `/resolve` preload leaves these values
232
- literal for both `--require` and `--import`, including when `RESENV_COMMANDS`
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).
187
+ - [Contributing, development and tests](https://github.com/ciqol/resenv/blob/main/CONTRIBUTING.md)
188
+ - [Request provider or platform support](https://github.com/ciqol/resenv/issues/new)
189
+ - [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
190
 
261
191
  ## License
262
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ciqol/resenv",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Replace environment references with their resolved values at application startup",
5
5
  "license": "MIT",
6
6
  "publishConfig": {