securenow 7.7.16 → 8.0.0

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/app-config.js CHANGED
@@ -3,13 +3,14 @@
3
3
  /**
4
4
  * Shared SecureNow configuration resolver.
5
5
  *
6
- * Local development and production are driven by ./.securenow/credentials.json.
6
+ * Local development is driven by ./.securenow/runtime.json.
7
+ * Legacy combined ./.securenow/credentials.json files remain readable.
7
8
  * Named runtime files such as ./.securenow/credentials.staging.json and
8
9
  * ./.securenow/credentials.production.json are also accepted when the
9
10
  * canonical file is not present. Filename selection is deterministic and does
10
11
  * not read environment variables.
11
- * Legacy environment fallbacks are disabled by default; every SDK setting has
12
- * a file-backed equivalent so customers do not need .env files.
12
+ * Every SDK setting has a file-backed equivalent so customers do not need
13
+ * .env files.
13
14
  */
14
15
 
15
16
  const fs = require('fs');
@@ -20,7 +21,6 @@ const FREE_TRIAL_INSTANCE = 'https://ingest.securenow.ai';
20
21
  const DEFAULT_API_URL = 'https://api.securenow.ai';
21
22
  const DEFAULT_FIREWALL_API_URL = FREE_TRIAL_INSTANCE;
22
23
  const LEGACY_SECURENOW_GATEWAY = 'https://api.securenow.ai/api/otlp';
23
- const LEGACY_ENV_FALLBACK_FLAG = 'SECURENOW_ENABLE_LEGACY_ENV';
24
24
  const CONFIG_SCHEMA_VERSION = 2;
