securenow 7.7.0 → 7.7.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/NPM_README.md CHANGED
@@ -415,8 +415,9 @@ Config files are stored in `~/.securenow/` (global) or `.securenow/` in the proj
415
415
  | `~/.securenow/config.json` | API URL, default app, output format |
416
416
  | `~/.securenow/credentials.json` | Auth token, app, API key, config - global (use `login --global`) |
417
417
  | `.securenow/credentials.json` | Auth token, app, API key, config, explanations - project-local default |
418
+ | `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
418
419
 
419
- **Resolution order:** project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Legacy CLI token overrides still work for existing automation.
420
+ **Resolution order:** project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Legacy CLI token overrides still work for existing automation.
420
421
 
421
422
  ### Global Flags
422
423
 
@@ -464,6 +465,10 @@ npx securenow credentials runtime --env production
464
465
 
465
466
  # Store .securenow/credentials.production.json as a deployment secret file,
466
467
  # then materialize it as .securenow/credentials.json in the running app.
468
+ # Since v7.7.2, mounting it as .securenow/credentials.production.json
469
+ # also works when the canonical credentials.json file is absent.
470
+ # The SDK checks named files in a fixed order and does not use env vars
471
+ # to pick the credentials filename.
467
472
 
468
473
  # Use --json for machine-readable output
469
474
  npx securenow logs --json --level error | jq '.logs'
@@ -1073,7 +1078,7 @@ npx securenow api-key set snk_live_abc123...
1073
1078
  npx securenow credentials runtime --env production
1074
1079
  ```
1075
1080
 
1076
- The SDK resolves the firewall key from project `./.securenow/credentials.json`, then global `~/.securenow/credentials.json`. Legacy `SECURENOW_API_KEY` overrides still work for existing deployments.
1081
+ The SDK resolves the firewall key from project `./.securenow/credentials.json`, then project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order, then global `~/.securenow/credentials.json`, then global named runtime credentials in the same fixed order. Legacy `SECURENOW_API_KEY` overrides still work for existing deployments.
1077
1082
 
1078
1083
  On startup, you'll see:
1079
1084
 
package/README.md CHANGED
@@ -152,11 +152,15 @@ npx securenow credentials runtime --env production
152
152
 
153
153
  It writes `.securenow/credentials.production.json`, with the same `app`, `apiKey`, `config`, and `_securenow.explanations` shape, but without the CLI OAuth `token`, `email`, or `expiresAt`. Store that JSON in your deployment secret manager and materialize it as `.securenow/credentials.json` at runtime.
154
154
 
155
+ Starting in v7.7.2, the SDK also accepts generated runtime filenames directly without reading environment variables to choose the file. If `.securenow/credentials.json` is missing, it checks named files in a deterministic order: staging, production, preview, local, test, development, dev, then prod.
156
+
155
157
  Resolution order:
156
158
 
157
159
  1. Project-local `.securenow/credentials.json`
158
- 2. Global `~/.securenow/credentials.json`
159
- 3. `package.json#name` (label only)
160
+ 2. Project-local named runtime credentials: `.securenow/credentials.staging.json`, then `.securenow/credentials.production.json`, then preview/local/test/development/dev/prod variants
161
+ 3. Global `~/.securenow/credentials.json`
162
+ 4. Global named runtime credentials in the same fixed order
163
+ 5. `package.json#name` (label only)
160
164
 
161
165
  Legacy environment variables are fallback-only for existing installs. New local, CI, Docker, and production setups should use the credentials file.
162
166
 
@@ -395,11 +399,12 @@ After install, the `securenow` CLI is available via `npx securenow` or globally
395
399
  | File | Purpose |
396
400
  |---|---|
397
401
  | `./.securenow/credentials.json` | Project-local or production runtime credentials |
