@ciqol/resenv 0.1.0-beta.1 → 0.1.0-beta.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.
- package/CONTRIBUTING.md +33 -13
- package/README.md +34 -20
- package/SECURITY.md +20 -20
- package/bin/resenv.cjs +21 -23
- package/docs/build.md +14 -15
- package/docs/dependencies.md +57 -501
- package/docs/doppler.md +69 -60
- package/docs/maintainer-handoff.md +29 -102
- package/docs/release-security.md +67 -131
- package/docs/releasing.md +32 -21
- package/doppler/auth.cjs +4 -110
- package/doppler/http.cjs +3 -8
- package/doppler/system.cjs +26 -75
- package/package.json +1 -1
- package/types/src/doppler/auth.d.ts +2 -17
- package/types/src/doppler/http.d.ts +1 -3
- package/types/src/doppler/system.d.ts +3 -8
- package/docs/maturity.md +0 -61
- package/docs/public-readiness.md +0 -138
- package/docs/security-performance-review.md +0 -150
- package/docs/validation.md +0 -700
package/CONTRIBUTING.md
CHANGED
|
@@ -6,9 +6,10 @@ and [repository instructions](https://github.com/ciqol/resenv/blob/beta/AGENTS.m
|
|
|
6
6
|
The [README](README.md) covers using resenv in an application. This guide covers
|
|
7
7
|
changing resenv itself and verifying those changes.
|
|
8
8
|
|
|
9
|
-
Releases start at `0.1.0-beta.1` on the sole branch `beta`, using npm
|
|
10
|
-
`beta
|
|
11
|
-
|
|
9
|
+
Releases start at `0.1.0-beta.1` on the sole branch `beta`, using npm tags
|
|
10
|
+
`beta` and `latest` for the same tested version. Semantic-release's official
|
|
11
|
+
commit-analysis and release-notes plugins select releasable changes and generate
|
|
12
|
+
notes; actual beta tags supply the next
|
|
12
13
|
prerelease counter. The root and ordinary local builds remain private prototypes.
|
|
13
14
|
CI generates the publishable manifest before packing and testing it, then stages
|
|
14
15
|
the exact archive through npm OIDC for maintainer approval. See
|
|
@@ -25,6 +26,14 @@ npm ci
|
|
|
25
26
|
npm run verify
|
|
26
27
|
```
|
|
27
28
|
|
|
29
|
+
The committed `.npmrc` omits automatically installed peer dependencies. The
|
|
30
|
+
release planner calls the two official plugins directly, so their full engine
|
|
31
|
+
peer and its publisher dependencies are intentionally not installed. Audit and
|
|
32
|
+
planning enforce that absence. `npm ls` still reports the declared missing
|
|
33
|
+
engine peers; standalone plugin behavior is covered directly by the release
|
|
34
|
+
specs. Keep this install policy when refreshing the lockfile; do not use force
|
|
35
|
+
or legacy peer resolution. See [the dependency review](docs/dependencies.md).
|
|
36
|
+
|
|
28
37
|
`npm run verify` is the complete local check: lint, strict source/spec type
|
|
29
38
|
checks, build, unit/process tests and installed-package checks. To build without
|
|
30
39
|
running the checks, use `npm run build`.
|
|
@@ -60,14 +69,22 @@ shipping them in JavaScript or declarations. Do not maintain separate handwritte
|
|
|
60
69
|
```sh
|
|
61
70
|
npm run test:unit # colocated Vitest specs, no build required
|
|
62
71
|
npm run test:watch # watch unit specs while editing
|
|
63
|
-
npm run test:coverage # source unit coverage, text and
|
|
72
|
+
npm run test:coverage # source unit coverage, text, lcov and Cobertura output
|
|
64
73
|
npm run test:integration # process checks, requires a build
|
|
65
74
|
```
|
|
66
75
|
|
|
67
76
|
`npm test` runs unit and process tests. Mock or inject provider calls, credential
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
lookup and process control in unit tests. Keep checks that spawn actual commands
|
|
78
|
+
or temporary applications in `test/`. The unit coverage
|
|
70
79
|
report measures source and is separate from packed-package and process checks.
|
|
80
|
+
`npm run verify` enforces a 90% global floor for source statements, branches,
|
|
81
|
+
functions and lines. Worker and preload entry files stay in the denominator;
|
|
82
|
+
the CLI is checked separately by process and packed-package tests. CI retains
|
|
83
|
+
LCOV and Cobertura reports for seven days in the `resenv-coverage` artifact.
|
|
84
|
+
The official `actions/upload-code-coverage` action uploads Cobertura in a separate
|
|
85
|
+
job; fork PRs retain the local gate and reports but cannot upload. CLI lookup
|
|
86
|
+
checks use injected helpers and temporary dummy executables; they never access
|
|
87
|
+
a developer credential store or authenticated provider session.
|
|
71
88
|
|
|
72
89
|
Use normal npm lifecycle behavior locally and in CI. Review install hooks,
|
|
73
90
|
including implicit native builds, when changing dependencies; record any
|
|
@@ -87,6 +104,8 @@ Pass an existing archive to test the same build CI uses:
|
|
|
87
104
|
`npm run test:package -- /absolute/path/to/package.tgz`. For a final CI candidate,
|
|
88
105
|
also pass its `release.json` as the second argument. The supplied package's
|
|
89
106
|
name, version, executable mode and manifest are checked before runtime probes.
|
|
107
|
+
The README inside the archive must match the checkout, install from npm's
|
|
108
|
+
`beta` channel and avoid unpublished or local-archive installation guidance.
|
|
90
109
|
`npm run verify` runs lint, typecheck, build and tests; `npm run release:check`
|
|
91
110
|
also previews the package contents. Neither publishes or creates a tag.
|
|
92
111
|
|
|
@@ -127,22 +146,20 @@ local. Leave its manifest, lockfile and CI configuration unchanged.
|
|
|
127
146
|
|
|
128
147
|
Rebuild resenv after source, metadata or documentation changes and restart the
|
|
129
148
|
application. An application `npm ci` can remove the link; recreate it when needed.
|
|
130
|
-
|
|
149
|
+
Authenticate with the official Doppler CLI from the application's directory:
|
|
131
150
|
|
|
132
151
|
```sh
|
|
133
|
-
|
|
152
|
+
doppler login
|
|
134
153
|
```
|
|
135
154
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
check; automated tests must use injected or mocked credentials.
|
|
155
|
+
resenv reuses that login at runtime. Automated tests use injected or mocked
|
|
156
|
+
credentials; real login is a manual integration check owned by Doppler's CLI.
|
|
139
157
|
|
|
140
158
|
## Optional integration checks
|
|
141
159
|
|
|
142
160
|
`npm run bench` measures fresh-process startup, a plain 200-variable environment,
|
|
143
161
|
and a mocked provider with eight distinct lookups. It needs a build first and
|
|
144
162
|
does not contact a provider. Timings are observations, not CI pass/fail thresholds.
|
|
145
|
-
See [the review](docs/security-performance-review.md) for the recorded comparison.
|
|
146
163
|
|
|
147
164
|
With an existing Nx installation:
|
|
148
165
|
|
|
@@ -168,7 +185,8 @@ enable that option; the CLI passes the returned copy to its child process.
|
|
|
168
185
|
|
|
169
186
|
Add regression tests for changed behavior and avoid real provider calls. Use
|
|
170
187
|
Conventional Commit subjects, for example `fix(resolver): preserve empty values`.
|
|
171
|
-
|
|
188
|
+
Review dependency changes under the [dependency policy](docs/dependencies.md)
|
|
189
|
+
and put the evidence in the PR. Update the guide when a lasting choice changes.
|
|
172
190
|
|
|
173
191
|
CODEOWNERS identifies the maintainer. Keep bug reports and pull requests focused
|
|
174
192
|
on observable behavior and include the checks run. Weekly dependency updates are
|
|
@@ -177,6 +195,8 @@ grouped, with major version updates reviewed separately; bots cannot merge them.
|
|
|
177
195
|
Keep the README focused on installation, authentication, application integration,
|
|
178
196
|
API behavior and troubleshooting. Put contributor tooling, test commands and
|
|
179
197
|
local development instructions here; detailed internals belong in `docs/`.
|
|
198
|
+
Keep docs current rather than appending session logs, dated audit reports or
|
|
199
|
+
test transcripts. Record run results in PRs and CI.
|
|
180
200
|
|
|
181
201
|
Security reports follow [SECURITY.md](SECURITY.md). Publication and repository
|
|
182
202
|
visibility changes require a separate maintainer decision; see the
|
package/README.md
CHANGED
|
@@ -20,45 +20,58 @@ Resolution happens once at startup. It does not watch the provider for changes;
|
|
|
20
20
|
restart the application to fetch updated values. No filename is passed to
|
|
21
21
|
resenv, and dotenv files are never rewritten.
|
|
22
22
|
|
|
23
|
-
Requires Node.js 20 or later. This is an experimental beta,
|
|
24
|
-
|
|
23
|
+
Requires Node.js 20 or later. This is an experimental beta, published on npm's
|
|
24
|
+
`beta` channel.
|
|
25
25
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
|
-
Install the
|
|
28
|
+
Install the beta in your application project:
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
|
-
npm install --save-exact /
|
|
31
|
+
npm install --save-exact @ciqol/resenv@beta
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
This records
|
|
35
|
-
|
|
36
|
-
published to npm, you will be able to install `@ciqol/resenv@beta` instead.
|
|
34
|
+
This records the selected beta version in your project's manifest and lockfile.
|
|
35
|
+
The `latest` tag points to the same beta; omitting `@beta` installs it too.
|
|
37
36
|
|
|
38
37
|
The CLI examples below use `npx --no-install` to run the copy installed in your
|
|
39
38
|
project. In an npm script, use `resenv` directly.
|
|
40
39
|
|
|
41
40
|
## Use with Doppler
|
|
42
41
|
|
|
42
|
+
Doppler manages application secrets. If you're new to it, start with
|
|
43
|
+
[Doppler's introduction](https://docs.doppler.com/docs/start).
|
|
44
|
+
|
|
43
45
|
### 1. Authenticate locally
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
Follow the [official Doppler CLI installation guide](https://docs.doppler.com/docs/cli#installation)
|
|
48
|
+
for your operating system, then log in from your application directory:
|
|
46
49
|
|
|
47
50
|
```sh
|
|
48
|
-
|
|
51
|
+
doppler login
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
Each developer authenticates with their own Doppler access.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
and
|
|
54
|
+
Each developer authenticates with their own Doppler access.
|
|
55
|
+
|
|
56
|
+
> [!WARNING]
|
|
57
|
+
> Authentication and credential storage depend on the Doppler CLI's implementation
|
|
58
|
+
> and may vary by platform or CLI version. resenv only reuses its existing token;
|
|
59
|
+
> it does not manage login, logout or credential storage, or access the OS
|
|
60
|
+
> credential store directly.
|
|
61
|
+
|
|
62
|
+
At startup, resenv uses `DOPPLER_TOKEN` when supplied; otherwise it reads the
|
|
63
|
+
existing Doppler CLI token for the application's working directory. It then
|
|
64
|
+
fetches only referenced secrets through the HTTPS API. Missing credentials or
|
|
65
|
+
access stop startup without opening a browser or prompting for login.
|
|
66
|
+
|
|
67
|
+
For CI and other unattended environments, supply `DOPPLER_TOKEN` through your
|
|
68
|
+
runner's secret facility; the CLI is not needed in that mode. See
|
|
69
|
+
[Doppler authentication and platform details](docs/doppler.md), including
|
|
70
|
+
migration from the earlier resenv-managed login.
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
login is not yet verified end to end. Windows needs `DOPPLER_TOKEN` because
|
|
60
|
-
native login/storage is not implemented there. See
|
|
61
|
-
[Doppler login and platform details](docs/doppler.md).
|
|
72
|
+
Need another secrets provider, operating system or CLI installation method?
|
|
73
|
+
[Request support](https://github.com/ciqol/resenv/issues/new) with your use case
|
|
74
|
+
and platform details.
|
|
62
75
|
|
|
63
76
|
### 2. Declare references in the environment your app already loads
|
|
64
77
|
|
|
@@ -94,7 +107,8 @@ npx --no-install resenv run -- node app.mjs
|
|
|
94
107
|
|
|
95
108
|
The wrapper supplies resolved values to the child process. It inherits
|
|
96
109
|
stdin/stdout/stderr, preserves exit status and forwards SIGINT, SIGTERM and
|
|
97
|
-
SIGHUP to that child.
|
|
110
|
+
SIGHUP to that child. On POSIX, the child runs in its own session so terminal
|
|
111
|
+
signals reach it only through the wrapper.
|
|
98
112
|
|
|
99
113
|
## Nx integration
|
|
100
114
|
|
package/SECURITY.md
CHANGED
|
@@ -9,22 +9,24 @@ any credentials supplied to it.
|
|
|
9
9
|
|
|
10
10
|
Doppler mode uses a fixed HTTPS API origin, disables redirects and fetches only
|
|
11
11
|
referenced secret names. It does not execute shell expressions. Credentials
|
|
12
|
-
come from an explicit
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
come from an explicit `DOPPLER_TOKEN` or the current directory's existing
|
|
13
|
+
Doppler CLI login. A fixed CLI helper captures that token privately; resenv
|
|
14
|
+
does not parse Doppler's config files. An API rejection never triggers a switch
|
|
15
|
+
to another identity. The official Doppler CLI owns browser authorization,
|
|
16
|
+
token storage, login and logout. See [Doppler details](docs/doppler.md)
|
|
17
|
+
for lookup rules, credential scope, migration and platform limits.
|
|
17
18
|
The canonical `resolve` preload and `run` command select Doppler only for
|
|
18
19
|
`doppler://` values. They do not infer shell execution from `$()` values.
|
|
19
|
-
Startup does not initiate login
|
|
20
|
-
|
|
20
|
+
Startup does not initiate login. Use `doppler login` separately for local
|
|
21
|
+
development or supply a suitably scoped token for unattended runs.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
Doppler's credential storage does not isolate credentials from other code
|
|
24
|
+
running as the same user with access to them. resenv does not read or write the
|
|
25
|
+
OS credential store directly or persist login tokens or fetched secrets.
|
|
26
|
+
It does not automatically remove an explicitly supplied `DOPPLER_TOKEN` from
|
|
27
|
+
the application environment. Tokens, captured CLI output and provider response
|
|
28
|
+
bodies are not logged. The Node preload's private worker exchanges credentials
|
|
29
|
+
and results over captured pipes, never process arguments or dotenv files.
|
|
28
30
|
|
|
29
31
|
Command mode executes trusted configuration with the current user's
|
|
30
32
|
permissions. Enable it only for configuration you have reviewed. Commands can
|
|
@@ -57,17 +59,15 @@ all changed properties; read-only properties, setters and Proxy targets are
|
|
|
57
59
|
rejected before any values are committed. Use `resolve()` to obtain a copy
|
|
58
60
|
when the input is not a writable environment record.
|
|
59
61
|
|
|
60
|
-
This experimental package has not had an independent security audit.
|
|
61
|
-
|
|
62
|
+
This experimental package has not had an independent security audit. See
|
|
63
|
+
[verification](CONTRIBUTING.md) and [Doppler platform limits](docs/doppler.md).
|
|
62
64
|
|
|
63
65
|
## Reporting a vulnerability
|
|
64
66
|
|
|
65
67
|
Do not open a public issue containing a vulnerability, credentials or sensitive
|
|
66
|
-
output.
|
|
67
|
-
|
|
68
|
-
an existing private channel.
|
|
69
|
-
maintainers must enable GitHub private vulnerability reporting and verify that
|
|
70
|
-
it is available.
|
|
68
|
+
output. [Report a vulnerability privately](https://github.com/ciqol/resenv/security/advisories/new)
|
|
69
|
+
through GitHub. If that option is unavailable, contact the maintainer through
|
|
70
|
+
an existing private channel.
|
|
71
71
|
|
|
72
72
|
Include affected versions, platform, impact and a minimal reproduction using
|
|
73
73
|
dummy data. Never include production tokens, private keys or environment dumps.
|
package/bin/resenv.cjs
CHANGED
|
@@ -6,32 +6,15 @@ var node_os = require('node:os');
|
|
|
6
6
|
var assertSafeDiagnostics = require('../assert-safe-diagnostics.cjs');
|
|
7
7
|
var preload = require('../preload.cjs');
|
|
8
8
|
var index = require('../index.cjs');
|
|
9
|
-
var auth = require('../doppler/auth.cjs');
|
|
10
|
-
var system = require('../doppler/system.cjs');
|
|
11
|
-
var references = require('../doppler/references.cjs');
|
|
12
9
|
var values = require('../values.cjs');
|
|
13
10
|
|
|
14
|
-
const usage = 'Usage: resenv run [--commands] -- <command> [arguments...]\
|
|
15
|
-
|
|
11
|
+
const usage = 'Usage: resenv run [--commands] -- <command> [arguments...]\nInfer secret providers from values already in the environment.\nManage authentication with doppler login and doppler logout, or supply DOPPLER_TOKEN.\n--doppler remains accepted for compatibility.\n--commands enables whole-value $(command) expressions using /bin/sh.\n';
|
|
12
|
+
function main() {
|
|
16
13
|
const args = process.argv.slice(2);
|
|
17
14
|
if (args.length === 1 && ['--help', '-h'].includes(args[0] || '')) {
|
|
18
15
|
process.stdout.write(usage);
|
|
19
16
|
return;
|
|
20
17
|
}
|
|
21
|
-
const explicitProvider = args[1] === 'doppler';
|
|
22
|
-
if (args[0] === 'login' && args.slice(explicitProvider ? 2 : 1).every(arg => ['--cli', '--no-browser'].includes(arg))) {
|
|
23
|
-
if (!explicitProvider && !references.references(process.env).length) {
|
|
24
|
-
process.stderr.write('Nothing to handle: no cloud secret references in the environment. Load your target environment first, or use resenv login doppler.\n');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
await auth.login({ cli: args.includes('--cli'), noBrowser: args.includes('--no-browser') });
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (args.length === 2 && args[0] === 'logout' && args[1] === 'doppler') {
|
|
31
|
-
system.createSystem().remove();
|
|
32
|
-
process.stderr.write('Removed the resenv credential. Doppler CLI credentials and DOPPLER_TOKEN are unchanged; revoke the token in Doppler to invalidate it.\n');
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
18
|
let commands = false;
|
|
36
19
|
while (args[0] === 'run' && ['--commands', '--doppler'].includes(args[1] || '')) {
|
|
37
20
|
if (args[1] === '--commands')
|
|
@@ -55,11 +38,23 @@ async function main() {
|
|
|
55
38
|
return;
|
|
56
39
|
}
|
|
57
40
|
assertSafeDiagnostics.assertSafeDiagnostics();
|
|
58
|
-
|
|
41
|
+
// Isolate the child from terminal group signals; the wrapper forwards them once.
|
|
42
|
+
const child = node_child_process.spawn(command, commandArgs, { env, stdio: 'inherit', shell: false, detached: process.platform !== 'win32' });
|
|
59
43
|
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
60
44
|
const handlers = new Map();
|
|
61
45
|
for (const signal of signals) {
|
|
62
|
-
const handler = () => {
|
|
46
|
+
const handler = () => {
|
|
47
|
+
try {
|
|
48
|
+
if (process.platform !== 'win32' && child.pid)
|
|
49
|
+
process.kill(-child.pid, signal);
|
|
50
|
+
else
|
|
51
|
+
child.kill(signal);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (!values.hasErrorCode(err, 'ESRCH'))
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
63
58
|
handlers.set(signal, handler);
|
|
64
59
|
process.on(signal, handler);
|
|
65
60
|
}
|
|
@@ -85,7 +80,10 @@ async function main() {
|
|
|
85
80
|
}
|
|
86
81
|
});
|
|
87
82
|
}
|
|
88
|
-
|
|
83
|
+
try {
|
|
84
|
+
main();
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
89
87
|
process.stderr.write(`${index.formatError(err)}\n`);
|
|
90
88
|
process.exitCode = 1;
|
|
91
|
-
}
|
|
89
|
+
}
|
package/docs/build.md
CHANGED
|
@@ -71,7 +71,7 @@ Public options use type aliases and readonly properties. `Environment` stays
|
|
|
71
71
|
mutable for optional in-place replacement. `resolve()` always returns a new
|
|
72
72
|
`Environment`, with input replacement enabled only by `{ replace: true }`. It
|
|
73
73
|
does not preserve a generic input type: resolved strings cannot retain a
|
|
74
|
-
literal reference type.
|
|
74
|
+
literal reference type.
|
|
75
75
|
ESLint uses type information and enforces the selected style rules. The compiler
|
|
76
76
|
checks all library/CLI source, colocated unit specs and both consumer formats with `strict` and
|
|
77
77
|
`noUncheckedIndexedAccess`. Provider responses and parsed worker input start as
|
|
@@ -92,9 +92,9 @@ and docs are copied into the distribution and included in the archive.
|
|
|
92
92
|
|
|
93
93
|
Ordinary local builds retain `private: true`. The CI build accepts a validated
|
|
94
94
|
release plan and sets its computed version, removing `private` before the final
|
|
95
|
-
archive is packed and tested. The development root stays private.
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
archive is packed and tested. The development root stays private. Only the
|
|
96
|
+
manual release workflow stages that tested archive for npm approval; see
|
|
97
|
+
[releasing](releasing.md).
|
|
98
98
|
|
|
99
99
|
`npm run pack:local` packs the generated directory. `npm run test:package`
|
|
100
100
|
instead packs into a temporary directory and installs it offline into a clean
|
|
@@ -122,15 +122,14 @@ package, extracts the same build
|
|
|
122
122
|
for the process tests, and never recompiles it. The matrix checks Node 20
|
|
123
123
|
and 22 on Linux plus the `.nvmrc` version on Linux and macOS.
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
Build and test jobs have read-only repository permissions and receive no application or
|
|
126
126
|
registry secrets. Missing artifacts and digest mismatches fail the run. The
|
|
127
|
-
archive is retained for seven days for maintainer review.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
run explicitly.
|
|
136
|
-
See the [maturity evaluation](maturity.md) for the static catalog results.
|
|
127
|
+
archive is retained for seven days for maintainer review. A separate release
|
|
128
|
+
job requires manual dispatch on `beta`, the protected `release` environment and
|
|
129
|
+
all candidate checks. Hosted results must be observed before treating the workflow definition
|
|
130
|
+
as a passing CI run.
|
|
131
|
+
|
|
132
|
+
Official `actions/*` and `github/*` actions use exact release tags under the
|
|
133
|
+
repository's GitHub publisher trust policy. Other remote actions require full
|
|
134
|
+
commit SHAs. Installs use normal npm lifecycle behavior, and dependency updates
|
|
135
|
+
require an install-hook review. Build commands run explicitly.
|