25
25
  const CREDENTIAL_FILE_ENVIRONMENTS = Object.freeze([
26
26
  'staging',
@@ -32,6 +32,8 @@ const CREDENTIAL_FILE_ENVIRONMENTS = Object.freeze([
32
32
  'dev',
33
33
  'prod',
34
34
  ]);
35
+ const RUNTIME_CREDENTIALS_FILENAME = 'runtime.json';
36
+ const ADMIN_CREDENTIALS_FILENAME = 'admin.json';
35
37
 
36
38
  const DEFAULT_CONFIG = Object.freeze({
37
39
  logging: {
@@ -90,8 +92,8 @@ const DEFAULT_CONFIG = Object.freeze({
90
92
  });
91
93
 
92
94
  const CONFIG_EXPLANATIONS = Object.freeze({
93
- 'token': 'CLI session token written by `npx securenow login`. Secret: do not commit.',
94
- 'apiKey': 'Scoped firewall API key (`snk_live_...`) minted by login or `securenow api-key set`. Secret: do not commit.',
95
+ 'token': 'Legacy CLI session token. New admin/control-plane auth is written to admin.json. Secret: do not commit.',
96
+ 'apiKey': 'Scoped runtime API key (`snk_live_...`) minted by `securenow app connect` or `securenow api-key set`. It authenticates telemetry ingestion and firewall sync. Secret: do not commit.',
95
97
  'app.key': 'SecureNow application routing UUID. The SDK uses this as OTel service.name so dashboard queries match exactly.',
96
98
  'app.name': 'Human-readable app label shown in CLI output.',
97
99
  'app.routing': 'Telemetry uses the default SecureNow ingestion gateway and routes by app.key; runtime credentials do not expose per-instance collector URLs.',
@@ -103,7 +105,7 @@ const CONFIG_EXPLANATIONS = Object.freeze({
103
105
  'config.otel.endpoint': 'Optional OTLP base endpoint override for advanced/self-hosted collectors. Leave null for the SecureNow ingestion gateway.',
104
106
  'config.otel.tracesEndpoint': 'Optional full traces endpoint override, for split collectors.',
105
107
  'config.otel.logsEndpoint': 'Optional full logs endpoint override, for split collectors.',
106
- 'config.otel.headers': 'Optional OTLP headers. The SDK auto-adds x-api-key from app.key when missing.',
108
+ 'config.otel.headers': 'Optional OTLP headers. The SDK auto-adds x-securenow-app-key from app.key and Authorization from apiKey when missing.',
107
109
  'config.otel.logLevel': 'OpenTelemetry diagnostic log level: error, warn, info, debug, or none.',
108
110
  'config.otel.disableInstrumentations': 'Optional OTel instrumentation package names to disable.',
109
111
  'config.runtime.deploymentEnvironment': 'deployment.environment resource attribute. Set this in the credentials file for production.',
@@ -111,7 +113,7 @@ const CONFIG_EXPLANATIONS = Object.freeze({
111
113
  'config.runtime.strict': 'If true, PM2/cluster workers exit when no app identity is resolvable.',
112
114
  'config.runtime.testSpan': 'If true, emit a startup smoke span. Prefer `npx securenow test-span` for manual checks.',
113
115
  'config.runtime.hideBanner': 'Hide the free-trial response banner when using the managed free-trial collector.',
114
- 'config.firewall.enabled': 'Secure default: app firewall enforcement starts when apiKey is present and the dashboard toggle is on.',
116
+ 'config.firewall.enabled': 'Deprecated local hint. Runtime firewall enforcement is controlled by the SecureNow dashboard/API app environment toggle; the SDK starts sync whenever apiKey is present.',
115
117
  'config.firewall.apiUrl': 'Optional SecureNow firewall control-plane base URL. Leave unset/default so hosted SDKs sync through the SecureNow ingest gateway.',
116
118
  'config.firewall.versionCheckInterval': 'Seconds between lightweight firewall version/ETag checks.',
117
119
  'config.firewall.syncInterval': 'Seconds between safety-net full firewall blocklist syncs.',
@@ -128,45 +130,6 @@ const CONFIG_EXPLANATIONS = Object.freeze({
128
130
  'config.networking.trustedProxies': 'Additional proxy IPs whose X-Forwarded-For headers should be trusted.',
129
131
  });
130
132
 
131
- const ENV_TO_CONFIG_PATH = Object.freeze({
132
- SECURENOW_LOGGING_ENABLED: 'logging.enabled',
133
- SECURENOW_CAPTURE_BODY: 'capture.body',
134
- SECURENOW_CAPTURE_MULTIPART: 'capture.multipart',
135
- SECURENOW_MAX_BODY_SIZE: 'capture.maxBodySize',
136
- SECURENOW_SENSITIVE_FIELDS: 'capture.sensitiveFields',
137
- SECURENOW_DISABLE_INSTRUMENTATIONS: 'otel.disableInstrumentations',
138
- OTEL_EXPORTER_OTLP_ENDPOINT: 'otel.endpoint',
139
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: 'otel.tracesEndpoint',
140
- OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: 'otel.logsEndpoint',
141
- OTEL_EXPORTER_OTLP_HEADERS: 'otel.headers',
142
- OTEL_LOG_LEVEL: 'otel.logLevel',
143
- SECURENOW_ENVIRONMENT: 'runtime.deploymentEnvironment',
144
- SECURENOW_DEPLOYMENT_ENVIRONMENT: 'runtime.deploymentEnvironment',
145
- NODE_ENV: 'runtime.deploymentEnvironment',
146
- SECURENOW_NO_UUID: 'runtime.noUuid',
147
- SECURENOW_STRICT: 'runtime.strict',
148
- SECURENOW_TEST_SPAN: 'runtime.testSpan',
149
- SECURENOW_HIDE_BANNER: 'runtime.hideBanner',
150
- SECURENOW_API_URL: 'firewall.apiUrl',
151
- SECURENOW_FIREWALL_VERSION_INTERVAL: 'firewall.versionCheckInterval',
152
- SECURENOW_FIREWALL_SYNC_INTERVAL: 'firewall.syncInterval',
153
- SECURENOW_FIREWALL_FAIL_MODE: 'firewall.failMode',
154
- SECURENOW_FIREWALL_STATUS_CODE: 'firewall.statusCode',
155
- SECURENOW_FIREWALL_LOG: 'firewall.log',
156
- SECURENOW_FIREWALL_TCP: 'firewall.tcp',
157
- SECURENOW_FIREWALL_IPTABLES: 'firewall.iptables',
158
- SECURENOW_FIREWALL_CLOUD: 'firewall.cloud',
159
- SECURENOW_FIREWALL_CLOUD_DRY_RUN: 'firewall.cloudDryRun',
160
- CLOUDFLARE_API_TOKEN: 'firewall.cloudflare.apiToken',
161
- CLOUDFLARE_ACCOUNT_ID: 'firewall.cloudflare.accountId',
162
- AWS_WAF_IP_SET_ID: 'firewall.aws.wafIpSetId',
163
- AWS_WAF_IP_SET_NAME: 'firewall.aws.wafIpSetName',
164
- AWS_WAF_SCOPE: 'firewall.aws.wafScope',
165
- GCP_PROJECT_ID: 'firewall.gcp.projectId',
166
- GCP_SECURITY_POLICY: 'firewall.gcp.securityPolicy',
167
- SECURENOW_TRUSTED_PROXIES: 'networking.trustedProxies',
168
- });
169
-
170
133
  function clone(value) {
171
134
  return value == null ? value : JSON.parse(JSON.stringify(value));
172
135
  }
@@ -244,7 +207,7 @@ function uniq(values) {
244
207
  return out;
245
208
  }
246
209
 
247
- function credentialRelativePaths() {
210
+ function legacyCredentialRelativePaths() {
248
211
  return uniq([
249
212
  path.join('.securenow', 'credentials.json'),
250
213
  ...CREDENTIAL_FILE_ENVIRONMENTS.map((envName) =>
@@ -253,7 +216,25 @@ function credentialRelativePaths() {
253
216
  ]);
254
217
  }
255
218
 
256
- function resolveLocalCredentialsFile() {
219
+ function runtimeCredentialRelativePaths() {
220
+ return uniq([
221
+ path.join('.securenow', RUNTIME_CREDENTIALS_FILENAME),
222
+ ...legacyCredentialRelativePaths(),
223
+ ]);
224
+ }
225
+
226
+ function adminCredentialRelativePaths() {
227
+ return uniq([
228
+ path.join('.securenow', ADMIN_CREDENTIALS_FILENAME),
229
+ ...legacyCredentialRelativePaths(),
230
+ ]);
231
+ }
232
+
233
+ function credentialRelativePaths() {
234
+ return runtimeCredentialRelativePaths();
235
+ }
236
+
237
+ function resolveLocalFileFromRelativePaths(relativePaths) {
257
238
  const starts = [];
258
239
  try {
259
240
  if (typeof process !== 'undefined' && process.cwd) starts.push(process.cwd());
@@ -263,7 +244,7 @@ function resolveLocalCredentialsFile() {
263
244
  if (process.argv && process.argv[1]) starts.push(path.dirname(process.argv[1]));
264
245
  if (require.main && require.main.filename) starts.push(path.dirname(require.main.filename));
265
246
 
266
- const candidates = credentialRelativePaths();
247
+ const candidates = relativePaths;
267
248
  for (const start of uniq(starts)) {
268
249
  const found = findUpFirstFile(start, candidates);
269
250
  if (found) return found;
@@ -272,7 +253,15 @@ function resolveLocalCredentialsFile() {
272
253
  return null;
273
254
  }
274
255
 
275
- function resolveGlobalCredentialsFile() {
256
+ function resolveLocalCredentialsFile() {
257
+ return resolveLocalFileFromRelativePaths(runtimeCredentialRelativePaths());
258
+ }
259
+
260
+ function resolveLocalAdminCredentialsFile() {
261
+ return resolveLocalFileFromRelativePaths(adminCredentialRelativePaths());
262
+ }
263
+
264
+ function resolveGlobalFileFromRelativePaths(relativePaths) {
276
265
  let home;
277
266
  try {
278
267
  home = os.homedir();
@@ -280,7 +269,7 @@ function resolveGlobalCredentialsFile() {
280
269
  return null;
281
270
  }
282
271
 
283
- for (const relativePath of credentialRelativePaths()) {
272
+ for (const relativePath of relativePaths) {
284
273
  const found = fileIfReadable(path.join(home, relativePath));
285
274
  if (found) return found;
286
275
  }
@@ -288,6 +277,36 @@ function resolveGlobalCredentialsFile() {
288
277
  return null;
289
278
  }
290
279
 
280
+ function resolveGlobalCredentialsFile() {
281
+ return resolveGlobalFileFromRelativePaths(runtimeCredentialRelativePaths());
282
+ }
283
+
284
+ function resolveGlobalAdminCredentialsFile() {
285
+ return resolveGlobalFileFromRelativePaths(adminCredentialRelativePaths());
286
+ }
287
+
288
+ function runtimeCredentialsFromDocument(credentials) {
289
+ if (!credentials || typeof credentials !== 'object' || Array.isArray(credentials)) return null;
290
+ if (!credentials.runtime || typeof credentials.runtime !== 'object' || Array.isArray(credentials.runtime)) {
291
+ return credentials;
292
+ }
293
+
294
+ const runtime = clone(credentials.runtime);
295
+ if (runtime.environment) {
296
+ runtime.config = runtime.config || {};
297
+ runtime.config.runtime = runtime.config.runtime || {};
298
+ runtime.config.runtime.deploymentEnvironment = runtime.environment;
299
+ delete runtime.environment;
300
+ }
301
+ if (runtime.instanceUrl) {
302
+ runtime.config = runtime.config || {};
303
+ runtime.config.otel = runtime.config.otel || {};
304
+ if (!runtime.config.otel.endpoint) runtime.config.otel.endpoint = runtime.instanceUrl;
305
+ delete runtime.instanceUrl;
306
+ }
307
+ return runtime;
308
+ }
309
+
291
310
  function resolvePackageJsonFile() {
292
311
  const starts = [];
293
312
  try {
@@ -308,7 +327,7 @@ function resolvePackageJsonFile() {
308
327
 
309
328
  function loadLocalCredentials() {
310
329
  try {
311
- return withCredentialDefaults(readJsonSafe(resolveLocalCredentialsFile()));
330
+ return withCredentialDefaults(runtimeCredentialsFromDocument(readJsonSafe(resolveLocalCredentialsFile())));
312
331
  } catch {
313
332
  return null;
314
333
  }
@@ -316,7 +335,7 @@ function loadLocalCredentials() {
316
335
 
317
336
  function loadGlobalCredentials() {
318
337
  try {
319
- return withCredentialDefaults(readJsonSafe(resolveGlobalCredentialsFile()));
338
+ return withCredentialDefaults(runtimeCredentialsFromDocument(readJsonSafe(resolveGlobalCredentialsFile())));
320
339
  } catch {
321
340
  return null;
322
341
  }
@@ -326,7 +345,7 @@ function loadCredentials() {
326
345
  try {
327
346
  const localFile = resolveLocalCredentialsFile();
328
347
  if (localFile) {
329
- return withCredentialDefaults(readJsonSafe(localFile));
348
+ return withCredentialDefaults(runtimeCredentialsFromDocument(readJsonSafe(localFile)));
330
349
  }
331
350
  return loadGlobalCredentials();
332
351
  } catch {
@@ -401,7 +420,7 @@ function withCredentialDefaults(credentials) {
401
420
  out._securenow = mergeMissing(out._securenow, {
402
421
  schemaVersion: CONFIG_SCHEMA_VERSION,
403
422
  note: 'Local SecureNow credentials and secure SDK defaults. This file may contain secrets; keep .securenow/ in .gitignore.',
404
- precedence: `The same credentials file works in local development and production. SDK runtime config is read from credentials JSON. Legacy environment fallbacks are disabled unless ${LEGACY_ENV_FALLBACK_FLAG}=1 is set.`,
423
+ precedence: 'The same credentials file works in local development and production. SDK runtime config is read from credentials JSON only.',
405
424
  explanations: CONFIG_EXPLANATIONS,
406
425
  });
407
426
  return out;
@@ -417,19 +436,6 @@ function getPath(obj, dotted) {
417
436
  return cur;
418
437
  }
419
438
 
420
- function legacyEnvFallbackEnabled() {
421
- const raw =
422
- process.env[LEGACY_ENV_FALLBACK_FLAG] ??
423
- process.env.SECURENOW_ALLOW_ENV_CONFIG ??
424
- process.env.SECURENOW_LEGACY_ENV;
425
- return /^(1|true|yes)$/i.test(String(raw || '').trim());
426
- }
427
-
428
- function rawEnv(key) {
429
- if (!legacyEnvFallbackEnabled()) return undefined;
430
- return process.env[key] ?? process.env[key.toUpperCase()] ?? process.env[key.toLowerCase()];
431
- }
432
-
433
439
  function normalizeInstanceEndpoint(value) {
434
440
  if (value === undefined || value === null || value === '') return value;
435
441
  const endpoint = String(value).trim().replace(/\/$/, '');
@@ -567,24 +573,11 @@ function headersToString(headers) {
567
573
  .join(',');
568
574
  }
569
575
 
570
- function toEnvString(value) {
571
- if (value === undefined || value === null || value === '') return undefined;
572
- if (typeof value === 'boolean') return value ? '1' : '0';
573
- if (Array.isArray(value)) return value.join(',');
574
- if (typeof value === 'object') return headersToString(value);
575
- return String(value);
576
- }
577
-
578
- function resolveConfigPath(configPath, envKeys = [], fallback) {
576
+ function resolveConfigPath(configPath, _envKeys = [], fallback) {
579
577
  const creds = loadCredentials();
580
578
  const fromCreds = pick(getPath(creds && creds.config, configPath));
581
579
  if (fromCreds != null) return fromCreds;
582
580
 
583
- for (const key of envKeys) {
584
- const fromEnv = pick(rawEnv(key));
585
- if (fromEnv != null) return fromEnv;
586
- }
587
-
588
581
  const fromDefault = pick(getPath(DEFAULT_CONFIG, configPath));
589
582
  if (fromDefault != null) return fromDefault;
590
583
 
@@ -610,28 +603,12 @@ function listConfig(configPath) {
610
603
  function resolveAppKey() {
611
604
  const creds = loadCredentials();
612
605
  if (creds && creds.app && pick(creds.app.key)) return String(pick(creds.app.key));
613
-
614
- const fromEnv = pick(rawEnv('SECURENOW_APPID')) || pick(rawEnv('securenow'));
615
- if (fromEnv) return String(fromEnv);
616
-
617
- // Legacy compatibility: older docs sometimes used SECURENOW_API_KEY as the
618
- // app routing UUID. Real firewall keys start with snk_live_ and are handled
619
- // by resolveApiKey(), not as service.name.
620
- const legacyApiKey = pick(rawEnv('SECURENOW_API_KEY'));
621
- if (legacyApiKey && !String(legacyApiKey).startsWith('snk_live_')) {
622
- return String(legacyApiKey);
623
- }
624
-
625
606
  return null;
626
607
  }
627
608
 
628
609
  function resolveAppName() {
629
610
  const creds = loadCredentials();
630
611
  if (creds && creds.app && pick(creds.app.name)) return String(pick(creds.app.name));
631
-
632
- const fromEnv = pick(rawEnv('OTEL_SERVICE_NAME'));
633
- if (fromEnv) return String(fromEnv);
634
-
635
612
  return loadPackageJsonName();
636
613
  }
637
614
 
@@ -643,23 +620,12 @@ function resolveApiKey() {
643
620
  const creds = loadCredentials();
644
621
  const fromCreds = creds && pick(creds.apiKey);
645
622
  if (fromCreds && String(fromCreds).startsWith('snk_live_')) return String(fromCreds);
646
-
647
- const fromEnv = pick(rawEnv('SECURENOW_API_KEY'));
648
- if (fromEnv && String(fromEnv).startsWith('snk_live_')) return String(fromEnv);
649
-
650
623
  return null;
651
624
  }
652
625
 
653
626
  function resolveInstance() {
654
627
  const fromConfig = pick(resolveConfigPath('otel.endpoint'));
655
628
  if (fromConfig) return normalizeInstanceEndpoint(fromConfig);
656
-
657
- const fromEnv =
658
- pick(rawEnv('SECURENOW_INSTANCE')) ||
659
- pick(rawEnv('securenow_instance')) ||
660
- pick(rawEnv('OTEL_EXPORTER_OTLP_ENDPOINT'));
661
- if (fromEnv) return normalizeInstanceEndpoint(fromEnv);
662
-
663
629
  return FREE_TRIAL_INSTANCE;
664
630
  }
665
631
 
@@ -684,51 +650,18 @@ function resolveNoUuid(opts = {}) {
684
650
  const fromConfig = pick(getPath(loadCredentials()?.config, 'runtime.noUuid'));
685
651
  if (fromConfig !== null) return parseBool(fromConfig, false);
686
652
 
687
- const raw = pick(rawEnv('SECURENOW_NO_UUID'));
688
- if (raw !== null) return parseBool(raw, false);
689
-
690
653
  return !!resolveAppKey();
691
654
  }
692
655
 
693
- function resolveEnvKey(key) {
694
- const upper = String(key).toUpperCase();
695
-
696
- if (upper === 'SECURENOW_APPID') return resolveAppKey();
697
- if (upper === 'OTEL_SERVICE_NAME') return resolveAppName();
698
- if (upper === 'SECURENOW_API_KEY') return resolveApiKey();
699
- if (upper === 'SECURENOW_INSTANCE' || upper === 'OTEL_EXPORTER_OTLP_ENDPOINT') return resolveInstance();
700
- if (upper === 'SECURENOW_FIREWALL_ENABLED') return resolveFirewallEnabled();
701
-
702
- const configPath = ENV_TO_CONFIG_PATH[upper];
703
- if (configPath) return resolveConfigPath(configPath);
704
-
705
- const fromEnv = pick(rawEnv(upper));
706
- if (fromEnv != null) return fromEnv;
707
-
708
- return undefined;
709
- }
710
-
711
- function env(key) {
712
- return toEnvString(resolveEnvKey(key));
713
- }
714
-
715
- function boolEnv(key, fallback = false) {
716
- return parseBool(resolveEnvKey(key), fallback);
717
- }
718
-
719
- function numberEnv(key, fallback, min) {
720
- return parseNumber(resolveEnvKey(key), fallback, min);
721
- }
722
-
723
- function listEnv(key) {
724
- return parseList(resolveEnvKey(key));
725
- }
726
-
727
656
  function resolveOtlpHeaders() {
728
657
  const headers = parseHeaders(configValue('otel.headers', {}));
729
658
  const appKey = resolveAppKey();
730
- if (appKey && !headers['x-api-key']) {
731
- headers['x-api-key'] = appKey;
659
+ const apiKey = resolveApiKey();
660
+ if (appKey && !headers['x-securenow-app-key']) {
661
+ headers['x-securenow-app-key'] = appKey;
662
+ }
663
+ if (apiKey && !headers.authorization) {
664
+ headers.authorization = `Bearer ${apiKey}`;
732
665
  }
733
666
  const deploymentEnvironment = resolveDeploymentEnvironment();
734
667
  if (deploymentEnvironment && !headers['x-securenow-environment']) {
@@ -754,10 +687,7 @@ function resolveEndpoints(options = {}) {
754
687
  }
755
688
 
756
689
  function resolveFirewallEnabled() {
757
- const fromConfig = pick(getPath(loadCredentials()?.config, 'firewall.enabled'));
758
- if (fromConfig != null) return parseBool(fromConfig, true);
759
-
760
- return parseBool(getPath(DEFAULT_CONFIG, 'firewall.enabled'), true);
690
+ return true;
761
691
  }
762
692
 
763
693
  function resolveFirewallOptions() {
@@ -799,12 +729,12 @@ module.exports = {
799
729
  DEFAULT_API_URL,
800
730
  DEFAULT_FIREWALL_API_URL,
801
731
  LEGACY_SECURENOW_GATEWAY,
802
- LEGACY_ENV_FALLBACK_FLAG,
803
732
  CONFIG_SCHEMA_VERSION,
804
733
  CREDENTIAL_FILE_ENVIRONMENTS,
734
+ RUNTIME_CREDENTIALS_FILENAME,
735
+ ADMIN_CREDENTIALS_FILENAME,
805
736
  DEFAULT_CONFIG,
806
737
  CONFIG_EXPLANATIONS,
807
- ENV_TO_CONFIG_PATH,
808
738
  withCredentialDefaults,
809
739
  mergeCredentials,
810
740
  resolveConfigPath,
@@ -818,7 +748,6 @@ module.exports = {
818
748
  resolveApiKey,
819
749
  resolveInstance,
820
750
  normalizeDeploymentEnvironment,
821
- legacyEnvFallbackEnabled,
822
751
  resolveDeploymentEnvironment,
823
752
  resolveAll,
824
753
  resolveNoUuid,
@@ -830,17 +759,18 @@ module.exports = {
830
759
  resolveFirewallApiUrl,
831
760
  resolveFirewallApiFallbacks,
832
761
  resolveFirewallOptions,
833
- env,
834
- boolEnv,
835
- numberEnv,
836
- listEnv,
837
762
  parseHeaders,
838
763
  normalizeInstanceEndpoint,
839
764
  normalizeSignalEndpoint,
840
765
  headersToString,
766
+ legacyCredentialRelativePaths,
767
+ runtimeCredentialRelativePaths,
768
+ adminCredentialRelativePaths,
841
769
  credentialRelativePaths,
842
770
  resolveLocalCredentialsFile,
843
771
  resolveGlobalCredentialsFile,
772
+ resolveLocalAdminCredentialsFile,
773
+ resolveGlobalAdminCredentialsFile,
844
774
  loadCredentials,
845
775
  loadLocalCredentials,
846
776
  loadGlobalCredentials,
package/cli/apiKey.js CHANGED
@@ -12,16 +12,24 @@ function maskKey(key) {
12
12
  async function create(args, flags) {
13
13
  requireAuth();
14
14
 
15
- const name = flags.name || args.join(' ').trim() || 'CLI firewall';
16
- const preset = flags.preset || 'firewall';
15
+ const name = flags.name || args.join(' ').trim() || 'CLI runtime';
16
+ const preset = flags.preset || 'runtime_app';
17
17
  const local = flags.global ? false : true;
18
+ const app = config.getApp();
18
19
 
19
20
  const s = ui.spinner(`Creating ${preset} API key`);
20
21
  try {
21
- const result = await api.post('/api-keys', { name, preset });
22
+ const body = { name, preset };
23
+ if (flags.app) {
24
+ body.apps = [flags.app];
25
+ } else if (preset === 'runtime_app' && app?.key) {
26
+ body.apps = [app.key];
27
+ }
28
+
29
+ const result = await api.post('/api-keys', body);
22
30
  const key = result && result.key;
23
31
  if (!key || !key.startsWith('snk_live_')) {
24
- throw new Error('API did not return a valid firewall key');
32
+ throw new Error('API did not return a valid runtime key');
25
33
  }
26
34
 
27
35
  config.setApiKey(key, { local });
@@ -34,16 +42,17 @@ async function create(args, flags) {
34
42
  name: result.apiKey?.name || name,
35
43
  preset,
36
44
  key: maskKey(key),
37
- savedTo: local ? 'project .securenow/credentials.json' : '~/.securenow/credentials.json',
45
+ appScope: body.apps || [],
46
+ savedTo: local ? 'project .securenow/runtime.json' : '~/.securenow/runtime.json',
38
47
  });
39
48
  return;
40
49
  }
41
50
 
42
51
  ui.success(`API key saved (${maskKey(key)})`);
43
52
  ui.info(local
44
- ? 'Stored in project .securenow/credentials.json (local)'
45
- : 'Stored in ~/.securenow/credentials.json (global)');
46
- ui.info('The plaintext key is not printed. The SDK will read it from the credentials file.');
53
+ ? 'Stored in project .securenow/runtime.json (local)'
54
+ : 'Stored in ~/.securenow/runtime.json (global)');
55
+ ui.info('The plaintext key is not printed. The SDK will read it from the runtime credentials file.');
47
56
  } catch (err) {
48
57
  s.fail('Failed to create API key');
49
58
  throw err;
@@ -68,8 +77,8 @@ async function set(args, flags) {
68
77
 
69
78
  ui.success(`API key saved (${maskKey(key)})`);
70
79
  ui.info(local
71
- ? 'Stored in project .securenow/credentials.json (local)'
72
- : 'Stored in ~/.securenow/credentials.json (global)');
80
+ ? 'Stored in project .securenow/runtime.json (local)'
81
+ : 'Stored in ~/.securenow/runtime.json (global)');
73
82
  ui.info('The firewall will now pick it up automatically — no SECURENOW_API_KEY env var needed.');
74
83
  }
75
84
 
@@ -87,11 +96,11 @@ async function clear(args, flags) {
87
96
  async function show() {
88
97
  const key = config.getApiKey();
89
98
  if (!key) {
90
- ui.info('Runtime firewall enforcement key is missing. Run `npx securenow login` or `npx securenow api-key set snk_live_...` to refresh .securenow/credentials.json.');
99
+ ui.info('Runtime API key is missing. Run `npx securenow app connect` or `npx securenow api-key create` to refresh .securenow/runtime.json.');
91
100
  return;
92
101
  }
93
102
  console.log(maskKey(key));
94
- ui.info(`Source: ${config.getAuthSource()}`);
103
+ ui.info(`Source: ${config.getRuntimeSource()}`);
95
104
  }
96
105
 
97
106
  module.exports = { create, set, clear, show };
package/cli/apps.js CHANGED
@@ -66,7 +66,7 @@ async function list(args, flags) {
66
66
  if (!defaultApp && apps.length > 0) {
67
67
  console.log(` ${ui.c.bold('Use one of these apps in the current project:')}`);
68
68
  console.log(` ${ui.c.bold('securenow apps default <key>')}`);
69
- console.log(` ${ui.c.dim('(or run `securenow login` to pick interactively)')}`);
69
+ console.log(` ${ui.c.dim('(or run `securenow app connect` to pick interactively)')}`);
70
70
  console.log('');
71
71
  }
72
72
  } catch (err) {
@@ -127,7 +127,7 @@ async function create(args, flags) {
127
127
  console.log('');
128
128
  console.log(` ${ui.c.bold('Use this app in the current project:')}`);
129
129
  console.log(` ${ui.c.bold(`securenow apps default ${app.key}`)}`);
130
- console.log(` ${ui.c.dim('(writes .securenow/credentials.json no env var needed)')}`);
130
+ console.log(` ${ui.c.dim('(writes .securenow/runtime.json - no env var needed)')}`);
131
131
  console.log('');
132
132
 
133
133
  if (flags.json) {
@@ -285,7 +285,7 @@ async function setDefault(args) {
285
285
  }
286
286
  config.setConfigValue('defaultApp', key);
287
287
 
288
- // Mirror into credentials.json so the SDK picks this app up with zero env vars.
288
+ // Mirror into runtime credentials so the SDK picks this app up with zero env vars.
289
289
  try {
290
290
  requireAuth();
291
291
  const appData = await api.get(`/applications/${key}`);