spfn 0.3.0-beta.12 → 0.3.0-beta.13
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 +100 -8
- package/dist/index.js +1346 -518
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -396,9 +396,29 @@ TLS by itself and `sslmode=disable` remains authoritative.
|
|
|
396
396
|
|
|
397
397
|
### `spfn env`
|
|
398
398
|
|
|
399
|
-
Schema-driven environment variable tooling
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
Schema-driven environment variable tooling. Routes vars to the right file: `NEXT_PUBLIC_*`
|
|
400
|
+
→ `.env`/`.env.local`, server vars → `.env.server`.
|
|
401
|
+
|
|
402
|
+
Without `-p`, every subcommand reads the **whole-app list**: the app's own schema modules
|
|
403
|
+
named in `spfn.config.js` under `env.schemas`, then every installed `@spfn/*` package whose
|
|
404
|
+
`./config` exports `envSchema` (found through the app's `package.json`, resolved from the
|
|
405
|
+
project root). Output is grouped by the source that declares each variable.
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
// spfn.config.js
|
|
409
|
+
export default {
|
|
410
|
+
env: {
|
|
411
|
+
// Modules exporting `envSchema`, relative to the project root.
|
|
412
|
+
// TypeScript and tsconfig path aliases load the way `spfn dev` loads them (tsx).
|
|
413
|
+
schemas: ['src/server/config/env.config.ts'],
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
A variable declared by two sources alike is listed once. Two sources that declare it with a
|
|
419
|
+
different type, `required` or `sensitive` are an error naming the key and both sources. A schema path that does not exist, or a
|
|
420
|
+
module without an `envSchema` export, is an error naming the path. Top-level `env` is not
|
|
421
|
+
`deployment.env`: the latter holds values to inject, the former says where the schemas are.
|
|
402
422
|
|
|
403
423
|
| Subcommand | Description |
|
|
404
424
|
|------------|-------------|
|
|
@@ -408,8 +428,41 @@ server vars → `.env.server`.
|
|
|
408
428
|
| `env init` | Generate `.env` template files (`-e <env>` for per-env, `-f` to overwrite) |
|
|
409
429
|
| `env check` | Check `.env` files against the schema (`-e <env>` for a full env chain) |
|
|
410
430
|
| `env validate` | Validate `process.env` against the schema — for CI/CD (`-e <env>`, `-s` strict) |
|
|
431
|
+
| `env audit` | Find direct `process.env` reads and app variables nothing reads; exits 1 on findings |
|
|
432
|
+
|
|
433
|
+
All but `env audit` accept `-p, --package <pkg>` to read that one package only (`env validate` uses
|
|
434
|
+
`-p, --packages <pkgs...>`).
|
|
435
|
+
|
|
436
|
+
**Audit.** `spfn env audit` prints `file:line NAME reason` — names only — and exits 1 when
|
|
437
|
+
it finds any of:
|
|
438
|
+
|
|
439
|
+
- a direct read — `process.env.X`, `process.env['X']`, `process.env[expr]`,
|
|
440
|
+
`const { X } = process.env` — outside the `env.schemas` modules, root-level
|
|
441
|
+
`*.config.{js,cjs,mjs,ts}` files and `env.audit.ignore` (`NODE_ENV` is always allowed);
|
|
442
|
+
- a name in the app's own schemas that no scanned file reads as a property (`env.X`), a
|
|
443
|
+
destructured key or a string literal, and that has no `readBy` (see the `@spfn/core/env`
|
|
444
|
+
README); a `readBy` file that does not exist or is not scanned. Skipped, with a notice,
|
|
445
|
+
when `env.schemas` is not set.
|
|
446
|
+
|
|
447
|
+
A list that cannot be built — a missing schema module, or a name declared differently by an
|
|
448
|
+
app schema and a package — is reported and exits 1.
|
|
449
|
+
|
|
450
|
+
```js
|
|
451
|
+
// spfn.config.js
|
|
452
|
+
export default {
|
|
453
|
+
env: {
|
|
454
|
+
schemas: ['src/server/config/env.config.ts'],
|
|
455
|
+
audit: {
|
|
456
|
+
include: ['src'], // default — directories or globs from the project root
|
|
457
|
+
ignore: ['src/**/*.test.ts'],
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
```
|
|
411
462
|
|
|
412
|
-
|
|
463
|
+
`node_modules`, build output, `*.d.ts` and symbolic links are never scanned. Sources —
|
|
464
|
+
`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.mts`, `.cts` — are parsed with TypeScript,
|
|
465
|
+
taken from the app, else from the CLI's optional peer, else from `@spfn/core`.
|
|
413
466
|
|
|
414
467
|
### `spfn key [preset]`
|
|
415
468
|
|
|
@@ -436,16 +489,55 @@ reads plain `process.env`.
|
|
|
436
489
|
|
|
437
490
|
| Subcommand | Description |
|
|
438
491
|
|------------|-------------|
|
|
439
|
-
| `secret set [key]` | Store a value (masked prompt). `--env local` → keychain; other envs → SOPS |
|
|
440
|
-
| `secret list` | List declared secrets and their status per env (never prints values) |
|
|
492
|
+
| `secret set [key]` | Store a value (masked prompt, or `--stdin`). `--env local` → keychain; other envs → SOPS |
|
|
493
|
+
| `secret list` | List declared secrets and their status per env; names in a deployed file that the list lacks show as "not in list" (never prints values) |
|
|
441
494
|
| `secret generate [key]` | Mint values for schema secrets with a `generate` strategy (`-a/--all`) |
|
|
442
495
|
| `secret rotate [key]` | Rotate values; external secrets are flagged for manual reissue (`-a/--all`) |
|
|
443
496
|
| `secret keygen` | Generate an age key pair for the SOPS no-cloud backend |
|
|
444
497
|
| `secret recipients <add\|remove\|list> [age1…]` | Manage `.sops.yaml` recipients + re-encrypt |
|
|
445
|
-
| `secret check` | Static lint — flag plaintext secret leaks |
|
|
498
|
+
| `secret check` | Static lint — flag plaintext secret leaks; with a deployed `--env`, names in the wrong layer's file |
|
|
499
|
+
| `secret export` | Decrypt and merge one deployment's layers into a 0600 dotenv file (prints names only) |
|
|
446
500
|
|
|
447
501
|
Options: `-e, --env <env>` (`local` default; also `development`/`staging`/`production`),
|
|
448
|
-
`-
|
|
502
|
+
`-i, --instance <name>` (one instance of a deployed environment: `secrets/<env>.<instance>.enc.json`;
|
|
503
|
+
lowercase letters, digits and dashes), `-p, --package <pkg>` (read that one package's
|
|
504
|
+
schema; without it, the whole-app list described under `spfn env`).
|
|
505
|
+
|
|
506
|
+
**`--stdin`.** `spfn secret set --stdin --env staging SIGNING_KEY < key.pem` stores the
|
|
507
|
+
whole of stdin as the value — one trailing newline dropped, inner newlines kept — for
|
|
508
|
+
local and deployed targets alike. It never prompts, and refuses a terminal on stdin.
|
|
509
|
+
Shaped values such as key pairs come from the app's own command, piped in.
|
|
510
|
+
|
|
511
|
+
**Layers.** A schema entry's `layer` (`'environment'` default, or `'instance'`) says where a
|
|
512
|
+
deployment's value comes from. Without `--instance`, `set`/`generate`/`rotate`/`list` on a
|
|
513
|
+
deployed environment work on the `environment` names in `secrets/<env>.enc.json`; with
|
|
514
|
+
`--instance blue`, on the `instance` names in `secrets/<env>.blue.enc.json`. A named key of
|
|
515
|
+
the other layer is refused.
|
|
516
|
+
|
|
517
|
+
**Export.**
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
spfn secret export --env <env> [--instance <name>] [--with <file>]... --out <file>
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Decrypts `secrets/<env>.enc.json` and — with `--instance`, when it exists —
|
|
524
|
+
`secrets/<env>.<instance>.enc.json`, reads each plaintext `--with` dotenv file (computed
|
|
525
|
+
instance values such as port and URLs), and writes the merge to `--out`. Only names in the
|
|
526
|
+
whole-app list are written; an unlisted name in an encrypted file is a warning by name.
|
|
527
|
+
The export fails, writing nothing, when a required name is in no layer, a name is in two
|
|
528
|
+
layers, an `instance` name is in the environment file, an `environment` name is in an
|
|
529
|
+
instance layer, a value fails its validator (the error names the variable and the
|
|
530
|
+
reason, never the value), or decryption fails. `--out` is required and its directory must
|
|
531
|
+
exist; relative paths resolve from the working directory. The file is created 0600 beside
|
|
532
|
+
the destination and renamed over it, so a failure leaves the previous file untouched
|
|
533
|
+
(POSIX only — Windows does not enforce the mode).
|
|
534
|
+
|
|
535
|
+
**SOPS rules for instance files.** The rule `spfn secret recipients add` writes,
|
|
536
|
+
`secrets/.*\.enc\.json$`, already covers `secrets/<env>.<instance>.enc.json`. A project
|
|
537
|
+
with per-environment rules needs each to accept the instance form too, e.g.
|
|
538
|
+
`path_regex: secrets/production(\.[a-z0-9-]+)?\.enc\.json$`. Writes need sops 3.10 or
|
|
539
|
+
later (`sops encrypt` from stdin, `sops set --value-stdin`), so a value never appears as a
|
|
540
|
+
process argument.
|
|
449
541
|
|
|
450
542
|
**Local (keychain).** `spfn secret set DB_URL` stores the value in the OS keychain
|
|
451
543
|
(macOS `security`, Windows Credential Manager via optional `@napi-rs/keyring`, Linux
|