398
- | `./.securenow/credentials.production.json` | Tokenless production file generated by `securenow credentials runtime --env production` |
402
+ | `./.securenow/credentials.<environment>.json` | Tokenless runtime file generated by `securenow credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
399
403
  | `~/.securenow/credentials.json` | Global (with `login --global`) |
404
+ | `~/.securenow/credentials.<environment>.json` | Global environment-specific runtime credentials |
400
405
  | `~/.securenow/config.json` | API URL, default app, preferences |
401
406
 
402
- Resolution order: project `.securenow/` -> global `~/.securenow/` -> package name fallback. Legacy env vars are fallback-only for older installs.
407
+ Resolution order: project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order -> package name fallback. Legacy env vars are fallback-only for older installs and do not choose the credentials filename.
403
408
 
404
409
  Override the dashboard API with `securenow config set apiUrl <url>`.
405
410
 
package/SKILL-API.md CHANGED
@@ -273,7 +273,7 @@ Instruments document load, fetch, XMLHttpRequest, and user interactions with bro
273
273
 
274
274
  ## Firewall — Multi-Layer IP Blocking
275
275
 
276
- The firewall auto-activates once an API key is resolvable and the app firewall toggle is on. Since **v7.5.1**, `npx securenow login` enables the selected app firewall by default and writes the scoped key to `.securenow/credentials.json`; `securenow api-key set` can still write/rotate the key later. Production should use the tokenless file generated by `securenow credentials runtime --env production`. Resolution order: project `./.securenow/credentials.json` -> global `~/.securenow/credentials.json`; legacy env vars are fallback-only for existing deployments.
276
+ The firewall auto-activates once an API key is resolvable and the app firewall toggle is on. Since **v7.5.1**, `npx securenow login` enables the selected app firewall by default and writes the scoped key to `.securenow/credentials.json`; `securenow api-key set` can still write/rotate the key later. Production should use the tokenless file generated by `securenow credentials runtime --env production`. Resolution order: project `./.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order; legacy env vars are fallback-only for existing deployments and do not choose the credentials filename.
277
277
 
278
278
  ```
279
279
  Layer 4: Cloud/Edge WAF → blocked at CDN (Cloudflare, AWS WAF, GCP Cloud Armor)
@@ -457,7 +457,7 @@ securenow redact @request.json --fields internal_id,sessionHash
457
457
 
458
458
  ## Credentials Configuration
459
459
 
460
- Local development and production use `.securenow/credentials.json`. Every setting below lives under `app` or `config`; `npx securenow credentials runtime --env production` creates a tokenless production file with the same structure. Environment variables are legacy fallbacks only.
460
+ Local development and production use `.securenow/credentials.json`. Every setting below lives under `app` or `config`; `npx securenow credentials runtime --env production` creates a tokenless production file with the same structure. Since v7.7.2, the SDK also accepts named runtime files such as `.securenow/credentials.production.json` when the canonical `credentials.json` file is absent. Filename lookup is deterministic and does not read environment variables. Environment variables are legacy fallbacks only.
461
461
 
462
462
  ### App Identity
463
463
 
package/SKILL-CLI.md CHANGED
@@ -39,11 +39,11 @@ securenow whoami # verify session (shows email, app, auth source)
39
39
 
40
40
  **Default-on security (v7.5.1+):** after picking or creating the app, `securenow login` turns on that app's firewall toggle, mints an API key with `firewall:read + blocklist:read + allowlist:read` scopes, and writes it into `.securenow/credentials.json`. Traces, logs, POST body capture, multipart metadata capture, and the firewall are enabled by default. No `SECURENOW_API_KEY` env var is needed. To add or rotate a key later without re-running login, use `securenow api-key set snk_live_...` (see [API Key Management](#api-key-management) below).
41
41
 
42
- Credentials resolve in order: project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Legacy env vars are fallback-only for existing deployments.
42
+ Credentials resolve in order: project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Legacy env vars are fallback-only for existing deployments and do not choose the credentials filename.
43
43
 
44
44
  The **firewall API key** should live in the same credentials file as `apiKey`. Legacy `SECURENOW_API_KEY` overrides are honored only when they start with `snk_live_`.
45
45
 
46
- For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`.
46
+ For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`. Since v7.7.2, mounting the generated `.securenow/credentials.production.json` filename directly also works when `credentials.json` is absent.
47
47
 
48
48
  **Environment model:** use one SecureNow app key for local, preview, staging, and production. The credentials field `config.runtime.deploymentEnvironment` separates traces/logs/firewall/forensics by environment. CLI security commands default to `production`; pass `--env local`, `--env staging`, or `--env all` only when that scope is intentional.
49
49
 
@@ -81,10 +81,11 @@ Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-proje
81
81
  | `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
