spfn 0.2.0-beta.52 → 0.2.0-beta.56

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 CHANGED
@@ -27,7 +27,7 @@ Requirements: Node.js 18.18+, Next.js 15+ (App Router, `src/` dir), PostgreSQL (
27
27
  npx spfn@beta create my-app
28
28
  cd my-app
29
29
  docker compose up -d # Postgres + Redis
30
- cp .env.local.example .env.local
30
+ # .env.local & .env.server are generated — put server secrets in .env.server
31
31
  pnpm spfn:dev # Next.js :3790 + SPFN API :8790
32
32
 
33
33
  # Add SPFN to an existing Next.js project
@@ -61,7 +61,9 @@ Tailwind, import alias `@/*`, no ESLint), sets up SVGR icons, then runs `init`.
61
61
 
62
62
  Adds SPFN to an existing Next.js project: copies the server templates, wires the RPC
63
63
  proxy route, Docker files, deploy + codegen config, updates `package.json` scripts/deps,
64
- and installs. See [Scaffold structure](#scaffold-structure) for what lands on disk.
64
+ and installs. With auth enabled it also adds the `/_auth/:path*` → SPFN API rewrite to
65
+ `next.config` (OAuth callbacks return to the app origin; merged manually if a `rewrites()`
66
+ already exists). See [Scaffold structure](#scaffold-structure) for what lands on disk.
65
67
 
66
68
  | Option | Description |
67
69
  |--------|-------------|
@@ -212,6 +214,45 @@ Options: `-l, --list`, `-b, --bytes <n>` (1–128), `-e, --env <name>`, `-c, --c
212
214
  The command prints the value to stdout for you to paste into an env file — it does **not**
213
215
  write any file.
214
216
 
217
+ ### `spfn secret`
218
+
219
+ Unified secret management: local secrets live in the OS keychain, deployed secrets in
220
+ encrypted SOPS files. The runtime never sees a reference — `spfn dev` injects local
221
+ values into the server process and GitOps injects them in production, so the app always
222
+ reads plain `process.env`.
223
+
224
+ | Subcommand | Description |
225
+ |------------|-------------|
226
+ | `secret set [key]` | Store a value (masked prompt). `--env local` → keychain; other envs → SOPS |
227
+ | `secret list` | List declared secrets and their status per env (never prints values) |
228
+ | `secret generate [key]` | Mint values for schema secrets with a `generate` strategy (`-a/--all`) |
229
+ | `secret rotate [key]` | Rotate values; external secrets are flagged for manual reissue (`-a/--all`) |
230
+ | `secret keygen` | Generate an age key pair for the SOPS no-cloud backend |
231
+ | `secret recipients <add\|remove\|list> [age1…]` | Manage `.sops.yaml` recipients + re-encrypt |
232
+ | `secret check` | Static lint — flag plaintext secret leaks |
233
+
234
+ Options: `-e, --env <env>` (`local` default; also `development`/`staging`/`production`),
235
+ `-p, --package <pkg>` (schema source, default `@spfn/core`).
236
+
237
+ **Local (keychain).** `spfn secret set DB_URL` stores the value in the OS keychain
238
+ (macOS `security`, Windows Credential Manager via optional `@napi-rs/keyring`, Linux
239
+ libsecret) and writes a `secret:keychain:spfn_DB_URL` reference into `.env.server`. The
240
+ reference is not sensitive; the real value never lands in the repo. `spfn dev` resolves
241
+ and injects it. Note: injection happens only when the server is started via `spfn dev` —
242
+ running the app another way (a bare `node`, tests) would see the raw reference, so use
243
+ `spfn dev` locally (a runtime resolver for other runners is planned).
244
+
245
+ **Deployed (SOPS).** `spfn secret set DB_URL --env production` writes the value into
246
+ `secrets/production.enc.json`, encrypted by SOPS. The backend (age / GCP KMS / AWS KMS)
247
+ is chosen by `.sops.yaml` creation rules — KMS needs no local key file (IAM + cloud
248
+ auth), age is the no-cloud fallback (`secret keygen` + `secret recipients add`). Commit
249
+ the encrypted file; your GitOps step decrypts it into env at deploy time. `sops`/`age`
250
+ are needed only for the deployed envs, never for local keychain use.
251
+
252
+ Schema-driven: a secret declared with `envSecret({ generate: 'base64url32' })` can be
253
+ minted/rotated automatically (`secret generate`/`rotate`); one without `generate` is an
254
+ external value you paste in (`secret set`).
255
+
215
256
  ### `spfn setup icons`
216
257
 
217
258
  Install and configure SVGR for SVG-as-component imports (Next.js only).
@@ -241,9 +282,10 @@ spfn.config.js # deployment config (subdomain/region/domai
241
282
  docker-compose.yml # Postgres + Redis (dev)
242
283
  docker-compose.production.yml
243
284
  Dockerfile, .dockerignore
244
- .env.example # committed, shared non-secret defaults
245
- .env.local.example # gitignored target template (Next.js local overrides)
246
- .env.server.example # copy to .env.server (gitignored, server secrets)
285
+ next.config.ts # patched when auth is enabled: /_auth/:path* rewrite → SPFN API
286
+ .env.example # committed reference every key, placeholder values
287
+ .env.local # generated, gitignored (Next.js-facing URLs)
288
+ .env.server # generated, gitignored (server secrets: DB, cache)
247
289
  ```
248
290
 
249
291
  `init` also patches `package.json` (scripts: `spfn:dev`, `spfn:server`, `spfn:next`,
@@ -328,8 +370,8 @@ type ships from `spfn` (`@type {import('spfn').SpfnConfig}`).
328
370
 
329
371
  - **`.env.server` is gitignored and server-only.** Put DB/secret values there, not in
330
372
  `.env` (committed) and not in `.env.local` (that's Next.js's local file). There is no
331
- `.env.server.local`. Copy `.env.server.example` `.env.server`. Load order is the
332
- standard dotenv chain ending with `.env.server`.
373
+ `.env.server.local`. `spfn init` generates `.env.server`; put DB/secret values there.
374
+ Load order is the standard dotenv chain ending with `.env.server`.
333
375
  - **Never commit secrets in `spfn.config.js`.** It's checked into Git; its `env` block is
334
376
  for non-sensitive values only. Use CI/CD secret management for credentials.
335
377
  - **`spfn dev` does not hot-reload by default.** Add `--watch` to restart on `src/server`
package/bin/spfn.js CHANGED
@@ -28,13 +28,20 @@ else
28
28
 
29
29
  async function tryRelaunchWithTsx()
30
30
  {
31
- // Verify tsx is resolvable
32
- await import('tsx/esm/api');
31
+ // Resolve tsx to an absolute URL from THIS package's location. A bare
32
+ // '--import tsx' is resolved by Node against the child's CWD, so running
33
+ // outside a project (no node_modules/tsx up the tree) crashes the child
34
+ // with ERR_MODULE_NOT_FOUND even though the guard import above succeeds
35
+ // (it resolves package-relative). Resolving here also doubles as the
36
+ // availability check — a throw falls back to running without tsx.
37
+ const { createRequire } = await import('module');
38
+ const { pathToFileURL } = await import('url');
39
+ const tsxUrl = pathToFileURL(createRequire(import.meta.url).resolve(TSX_MODULE)).href;
33
40
 
34
41
  const { spawn } = await import('child_process');
35
42
  const child = spawn(
36
43
  process.execPath,
37
- [TSX_FLAG, TSX_MODULE, ...process.execArgv, process.argv[1], ...process.argv.slice(2)],
44
+ [TSX_FLAG, tsxUrl, ...process.execArgv, process.argv[1], ...process.argv.slice(2)],
38
45
  { stdio: 'inherit' },
39
46
  );
40
47