securenow 7.0.0-anas.2 → 7.1.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/SKILL-CLI.md CHANGED
@@ -18,13 +18,17 @@ npx securenow <command>
18
18
  ### Authenticate
19
19
 
20
20
  ```bash
21
- securenow login # opens browser OAuth; stores JWT in ~/.securenow/credentials.json
21
+ securenow login # opens browser OAuth + app picker; writes ./.securenow/credentials.json (project-local by default)
22
+ securenow login --global # save to ~/.securenow/ instead (shared across projects)
22
23
  securenow login --token <JWT> # headless / CI login (get token from dashboard Settings)
23
- securenow login --local # save credentials to this project only (.securenow/)
24
- securenow whoami # verify session (shows auth source)
24
+ securenow whoami # verify session (shows email, app, auth source)
25
25
  ```
26
26
 
27
- **Per-project credentials:** Use `--local` to keep separate logins in different project directories on the same machine. Credentials resolve in order: `SECURENOW_TOKEN` env var project `.securenow/credentials.json` global `~/.securenow/credentials.json`.
27
+ **Zero-config flow (v7+):** the browser step lets the user pick (or create) an app. The CLI stores the app's **key (UUID)**, **name**, and **instance URL** in `.securenow/credentials.json`. The SDK reads this file at boot and sends traces/logs to the right app bucket — **no env vars required for local dev**.
28
+
29
+ Credentials resolve in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
30
+
31
+ For CI / Docker / production, set env vars directly (always win over the file): `SECURENOW_APPID=<uuid>`, `SECURENOW_INSTANCE=<url>`, `SECURENOW_API_KEY=<uuid>`.
28
32
 
29
33
  ### Integrate With Your App
30
34
 
package/app-config.js CHANGED
@@ -14,6 +14,10 @@
14
14
  * - What human label to show? (resolveAppName) — display only, never
15
15
  * sent to the collector.
16
16
  * - Which OTLP collector to hit? (resolveInstance)
17
+ * - Which firewall API key? (resolveApiKey) — the snk_live_... key the
18
+ * firewall sends as Bearer to /api/firewall
19
+ * for blocklist sync. Separate from the
20
+ * app routing key: this one is user-scoped.
17
21
  *
18
22
  * Resolution order (first non-empty wins):
19
23
  *
@@ -25,7 +29,7 @@
25
29
  * 5. Hard default / null
26
30
  *
27
31
  * Credentials file schema:
28
- * { token, email, expiresAt, app: { key: <uuid>, name: <display>, instance } }
32
+ * { token, email, expiresAt, apiKey: <snk_live_...>, app: { key, name, instance } }
29
33
  */
30
34
 
31
35
  const fs = require('fs');
@@ -112,6 +116,21 @@ function resolveAppId() {
112
116
  return resolveAppKey() || resolveAppName();
113
117
  }
114
118
 