82
82
  | `~/.securenow/credentials.json` | `token`, `email`, `expiresAt`, `apiKey`, `app`, `config` (global, use `login --global`) |
83
83
  | `.securenow/credentials.json` | `token`, `email`, `expiresAt`, `apiKey`, `app`, `config`, `_securenow.explanations` (project-local default) |
84
-
85
- **Credential resolution order:** `.securenow/credentials.json` (project) -> `~/.securenow/credentials.json` (global). Legacy env vars are fallback-only for existing deployments.
86
-
87
- **Firewall API key resolution (v7.5.1+):** project `.securenow/credentials.json` -> global `~/.securenow/credentials.json`. Use `securenow login` for default setup or `securenow api-key set` to rotate a key without touching env vars.
84
+ | `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `securenow credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
85
+
86
+ **Credential resolution order:** `.securenow/credentials.json` (project) -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> `~/.securenow/credentials.json` (global) -> global named runtime credentials in the same fixed order. Legacy env vars are fallback-only for existing deployments.
87
+
88
+ **Firewall API key resolution (v7.5.1+):** project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `~/.securenow/credentials.json` -> global named runtime credentials in the same fixed order. Use `securenow login` for default setup or `securenow api-key set` to rotate a key without touching env vars.
88
89
 
89
90
  ```bash
90
91
  securenow config set apiUrl https://api.securenow.ai
package/app-config.js CHANGED
@@ -4,6 +4,10 @@
4
4
  * Shared SecureNow configuration resolver.
5
5
  *
6
6
  * Local development and production are driven by ./.securenow/credentials.json.
7
+ * Named runtime files such as ./.securenow/credentials.staging.json and
8
+ * ./.securenow/credentials.production.json are also accepted when the
9
+ * canonical file is not present. Filename selection is deterministic and does
10
+ * not read environment variables.
7
11
  * Legacy environment variables are only fallback inputs for existing installs;
8
12
  * every SDK setting has a file-backed equivalent so customers do not need .env
9
13
  * files.
@@ -16,6 +20,16 @@ const os = require('os');
16
20
  const FREE_TRIAL_INSTANCE = 'https://freetrial.securenow.ai:4318';
17
21
  const DEFAULT_API_URL = 'https://api.securenow.ai';
18
22
  const CONFIG_SCHEMA_VERSION = 2;
23
+ const CREDENTIAL_FILE_ENVIRONMENTS = Object.freeze([
24
+ 'staging',
25
+ 'production',
26
+ 'preview',
27
+ 'local',
28
+ 'test',
29
+ 'development',
30
+ 'dev',
31
+ 'prod',
32
+ ]);
19
33
 
20
34
  const DEFAULT_CONFIG = Object.freeze({
21
35
  logging: {
@@ -157,7 +171,9 @@ function clone(value) {
157
171
 
158
172
  function readJsonSafe(filepath) {
159
173
  try {
160
- return JSON.parse(fs.readFileSync(filepath, 'utf8'));
174
+ let content = fs.readFileSync(filepath, 'utf8');
175
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
176
+ return JSON.parse(content);
161
177
  } catch {
162
178
  return null;
163
179
  }
@@ -191,6 +207,30 @@ function findUpFile(startDir, relativePath) {
191
207
  }
192
208
  }
193
209
 
210
+ function findUpFirstFile(startDir, relativePaths) {
211
+ if (!startDir) return null;
212
+ const candidates = Array.isArray(relativePaths) ? relativePaths.filter(Boolean) : [relativePaths].filter(Boolean);
213
+ if (!candidates.length) return null;
214
+
215
+ let dir;
216
+ try {
217
+ dir = path.resolve(startDir);
218
+ } catch {
219
+ return null;
220
+ }
221
+
222
+ while (true) {
223
+ for (const relativePath of candidates) {
224
+ const found = fileIfReadable(path.join(dir, relativePath));
225
+ if (found) return found;
226
+ }
227
+
228
+ const parent = path.dirname(dir);
229
+ if (!parent || parent === dir) return null;
230
+ dir = parent;
231
+ }
232
+ }
233
+
194
234
  function uniq(values) {
195
235
  const seen = new Set();
196
236
  const out = [];
@@ -202,6 +242,15 @@ function uniq(values) {
202
242
  return out;
203
243
  }
204
244
 
245
+ function credentialRelativePaths() {
246
+ return uniq([
247
+ path.join('.securenow', 'credentials.json'),
248
+ ...CREDENTIAL_FILE_ENVIRONMENTS.map((envName) =>
249
+ path.join('.securenow', `credentials.${envName}.json`)
250
+ ),
251
+ ]);
252
+ }
253
+
205
254
  function resolveLocalCredentialsFile() {
206
255
  const starts = [];
207
256
  try {
@@ -212,8 +261,25 @@ function resolveLocalCredentialsFile() {
212
261
  if (process.argv && process.argv[1]) starts.push(path.dirname(process.argv[1]));
213
262
  if (require.main && require.main.filename) starts.push(path.dirname(require.main.filename));
214
263
 
264
+ const candidates = credentialRelativePaths();
215
265
  for (const start of uniq(starts)) {
216
- const found = findUpFile(start, path.join('.securenow', 'credentials.json'));
266
+ const found = findUpFirstFile(start, candidates);
267
+ if (found) return found;
268
+ }
269
+
270
+ return null;
271
+ }
272
+
273
+ function resolveGlobalCredentialsFile() {
274
+ let home;
275
+ try {
276
+ home = os.homedir();
277
+ } catch {
278
+ return null;
279
+ }
280
+
281
+ for (const relativePath of credentialRelativePaths()) {
282
+ const found = fileIfReadable(path.join(home, relativePath));
217
283
  if (found) return found;
218
284
  }
219
285
 
@@ -248,7 +314,7 @@ function loadLocalCredentials() {
248
314
 
249
315
  function loadGlobalCredentials() {
250
316
  try {
251
- return withCredentialDefaults(readJsonSafe(path.join(os.homedir(), '.securenow', 'credentials.json')));
317
+ return withCredentialDefaults(readJsonSafe(resolveGlobalCredentialsFile()));
252
318
  } catch {
253
319
  return null;
254
320
  }
@@ -642,6 +708,7 @@ module.exports = {
642
708
  FREE_TRIAL_INSTANCE,
643
709
  DEFAULT_API_URL,
644
710
  CONFIG_SCHEMA_VERSION,
711
+ CREDENTIAL_FILE_ENVIRONMENTS,
645
712
  DEFAULT_CONFIG,
646
713
  CONFIG_EXPLANATIONS,
647
714
  ENV_TO_CONFIG_PATH,
@@ -668,6 +735,9 @@ module.exports = {
668
735
  listEnv,
669
736
  parseHeaders,
670
737
  headersToString,
738
+ credentialRelativePaths,
739
+ resolveLocalCredentialsFile,
740
+ resolveGlobalCredentialsFile,
671
741
  loadCredentials,
672
742
  loadLocalCredentials,
673
743
  loadGlobalCredentials,
package/cli/config.js CHANGED
@@ -28,7 +28,9 @@ function ensureDir(dir) {
28
28
 
29
29
  function loadJSON(filepath) {
30
30
  try {
31
- return JSON.parse(fs.readFileSync(filepath, 'utf8'));
31
+ let content = fs.readFileSync(filepath, 'utf8');
32
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
33
+ return JSON.parse(content);
32
34
  } catch {
33
35
  return {};
34
36
  }
@@ -50,17 +52,16 @@ function credentialsFileForLocal(local) {
50
52
  }
51
53
 
52
54
  function hasLocalCredentials() {
53
- return fs.existsSync(LOCAL_CREDENTIALS_FILE);
55
+ return !!appConfig.resolveLocalCredentialsFile();
54
56
  }
55
57
 
56
58
  function resolveCredentialsFile() {
57
- if (fs.existsSync(LOCAL_CREDENTIALS_FILE)) return LOCAL_CREDENTIALS_FILE;
58
- return CREDENTIALS_FILE;
59
+ return appConfig.resolveLocalCredentialsFile() || appConfig.resolveGlobalCredentialsFile() || CREDENTIALS_FILE;
59
60
  }
60
61
 
61
62
  function getAuthSource() {
62
63
  if (process.env.SECURENOW_TOKEN) return 'env (SECURENOW_TOKEN)';
63
- if (fs.existsSync(LOCAL_CREDENTIALS_FILE)) return 'project (.securenow/)';
64
+ if (appConfig.resolveLocalCredentialsFile()) return 'project (.securenow/)';
64
65
  return 'global (~/.securenow/)';
65
66
  }
66
67
 
@@ -91,9 +92,8 @@ function setConfigValue(key, value) {
91
92
  }
92
93
 
93
94
  function loadCredentials() {
94
- const credentials = fs.existsSync(LOCAL_CREDENTIALS_FILE)
95
- ? loadJSON(LOCAL_CREDENTIALS_FILE)
96
- : loadJSON(CREDENTIALS_FILE);
95
+ const credentialsFile = resolveCredentialsFile();
96
+ const credentials = loadJSON(credentialsFile);
97
97
  return appConfig.withCredentialDefaults(credentials) || {};
98
98
  }
99
99
 
@@ -40,7 +40,7 @@ function buildRuntimeCredentials(options = {}) {
40
40
  },
41
41
  _securenow: {
42
42
  ...(creds._securenow || {}),
43
- note: 'Runtime SecureNow credentials and SDK defaults. Mount or copy this JSON as .securenow/credentials.json in production. Do not commit it.',
43
+ note: 'Runtime SecureNow credentials and SDK defaults. Mount or copy this JSON as .securenow/credentials.json or .securenow/credentials.<environment>.json in production. Do not commit it.',
44
44
  runtimeOnly: 'This file intentionally omits CLI OAuth fields: token, email, and expiresAt.',
45
45
  production: 'Production can use this same file shape instead of environment variables.',
46
46
  },
@@ -83,6 +83,8 @@ async function runtime(_args, flags) {
83
83
 
84
84
  ui.success(`Wrote runtime credentials to ${output}`);
85
85
  ui.info('Deploy this JSON as .securenow/credentials.json on the server/container.');
86
+ ui.info('SDK v7.7.2+ can also read this generated filename directly when credentials.json is absent.');
87
+ ui.info('Credential filename lookup is fixed-order and does not depend on NODE_ENV.');
86
88
  ui.info(`Environment: ${envName}`);
87
89
  ui.info(`App: ${creds.app?.name || '(unnamed)'} ${creds.app?.key ? `(${creds.app.key})` : ''}`);
88
90
  ui.info(`Firewall key: ${creds.apiKey ? maskSecret(creds.apiKey) : '(missing)'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "7.7.0",
3
+ "version": "7.7.2",
4
4
  "description": "OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",