119
+ // Firewall / user-scoped API key (snk_live_...).
120
+ // Distinct from resolveAppKey: that one is the application UUID for OTel
121
+ // routing. This one authenticates the firewall blocklist sync to /api/firewall,
122
+ // so it must look like a real `snk_live_` key — the app UUID won't pass auth.
123
+ function resolveApiKey() {
124
+ const fromEnv = pick(process.env.SECURENOW_API_KEY);
125
+ if (fromEnv && fromEnv.startsWith('snk_live_')) return fromEnv;
126
+
127
+ const creds = loadCredentials();
128
+ const fromCreds = creds && pick(creds.apiKey);
129
+ if (fromCreds && fromCreds.startsWith('snk_live_')) return fromCreds;
130
+
131
+ return null;
132
+ }
133
+
115
134
  function resolveInstance() {
116
135
  const fromEnv =
117
136
  pick(process.env.SECURENOW_INSTANCE) ||
@@ -143,6 +162,7 @@ module.exports = {
143
162
  resolveAppKey,
144
163
  resolveAppName,
145
164
  resolveAppId,
165
+ resolveApiKey,
146
166
  resolveInstance,
147
167
  resolveAll,
148
168
  loadCredentials,
package/cli/apiKey.js ADDED
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ const config = require('./config');
4
+ const ui = require('./ui');
5
+
6
+ function maskKey(key) {
7
+ if (!key || key.length < 16) return key || '';
8
+ return `${key.slice(0, 12)}••••••${key.slice(-4)}`;
9
+ }
10
+
11
+ async function set(args, flags) {
12
+ const key = args[0];
13
+ if (!key) {
14
+ ui.error('API key is required. Usage: securenow api-key set <snk_live_...>');
15
+ process.exit(1);
16
+ }
17
+ if (!key.startsWith('snk_live_')) {
18
+ ui.error('API key must start with "snk_live_"');
19
+ ui.info('Create one in the dashboard: Settings → API Keys');
20
+ process.exit(1);
21
+ }
22
+
23
+ const local = flags.global ? false : true;
24
+ config.setApiKey(key, { local });
25
+ if (local) config.ensureLocalGitignore();
26
+
27
+ ui.success(`API key saved (${maskKey(key)})`);
28
+ ui.info(local
29
+ ? 'Stored in project .securenow/credentials.json (local)'
30
+ : 'Stored in ~/.securenow/credentials.json (global)');
31
+ ui.info('The firewall will now pick it up automatically — no SECURENOW_API_KEY env var needed.');
32
+ }
33
+
34
+ async function clear(args, flags) {
35
+ const local = flags.global ? false : true;
36
+ const existing = config.getApiKey();
37
+ config.clearApiKey({ local });
38
+ if (existing) {
39
+ ui.success(`Cleared API key (${maskKey(existing)})`);
40
+ } else {
41
+ ui.info('No API key was stored');
42
+ }
43
+ }
44
+
45
+ async function show() {
46
+ const key = config.getApiKey();
47
+ if (!key) {
48
+ ui.info('No API key stored in credentials. Falling back to SECURENOW_API_KEY env var.');
49
+ return;
50
+ }
51
+ console.log(maskKey(key));
52
+ ui.info(`Source: ${config.getAuthSource()}`);
53
+ }
54
+
55
+ module.exports = { set, clear, show };
package/cli/apps.js CHANGED
@@ -64,17 +64,10 @@ async function list(args, flags) {
64
64
  ui.table(['Name', 'Key', 'Instance', 'Created'], rows);
65
65
  console.log('');
66
66
 
67
- if (apps.length > 0) {
68
- console.log(` ${ui.c.bold('Add to your .env:')}`);
69
- const first = apps.find(a => a.key === defaultApp) || apps[0];
70
- const firstInst = first.instanceId ? instMap[first.instanceId] : null;
71
- console.log(` SECURENOW_APPID=${first.key}`);
72
- console.log(` SECURENOW_INSTANCE=${instanceUrl(firstInst)}`);
73
- console.log('');
74
- }
75
-
76
67
  if (!defaultApp && apps.length > 0) {
77
- ui.info(`Tip: Set a default app with ${ui.c.bold('securenow config set defaultApp <key>')}`);
68
+ console.log(` ${ui.c.bold('Use one of these apps in the current project:')}`);
69
+ console.log(` ${ui.c.bold('securenow apps default <key>')}`);
70
+ console.log(` ${ui.c.dim('(or run `securenow login` to pick interactively)')}`);
78
71
  console.log('');
79
72
  }
80
73
  } catch (err) {
@@ -133,12 +126,9 @@ async function create(args, flags) {
133
126
  ]);
134
127
 
135
128
  console.log('');
136
- console.log(` ${ui.c.bold('Add to your .env.local:')}`);
137
- console.log('');
138
- console.log(` SECURENOW_APPID=${app.key}`);
139
- console.log(` SECURENOW_INSTANCE=${envUrl}`);
140
- console.log('');
141
- ui.info(`Set as default: ${ui.c.bold(`securenow config set defaultApp ${app.key}`)}`);
129
+ console.log(` ${ui.c.bold('Use this app in the current project:')}`);
130
+ console.log(` ${ui.c.bold(`securenow apps default ${app.key}`)}`);
131
+ console.log(` ${ui.c.dim('(writes .securenow/credentials.json — no env var needed)')}`);
142
132
  console.log('');
143
133
 
144
134
  if (flags.json) {
@@ -251,9 +241,8 @@ async function info(args, flags) {
251
241
  ]);
252
242
 
253
243
  console.log('');
254
- console.log(` ${ui.c.bold('Environment variables:')}`);
255
- console.log(` SECURENOW_APPID=${app.key}`);
256
- console.log(` SECURENOW_INSTANCE=${envUrl}`);
244
+ console.log(` ${ui.c.bold('Use in current project:')} ${ui.c.bold(`securenow apps default ${app.key}`)}`);
245
+ console.log(` ${ui.c.bold('Or override via env:')} ${ui.c.dim(`SECURENOW_APPID=${app.key} SECURENOW_INSTANCE=${envUrl}`)}`);
257
246
  console.log('');
258
247
  } catch (err) {
259
248
  s.fail('Failed to fetch application');
package/cli/auth.js CHANGED
@@ -45,6 +45,7 @@ async function loginWithBrowser() {
45
45
  return new Promise((resolve, reject) => {
46
46
  let pendingToken = null;
47
47
  let pendingApp = null;
48
+ let pendingApiKey = null;
48
49
 
49
50
  const server = http.createServer((req, res) => {
50
51
  const url = new URL(req.url, `http://127.0.0.1`);
@@ -56,6 +57,7 @@ async function loginWithBrowser() {
56
57
  const appKey = url.searchParams.get('app_key');
57
58
  const appName = url.searchParams.get('app_name');
58
59
  const appInstance = url.searchParams.get('app_instance');
60
+ const apiKey = url.searchParams.get('api_key');
59
61
 
60
62
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
61
63
 
@@ -82,6 +84,9 @@ async function loginWithBrowser() {
82
84
  instance: appInstance || null,
83
85
  };
84
86
  }
87
+ if (apiKey && apiKey.startsWith('snk_live_')) {
88
+ pendingApiKey = apiKey;
89
+ }
85
90
  const payload = decodeJwtPayload(token);
86
91
  const email = payload?.email || 'unknown account';
87
92
  const safeEmail = email.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -139,10 +144,12 @@ async function loginWithBrowser() {
139
144
  res.end('{"ok":true}');
140
145
  const token = pendingToken;
141
146
  const app = pendingApp;
147
+ const apiKeyFromLogin = pendingApiKey;
142
148
  pendingToken = null;
143
149
  pendingApp = null;
150
+ pendingApiKey = null;
144
151
  server.close();
145
- resolve({ token, app });
152
+ resolve({ token, app, apiKey: apiKeyFromLogin });
146
153
  return;
147
154
  }
148
155
 
@@ -219,12 +226,13 @@ async function login(args, flags) {
219
226
  }
220
227
 
221
228
  try {
222
- const { token, app } = await loginWithBrowser();
229
+ const { token, app, apiKey } = await loginWithBrowser();
223
230
  const payload = decodeJwtPayload(token);
224
231
  const email = payload?.email || 'unknown';
225
232
  const exp = payload?.exp ? payload.exp * 1000 : null;
226
233
 
227
234
  config.setAuth(token, email, exp, { local, app });
235
+ if (apiKey) config.setApiKey(apiKey, { local });
228
236
  if (local) config.ensureLocalGitignore();
229
237
  console.log('');
230
238
  ui.success(`Logged in as ${ui.c.bold(email)}`);
@@ -232,6 +240,9 @@ async function login(args, flags) {
232
240
  if (app && (app.name || app.key)) {
233
241
  ui.info(`Linked to app ${ui.c.bold(app.name || app.key)}${app.key ? ` (${ui.c.dim(app.key)})` : ''}`);
234
242
  }
243
+ if (apiKey) {
244
+ ui.info(`Firewall API key saved — the firewall will activate automatically on next start`);
245
+ }
235
246
  if (exp) {
236
247
  const days = Math.ceil((exp - Date.now()) / (1000 * 60 * 60 * 24));
237
248
  ui.info(`Session expires in ${days} days`);
package/cli/config.js CHANGED
@@ -135,6 +135,27 @@ function getApp() {
135
135
  return creds && creds.app ? creds.app : null;
136
136
  }
137
137
 
138
+ function setApiKey(apiKey, { local } = {}) {
139
+ const useLocal = local === true || (local == null && hasLocalCredentials());
140
+ const targetFile = useLocal ? LOCAL_CREDENTIALS_FILE : CREDENTIALS_FILE;
141
+ const existing = loadJSON(targetFile);
142
+ saveJSON(targetFile, { ...existing, apiKey });
143
+ }
144
+
145
+ function clearApiKey({ local } = {}) {
146
+ const useLocal = local === true || (local == null && hasLocalCredentials());
147
+ const targetFile = useLocal ? LOCAL_CREDENTIALS_FILE : CREDENTIALS_FILE;
148
+ const existing = loadJSON(targetFile);
149
+ if (!existing || !existing.apiKey) return;
150
+ delete existing.apiKey;
151
+ saveJSON(targetFile, existing);
152
+ }
153
+
154
+ function getApiKey() {
155
+ const creds = loadCredentials();
156
+ return creds && creds.apiKey ? creds.apiKey : null;
157
+ }
158
+
138
159
  function setApp(app, { local } = {}) {
139
160
  const useLocal = local === true || (local == null && hasLocalCredentials());
140
161
  const targetFile = useLocal ? LOCAL_CREDENTIALS_FILE : CREDENTIALS_FILE;
@@ -193,6 +214,9 @@ module.exports = {
193
214
  setAuth,
194
215
  getApp,
195
216
  setApp,
217
+ setApiKey,
218
+ clearApiKey,
219
+ getApiKey,
196
220
  getAuthSource,
197
221
  hasLocalCredentials,
198
222
  ensureLocalGitignore,
@@ -25,7 +25,9 @@ function resolvedConfig() {
25
25
  ? `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT.replace(/\/$/, '')}/v1/logs`
26
26
  : `${instance.replace(/\/$/, '')}/v1/logs`);
27
27
  const headers = process.env.OTEL_EXPORTER_OTLP_HEADERS || '';
28
- const apiKey = process.env.SECURENOW_API_KEY || '';
28
+ // Resolve firewall API key the same way the SDK does: env, then
29
+ // .securenow/credentials.json (project-local, then global).
30
+ const apiKey = require('../app-config').resolveApiKey() || '';
29
31
  const apiUrl = config.getApiUrl();
30
32
  const loggingEnabled = !/^(0|false)$/i.test(String(process.env.SECURENOW_LOGGING_ENABLED ?? ''));
31
33
  const captureBody = !/^(0|false)$/i.test(String(process.env.SECURENOW_CAPTURE_BODY ?? ''));
package/cli.js CHANGED
@@ -73,6 +73,30 @@ const COMMANDS = {
73
73
  usage: 'securenow whoami',
74
74
  run: () => require('./cli/auth').whoami(),
75
75
  },
76
+ 'api-key': {
77
+ desc: 'Manage the firewall API key stored in .securenow/credentials.json',
78
+ usage: 'securenow api-key <subcommand> [options]',
79
+ sub: {
80
+ set: {
81
+ desc: 'Save an API key (snk_live_...) to the credentials file',
82
+ usage: 'securenow api-key set <snk_live_...> [--global]',
83
+ flags: { global: 'Save to ~/.securenow/ instead of project-local' },
84
+ run: (a, f) => require('./cli/apiKey').set(a, f),
85
+ },
86
+ clear: {
87
+ desc: 'Remove the stored API key',
88
+ usage: 'securenow api-key clear [--global]',
89
+ flags: { global: 'Clear from ~/.securenow/ instead of project-local' },
90
+ run: (a, f) => require('./cli/apiKey').clear(a, f),
91
+ },
92
+ show: {
93
+ desc: 'Print the masked API key currently in use',
94
+ usage: 'securenow api-key show',
95
+ run: () => require('./cli/apiKey').show(),
96
+ },
97
+ },
98
+ defaultSub: 'show',
99
+ },
76
100
  apps: {
77
101
  desc: 'Manage applications',
78
102
  usage: 'securenow apps <subcommand> [options]',
@@ -2,6 +2,14 @@
2
2
 
3
3
  Protect any Node.js app in minutes. This guide covers **installation, CLI commands, the forensics chat, and IP blocking** for all 11 supported frameworks.
4
4
 
5
+ > **v7+ — zero-config shortcut.** For local dev, the short version is:
6
+ > ```bash
7
+ > npm install securenow
8
+ > npx securenow login # pick/create app in the browser
9
+ > node -r securenow/register app.js
10
+ > ```
11
+ > No `.env` setup. Credentials live in `.securenow/credentials.json` (gitignored automatically). Skip "Step 2 — Set Environment Variables" below unless you're configuring CI / Docker / prod.
12
+
5
13
  ---
6
14
 
7
15
  ## Table of Contents
@@ -2,32 +2,41 @@
2
2
 
3
3
  Complete reference for all environment variables supported by SecureNow.
4
4
 
5
+ > **v7+: env vars are all optional.** For local dev, `npx securenow login` writes `.securenow/credentials.json` and the SDK reads it at boot — no env vars needed. Env vars are still supported (and always take precedence) for CI / Docker / production.
6
+
7
+ > **Resolution order** (first non-empty wins): env var → `./.securenow/credentials.json` → `~/.securenow/credentials.json` → `package.json#name` (label only) → default.
8
+
5
9
  ---
6
10
 
7
11
  ## Quick Reference Table
8
12
 
9
13
  | Variable | Type | Default | Description |
10
14
  |----------|------|---------|-------------|
11
- | **SECURENOW_APPID** | Required | - | Application identifier / service name |
12
- | **SECURENOW_INSTANCE** | Required | `https://freetrial.securenow.ai:4318` | OTLP collector base URL |
13
- | **SECURENOW_LOGGING_ENABLED** | Optional | `1` | Enable/disable logging |
14
- | **SECURENOW_NO_UUID** | Optional | `0` | Disable UUID suffix on service name |
15
- | **SECURENOW_STRICT** | Optional | `0` | Exit if APPID missing in cluster mode |
16
- | **SECURENOW_CAPTURE_BODY** | Optional | `0` | Enable request body capture |
15
+ | **SECURENOW_APPID** | Optional | from credentials file | App routing key (UUID). Sent as OTel `service.name`. |
16
+ | **SECURENOW_INSTANCE** | Optional | `https://freetrial.securenow.ai:4318` | OTLP collector base URL |
17
+ | **SECURENOW_API_KEY** | Optional | from credentials file | API key (same UUID as APPID). Enables firewall. |
18
+ | **SECURENOW_LOGGING_ENABLED** | Optional | `1` (on) | Forward `console.*` as OTLP logs. Set to `0` to disable. |
19
+ | **SECURENOW_CAPTURE_BODY** | Optional | `1` (on) | Capture request body. Set to `0` for Fastify/Hapi/Hono. |
20
+ | **SECURENOW_CAPTURE_MULTIPART** | Optional | `1` (on) | Capture multipart field/file metadata. |
17
21
  | **SECURENOW_MAX_BODY_SIZE** | Optional | `10240` | Max body size in bytes |
18
- | **SECURENOW_SENSITIVE_FIELDS** | Optional | - | Comma-separated list of fields to redact |
19
- | **SECURENOW_CAPTURE_MULTIPART** | Optional | `0` | Enable multipart/form-data streaming capture |
20
- | **SECURENOW_DISABLE_INSTRUMENTATIONS** | Optional | - | Comma-separated list of packages to disable |
21
- | **SECURENOW_TEST_SPAN** | Optional | `0` | Emit test span on startup |
22
- | **OTEL_SERVICE_NAME** | Optional | - | Alternative to SECURENOW_APPID |
22
+ | **SECURENOW_SENSITIVE_FIELDS** | Optional | - | Comma-separated extra fields to redact |
23
+ | **SECURENOW_NO_UUID** | Optional | `0` | Disable UUID suffix on `service.instance.id` |
24
+ | **SECURENOW_STRICT** | Optional | `0` | Exit if APPID missing in PM2 cluster mode |
25
+ | **SECURENOW_DISABLE_INSTRUMENTATIONS** | Optional | - | Comma-separated list of OTel instrumentations to disable |
26
+ | **SECURENOW_TEST_SPAN** | Optional | `0` | Emit a single test span on startup (prefer `npx securenow test-span`) |
27
+ | **SECURENOW_HIDE_BANNER** | Optional | `0` | Hide the free-trial banner |
28
+ | **SECURENOW_FIREWALL_ENABLED** | Optional | `1` (on when API key is set) | Firewall master switch |
29
+ | **SECURENOW_ENABLE_MONGODB_INSTRUMENTATION** | Optional | `0` | Opt in to MongoDB instrumentation (off by default since a cursor bug on mongodb@6.6+; safe since SDK v6.0.2) |
30
+ | **OTEL_SERVICE_NAME** | Optional | - | Alternative to SECURENOW_APPID (label only, no routing) |
23
31
  | **OTEL_EXPORTER_OTLP_ENDPOINT** | Optional | - | Alternative to SECURENOW_INSTANCE |
24
- | **OTEL_EXPORTER_OTLP_HEADERS** | Optional | - | Headers for OTLP requests |
32
+ | **OTEL_EXPORTER_OTLP_HEADERS** | Optional | auto (`x-api-key` injected) | Additional OTLP headers |
25
33
  | **OTEL_EXPORTER_OTLP_TRACES_ENDPOINT** | Optional | - | Override traces endpoint |
26
34
  | **OTEL_EXPORTER_OTLP_LOGS_ENDPOINT** | Optional | - | Override logs endpoint |
27
- | **OTEL_LOG_LEVEL** | Optional | `none` | SDK log level |
35
+ | **OTEL_LOG_LEVEL** | Optional | `none` | SDK log verbosity (`debug`/`info`/`warn`/`error`) |
28
36
  | **NODE_ENV** | Optional | `production` | Environment name |
29
- | **SECURENOW_API_KEY** | Optional | - | API key for firewall (auto-activates when set) |
30
- | **SECURENOW_API_URL** | Optional | `https://api.securenow.ai` | API base URL |
37
+ | **SECURENOW_API_URL** | Optional | `https://api.securenow.ai` | Dashboard API base URL |
38
+ | **SECURENOW_APP_URL** | Optional | `https://app.securenow.ai` | Dashboard web base URL |
39
+ | **SECURENOW_TOKEN** | Optional | from credentials file | Auth token (overrides credentials file for CI) |
31
40
  | **SECURENOW_FIREWALL_ENABLED** | Optional | `1` | Firewall master kill-switch |
32
41
  | **SECURENOW_FIREWALL_SYNC_INTERVAL** | Optional | `60` | Blocklist refresh interval (seconds) |
33
42
  | **SECURENOW_FIREWALL_FAIL_MODE** | Optional | `open` | Behavior when API unreachable: open/closed |
@@ -1,36 +1,23 @@
1
- # SecureNow Logging - Quick Start
1
+ # SecureNow Logging Quick Start
2
2
 
3
- Get logging set up in your Node.js app in under 2 minutes!
3
+ Get logging sent to your SecureNow dashboard in under 2 minutes.
4
4
 
5
- **Since v5.6.0:** When `SECURENOW_LOGGING_ENABLED=1` is set, all `console.log` / `warn` / `error` / `info` / `debug` calls are automatically forwarded as OTLP log records. You only need `require('securenow/register')`—a separate `console-instrumentation` preload is no longer required.
5
+ **Since v7.0.0:** Logging is **on by default**. `console.log` / `warn` / `error` / `info` / `debug` calls are automatically forwarded as OTLP log records. Disable with `SECURENOW_LOGGING_ENABLED=0` if you don't want it.
6
6
 
7
7
  ---
8
8
 
9
- ## 1. Install
9
+ ## 1. Install + login
10
10
 
11
11
  ```bash
12
12
  npm install securenow
13
+ npx securenow login # pick/create your app in the browser
13
14
  ```
14
15
 
15
- ---
16
-
17
- ## 2. Configure Environment
18
-
19
- Create `.env` file or export variables:
20
-
21
- ```bash
22
- SECURENOW_LOGGING_ENABLED=1
23
- SECURENOW_APPID=my-app
24
- SECURENOW_INSTANCE=http://your-otlp-backend:4318
25
-
26
- # For SecureNow / hosted OTLP (example):
27
- # SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
28
- # OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your-key>"
29
- ```
16
+ `login` writes `.securenow/credentials.json` locally. No `.env` setup required.
30
17
 
31
18
  ---
32
19
 
33
- ## 3. Add to Your App
20
+ ## 2. Add to Your App
34
21
 
35
22
  **Option A: Automatic Console Logging (Easiest)**
36
23
 
@@ -53,7 +40,7 @@ NODE_OPTIONS="-r securenow/register" node app.js
53
40
 
54
41
  ---
55
42
 
56
- ## 4. Run Your App
43
+ ## 3. Run Your App
57
44
 
58
45
  ```bash
59
46
  node app.js
@@ -70,12 +57,18 @@ You should see:
70
57
 
71
58
  ---
72
59
 
73
- ## 5. View Logs in SecureNow
60
+ ## 4. View Logs in SecureNow
74
61
 
75
62
  1. Open your SecureNow dashboard
76
63
  2. Go to **Logs** section
77
- 3. Filter by `service.name = my-app`
78
- 4. See all your logs with automatic trace correlation!
64
+ 3. Your logs appear under the app you picked during `securenow login`
65
+ 4. All logs come with automatic trace correlation
66
+
67
+ Or from the CLI:
68
+
69
+ ```bash
70
+ npx securenow logs --minutes 5
71
+ ```
79
72
 
80
73
  ---
81
74
 
@@ -102,20 +95,13 @@ app.listen(3000);
102
95
 
103
96
  ```typescript
104
97
  // instrumentation.ts (in project root)
105
- export async function register() {
106
- if (process.env.NEXT_RUNTIME === 'nodejs') {
107
- process.env.SECURENOW_LOGGING_ENABLED = '1';
108
- await import('securenow/register');
109
- }
98
+ import { registerSecureNow } from 'securenow/nextjs';
99
+ export function register() {
100
+ registerSecureNow();
110
101
  }
111
102
  ```
112
103
 
113
- ```bash
114
- # .env.local
115
- SECURENOW_LOGGING_ENABLED=1
116
- SECURENOW_APPID=my-nextjs-app
117
- SECURENOW_INSTANCE=http://localhost:4318
118
- ```
104
+ No `.env.local` needed — credentials come from `.securenow/credentials.json` after `npx securenow login`.
119
105
 
120
106
  ### Fastify
121
107
 
@@ -157,9 +143,10 @@ bootstrap();
157
143
 
158
144
  **Logs not appearing?**
159
145
 
160
- 1. Check `SECURENOW_LOGGING_ENABLED=1` is set
161
- 2. Verify your OTLP / SecureNow endpoint is correct
162
- 3. Enable debug: `OTEL_LOG_LEVEL=debug`
146
+ 1. Check `.securenow/credentials.json` exists (run `npx securenow whoami`).
147
+ 2. Confirm `SECURENOW_LOGGING_ENABLED` is not set to `0`.
148
+ 3. Run `npx securenow doctor` — it probes the full pipeline and reports the failure mode.
149
+ 4. Enable verbose output: `OTEL_LOG_LEVEL=debug`.
163
150
 
164
151
  **Console logs not forwarding?**
165
152
 
@@ -1,67 +1,77 @@
1
- # Next.js + SecureNow Quick Start
1
+ # Next.js + SecureNow 30 seconds
2
2
 
3
- ## Installation (30 seconds)
3
+ ## The whole setup
4
4
 
5
5
  ```bash
6
+ # 1. Install
6
7
  npm install securenow
7
- ```
8
-
9
- **🎉 The installer will automatically offer to create the instrumentation file!**
10
8
 
11
- Just answer "Y" when prompted, and it's done!
12
-
13
- ---
9
+ # 2. Pick (or create) your app in the browser — writes .securenow/ locally
10
+ npx securenow login
14
11
 
15
- ## Alternative: Manual Setup
12
+ # 3. Scaffold instrumentation.ts and wrap next.config.js
13
+ npx securenow init
16
14
 
17
- If you skipped auto-setup or want to do it manually:
15
+ # 4. Run
16
+ npm run dev
17
+ ```
18
18
 
19
- ### Option 1: Use CLI (Recommended)
19
+ No `.env.local` edits. No API key copy-paste. The app you picked in step 2 is where your traces land.
20
20
 
21
- ```bash
22
- npx securenow init
23
- ```
21
+ ---
24
22
 
25
- ### Option 2: Create File Manually
23
+ ## What `npx securenow init` generates
26
24
 
27
- Create `instrumentation.ts` at project root:
25
+ **`instrumentation.ts`** (or `.js`, auto-detected):
28
26
 
29
27
  ```typescript
30
28
  import { registerSecureNow } from 'securenow/nextjs';
31
- export function register() { registerSecureNow(); }
32
- ```
33
-
34
- ### 2. Create `.env.local`:
35
-
36
- ```bash
37
- SECURENOW_APPID=my-nextjs-app
38
- SECURENOW_INSTANCE=http://your-securenow:4318
29
+ export function register() {
30
+ registerSecureNow();
31
+ }
39
32
  ```
40
33
 
41
- ### 3. (Next.js 14 only) Update `next.config.js`:
34
+ It also tells you to wrap `next.config.js`:
42
35
 
43
36
  ```javascript
44
- module.exports = {
45
- experimental: { instrumentationHook: true }
46
- }
37
+ const { withSecureNow } = require('securenow/nextjs-webpack-config');
38
+
39
+ module.exports = withSecureNow({
40
+ // your existing config
41
+ });
47
42
  ```
48
43
 
49
- ## Run
44
+ `withSecureNow()` auto-detects Next.js 14 vs 15 vs 16 and sets the right externalization config.
50
45
 
51
- ```bash
52
- npm run dev
53
- ```
46
+ ---
54
47
 
55
48
  ## Verify
56
49
 
57
- Look for:
50
+ Start your app. In the console you should see:
51
+
58
52
  ```
59
- [securenow] OpenTelemetry started for Next.js
53
+ [securenow] Next.js integration loading (pid=…)
54
+ [securenow] ✅ OpenTelemetry started for Next.js → https://freetrial.securenow.ai:4318/v1/traces
60
55
  ```
61
56
 
62
- Open SecureNow → check for traces from `my-nextjs-app`
57
+ Then:
58
+
59
+ ```bash
60
+ npx securenow test-span # emit a test span
61
+ npx securenow traces # see it appear
62
+ ```
63
+
64
+ If `traces` shows your span under the app name you picked, you're done.
63
65
 
64
66
  ---
65
67
 
66
- **That's it!** See [NEXTJS-GUIDE.md](./NEXTJS-GUIDE.md) for advanced configuration.
68
+ ## Overriding for CI / Docker / Vercel
69
+
70
+ `.securenow/credentials.json` is for local dev. For anywhere you can't run `npx securenow login`, set env vars — they always win:
71
+
72
+ ```bash
73
+ SECURENOW_APPID=<app-key-uuid> # from: npx securenow apps
74
+ SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
75
+ ```
67
76
 
77
+ See [NEXTJS-GUIDE.md](./NEXTJS-GUIDE.md) for Vercel, standalone builds, and edge runtime details.