securenow 8.24.0 → 8.25.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/NPM_README.md CHANGED
@@ -1,25 +1,28 @@
1
- # SecureNow - Complete OpenTelemetry Observability for Node.js
1
+ # SecureNow - Complete OpenTelemetry Observability for Node.js
2
2
 
3
3
  > **Node.js security:** Use a supported, patched runtime: Node.js 22.23.4+, 24.22.1+, or 26.12.1+ on its respective release line. Earlier patches are affected by [CVE-2026-111497](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases). Upgrading the SecureNow package does not patch Node.js; upgrade the runtime and restart every application process.
4
4
 
5
- OpenTelemetry instrumentation library for Node.js, Next.js, and Nuxt applications. Send distributed traces and logs to any OTLP-compatible observability backend.
5
+ OpenTelemetry instrumentation and an in-process firewall for Node.js, Next.js, and Nuxt applications. Traces, logs, crashes and security events go to the SecureNow ingest gateway (`https://ingest.securenow.ai`), where detection runs; an advanced setting can point the OpenTelemetry export at your own OTLP collector instead.
6
+
7
+ This is the long reference. The step-by-step integration playbook for coding agents is [`SKILL-API.md`](./SKILL-API.md); the CLI by task is [`SKILL-CLI.md`](./SKILL-CLI.md). Items marked **(8.25.0+)** need securenow 8.25.0 or newer.
6
8
 
7
9
  **Features:**
8
- - Zero-config automatic instrumentation
9
- - Distributed tracing for all popular frameworks
10
- - Automatic logging with console instrumentation
11
- - Built-in sensitive data redaction
12
- - Request body capture for debugging
13
- - Multi-layer firewall -- auto-blocks IPs from your SecureNow blocklist
10
+ - Zero-config automatic instrumentation: one `-r securenow/register` flag for CommonJS, ESM and TypeScript apps
11
+ - Distributed tracing for Express, Fastify, NestJS, Koa, Hapi, Hono, Polka, Feathers, h3, raw `http`, Next.js and Nuxt
12
+ - Automatic logging with console instrumentation, and crash capture
13
+ - Built-in sensitive data redaction (field names, OTP codes, secret-shaped values)
14
+ - Request header and body capture for detection and debugging
15
+ - Multi-layer firewall -- blocks IPs from your SecureNow blocklist, plus rate limits (429) and proof-of-work challenges
16
+ - Custom security events (`securenow/events`) and session revocation (`securenow/sessions`)
14
17
  - `securenow init` scaffolds Next.js instrumentation, safe `serverExternalPackages`, and standalone output tracing includes
15
18
  - `securenow/firewall-only` entry point for firewall without tracing overhead
16
19
  - Split local credentials: `.securenow/admin.json` for CLI/MCP admin auth and `.securenow/runtime.json` for SDK runtime
17
- - Single `-r securenow/register` flag -- works for both CJS and ESM apps
18
- - Native Nuxt 3 module (`securenow/nuxt`)
20
+ - Native Nuxt 3 and 4 module (`securenow/nuxt`)
21
+ - CLI and MCP server for coding agents
19
22
 
20
23
  ---
21
24
 
22
- > **v8.0 credential model:** admin/control-plane CLI and MCP auth lives in `.securenow/admin.json`; SDK runtime app config and the runtime API key live in `.securenow/runtime.json`. `npx securenow login` can run both lanes for onboarding, `npx securenow admin login` refreshes only admin auth, and `npx securenow app connect` refreshes only runtime app config. Legacy combined `.securenow/credentials.json` files are still read, and production runtime exports can still be mounted as `.securenow/credentials.json`.
25
+ > **v8.0 credential model:** admin/control-plane CLI and MCP auth lives in `.securenow/admin.json`; SDK runtime app config and the runtime API key live in `.securenow/runtime.json`. `npx securenow login` can run both lanes for onboarding, `npx securenow admin login` refreshes only admin auth, and `npx securenow app connect` refreshes only runtime app config. Legacy combined `.securenow/credentials.json` files are still read, and production runtime exports are mounted as `.securenow/credentials.json`.
23
26
 
24
27
  ---
25
28
 
@@ -27,20 +30,26 @@ OpenTelemetry instrumentation library for Node.js, Next.js, and Nuxt application
27
30
 
28
31
  - [Installation](#installation)
29
32
  - [Quick Start](#quick-start)
30
- - [CLI -- Command Line Interface](#cli--command-line-interface)
31
- - [MCP for Codex and Claude](#mcp-for-codex-and-claude)
33
+ - [CLI -- Command Line Interface](#cli----command-line-interface)
32
34
  - [Framework-Specific Setup](#framework-specific-setup)
33
35
  - [Express.js](#expressjs)
34
- - [Next.js](#nextjs)
35
- - [Nuxt 3](#nuxt-3)
36
36
  - [Fastify](#fastify)
37
- - [NestJS](#nestjs)
38
37
  - [Koa](#koa)
38
+ - [NestJS](#nestjs)
39
39
  - [Hapi](#hapi)
40
- - [Firewall -- Automatic IP Blocking](#firewall--automatic-ip-blocking)
41
- - [Environment Variables Reference](#environment-variables-reference)
40
+ - [h3, Polka, raw HTTP, Hono, Feathers](#h3-unjs)
41
+ - [Next.js](#nextjs)
42
+ - [Nuxt 3 and 4](#nuxt-3-and-4)
43
+ - [Vite and Bun](#vite)
44
+ - [Compatibility Matrix](#compatibility-matrix)
45
+ - [Firewall -- Automatic IP Blocking](#firewall----automatic-ip-blocking)
46
+ - [Credentials Configuration](#credentials-configuration)
47
+ - [Entry Points Reference](#entry-points-reference)
42
48
  - [Logging Setup](#logging-setup)
43
49
  - [Request Body Capture](#request-body-capture)
50
+ - [Custom Security Events](#custom-security-events)
51
+ - [Session Revocation](#session-revocation)
52
+ - [Deployment](#deployment)
44
53
  - [Advanced Configuration](#advanced-configuration)
45
54
  - [TypeScript Support](#typescript-support)
46
55
  - [Troubleshooting](#troubleshooting)
@@ -50,7 +59,7 @@ OpenTelemetry instrumentation library for Node.js, Next.js, and Nuxt application
50
59
  ## Installation
51
60
 
52
61
  ```bash
53
- npm install securenow
62
+ npm install securenow@latest
54
63
  ```
55
64
 
56
65
  Or with yarn:
@@ -71,7 +80,7 @@ Run login - it is a browser flow that picks or creates an app and connects the f
71
80
  npx securenow login
72
81
  ```
73
82
 
74
- During the browser step, the dashboard enables the selected app's firewall toggle, mints a one-time plaintext runtime API key scoped to that app only (scopes: `traces:write`, `logs:write`, `firewall:read`, `blocklist:read`, `allowlist:read`), and the CLI writes it as `apiKey` in `.securenow/runtime.json`. Admin auth, when part of the flow, is saved separately to `.securenow/admin.json`. Traces, logs, POST body capture, multipart metadata capture, and firewall protection are enabled by default. No env vars, no copy-pasting keys.
83
+ During the browser step, the dashboard enables the selected app's firewall toggle, mints a one-time plaintext runtime API key scoped to that app only (scopes: `traces:write`, `logs:write`, `firewall:read`, `blocklist:read`, `allowlist:read`), and the CLI writes it as `apiKey` in `.securenow/runtime.json`. Admin auth, when part of the flow, is saved separately to `.securenow/admin.json`. Both files are added to `.gitignore` (without ignoring the whole `.securenow/` folder). Traces, logs, POST body capture, multipart metadata capture, and firewall protection are enabled by default. No env vars, no copy-pasting keys.
75
84
 
76
85
  Metrics export is intentionally off by default. SecureNow sets `OTEL_METRICS_EXPORTER=none` when the app has not explicitly configured metrics, preventing the OpenTelemetry SDK from creating a default metrics reader that retries `localhost:4318`.
77
86
 
@@ -84,10 +93,11 @@ npx securenow init --key snk_live_abc123...
84
93
  ```
85
94
 
86
95
  This detects your framework and:
87
- - **Credentials**: Ensures `.securenow/runtime.json` has secure defaults and explanations
88
- - **Next.js**: Creates `instrumentation.ts`, adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when safe, or prints a Codex/Claude-ready merge prompt for existing files
89
- - **Nuxt 3**: Suggests adding `securenow/nuxt` to modules
96
+ - **Credentials**: Ensures `.securenow/runtime.json` has secure defaults and explanations, with `config.runtime.deploymentEnvironment` set to `local` (change it with `--env staging`, etc.)
97
+ - **Next.js**: Creates `instrumentation.ts` (`src/instrumentation.ts` in a `src/` layout, 8.25.0+), adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when safe, or prints a Codex/Claude-ready merge prompt for existing files. Next.js 14 gets the `experimental` keys **(8.25.0+)**
98
+ - **Nuxt 3 and 4**: Reminds you to add `securenow/nuxt` to modules
90
99
  - **Express / Node.js**: Shows how to add `-r securenow/register` to your start script
100
+ - **PM2**: Warns when an ecosystem file passes a bare `-r securenow/register`
91
101
  - **All**: Stores an optional `--key` in `.securenow/runtime.json`; no local `.env` file is needed. If the runtime API key is missing, run `npx securenow app connect` or `npx securenow api-key create` before sending telemetry.
92
102
 
93
103
  ### 2. Manual Setup
@@ -104,7 +114,7 @@ Add `-r securenow/register` to your start command -- that's the only change:
104
114
  node -r securenow/register app.js
105
115
  ```
106
116
 
107
- ESM and CJS apps are both handled automatically (Node >=20.6 auto-registers the ESM loader hook via `module.register()`).
117
+ ESM and CJS apps are both handled automatically: for an ESM app (`"type": "module"` or an `.mjs` entry) the preload registers the OpenTelemetry ESM loader hook with `module.register()` (Node >= 20.6) and prints `[securenow] ESM loader hook auto-registered (module.register)`. TypeScript: `node -r securenow/register --import tsx src/index.ts`, or `node -r securenow/register -r ts-node/register src/index.ts`.
108
118
 
109
119
  **package.json** example:
110
120
 
@@ -121,6 +131,14 @@ ESM and CJS apps are both handled automatically (Node >=20.6 auto-registers the
121
131
  NODE_OPTIONS="-r securenow/register" npm start
122
132
  ```
123
133
 
134
+ Every Node process started with that variable loads SecureNow, npm itself included, so the flag in the start script is cleaner when you can edit it.
135
+
136
+ **Alternative: the CLI wrapper**
137
+
138
+ ```bash
139
+ npx securenow run app.js # adds the preload (and the ESM hook for ESM apps), passes Node flags through
140
+ ```
141
+
124
142
  **CJS only: Code-based initialization**
125
143
 
126
144
  ```javascript
@@ -132,21 +150,31 @@ const app = express();
132
150
  // ...
133
151
  ```
134
152
 
153
+ In an ESM app, don't `import 'securenow/register'` inside your code: static imports load before it runs, so libraries are not instrumented. Use the `-r` flag.
154
+
135
155
  You'll see confirmation in the console:
136
156
 
137
157
  ```
138
- [securenow] OTel SDK started -> https://ingest.securenow.ai/v1/traces
158
+ [securenow] preload loaded pid=48211
159
+ [securenow] pid=48211 appId="<app-uuid>" instance="https://ingest.securenow.ai" apiKey=set credentials=/srv/app/.securenow/runtime.json → service.name=<app-uuid> instance.id=<app-uuid>-<uuid>
160
+ [securenow] OTel SDK started → https://ingest.securenow.ai/v1/traces
161
+ [securenow] Logging: ENABLED → https://ingest.securenow.ai/v1/logs
162
+ [securenow] Request body capture: ENABLED (max: 10240 bytes, redacting 68 sensitive fields)
139
163
  [securenow] Firewall: ENABLED
140
- [securenow] Firewall: synced 142 blocked IPs (138 exact + 4 CIDR ranges)
164
+ [securenow] Firewall: environment=local
165
+ [securenow] Firewall: Layer 1 (HTTP 403) active
166
+ [securenow] Firewall: synced 142 global blocked IPs (138 exact + 4 CIDR ranges) and 0 scoped block rules
141
167
  ```
142
168
 
169
+ `credentials=` names the file (or `env:SECURENOW_CREDENTIALS`) the settings came from; `service.name` must be your app key.
170
+
143
171
  ---
144
172
 
145
173
  ## CLI -- Command Line Interface
146
174
 
147
- The `securenow` CLI gives you full access to the SecureNow platform from the terminal -- no browser required for day-to-day workflows. Zero additional dependencies.
175
+ The `securenow` CLI gives you full access to the SecureNow platform from the terminal -- no browser required for day-to-day workflows. Zero additional dependencies. `npx securenow help` lists every command and `npx securenow help <command>` its subcommands and flags (offline). [`SKILL-CLI.md`](./SKILL-CLI.md) organizes the commands by task.
148
176
 
149
- **Full CLI/SDK parity:** every SDK export has a matching CLI command. `redactSensitiveData` -> `securenow redact`, `createMatcher` -> `securenow cidr match`, `getLogger().emit()` -> `securenow log send`, startup smoke spans -> `securenow test-span`, `node -r securenow/firewall-only` -> `securenow run --firewall-only`. False-positive triage (`fp create`, `fp ai-fill`, `fp mark`) works from the terminal without the web dashboard.
177
+ **CLI/SDK parity:** SDK helpers have matching CLI commands. `redactSensitiveData` -> `securenow redact`, `createMatcher` -> `securenow cidr match`, `getLogger().emit()` -> `securenow log send`, `track()` -> `securenow event send`, startup smoke spans -> `securenow test-span`, `node -r securenow/firewall-only` -> `securenow run --firewall-only`. False-positive triage (`fp create`, `fp ai-fill`, `fp mark`) works from the terminal without the web dashboard.
150
178
 
151
179
  ### Getting Started
152
180
 
@@ -156,7 +184,7 @@ npx securenow login
156
184
 
157
185
  # Admin/control-plane CLI and MCP auth only.
158
186
  npx securenow admin login
159
- npx securenow admin login --token <YOUR_JWT>
187
+ npx securenow admin login --token <YOUR_TOKEN>
160
188
 
161
189
  # App/runtime SDK config only.
162
190
  npx securenow app connect
@@ -164,7 +192,7 @@ npx securenow app connect
164
192
  # Save to ~/.securenow/ instead of this project
165
193
  npx securenow login --global
166
194
 
167
- # Check who you're logged in as (shows auth source)
195
+ # Check who you're logged in as (admin auth and runtime app)
168
196
  npx securenow whoami
169
197
 
170
198
  # Need or already have a runtime API key? Create or store it without re-running login:
@@ -182,9 +210,12 @@ npx securenow init
182
210
 
183
211
  # Optional: pass an existing runtime API key to store it in .securenow/runtime.json
184
212
  npx securenow init --key snk_live_abc123...
213
+
214
+ # Force the instrumentation file language
215
+ npx securenow init --typescript # or --javascript
185
216
  ```
186
217
 
187
- For Next.js projects, `init` creates `instrumentation.ts` (or `.js` if no TypeScript), adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when safe, and prints exact merge instructions for Codex/Claude when existing files need judgment. For Nuxt, it suggests adding `securenow/nuxt` to your modules. For Express/Node, it shows the `-r securenow/register` flag.
218
+ For Next.js projects, `init` creates `instrumentation.ts` (or `.js` if no TypeScript), adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when safe, and prints exact merge instructions for Codex/Claude when existing files need judgment. For Nuxt, it reminds you to add `securenow/nuxt` to your modules. For Express/Node, it shows the `-r securenow/register` flag.
188
219
 
189
220
  ### MCP for Codex and Claude
190
221
 
@@ -192,14 +223,17 @@ SecureNow includes a local stdio MCP server that resolves credentials by operati
192
223
 
193
224
  ```bash
194
225
  npx securenow login
226
+ claude mcp add securenow -- npx securenow mcp
195
227
  codex mcp add securenow -- npx securenow mcp
196
- # or
228
+ # or run the bin directly
197
229
  npx -p securenow securenow-mcp
198
230
  ```
199
231
 
200
- Admin/global tools use `.securenow/admin.json`. App-scoped runtime reads can use `.securenow/runtime.json` or an explicit app key. The MCP surface exposes tools for applications, traces, logs, firewall, IP intelligence, forensics, notifications, blocklist, allowlist, trusted IPs, rate-limit remediation, and docs-backed prompts/resources. Write actions require `confirm:true` and a reason. Use `securenow_blocklist_unblock` to stop firewall enforcement while keeping the block report/history; `securenow_blocklist_remove` is a compatibility alias.
232
+ Cursor uses `.cursor/mcp.json`: `{ "mcpServers": { "securenow": { "command": "npx", "args": ["securenow", "mcp"] } } }`.
201
233
 
202
- For hosted clients, SecureNow can expose the same surface at `https://api.securenow.ai/mcp`. The hosted endpoint uses the same API authentication and scope checks as the rest of SecureNow.
234
+ Admin/global tools use `.securenow/admin.json`. App-scoped runtime reads can use `.securenow/runtime.json` or an explicit app key. The MCP surface exposes tools for applications, traces, logs, firewall, IP intelligence, forensics, notifications, both review queues, alert rules, starter protection, blocklist, allowlist, trusted IPs, rate limits, challenges, automations, and docs-backed prompts/resources. Write actions require `confirm:true` and a reason. Use `securenow_blocklist_unblock` to stop firewall enforcement while keeping the block report/history; `securenow_blocklist_remove` is a compatibility alias. `npx securenow mcp --profile investigator|detection|responder` serves a smaller tool set. Session revocation has no MCP tool: use `securenow revoke`.
235
+
236
+ For hosted clients, the same surface is at `https://api.securenow.ai/mcp`, with OAuth sign-in (or `Authorization: Bearer <API key>`), e.g. `claude mcp add --transport http securenow https://api.securenow.ai/mcp`. The hosted endpoint uses the same API authentication and scope checks as the rest of SecureNow.
203
237
 
204
238
  ### Managing Applications
205
239
 
@@ -220,12 +254,23 @@ npx securenow apps default <app-key>
220
254
  npx securenow apps delete <app-id> --force
221
255
  ```
222
256
 
257
+ ### Overview and attackers
258
+
259
+ ```bash
260
+ # The dashboard's Overview: what happened, what SecureNow did, what needs you
261
+ npx securenow today
262
+ npx securenow today --since 7d --attackers 20
263
+
264
+ # Why an address was flagged: verdict, signals, traffic vs normal, detections, timeline
265
+ npx securenow attacker 203.0.113.42
266
+ ```
267
+
223
268
  ### Viewing Traces
224
269
 
225
270
  ```bash
226
271
  # List recent traces (uses default app, or specify --app)
227
272
  npx securenow traces
228
- npx securenow traces --app <key> --limit 50
273
+ npx securenow traces --app <key> --env production --limit 50
229
274
 
230
275
  # Show detailed spans for a trace
231
276
  npx securenow traces show <traceId>
@@ -239,10 +284,13 @@ npx securenow traces analyze <traceId>
239
284
  ```bash
240
285
  # List recent logs
241
286
  npx securenow logs
242
- npx securenow logs --app <key> --minutes 30 --level error
287
+ npx securenow logs --app <key> --since 30m --level error
243
288
 
244
289
  # Show logs for a specific trace
245
290
  npx securenow logs trace <traceId>
291
+
292
+ # What is failing: error lines grouped by message
293
+ npx securenow logs errors --since 6h
246
294
  ```
247
295
 
248
296
  ### Notifications
@@ -250,6 +298,7 @@ npx securenow logs trace <traceId>
250
298
  ```bash
251
299
  # List notifications
252
300
  npx securenow notifications
301
+ npx securenow notifications list --search "SQL injection" --severity high
253
302
 
254
303
  # Check unread count
255
304
  npx securenow notifications unread
@@ -259,30 +308,24 @@ npx securenow notifications read <id>
259
308
  npx securenow notifications read-all
260
309
  ```
261
310
 
311
+ The two review queues: `npx securenow review` (Alert Review: "is this rule right?") and `npx securenow human` (Requires Human: block, false positive or close). See SKILL-CLI.md.
312
+
262
313
  ### Alerting
263
314
 
264
- ### Revoke / kill sessions (new in 8.3)
315
+ #### Revoke / kill sessions
265
316
 
266
- ```js
267
- const securenow = require('securenow/sessions');
268
- // One line: emits session.seen (feeds impossible-travel / concurrent-session
269
- // detection) AND rejects revoked sessions. Auto-detects NextAuth/connect.sid/JWT.
270
- app.use(securenow.guard());
271
- // ...or check manually:
272
- if (securenow.isRevoked({ sessionId, userId })) return res.status(401).send('re-auth');
273
- // ...or emit-only (detection without enforcement):
274
- app.use(securenow.capture());
275
- ```
276
-
277
- Revoke from the CLI (the SDK enforces within ~seconds, outbound-only, fail-open):
317
+ Revoke from the CLI; the SDK enforces it within 30 seconds (outbound sync, fail-open):
278
318
 
279
319
  ```bash
280
320
  npx securenow revoke session <session-id> --reason "impossible travel"
281
321
  npx securenow revoke user <user-id> --duration 7d # kill all of a user's sessions
282
322
  npx securenow revoke list
323
+ npx securenow revoke restore <revocation-id>
283
324
  ```
284
325
 
285
- ### Emit custom security events (new in 8.2)
326
+ How the app enforces it (zero-code config **(8.25.0+)**, `guard()` middleware, or `check()` **(8.25.0+)**): [Session Revocation](#session-revocation).
327
+
328
+ #### Emit custom security events
286
329
 
287
330
  ```js
288
331
  const { track } = require('securenow/events');
@@ -296,19 +339,19 @@ track('auth.login.success', {
296
339
  });
297
340
  ```
298
341
 
299
- Events become queryable by alert rules immediately (`attributes_string['event.type']`, `['enduser.id']`, `['session.id']`, `['http.client_ip']`). Non-JS apps emit the same thing with a plain POST to `/v1/events`:
342
+ Events become queryable by alert rules immediately (`attributes_string['event.type']`, `['enduser.id']`, `['session.id']`, `['http.client_ip']`). Non-JS apps emit the same thing with a plain POST to `/v1/events` on the ingest gateway:
300
343
 
301
344
  ```
302
- POST https://<your-ingest-host>/v1/events
345
+ POST https://ingest.securenow.ai/v1/events
303
346
  Authorization: Bearer snk_live_...
304
347
  X-SecureNow-App-Key: <app-uuid>
305
348
 
306
349
  { "events": [ { "type": "auth.login.success", "user_id": "u_1", "session_id": "s_1", "ip": "1.2.3.4" } ] }
307
350
  ```
308
351
 
309
- CLI: `npx securenow event send auth.login.failure --user u_1 --ip 1.2.3.4 --attrs reason=bad_token`
352
+ CLI: `npx securenow event send auth.login.failure --user u_1 --ip 1.2.3.4 --attrs reason=bad_token`. More in [Custom Security Events](#custom-security-events).
310
353
 
311
- ### Build alert SQL from the installed telemetry contract
354
+ #### Build alert SQL from the installed telemetry contract
312
355
 
313
356
  The NPM package ships the exact ClickHouse table/field contract used for alert-rule authoring.
314
357
  Agents should read it instead of guessing SQL columns:
@@ -338,13 +381,20 @@ Mark detections `NEEDS_INSTRUMENTATION` when the repository must add a `track()`
338
381
  verification plus SQL validation. See `node_modules/securenow/TELEMETRY-SQL.md` for tables,
339
382
  timestamp handling, privacy rules, templates, and the full test-to-production lifecycle.
340
383
 
384
+ #### Rules from English, SQL rules, runs
385
+
341
386
  ```bash
342
- # Describe a detection in English (new in 8.16): SecureNow picks an instant rule (every request,
387
+ # Describe a detection in English: SecureNow picks an instant rule (every request,
343
388
  # as it arrives) or a periodic one (SQL on a schedule), writes it from your app's real data,
344
389
  # checks it against that data, and creates it in test mode when you confirm
345
390
  npx securenow alerts rules from-text "a sign-in from a new address, then a password change within 5 minutes" --app <app-key>
391
+ npx securenow alerts rules from-text "one IP failing 10 sign-ins in 5 minutes" --app <app-key> --draft-only
346
392
 
347
- # Create a custom detection rule from your own SQL (new in 8.1)
393
+ # Test it: dry run on real data, or live with 6 synthetic attack requests
394
+ npx securenow alerts rules test <rule-id> --wait
395
+ npx securenow alerts rules test <rule-id> --mode live --wait
396
+
397
+ # Create a custom detection rule from your own SQL
348
398
  npx securenow alerts rules create \
349
399
  --name "Auth: magic-link brute force" \
350
400
  --sql @rule.sql \
@@ -352,11 +402,17 @@ npx securenow alerts rules create \
352
402
  --severity high \
353
403
  --nlp "single IP flooding /api/auth/signin or /api/auth/callback"
354
404
 
355
- # What each run did, run one now, and why a rule didn't alert on an address (new in 8.17)
405
+ # What each run did, run one now, and why a rule didn't alert on an address
356
406
  npx securenow alerts rules runs <rule-id>
357
407
  npx securenow alerts rules run <rule-id>
358
408
  npx securenow alerts rules why <rule-id> --ip 203.0.113.50 --since 2h
359
409
 
410
+ # Rewrite from English, check precision, promote (a person) or ask for promotion (an agent)
411
+ npx securenow alerts rules change <rule-id> "raise the threshold to 20"
412
+ npx securenow alerts rules quality <rule-id>
413
+ npx securenow alerts rules promote <rule-id> --yes
414
+ npx securenow alerts rules request-promotion <rule-id> --reason "Quiet for a week, caught the replayed attack"
415
+
360
416
  # View / manage alert rules, channels, and history
361
417
  npx securenow alerts rules
362
418
  npx securenow alerts rules show <rule-id>
@@ -368,7 +424,17 @@ npx securenow alerts channels
368
424
  npx securenow alerts history --limit 20
369
425
  ```
370
426
 
371
- Your detection SQL scopes app keys with the `__USER_APP_KEYS__` placeholder and selects an `ip` column for per-IP aggregation and remediation (SQL without one is refused: it would never notify). SecureNow stores the query, runs it once right away and then on the rule's schedule (default every 15 min), and routes matches to your in-app channel (override with `--channel <id>`). The throttle sends one alert per address every 15 minutes by default; other addresses still alert. Requires an API key with `alerts:write`.
427
+ Your detection SQL scopes app keys with the `__USER_APP_KEYS__` placeholder and selects an `ip` column for per-IP aggregation and remediation (SQL without one is refused: it would never notify). SecureNow stores the query, runs it once right away and then on the rule's schedule (default every 15 min), and routes matches to your in-app channel (override with `--channel <id>`). The throttle sends one alert per address every 15 minutes by default; other addresses still alert. New rules start in test mode (alerts only: no instant block, automation or agent action) until promoted. Requires an API key with `alerts:write`.
428
+
429
+ #### Starter protection
430
+
431
+ ```bash
432
+ npx securenow protect # mode, rules, alerts, ready to block?
433
+ npx securenow protect block --app <key> # a signed-in person turns blocking on
434
+ npx securenow protect monitor --app <key>
435
+ ```
436
+
437
+ Every app gets about 20 starter rules in monitor mode on first traffic. Blocking is refused while more than 20% of the app's traffic arrives through a proxy the SDK doesn't trust.
372
438
 
373
439
  ### IP Intelligence & Blocklist
374
440
 
@@ -382,6 +448,7 @@ npx securenow ip traces 203.0.113.42
382
448
  # Manage the blocklist
383
449
  npx securenow blocklist
384
450
  npx securenow blocklist add 203.0.113.42 --reason "Brute force scanner"
451
+ npx securenow blocklist add 203.0.113.42 --route /admin --mode prefix --duration 24h --reason "Admin probing"
385
452
  npx securenow blocklist unblock <id> --reason "Reviewed as safe"
386
453
  npx securenow blocklist remove <id> # compatibility alias
387
454
  npx securenow blocklist list --status removed
@@ -393,6 +460,25 @@ npx securenow trusted add 10.0.0.1 --label "Office VPN"
393
460
  npx securenow trusted remove <id>
394
461
  ```
395
462
 
463
+ ### Rate Limits & Challenges
464
+
465
+ ```bash
466
+ # Rate limits (429 + Retry-After), applied by running apps within about 10 seconds
467
+ npx securenow ratelimit add --route /api/login --method POST --limit 5 --window 1m --env production
468
+ npx securenow ratelimit add --route /api/ --limit 100 --window 1m --header x-api-key
469
+ npx securenow ratelimit from-text "5 per minute per IP on POST /api/login for 2h" --yes
470
+ npx securenow ratelimit test 203.0.113.42 --path /api/login --method POST
471
+ npx securenow ratelimit list
472
+
473
+ # Proof-of-work challenges (browsers solve it invisibly; a pass lasts 30 minutes by default)
474
+ npx securenow challenge add --route /login --clearance 30m
475
+ npx securenow challenge test 203.0.113.42 --path /login
476
+ npx securenow challenge list
477
+
478
+ # Who the firewall stopped
479
+ npx securenow firewall hits --since 1h
480
+ ```
481
+
396
482
  ### Forensic Queries
397
483
 
398
484
  Ask questions in plain English -- the AI translates them to SQL and runs them against your data.
@@ -402,20 +488,15 @@ Ask questions in plain English -- the AI translates them to SQL and runs them ag
402
488
  npx securenow forensics "show top 10 attacking IPs in the last hour"
403
489
  npx securenow forensics "which endpoints had 5xx errors today"
404
490
 
491
+ # Chat with memory: follow-ups see earlier answers
492
+ npx securenow forensics chat --app <key> "which accounts had failed logins then a success?"
493
+ npx securenow forensics chat --conversation <id> "what else did those IPs do?"
494
+ npx securenow forensics conversations
495
+
405
496
  # Browse the saved query library
406
497
  npx securenow forensics library
407
498
  ```
408
499
 
409
- ### API Map
410
-
411
- ```bash
412
- # View all discovered API endpoints
413
- npx securenow api-map
414
-
415
- # API map statistics
416
- npx securenow api-map stats
417
- ```
418
-
419
500
  ### Analytics & Dashboard
420
501
 
421
502
  ```bash
@@ -424,6 +505,7 @@ npx securenow analytics --app <key>
424
505
 
425
506
  # Full dashboard overview (apps, protection status, unread alerts)
426
507
  npx securenow status
508
+ npx securenow status --env all
427
509
  ```
428
510
 
429
511
  ### Instances
@@ -467,28 +549,31 @@ npx securenow fp edit <id> --active false
467
549
  npx securenow fp delete <id> --yes
468
550
  ```
469
551
 
470
- ### Telemetry -- Emit Logs and Spans From the Shell
552
+ ### Telemetry -- Emit Logs, Events and Spans From the Shell
471
553
 
472
- Mirrors the SDK's `getLogger()` and tracing APIs. Useful for cron jobs, shell scripts, and CI pipelines that need to push events into SecureNow **without booting the full OTel SDK**.
554
+ Mirrors the SDK's `getLogger()`, `track()` and tracing APIs. Useful for cron jobs, shell scripts, CI pipelines and non-JS services that need to push data into SecureNow **without booting the full OTel SDK**.
473
555
 
474
556
  ```bash
475
- # Send a structured log record to your OTLP collector
557
+ # Send a structured log record
476
558
  npx securenow log send "Deployment completed" --level info --attrs version=1.2.3,service=api
477
559
  npx securenow log send "Backup failed" --level error --attrs host=db-01
478
560
 
479
- # Emit a test span to verify the collector accepts OTLP traffic
561
+ # Send a custom security event (same as track())
562
+ npx securenow event send auth.login.failure --user u_1 --ip 203.0.113.10 --attrs reason=bad_password
563
+
564
+ # Emit a test span to verify the ingest accepts OTLP traffic
480
565
  npx securenow test-span
481
566
  npx securenow test-span "ci.smoke-test" # custom span name
482
567
  ```
483
568
 
484
- Both commands use the resolved credentials JSON endpoints and headers. Non-zero exit on HTTP errors so CI/cron can detect failures.
569
+ All three use the resolved runtime credentials (file or `SECURENOW_CREDENTIALS`) and exit non-zero on HTTP errors so CI/cron can detect failures.
485
570
 
486
571
  ### Utilities -- Redaction, CIDR, Diagnostics
487
572
 
488
573
  SDK helpers surfaced as CLI commands so agents (and humans) can validate behavior without writing Node.
489
574
 
490
575
  ```bash
491
- # Redact sensitive fields (password, token, card, ssn, etc.)
576
+ # Redact sensitive fields (password, token, email, card, OTP codes, etc.)
492
577
  npx securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
493
578
  npx securenow redact @request.json --fields internal_id,sessionHash
494
579
 
@@ -503,6 +588,10 @@ npx securenow env --json # pipe to jq
503
588
  # End-to-end diagnostic: probe OTLP + API endpoints
504
589
  npx securenow doctor # exits 0 if healthy, 1 otherwise
505
590
  npx securenow doctor --json
591
+
592
+ # Threat-modeling prompts for your coding agent
593
+ npx securenow threat-model --list
594
+ npx securenow threat-model authentication --out threat-prompt.md
506
595
  ```
507
596
 
508
597
  ### Configuration
@@ -514,7 +603,6 @@ npx securenow config get
514
603
  # Set values
515
604
  npx securenow config set apiUrl https://custom-api.example.com
516
605
  npx securenow config set defaultApp <app-key>
517
- npx securenow config set format json
518
606
 
519
607
  # Show config file paths
520
608
  npx securenow config path
@@ -524,15 +612,15 @@ Config files are stored in `~/.securenow/` (global) or `.securenow/` in the proj
524
612
 
525
613
  | File | Description |
526
614
  |------|-------------|
527
- | `~/.securenow/config.json` | API URL, default app, output format |
615
+ | `~/.securenow/config.json` | API URL, dashboard URL, default app |
528
616
  | `~/.securenow/admin.json` | Global admin/control-plane CLI and MCP auth |
529
617
  | `~/.securenow/runtime.json` | Global SDK runtime app config and runtime API key |
530
618
  | `.securenow/admin.json` | Project-local admin/control-plane CLI and MCP auth |
531
619
  | `.securenow/runtime.json` | Project-local SDK runtime app config and runtime API key |
532
- | `.securenow/credentials.json` | Legacy combined credentials; still read for backward compatibility |
620
+ | `.securenow/credentials.json` | Legacy combined credentials, or the production runtime file mounted on a server |
533
621
  | `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
534
622
 
535
- **Resolution order:** runtime config resolves from project `.securenow/runtime.json` -> legacy project `.securenow/credentials.json` -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global `.securenow/runtime.json` -> legacy global credentials -> global named runtime credentials. Admin auth resolves from `admin.json` first, then legacy `credentials.json`. Legacy CLI token overrides still work for existing automation.
623
+ **Resolution order:** the SDK reads `SECURENOW_CREDENTIALS` first. Otherwise, starting from the working directory and the entry script's folder (under PM2, the app folder PM2 recorded) and walking up through parent folders, the first file found wins, checked in this order in each folder: `.securenow/runtime.json`, `.securenow/credentials.json`, then named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order. Then the same names in `~/.securenow/`. Files are never merged. Admin auth resolves from `admin.json` first, then legacy `credentials.json`.
536
624
 
537
625
  ### Global Flags
538
626
 
@@ -542,16 +630,20 @@ Every command supports these flags:
542
630
  |------|-------|-------------|
543
631
  | `--json` | `-j` | Output as JSON for scripting and CI/CD |
544
632
  | `--help` | | Show help for the command |
545
- | `--app <key>` | | Override the default application key |
633
+ | `--yes` | `-y` | Confirm a write without the prompt |
634
+
635
+ Most commands also accept `--app <key>` (target another app) and `--env <environment>` (default `production` for security commands).
546
636
 
547
637
  ### CLI Configuration
548
638
 
549
- Normal SDK runtime setup uses `.securenow/runtime.json`; admin CLI/MCP auth uses `.securenow/admin.json`.
550
- SecureNow-specific environment overrides are no longer supported by the SDK or CLI.
639
+ Normal SDK runtime setup uses `.securenow/runtime.json`; admin CLI/MCP auth uses `.securenow/admin.json`. The SDK reads only `SECURENOW_CREDENTIALS` and `SECURENOW_DISABLED` from the environment; every other `SECURENOW_*` variable is ignored.
551
640
 
552
- | Override | Description |
641
+ | Variable | Description |
553
642
  |----------|-------------|
554
643
  | `NO_COLOR` | Disable colored CLI output |
644
+ | `SECURENOW_MCP_PROFILE` | MCP tool profile (`investigator`, `detection`, `responder`), same as `securenow mcp --profile` |
645
+ | `SECURENOW_CREDENTIALS` | Runtime credentials JSON (raw or base64) for the SDK and for `env`, `doctor`, `test-span`, `log send`, `event send` |
646
+ | `SECURENOW_DISABLED` | `1` turns the SDK off in that process (test runs) |
555
647
 
556
648
  ### Multi-Project Sessions
557
649
 
@@ -580,8 +672,9 @@ npx securenow credentials runtime --env production
580
672
 
581
673
  # Store .securenow/credentials.production.json as a deployment secret file,
582
674
  # then materialize it as .securenow/credentials.json in the running app.
583
- # Since v7.7.2, mounting it as .securenow/credentials.production.json
584
- # also works when the canonical credentials.json file is absent.
675
+ # Mounting it as .securenow/credentials.production.json also works when no
676
+ # other credentials file is present (a runtime.json or credentials.json
677
+ # next to it would win).
585
678
  # The SDK checks named files in a fixed order and does not use env vars
586
679
  # to pick the credentials filename.
587
680
  # Hosts without file mounts (Vercel, Railway, Render, Heroku, Fly):
@@ -589,6 +682,9 @@ npx securenow credentials runtime --env production --format env
589
682
  # -> SECURENOW_CREDENTIALS=<base64>; store it as a secret environment variable.
590
683
  # It takes precedence over credential files.
591
684
 
685
+ # Test runs: keep them from reporting to the real app
686
+ SECURENOW_DISABLED=1 npm test
687
+
592
688
  # Use --json for machine-readable output
593
689
  npx securenow logs --json --level error | jq '.logs'
594
690
  ```
@@ -597,59 +693,77 @@ npx securenow logs --json --level error | jq '.logs'
597
693
 
598
694
  | Category | Command | Description |
599
695
  |----------|---------|-------------|
600
- | **Setup** | `init` | Auto-scaffold instrumentation for your framework |
696
+ | **Setup** | `init` | Auto-scaffold instrumentation for your framework (`--env`, `--key`, `--typescript`/`--javascript`) |
697
+ | | `run <script>` | Run a Node app with the preload (`--firewall-only`, Node flags pass through) |
601
698
  | **Auth** | `login` | Friendly onboarding: admin auth + app runtime connection |
602
- | | `admin login` | Authenticate admin/control-plane CLI and MCP tools |
699
+ | | `admin login` / `admin logout` | Authenticate (or clear) admin/control-plane CLI and MCP tools |
603
700
  | | `app connect` | Select/create app and write SDK runtime config |
604
701
  | | `logout` | Clear admin auth; runtime app config remains |
605
702
  | | `whoami` | Show admin auth and runtime app status separately |
703
+ | | `credentials runtime` | Tokenless production runtime file (`--env`, `--out`, `--stdout`, `--format env`) |
606
704
  | **Apps** | `apps` | List applications |
607
- | | `apps create <name>` | Create application |
705
+ | | `apps create <name>` | Create application (`--hosts`, `--instance`) |
608
706
  | | `apps info <id>` | Application details |
609
707
  | | `apps delete <id>` | Delete application |
610
708
  | | `apps default <key>` | Set default app |
709
+ | | `apps discover` / `apps scan` | Discover subdomains and add them as apps |
611
710
  | **API Key** | `api-key create [--name "CLI runtime"] [--app <key-or-id>] [--global]` | Mint and save a one-app `runtime_app` key with your logged-in session. Defaults to the current runtime app |
612
711
  | | `api-key set <snk_live_...> [--global]` | Save runtime API key to `.securenow/runtime.json` |
613
712
  | | `api-key show` | Show masked key + source |
614
713
  | | `api-key clear [--global]` | Remove stored key (leaves session/app) |
615
- | **Observe** | `traces` | List traces |
714
+ | **Observe** | `today` | The Overview: what happened, what SecureNow did, what needs you |
715
+ | | `attacker <ip>` | Why an address was flagged |
716
+ | | `traces` | List traces |
616
717
  | | `traces show <id>` | Trace details |
617
718
  | | `traces analyze <id>` | AI trace analysis |
618
719
  | | `logs` | List logs |
619
720
  | | `logs trace <id>` | Logs for a trace |
721
+ | | `logs errors` | Error lines grouped by message |
620
722
  | | `analytics` | Response analytics |
621
723
  | | `status` | Dashboard overview |
622
- | **Detect** | `notifications` | List notifications |
724
+ | **Detect** | `protect` | Starter protection: `status`, `enable`, `disable`, `block`, `monitor`, `learn` |
725
+ | | `notifications` | List notifications |
623
726
  | | `notifications unread` | Unread count |
624
727
  | | `notifications read <id>` | Mark read |
625
728
  | | `notifications read-all` | Mark all read |
729
+ | | `review` | Alert Review: `list`, `show`, `keep`, `close`, `dismiss`, `group` |
730
+ | | `human` | Requires Human: `list`, `show`, `block`, `fp`, `close`, `report`, `action`, `prompt`, `work`, `promotions`, `approve-promotion`, `deny-promotion` |
731
+ | | `agent` | The autonomous agent: `status`, `on`, `off`, `mode`, `activity`, `undo`, `runs`, `run`, `verify`, `prompt`, `prompts` |
626
732
  | | `alerts rules` | List rules (status, apps, schedule) |
733
+ | | `alerts rules from-text "<english>" --app <key>` | Draft, check and create a rule from English (test mode) |
734
+ | | `alerts rules create` | Create a rule from SQL (`--sql`) or instant conditions (`--conditions`) |
627
735
  | | `alerts rules show <id>` | Rule detail |
628
736
  | | `alerts rules update <id> --applications-all` / `--apps k1,k2` | Application scope |
737
+ | | `alerts rules test <id> [--mode live] --wait` | Dry run, or live with synthetic attacks |
629
738
  | | `alerts rules runs <id>` / `run <id>` | Run history (outcome of each run) / run a periodic rule now |
630
739
  | | `alerts rules why <id> --ip <ip>` | Why the rule did or didn't alert on an address |
740
+ | | `alerts rules change <id> "<english>"` | Rewrite a rule from English as a new version |
741
+ | | `alerts rules promote <id>` / `demote <id>` | Test mode to prod and back (a person) |
742
+ | | `alerts rules request-promotion <id>` | Ask a person to promote (agents) |
743
+ | | `alerts rules validate --sql @rule.sql --app <key>` | Dry-run candidate SQL |
631
744
  | | `alerts channels` | Alert channels |
632
745
  | | `alerts history` | Alert history |
633
746
  | **Investigate** | `ip <addr>` | IP intelligence |
634
747
  | | `ip traces <addr>` | Traces from IP |
635
748
  | | `forensics "<query>"` | NL forensic query |
749
+ | | `forensics chat` / `forensics conversations` | Forensics chat with memory |
636
750
  | | `forensics library` | Saved queries |
637
- | | `api-map` | API endpoints |
638
- | | `api-map stats` | API stats |
639
- | **Firewall** | `firewall status` | Firewall status and API key info |
751
+ | **Firewall** | `firewall status` | Firewall status and layers |
752
+ | | `firewall apps` | Apps with their firewall on/off state |
753
+ | | `firewall enable` / `firewall disable` | Per-app, per-environment toggle |
640
754
  | | `firewall test-ip <ip>` | Check if an IP would be blocked |
755
+ | | `firewall hits` | Blocks, 429s and challenges per rule and per IP |
641
756
  | | `run --firewall-only <script>` | Preload firewall without OTel tracing overhead |
642
757
  | **Remediate** | `blocklist` | Blocked IPs |
643
- | | `automation` | List blocklist automation rules |
644
- | | `automation dry-run <id>` | Preview automation matches without writing blocks |
645
- | | `automation execute <id> --yes` | Run an automation rule now |
646
- | | `blocklist add <ip>` | Block IP |
758
+ | | `blocklist add <ip>` | Block IP (`--route`, `--mode`, `--method`, `--duration`, `--reason`) |
647
759
  | | `blocklist unblock <id>` | Unblock IP and retain report/history |
648
760
  | | `blocklist remove <id>` | Compatibility alias for unblock |
761
+ | | `blocklist approve <id>` / `blocklist reject <id>` | Decide a pending block |
649
762
  | | `blocklist stats` | Block stats |
650
- | | `ratelimit parse "<request>"` | Fill a rate-limit draft from natural language |
651
- | | `ratelimit from-text "<request>" --yes` | Create a soft rate-limit rule from natural language |
652
- | | `ratelimit test <ip> --path /api/login` | Check whether a request would match rate-limit rules |
763
+ | | `automation` | List blocklist automation rules (`defaults`, `show`, `create`, `update`, `dry-run`, `execute`, `delete`) |
764
+ | | `ratelimit add` / `from-text` / `parse` / `test` / `list` / `enable` / `disable` / `remove` | Rate-limit rules (alias `rate-limit`) |
765
+ | | `challenge add` / `test` / `list` / `enable` / `disable` / `remove` | Proof-of-work challenge rules |
766
+ | | `revoke session <id>` / `revoke user <id>` / `revoke list` / `revoke restore <id>` | Session revocation |
653
767
  | | `allowlist` | Allowed IPs (restrict-mode) |
654
768
  | | `allowlist add <ip>` | Allow IP (`--label`, `--reason`) |
655
769
  | | `allowlist remove <id>` | Remove from allowlist |
@@ -666,17 +780,21 @@ npx securenow logs --json --level error | jq '.logs'
666
780
  | | `fp dry-run --conditions '[...]'` | Dry-run against last 3 days of traces |
667
781
  | | `fp ai-fill --description "..."` | AI-generate exclusion conditions |
668
782
  | | `fp mark <notification-id> <ip>` | Mark an IP as FP on a notification |
669
- | **Telemetry** | `log send "<msg>" --level info --attrs k=v` | Emit an OTLP log record |
783
+ | **Telemetry** | `telemetry schema` / `telemetry fields` | Alert-SQL contract and fields observed for an app |
784
+ | | `log send "<msg>" --level info --attrs k=v` | Emit an OTLP log record |
785
+ | | `event send <type> --user <id> --ip <ip>` | Emit a custom security event |
670
786
  | | `test-span [<name>]` | Emit a test span to the collector |
671
787
  | **Utilities** | `redact '<json>' [--fields f1,f2]` | Redact sensitive fields (accepts `@file.json`) |
672
788
  | | `cidr match <ip> <cidrs>` | IP vs. CIDR list (exit 0 hit / 2 miss) |
673
789
  | | `cidr parse <cidr>` | Parse CIDR (network, broadcast, mask, size) |
674
790
  | | `env [--json]` | Show resolved config (service name, endpoints, credentials) |
675
791
  | | `doctor [--json]` | Probe OTLP + API endpoints, check config |
792
+ | | `threat-model <category>` | Print a threat-modeling prompt (`--list`, `--out`) |
793
+ | | `mcp [--profile <name>]` | Start the MCP server over stdio |
676
794
  | **Settings** | `instances` | List instances |
677
795
  | | `instances test <id>` | Test connection |
678
796
  | | `config get` | Show config |
679
- | | `config set <k> <v>` | Set config value |
797
+ | | `config set <k> <v>` | Set config value (`apiUrl`, `appUrl`, `defaultApp`) |
680
798
  | | `config path` | Config file paths |
681
799
  | | `version` | Show version |
682
800
 
@@ -684,8 +802,10 @@ npx securenow logs --json --level error | jq '.logs'
684
802
 
685
803
  ## Framework-Specific Setup
686
804
 
687
- > **v5.6.0+:** When `config.logging.enabled` is `true`, all `console.log`/`warn`/`error`/`info`/`debug` calls
688
- > are **automatically** forwarded as OTLP log records. The separate `require('securenow/console-instrumentation')` is no longer needed (but still available for backward compat).
805
+ > When `config.logging.enabled` is `true` (the default), all `console.log`/`warn`/`error`/`info`/`debug` calls
806
+ > are **automatically** forwarded as OTLP log records. No extra require is needed.
807
+
808
+ Every server below runs on `node:http`, so one preload covers it: `node -r securenow/register <entry>`. In a CommonJS entry, `require('securenow/register')` as the very first line is equivalent. The examples contain no SecureNow code.
689
809
 
690
810
  ### Express.js
691
811
 
@@ -695,7 +815,6 @@ npm install securenow express
695
815
 
696
816
  ```javascript
697
817
  // app.js
698
- require('securenow/register');
699
818
  const express = require('express');
700
819
 
701
820
  const app = express();
@@ -712,9 +831,11 @@ app.listen(3000, () => console.log('Express running on port 3000'));
712
831
  ```
713
832
 
714
833
  ```bash
715
- node app.js
834
+ node -r securenow/register app.js
716
835
  ```
717
836
 
837
+ Express 4 and 5 record `http.route`. One span per middleware is off by default (`config.otel.middlewareSpans: true` turns it on).
838
+
718
839
  #### With PM2
719
840
 
720
841
  ```javascript
@@ -723,6 +844,7 @@ module.exports = {
723
844
  apps: [{
724
845
  name: 'my-app',
725
846
  script: './app.js',
847
+ cwd: __dirname,
726
848
  instances: 'max', // or leave out for fork mode
727
849
  // An absolute path, resolved when PM2 loads this file: in cluster mode Node resolves a bare
728
850
  // '-r securenow/register' from the PM2 daemon's folder, so workers started by `pm2 resurrect`
@@ -732,7 +854,7 @@ module.exports = {
732
854
  };
733
855
  ```
734
856
 
735
- > **Important:** Put `.securenow/runtime.json` in the PM2 app root for local/dev deployments, or mount the production runtime export as `.securenow/credentials.json`. PM2 restarts will then load the SDK, traces/logs, body capture, and firewall without any SecureNow env vars. Fork and cluster mode both read the app folder PM2 started (`pm_cwd`), whichever folder the PM2 daemon itself was started from (before 8.24.0 a cluster worker searched the daemon's folder and could start without credentials).
857
+ > **Important:** Put `.securenow/runtime.json` in the PM2 app root for local/dev deployments, or mount the production runtime export as `.securenow/credentials.json`. PM2 restarts will then load the SDK, traces/logs, body capture, and firewall without any SecureNow env vars. Fork and cluster mode both read the app folder PM2 started (`pm_cwd`), whichever folder the PM2 daemon itself was started from (before 8.24.0 a cluster worker searched the daemon's folder and could start without credentials). After changing `node_args`, run `pm2 delete my-app && pm2 start ecosystem.config.cjs && pm2 save`.
736
858
 
737
859
  ---
738
860
 
@@ -744,7 +866,6 @@ npm install securenow fastify
744
866
 
745
867
  ```javascript
746
868
  // app.js
747
- require('securenow/register');
748
869
  const Fastify = require('fastify');
749
870
  const fastify = Fastify({ logger: true });
750
871
 
@@ -763,7 +884,11 @@ fastify.listen({ port: 3000 }, (err) => {
763
884
  });
764
885
  ```
765
886
 
766
- > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. If a specific Fastify version or plugin stack reports request-stream conflicts, set `config.capture.body=false` in credentials as a local override.
887
+ ```bash
888
+ node -r securenow/register app.js
889
+ ```
890
+
891
+ > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. The route template (`http.route`, span name `GET /tasks`) and handler exceptions are recorded through Fastify's `fastify.initialization` diagnostics channel (8.24.0+). Tested on Fastify 5.12.
767
892
 
768
893
  ---
769
894
 
@@ -775,7 +900,6 @@ npm install securenow koa @koa/router koa-bodyparser
775
900
 
776
901
  ```javascript
777
902
  // app.js
778
- require('securenow/register');
779
903
  const Koa = require('koa');
780
904
  const Router = require('@koa/router');
781
905
  const bodyParser = require('koa-bodyparser');
@@ -798,6 +922,12 @@ app.use(router.allowedMethods());
798
922
  app.listen(3000, () => console.log('Koa running on port 3000'));
799
923
  ```
800
924
 
925
+ ```bash
926
+ node -r securenow/register app.js
927
+ ```
928
+
929
+ `http.route` comes from `@koa/router`; body capture records what the body parser reads.
930
+
801
931
  ---
802
932
 
803
933
  ### NestJS
@@ -839,6 +969,8 @@ bootstrap();
839
969
 
840
970
  ```bash
841
971
  node -r securenow/register -r ts-node/register app.ts
972
+ # compiled output (production):
973
+ node -r securenow/register dist/main.js
842
974
  ```
843
975
 
844
976
  PM2 config:
@@ -862,7 +994,6 @@ npm install securenow @hapi/hapi
862
994
 
863
995
  ```javascript
864
996
  // app.js
865
- require('securenow/register');
866
997
  const Hapi = require('@hapi/hapi');
867
998
 
868
999
  const init = async () => {
@@ -886,11 +1017,15 @@ const init = async () => {
886
1017
  init().catch((err) => { console.error(err); process.exit(1); });
887
1018
  ```
888
1019
 
889
- > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. If a specific Hapi version or payload plugin reports request-stream conflicts, set `config.capture.body=false` in credentials as a local override.
1020
+ ```bash
1021
+ node -r securenow/register app.js
1022
+ ```
1023
+
1024
+ > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. Hapi records `http.route`. Tested on Hapi 21.4.
890
1025
 
891
1026
  ---
892
1027
 
893
- ### h3 (UnJS / Nitro)
1028
+ ### h3 (UnJS)
894
1029
 
895
1030
  ```bash
896
1031
  npm install securenow h3
@@ -898,7 +1033,6 @@ npm install securenow h3
898
1033
 
899
1034
  ```javascript
900
1035
  // app.js
901
- require('securenow/register');
902
1036
  const { createApp, createRouter, defineEventHandler, readBody, setResponseStatus, toNodeListener } = require('h3');
903
1037
  const http = require('http');
904
1038
 
@@ -921,6 +1055,12 @@ http.createServer(toNodeListener(app)).listen(3000, () => {
921
1055
  });
922
1056
  ```
923
1057
 
1058
+ ```bash
1059
+ node -r securenow/register app.js
1060
+ ```
1061
+
1062
+ `http.route` from the h3 router **(8.25.0+)**; before, the raw path. Nitro inside Nuxt: see [Nuxt 3 and 4](#nuxt-3-and-4).
1063
+
924
1064
  ---
925
1065
 
926
1066
  ### Polka
@@ -933,7 +1073,6 @@ Polka is minimalist -- no built-in body parser:
933
1073
 
934
1074
  ```javascript
935
1075
  // app.js
936
- require('securenow/register');
937
1076
  const polka = require('polka');
938
1077
 
939
1078
  function jsonBody(req, res, next) {
@@ -958,6 +1097,12 @@ polka()
958
1097
  .listen(3000, () => console.log('Polka running on port 3000'));
959
1098
  ```
960
1099
 
1100
+ ```bash
1101
+ node -r securenow/register app.js
1102
+ ```
1103
+
1104
+ Pass errors to `next(err)` (a thrown error crashes Polka). `http.route` **(8.25.0+)**.
1105
+
961
1106
  ---
962
1107
 
963
1108
  ### Micro / Raw HTTP
@@ -970,7 +1115,6 @@ npm install securenow
970
1115
 
971
1116
  ```javascript
972
1117
  // app.js
973
- require('securenow/register');
974
1118
  const http = require('http');
975
1119
 
976
1120
  function sendJson(res, status, body) {
@@ -998,6 +1142,12 @@ async function handler(req, res) {
998
1142
  http.createServer(handler).listen(3000, () => console.log('HTTP running on port 3000'));
999
1143
  ```
1000
1144
 
1145
+ ```bash
1146
+ node -r securenow/register app.js
1147
+ ```
1148
+
1149
+ `https.createServer` and the HTTP/2 compatibility API (`'request'` event) are covered the same way; spans carry the raw path.
1150
+
1001
1151
  ---
1002
1152
 
1003
1153
  ### Hono (ESM)
@@ -1030,7 +1180,7 @@ serve({ fetch: app.fetch, port: 3000 }, () => console.log('Hono running on port
1030
1180
  node -r securenow/register app.mjs
1031
1181
  ```
1032
1182
 
1033
- > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. For Hono ESM apps, keep using the `node -r securenow/register app.mjs` preload instead of adding `require('securenow/register')` inside `.mjs` files.
1183
+ > **Default:** Traces, logs, POST body capture, multipart metadata capture, and firewall protection are on. Keep using the `node -r securenow/register app.mjs` preload instead of importing `securenow/register` inside `.mjs` files. Only Hono on `@hono/node-server` is covered (not `Bun.serve`, Deno or Workers). `http.route` **(8.25.0+)**.
1034
1184
 
1035
1185
  ---
1036
1186
 
@@ -1044,7 +1194,6 @@ Feathers uses Express transport -- same setup as Express:
1044
1194
 
1045
1195
  ```javascript
1046
1196
  // app.js
1047
- require('securenow/register');
1048
1197
  const feathers = require('@feathersjs/feathers');
1049
1198
  const express = require('@feathersjs/express');
1050
1199
  const errors = require('@feathersjs/errors');
@@ -1071,6 +1220,12 @@ app.use(express.errorHandler());
1071
1220
  app.listen(3000, () => console.log('Feathers running on port 3000'));
1072
1221
  ```
1073
1222
 
1223
+ ```bash
1224
+ node -r securenow/register app.js
1225
+ ```
1226
+
1227
+ `http.route` **(8.25.0+)**. The Koa transport works the same way.
1228
+
1074
1229
  ---
1075
1230
 
1076
1231
  ### Next.js
@@ -1079,7 +1234,7 @@ Use `npx securenow init` for the current Next.js integration; it creates or prin
1079
1234
 
1080
1235
  #### Option A: `securenow init` (Recommended)
1081
1236
 
1082
- The init command handles the boring parts: credentials defaults, `instrumentation.ts`, body auto-capture, `serverExternalPackages`, and standalone output tracing includes for Next 15+. If existing files are custom, it prints a Codex/Claude-ready prompt instead of guessing.
1237
+ The init command handles the boring parts: credentials defaults, `instrumentation.ts`, body auto-capture, `serverExternalPackages`, and standalone output tracing includes for Next 15+ (Next 14 **(8.25.0+)**). If existing files are custom, it prints a Codex/Claude-ready prompt instead of guessing.
1083
1238
 
1084
1239
  ```bash
1085
1240
  npx securenow login
@@ -1116,13 +1271,17 @@ const nextConfig = {
1116
1271
  export default nextConfig;
1117
1272
  ```
1118
1273
 
1119
- No `.env.local` is needed locally or in production. Local setup reads `.securenow/runtime.json`; production should use `npx securenow credentials runtime --env production` and mount/copy the resulting JSON as `.securenow/credentials.json` (in a standalone Docker image, next to `server.js`). On Vercel or another host without file mounts, run `npx securenow credentials runtime --env production --format env` and store the printed `SECURENOW_CREDENTIALS` value as a secret environment variable.
1274
+ No `.env.local` is needed locally or in production. Local setup reads `.securenow/runtime.json`; production should use `npx securenow credentials runtime --env production` and mount/copy the resulting JSON as `.securenow/credentials.json` (in a standalone Docker image, next to `server.js`, i.e. `.next/standalone/.securenow/credentials.json` before copying). On Vercel or another host without file mounts, run `npx securenow credentials runtime --env production --format env` and store the printed `SECURENOW_CREDENTIALS` value as a secret environment variable. On Vercel the integration uses `@vercel/otel`; that setup is not tested end to end and the firewall is not verified on Vercel serverless functions.
1120
1275
 
1121
1276
  Keep the imports inside the `nodejs` block and without `webpackIgnore`: Next.js then drops them from the Edge build and traces `securenow` and its dependencies into `output: 'standalone'`. An instrumentation file written by `init` before 8.24.0 used `webpackIgnore`, so standalone images shipped without OpenTelemetry and every request returned 500; run `npx securenow init` again to replace it.
1122
1277
 
1278
+ **Where the file goes:** next to `app/` or `pages/`. With a `src/` layout (`src/app`, `src/pages`), Next.js 14 and 15 only load `src/instrumentation.ts` (16 also loads a root file). From 8.25.0 `init` writes it there and moves a root file it generated before; `--src`/`--root` force the folder, `--force` replaces an existing file (the old one is kept as `.bak`).
1279
+
1280
+ **What's covered:** HTTP server spans with client IP and redacted headers and bodies, console logs, the firewall, rate limits and challenges, for Node.js route handlers, server components and API routes. The Edge runtime is not instrumented (Edge middleware and `export const runtime = 'edge'` routes get nothing). `http.route` is on the HTTP span **(8.25.0+)**; before, Next.js 16 put it on Next's own span and Next.js 15 recorded `next.route`.
1281
+
1123
1282
  #### Option B: Manual configuration
1124
1283
 
1125
- If you prefer not to run `init`, manually externalize SecureNow:
1284
+ If you prefer not to run `init`, create the instrumentation file above and externalize SecureNow:
1126
1285
 
1127
1286
  ```javascript
1128
1287
  // next.config.js (Next.js 15+)
@@ -1135,19 +1294,21 @@ module.exports = {
1135
1294
  ```
1136
1295
 
1137
1296
  ```javascript
1138
- // next.config.js (Next.js 14)
1297
+ // next.config.js (Next.js 14): all three keys live under experimental
1139
1298
  module.exports = {
1140
1299
  experimental: {
1141
1300
  instrumentationHook: true,
1142
1301
  serverComponentsExternalPackages: ['securenow'],
1143
- },
1144
- outputFileTracingIncludes: {
1145
- '/*': ['./node_modules/securenow/**/*'],
1302
+ outputFileTracingIncludes: {
1303
+ '/*': ['./node_modules/securenow/**/*'],
1304
+ },
1146
1305
  },
1147
1306
  };
1148
1307
  ```
1149
1308
 
1150
- **Why is this needed?** Next.js bundles server code with webpack or Turbopack, which can break OpenTelemetry's dynamic `require()` calls and monkey-patching. Externalizing `securenow` keeps the SDK as normal Node.js runtime code, and Next.js traces its dependencies into standalone output. `outputFileTracingIncludes` adds SecureNow's own files that are loaded at runtime, including the firewall.
1309
+ **Why is this needed?** Next.js bundles server code with webpack or Turbopack, which can break OpenTelemetry's dynamic `require()` calls and monkey-patching. Externalizing `securenow` keeps the SDK as normal Node.js runtime code, and Next.js traces its dependencies into standalone output. `outputFileTracingIncludes` adds SecureNow's own files that are loaded at runtime, including the firewall. Next.js 15.5 and 16.4 are tested (Turbopack and webpack, `next start` and standalone); Next.js 14 is expected to work but is not tested.
1310
+
1311
+ You don't need `securenow/nextjs-middleware` or `securenow/nextjs-wrapper` for body capture: since 8.24.0 bodies are captured at the HTTP layer, and `securenow/nextjs-auto-capture` is only a fallback.
1151
1312
 
1152
1313
  ---
1153
1314
 
@@ -1165,37 +1326,66 @@ export default defineNuxtConfig({
1165
1326
  });
1166
1327
  ```
1167
1328
 
1168
- Local app identity, runtime API key, and secure defaults come from `.securenow/runtime.json`; production can mount the generated tokenless runtime file as `.securenow/credentials.json` or `.securenow/credentials.<env>.json` next to the `.output` server, or set `SECURENOW_CREDENTIALS` (see `credentials runtime --format env`).
1329
+ Then run `npx securenow init` to fill `.securenow/runtime.json`. Local app identity, runtime API key, and secure defaults come from that file; production can mount the generated tokenless runtime file as `.securenow/credentials.json` in the folder you start `node .output/server/index.mjs` from, or set `SECURENOW_CREDENTIALS` (see `credentials runtime --format env`).
1169
1330
 
1170
1331
  That's it: the module's Nitro plugin loads the same runtime as `node -r securenow/register` (tracing with header and body capture, logs, crash capture, firewall, rate limits, challenges) and keeps `securenow` external, so Nitro copies it with its dependencies into `.output/server/node_modules`. It also records the matched route as `http.route` and handler errors as exceptions. Settings go in `.securenow/runtime.json` (`config.capture.body`, `config.logging.enabled`, ...); `securenow: { ... }` options in `nuxt.config` are no longer used, and the build prints a warning if you set them.
1171
1332
 
1172
- Before 8.24.0 the module's plugin crashed the `nuxt build` server at startup. Starting `.output/server/index.mjs` with `node -r securenow/register` and no module also works.
1333
+ `nuxt dev` is traced too. Its requests arrive through Nuxt's dev proxy: from 8.25.0 the client IP is known there; before, it was unknown and IP blocking only applied to the built server.
1334
+
1335
+ Before 8.24.0 the module's plugin crashed the `nuxt build` server at startup. Starting `.output/server/index.mjs` with `node -r securenow/register` and no module also works. Tested on Nuxt 3.21 and 4.6.
1336
+
1337
+ ---
1338
+
1339
+ ### Vite
1340
+
1341
+ Browser telemetry is not sent to SecureNow: browser ingestion is disabled since 8.0, and `securenow/web-vite` sends nothing. Instrument the Node process that serves the app:
1342
+
1343
+ ```bash
1344
+ # vite preview
1345
+ node -r securenow/register-vite node_modules/vite/bin/vite.js preview
1346
+
1347
+ # a Vite SSR server (Express + Vite middleware, ...)
1348
+ node -r securenow/register server.js
1349
+ ```
1350
+
1351
+ `securenow/register-vite` is the same runtime as `securenow/register`. A static build served by nginx or a CDN has no Node process for SecureNow to run in.
1352
+
1353
+ ### Bun
1354
+
1355
+ ```bash
1356
+ bun --preload securenow/register app.js
1357
+ ```
1358
+
1359
+ Works for apps served through `node:http` (tested with Express 5 on Bun 1.3). `Bun.serve` is not traced or protected. Deno is not supported.
1173
1360
 
1174
1361
  ---
1175
1362
 
1176
1363
  ### Compatibility Matrix
1177
1364
 
1178
- | Framework | Traces | Logs | Body Capture | Firewall | Notes |
1179
- |-----------|--------|------|--------------|----------|-------|
1180
- | Express 4 and 5 | Yes | Yes | Yes | Yes | Fully compatible (CommonJS, ESM, TypeScript via tsx) |
1181
- | Fastify 5 | Yes | Yes | Yes | Yes | `http.route` and handler exceptions recorded through Fastify's `fastify.initialization` channel (8.24.0+) |
1182
- | Koa 3 | Yes | Yes | Yes | Yes | Body needs a body parser; `http.route` needs `@koa/router` |
1183
- | NestJS 11 | Yes | Yes | Yes | Yes | `-r securenow/register` before `-r ts-node/register` for TypeScript |
1184
- | Hapi 21 | Yes | Yes | Yes | Yes | Fully compatible |
1185
- | h3 1 | Yes | Yes | Yes | Yes | Uses `toNodeListener()`; no `http.route` (raw path) |
1186
- | Polka | Yes | Yes | Yes | Yes | Needs a body parser; no `http.route`; pass errors to `next(err)` (a thrown error crashes Polka) |
1187
- | Micro/HTTP | Yes | Yes | Yes | Yes | Full control; no route templates |
1188
- | Hono 4 (`@hono/node-server`) | Yes | Yes | Yes | Yes | No `http.route` (raw path); errors reach the logs through Hono's default handler |
1189
- | Feathers 5 | Yes | Yes | Yes | Yes | Koa or Express transport; no `http.route` |
1190
- | Next.js 15 and 16 | Yes | Yes | Yes | Yes | `instrumentation.ts` + `serverExternalPackages` + `outputFileTracingIncludes`; `next start` and `output: 'standalone'` |
1191
- | Nuxt 3 and 4 | Yes | Yes | Yes | Yes | `securenow/nuxt` module; `nuxt build` output |
1192
- | Bun 1.3 (`node:http` apps) | Yes | Yes | Yes | Yes | `bun --preload securenow/register`; `Bun.serve` is not supported |
1365
+ | Framework | Traces | Logs | Body Capture | Firewall | `http.route` | Notes |
1366
+ |-----------|--------|------|--------------|----------|--------------|-------|
1367
+ | Express 4 and 5 | Yes | Yes | Yes | Yes | Yes | CommonJS, ESM, TypeScript via tsx (tested 4.22, 5.3) |
1368
+ | Fastify 5 | Yes | Yes | Yes | Yes | Yes | Route and handler exceptions through Fastify's `fastify.initialization` channel (8.24.0+) |
1369
+ | Koa 3 | Yes | Yes | Yes | Yes | With `@koa/router` | Body needs a body parser |
1370
+ | NestJS 11 | Yes | Yes | Yes | Yes | Yes | `-r securenow/register` before `-r ts-node/register` for TypeScript |
1371
+ | Hapi 21 | Yes | Yes | Yes | Yes | Yes | |
1372
+ | h3 1 | Yes | Yes | Yes | Yes | 8.25.0+ | Uses `toNodeListener()`; handler exceptions on the span 8.25.0+ |
1373
+ | Polka 0.5 | Yes | Yes | Yes | Yes | 8.25.0+ | Needs a body parser; pass errors to `next(err)` (a thrown error crashes Polka); `next(err)` exceptions on the span 8.25.0+ |
1374
+ | Raw `http`/`https`/`http2` | Yes | Yes | Yes | Yes | No (raw path) | HTTP/2 compatibility API covered, raw `'stream'` servers not |
1375
+ | Hono 4 (`@hono/node-server`) | Yes | Yes | Yes | Yes | 8.25.0+ | Errors reach the logs through Hono's default handler; handler exceptions on the span 8.25.0+ |
1376
+ | Feathers 5 | Yes | Yes | Yes | Yes | 8.25.0+ | Koa or Express transport; 5xx service errors on the span 8.25.0+ |
1377
+ | Next.js 15 and 16 | Yes | Yes | Yes | Yes | On the HTTP span 8.25.0+ | `instrumentation.ts` + `serverExternalPackages` + `outputFileTracingIncludes`; `next start` and `output: 'standalone'`; Node.js runtime only |
1378
+ | Next.js 14 | Yes | Yes | Yes | Yes | | `init` support 8.25.0+; tested on 14.2.35 |
1379
+ | Nuxt 3 and 4 | Yes | Yes | Yes | Yes | Yes | `securenow/nuxt` module; `nuxt build` output and `nuxt dev` |
1380
+ | Bun 1.3 (`node:http` apps) | Yes | Yes | Yes | Yes | As the framework | `bun --preload securenow/register`; `Bun.serve` is not supported |
1381
+
1382
+ Not supported: Deno, edge runtimes (Cloudflare Workers, Vercel Edge), `Bun.serve`, browser telemetry. Not tested: Vercel, AWS Lambda and other serverless platforms.
1193
1383
 
1194
1384
  ---
1195
1385
 
1196
1386
  ## Firewall -- Automatic IP Blocking
1197
1387
 
1198
- SecureNow can automatically block IPs from your blocklist at the application layer. No code changes -- provide an API key via `securenow login`, the `api-key` CLI, or a runtime credentials file and the firewall activates.
1388
+ SecureNow can automatically block IPs from your blocklist at the application layer. No code changes -- provide a runtime API key via `securenow login`, the `api-key` CLI, or a runtime credentials file and the firewall activates.
1199
1389
 
1200
1390
  ### Firewall Is Enabled by Default
1201
1391
 
@@ -1213,28 +1403,32 @@ npx securenow api-key set snk_live_abc123...
1213
1403
  npx securenow credentials runtime --env production
1214
1404
  ```
1215
1405
 
1216
- The SDK resolves the runtime API key from project `./.securenow/runtime.json`, legacy project `./.securenow/credentials.json`, project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order, global `~/.securenow/runtime.json`, legacy global credentials, then global named runtime credentials in the same fixed order.
1406
+ The SDK resolves the runtime API key from the same source as everything else: `SECURENOW_CREDENTIALS`, then project `./.securenow/runtime.json`, legacy project `./.securenow/credentials.json`, project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order, then the same files in `~/.securenow/`.
1217
1407
 
1218
1408
  On startup, you'll see:
1219
1409
 
1220
1410
  ```
1221
1411
  [securenow] Firewall: ENABLED
1412
+ [securenow] Firewall: environment=production
1222
1413
  [securenow] Firewall: Layer 1 (HTTP 403) active
1223
- [securenow] Firewall: synced 142 blocked IPs (138 exact + 4 CIDR ranges)
1414
+ [securenow] Firewall: synced 142 global blocked IPs (138 exact + 4 CIDR ranges) and 0 scoped block rules
1224
1415
  ```
1225
1416
 
1417
+ Each app and environment also has a remote on/off switch: `npx securenow firewall enable` / `disable --app <key> --env production`, applied without a deploy. `config.firewall.enabled` in the file is ignored.
1418
+
1226
1419
  ### How It Works
1227
1420
 
1228
1421
  The firewall uses a version-based sync protocol for efficiency:
1229
1422
 
1230
- 1. **Version check** every 10 seconds (lightweight request with ETag; unchanged polls return 304 with no body)
1231
- 2. **Full blocklist sync** only when the version changes (or every hour as a safety net)
1232
- 3. **In-memory matching** with a pre-compiled set (exact IPs) and sorted CIDR list for sub-millisecond lookups
1233
- 4. **Exponential backoff** with jitter when the API is temporarily unreachable
1234
- 5. **Allowlist support** -- trusted IPs are never blocked, even if they appear on the blocklist
1235
- 6. **Localhost fallback** -- when the configured API URL is unreachable (ECONNREFUSED), the SDK automatically tries `http://localhost:4000` for co-located deployments
1423
+ 1. **Version check** every 10 seconds (`config.firewall.versionCheckInterval`; a lightweight request with ETag, unchanged polls return 304 with no body)
1424
+ 2. **Full blocklist sync** only when the version changes (or every hour as a safety net, `config.firewall.syncInterval`)
1425
+ 3. **In-memory matching** with a pre-compiled set (exact IPs) and IPv4 and IPv6 CIDR lists (IPv6 8.25.0+) for sub-millisecond lookups
1426
+ 4. **Exponential backoff** with jitter when the API is temporarily unreachable, a circuit breaker after 5 consecutive errors (2-minute pause), and 429 `Retry-After` support
1427
+ 5. **Allowlist** -- deny-by-default: when an app/environment has any active allowlist entry, only listed IPs get through, and listed IPs skip the blocklist, rate limits and challenges
1428
+ 6. **Order per request** -- allowlist, blocklist, scoped (route/method) blocks, challenge, rate limit
1429
+ 7. **Fail-open** by default (`config.firewall.failMode: "open"`); `"closed"` answers 403 to every request until the first sync succeeds
1236
1430
 
1237
- After you block an IP in the dashboard or CLI, it typically takes 10-15 seconds to propagate to all running instances.
1431
+ After you block an IP in the dashboard or CLI, it reaches running instances within about 10 seconds (set `versionCheckInterval` to 1–2 for near-instant propagation).
1238
1432
 
1239
1433
  ### Firewall-Only Mode (No Tracing)
1240
1434
 
@@ -1262,7 +1456,7 @@ This is useful when:
1262
1456
  - You're adding the firewall to a project that uses a different tracing solution
1263
1457
  - For non-Next Node apps, this avoids framework-specific tracing setup entirely
1264
1458
 
1265
- Firewall-only mode uses the same runtime API key written by `login`, `app connect`, `api-key create`, `api-key set`, or mounted from a runtime credentials file:
1459
+ Firewall-only mode includes the blocklist, allowlist, scoped blocks, rate limits, challenges and, **(8.25.0+)**, the zero-code session guard. It uses the same runtime API key written by `login`, `app connect`, `api-key create`, `api-key set`, or mounted from a runtime credentials file:
1266
1460
 
1267
1461
  ```bash
1268
1462
  npx securenow credentials runtime --env production
@@ -1275,29 +1469,42 @@ The firewall supports four layers -- Layer 1 is always on, the rest are opt-in:
1275
1469
 
1276
1470
  | Layer | Credentials key | Description |
1277
1471
  |-------|---------|-------------|
1278
- | **Layer 1: HTTP** | *(always on)* | Returns 403 Forbidden with a security alert page. Works with proxy headers. |
1279
- | **Layer 2: TCP** | `config.firewall.tcp=true` | `socket.destroy()` -- zero bytes sent back |
1280
- | **Layer 3: iptables** | `config.firewall.iptables=true` | Kernel-level DROP (Linux, requires root) |
1281
- | **Layer 4: Cloud WAF** | `config.firewall.cloud="cloudflare"` | Pushes to Cloudflare, AWS WAF, or GCP Cloud Armor |
1472
+ | **Layer 1: HTTP** | *(always on)* | Returns 403 (`config.firewall.statusCode`): JSON `{"error":"Forbidden","ip":"..."}` for API clients, a security alert page for browsers. Works with proxy headers. |
1473
+ | **Layer 2: TCP** | `config.firewall.tcp=true` | `socket.destroy()` before HTTP parsing -- zero bytes sent back. Sees only the connection's source IP; connections from trusted proxies are left to Layer 1. Checked on each server `connection` **(8.25.0+)**; before, it hooked `listen()` and missed the servers most apps start before the SDK has finished starting |
1474
+ | **Layer 3: iptables** | `config.firewall.iptables=true` | Kernel-level DROP in a dedicated `SECURENOW_BLOCK` chain (ip6tables for IPv6), or a `securenow` nft table where iptables is missing **(8.25.0+)**. Linux, root or `CAP_NET_ADMIN`; direct client connections only. 8.25.0 validates every entry, runs without a shell and applies each sync as one batch |
1475
+ | **Layer 4: Cloud WAF** | `config.firewall.cloud="cloudflare"` / `"aws"` / `"gcp"` | Pushes the global blocklist to Cloudflare (an account IP list named `securenow-blocklist` that your own WAF rule must reference), AWS WAFv2 (IP set; `npm i @aws-sdk/client-wafv2`) or GCP Cloud Armor (`deny(403)` rules at priority 2000; `npm i @google-cloud/compute`), at most every 30 seconds. `config.firewall.cloudDryRun=true` only logs. 8.25.0 fixes IPv6 ranges, Cloud Armor's 10-range limit and stale rules. Request format tested, not run against real accounts |
1476
+
1477
+ Layers 2 to 4 carry the global blocklist; scoped blocks, rate limits and challenges are Layer 1 only. Turn Layers 2 to 4 on with 8.25.0+. In a container, Layer 3 needs `--cap-add NET_ADMIN` and the `iptables` package.
1282
1478
 
1283
1479
  ### Blocked Page
1284
1480
 
1285
1481
  When an IP is blocked at Layer 1, the user sees a full-page security alert with:
1286
- - Their detected IP address
1482
+ - Their detected IP address (masked)
1287
1483
  - A warning that malicious activity was detected
1288
1484
  - Contact information (`contact@securenow.ai`) for false positives
1289
1485
 
1290
1486
  ### Get an API Key
1291
1487
 
1292
1488
  ```bash
1293
- npx securenow login
1294
- npx securenow firewall status
1489
+ npx securenow app connect # pick/create the app; mints and saves the runtime key
1490
+ npx securenow api-key create # with admin auth: mint and save a key for the current app
1491
+ npx securenow firewall status # then check the firewall state
1295
1492
  ```
1296
1493
 
1297
- Or create one from the dashboard with the `firewall:read` scope.
1494
+ Or create one in the dashboard with the runtime scopes and save it with `npx securenow api-key set snk_live_...`.
1298
1495
 
1299
1496
  Use `npx securenow firewall status`, `npx securenow firewall apps`, and `npx securenow help firewall` for the current firewall reference.
1300
1497
 
1498
+ ### Trusted Proxies and the Client IP
1499
+
1500
+ Forwarding headers (`X-Forwarded-For`, then `CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`, `X-Client-IP`) count only when the connection comes from a trusted proxy. Trusted by default: private ranges, loopback, the host's own addresses and, **(8.25.0+)**, Unix-socket and named-pipe connections (for example nginx `proxy_pass http://unix:/run/app.sock`). Add public load balancers and CDNs:
1501
+
1502
+ ```json
1503
+ { "config": { "networking": { "trustedProxies": ["cloudflare", "203.0.113.10", "198.51.100.0/24"] } } }
1504
+ ```
1505
+
1506
+ Entries are exact IPv4/IPv6 addresses, IPv4 or IPv6 CIDRs (IPv6 8.25.0+), or `"cloudflare"` for Cloudflare's published edge ranges (IPv4, and IPv6 from 8.25.0). A client can't choose its IP through `X-Forwarded-For`.
1507
+
1301
1508
  ---
1302
1509
 
1303
1510
  ## Credentials Configuration
@@ -1310,22 +1517,49 @@ Use `.securenow/runtime.json` as the local source of truth. Run `npx securenow e
1310
1517
 
1311
1518
  | Field | Description | Default |
1312
1519
  |----------|-------------|---------|
1313
- | `app.key` | App routing UUID. The SecureNow ingestion gateway routes telemetry by this key. | selected during login |
1520
+ | `app.key` | App routing UUID. It becomes `service.name`; the SecureNow ingestion gateway routes telemetry by this key. | selected during login |
1314
1521
  | `app.name` | Human-readable app label. | selected during login |
1315
1522
  | `apiKey` | One-app scoped runtime API key (`snk_live_...`) for telemetry ingestion and firewall sync. Runtime scopes are `traces:write`, `logs:write`, `firewall:read`, `blocklist:read`, and `allowlist:read`. | minted during login |
1316
- | `config.runtime.deploymentEnvironment` | `deployment.environment` trace/log scope. | `local` from init, `production` from runtime credentials |
1317
- | `config.logging.enabled` | Automatic console log export. | `true` |
1523
+ | `config.runtime.deploymentEnvironment` | `deployment.environment` trace/log/firewall scope (`dev`/`development` become `local`, `prod` becomes `production`). | `production`; `local` from init |
1524
+ | `config.runtime.noUuid` | One service name for every worker. Leave it automatic: `false` breaks dashboard routing. | `null` (automatic) |
1525
+ | `config.runtime.strict` | Exit PM2/cluster workers when no app identity resolves. | `false` |
1526
+ | `config.runtime.testSpan` | Emit a startup smoke span. | `false` |
1527
+ | `config.logging.enabled` | Automatic console log export (and crash capture). | `true` |
1528
+ | `config.logging.redact` | Redact credential fields and secret shapes in exported log lines (8.25.0+). | `true` |
1318
1529
  | `config.capture.body` | Request body capture with redaction. | `true` |
1319
1530
  | `config.capture.multipart` | Multipart metadata capture, never file content. | `true` |
1531
+ | `config.capture.maxBodySize` | Largest body captured, in bytes (minimum 1024); larger bodies are recorded as `[TOO LARGE: <n> bytes]`. | `10240` |
1532
+ | `config.capture.sensitiveFields` | Extra field names to redact (also applied to header names). | `[]` |
1533
+ | `config.otel.endpoint` | Advanced: your own OTLP collector instead of `https://ingest.securenow.ai`. | `null` |
1534
+ | `config.otel.tracesEndpoint` / `logsEndpoint` | Advanced: split collectors. | `null` |
1535
+ | `config.otel.headers` | Extra OTLP headers (app key, auth and environment are added automatically). | `{}` |
1536
+ | `config.otel.logLevel` | OpenTelemetry diagnostics: `error`, `warn`, `info`, `debug`, `none`. | `error` |
1537
+ | `config.otel.disableInstrumentations` | Instrumentation package names to skip, e.g. `@opentelemetry/instrumentation-dns`. | `[]` |
1538
+ | `config.otel.middlewareSpans` | One span per Express/Koa middleware per request. | `false` |
1539
+ | `config.networking.trustedProxies` | Extra trusted proxies: IPv4/IPv6 addresses and CIDRs, `"cloudflare"`. | `[]` |
1540
+ | `config.firewall.failMode` | `open` or `closed` (403 for everyone until the first sync). | `open` |
1541
+ | `config.firewall.statusCode` | HTTP status for blocked requests. | `403` |
1542
+ | `config.firewall.log` | Firewall console lines (startup and each decision). | `true` |
1543
+ | `config.firewall.tcp` / `iptables` / `cloud` / `cloudDryRun` | Layers 2, 3, 4 and the Layer 4 dry run. | `false` / `false` / `null` / `false` |
1544
+ | `config.firewall.cloudflare.*`, `aws.*`, `gcp.*` | Layer 4 provider settings (`apiToken`, `accountId`; `wafIpSetId`, `wafIpSetName`, `wafScope`; `projectId`, `securityPolicy`). | `aws.wafIpSetName: "securenow-blocklist"`, `aws.wafScope: "REGIONAL"` |
1545
+ | `config.firewall.rateLimitStore` | `redis://` or `rediss://` URL for rate-limit counters shared across processes and hosts. | `null` (per process) |
1546
+ | `config.firewall.versionCheckInterval` | Seconds between lightweight version checks. | `10` |
1547
+ | `config.firewall.syncInterval` | Seconds between safety-net full resyncs. | `3600` |
1320
1548
  | `config.firewall.enabled` | Deprecated local hint. Runtime enable/disable uses the dashboard/API environment toggle. | ignored |
1321
- | `config.otel.*` | Optional custom endpoints, headers, and log level. | empty |
1549
+ | `config.sessions.guard` **(8.25.0+)** | 401 for revoked sessions in every framework, no code. | `false` |
1550
+ | `config.sessions.capture` **(8.25.0+)** | Send `session.seen` for session-theft detection. | `false` |
1551
+ | `config.sessions.cookieNames` **(8.25.0+)** | Extra session cookie names. | `[]` |
1552
+
1553
+ `app.instance` from older files is ignored; use `config.otel.endpoint` for a self-hosted collector.
1322
1554
 
1323
1555
  The credentials file is versioned with `_securenow.schemaVersion`. The SDK reads
1324
1556
  all runtime settings from this JSON plus built-in defaults. Production should
1325
1557
  mount a tokenless runtime credentials file at `.securenow/credentials.json`.
1326
- Environment-variable fallback is not supported.
1558
+ The only environment variables the SDK reads are `SECURENOW_CREDENTIALS` (the same
1559
+ JSON, raw or base64, for hosts that can't mount a file; it wins over every file)
1560
+ and `SECURENOW_DISABLED`. Every other `SECURENOW_*` variable is ignored.
1327
1561
 
1328
- **Default sensitive fields (auto-redacted):** `password`, `passwd`, `pwd`, `secret`, `token`, `api_key`, `apikey`, `access_token`, `auth`, `credentials`, `mysql_pwd`, `stripeToken`, `card`, `cardnumber`, `ccv`, `cvc`, `cvv`, `ssn`, `pin`, `code`, `otpCode`, `oneTimeCode`, `verificationCode`, `passcode`
1562
+ **Default sensitive fields (auto-redacted, 68 names):** passwords and secrets (`password`, `passwd`, `pwd`, `secret`, `credentials`, `mysql_pwd`), tokens and auth (`token`, `api_key`, `apikey`, `access_token`, `auth`, `authorization`, `bearer`, `sessionid`, `session_id`, `cookie`, `set-cookie`, `otp`, `mfa`, `totp`), payment (`stripeToken`, `card`, `cardnumber`, `ccv`, `cvc`, `cvv`, `iban`, `account`, `accountnumber`, `routing`, `sortcode`, `taxid`), personal data (`ssn`, `pin`, `email`, `e_mail`, `phone`, `mobile`, `dob`, `birthdate`, `firstname`, `lastname`, `fullname`, `address`, `postcode`, `zip`, `passport`, `license`), and OTP names matched exactly (`code`, `otpCode`, `oneTimeCode`, `oneTimePassword`, `passcode`, `verificationCode`, `confirmationCode`, `emailCode`, `smsCode`, `loginCode`, `signInCode`, `authCode`, `authenticationCode`, `mfaCode`, `totpCode`, `2faCode`, `securityCode`, `recoveryCode`, `backupCode`).
1329
1563
 
1330
1564
  For instrumentation, firewall layers, debugging, trusted proxies, and deployment
1331
1565
  environment, edit the matching `config.*` keys in `.securenow/runtime.json`
@@ -1344,12 +1578,18 @@ SecureNow provides multiple entry points depending on your needs:
1344
1578
  | `securenow/register` | `node -r securenow/register app.js` | Yes | Yes | Default -- full tracing + firewall |
1345
1579
  | `securenow/firewall-only` | `node -r securenow/firewall-only app.js` | No | Yes | Firewall only, no OTel overhead |
1346
1580
  | `securenow/nextjs` | `await import('securenow/nextjs')` inside the `nodejs` block of `instrumentation.ts` | Yes | Yes | Next.js instrumentation hook |
1581
+ | `securenow/nextjs-auto-capture` | imported by the generated `instrumentation.ts` | - | - | Body-capture fallback |
1347
1582
  | `securenow/nuxt` | `modules: ['securenow/nuxt']` | Yes | Yes | Nuxt 3 and 4 module |
1348
1583
  | `securenow/register-vite` | `node -r securenow/register-vite node_modules/vite/bin/vite.js preview` | Yes | Yes | Same as `securenow/register`, for the Node process serving a Vite app |
1349
- | `securenow/nextjs-webpack-config` | `withSecureNow(config)` | - | - | Next.js config wrapper |
1350
- | `securenow/firewall` | `require('securenow/firewall').init({...})` | No | Yes | Programmatic firewall API |
1351
- | `securenow/rate-limits` | `require('securenow/rate-limits').parseRateLimitText(...)` | No | Yes | Rate-limit remediation API helper |
1352
- | `securenow/tracing` | `require('securenow/tracing')` | Yes | No | Programmatic tracing API |
1584
+ | `securenow/events` | `require('securenow/events').track(type, props)` | No | No | Custom security events |
1585
+ | `securenow/sessions` | `require('securenow/sessions').guard()` / `.check(req)` **(8.25.0+)** | No | No | Session revocation |
1586
+ | `securenow/firewall` | `require('securenow/firewall').getStats()` | No | Yes | Programmatic firewall API (`init`, `shutdown`, `getStats`, `getMatcher`, `getAllowlistMatcher`) |
1587
+ | `securenow/rate-limits` | `require('securenow/rate-limits').parseRateLimitText(...)` | No | No | Rate-limit remediation API helper (calls the SecureNow API) |
1588
+ | `securenow/resolve-ip` | `require('securenow/resolve-ip').resolveClientIp(req)` | No | No | Client IP with the trusted-proxy rules |
1589
+ | `securenow/cidr` | `require('securenow/cidr').createMatcher([...]).isBlocked(ip)` | No | No | Exact-IP and CIDR matching, IPv4 and IPv6 (IPv6 ranges 8.25.0+) |
1590
+ | `securenow/tracing` | `require('securenow/tracing').getLogger(name, version)` | Yes | No | Programmatic logging API |
1591
+ | `securenow/telemetry-schema` | `require('securenow/telemetry-schema')` | No | No | Alert-SQL contract |
1592
+ | `securenow/nextjs-webpack-config` | `withSecureNow(config)` | - | - | Legacy Next.js config wrapper (also adds a webpack hook); prefer the plain config above |
1353
1593
 
1354
1594
  ---
1355
1595
 
@@ -1359,11 +1599,10 @@ SecureNow provides multiple entry points depending on your needs:
1359
1599
 
1360
1600
  Console log forwarding is enabled by default through
1361
1601
  `config.logging.enabled: true`; all console calls are automatically forwarded as
1362
- OTLP log records:
1602
+ OTLP log records, linked to the active trace:
1363
1603
 
1364
1604
  ```javascript
1365
- // At the top of your main file
1366
- require('securenow/register');
1605
+ // Started with: node -r securenow/register app.js
1367
1606
 
1368
1607
  // With config.logging.enabled=true, all console logs are automatically sent
1369
1608
  console.log('Application started');
@@ -1380,21 +1619,22 @@ console.debug('Debug info');
1380
1619
  - `console.error()` -> ERROR
1381
1620
  - `console.debug()` -> DEBUG
1382
1621
 
1383
- Logging is controlled by `config.logging.enabled` in `.securenow/runtime.json` locally or the mounted production runtime credentials file.
1622
+ A first string argument with printf placeholders (`%s`, `%d`...) is formatted like `console.log` does; objects are sent as JSON; errors keep their stack. Exported lines are redacted (8.25.0+; `config.logging.redact: false` turns it off): credential fields (password, token, apiKey, authorization, cookie, OTP codes, card numbers...) in logged objects, JSON and `key=value` text, and secret shapes anywhere in the line (JWTs, Bearer/Basic credentials, `sk_`/`pk_`/`rk_`/`snk_` keys, card numbers, emails) become `[REDACTED]`. The terminal shows the line as written.
1623
+
1624
+ Logging is controlled by `config.logging.enabled` in `.securenow/runtime.json` locally or the mounted production runtime credentials file. Crash capture (uncaught exceptions and unhandled rejections as `FATAL` records, flushed before the process exits) needs it on.
1384
1625
 
1385
1626
  ### Direct Logger API
1386
1627
 
1387
1628
  For more control, use the OpenTelemetry logger API:
1388
1629
 
1389
1630
  ```javascript
1390
- require('securenow/register');
1391
1631
  const { getLogger } = require('securenow/tracing');
1392
1632
 
1393
- // Get a logger instance
1633
+ // Get a logger instance (null when logging is disabled)
1394
1634
  const logger = getLogger('my-module', '1.0.0');
1395
1635
 
1396
1636
  // Emit structured logs
1397
- logger.emit({
1637
+ logger?.emit({
1398
1638
  severityNumber: 9, // INFO
1399
1639
  severityText: 'INFO',
1400
1640
  body: 'User logged in',
@@ -1411,19 +1651,20 @@ logger.emit({
1411
1651
  - `9` - INFO
1412
1652
  - `13` - WARN
1413
1653
  - `17` - ERROR
1654
+ - `21` - FATAL
1414
1655
 
1415
1656
  ### Using with NODE_OPTIONS
1416
1657
 
1417
1658
  ```bash
1418
- # Enable tracing + logging (console auto-forwarding is built-in since v5.6.0)
1419
- node -r securenow/register app.js
1659
+ # Tracing + logging without editing the start script
1660
+ NODE_OPTIONS="-r securenow/register" npm start
1420
1661
  ```
1421
1662
 
1422
1663
  ---
1423
1664
 
1424
1665
  ## Request Body Capture
1425
1666
 
1426
- SecureNow captures HTTP request bodies in traces by default, with sensitive fields automatically redacted. Set `config.capture.body=false` in `.securenow/runtime.json` locally or the mounted production runtime credentials file only when you need an opt-out.
1667
+ SecureNow captures HTTP request bodies in traces by default, with sensitive fields automatically redacted. Detections read them (instant rules see attacks in POST bodies). Set `config.capture.body=false` in `.securenow/runtime.json` locally or the mounted production runtime credentials file only when you need an opt-out.
1427
1668
 
1428
1669
  ### Body Capture Defaults
1429
1670
 
@@ -1446,6 +1687,8 @@ SecureNow captures HTTP request bodies in traces by default, with sensitive fiel
1446
1687
  - `application/graphql`
1447
1688
  - `multipart/form-data` (metadata capture is on unless `config.capture.multipart=false`)
1448
1689
 
1690
+ Other content types are not captured. The body is observed while your app reads it and never consumed, so a route that never reads its body (no body parser) records none. A body larger than `maxBodySize` is recorded as `[TOO LARGE: <n> bytes]`. Request headers are captured as `http.request.headers` with credential headers (`cookie`, `authorization`, anything containing `token`, `secret`, `session`, `csrf`, `api-key`...) redacted.
1691
+
1449
1692
  ### Multipart Body Capture (v5.8.0+)
1450
1693
 
1451
1694
  Multipart/form-data metadata capture is enabled by default. Set `config.capture.multipart=false` to disable it. Uses a streaming parser that never buffers file content -- memory stays at ~few KB regardless of upload size.
@@ -1473,7 +1716,9 @@ SecureNow 8.11.2 redacts sign-in credentials named `code`, `otpCode`, `oneTimeCo
1473
1716
 
1474
1717
  All request bodies are automatically scanned and sensitive fields are redacted:
1475
1718
 
1476
- **Automatically redacted fields:** `password`, `secret`, `token`, `api_key`, `card`, `cvv`, `ssn`, and more.
1719
+ **Automatically redacted fields:** `password`, `secret`, `token`, `api_key`, `card`, `cvv`, `ssn`, `email`, `phone`, `address` and more (the full list is in [Credentials Configuration](#credentials-configuration)). Field names match case-insensitively as substrings, so `creditCard` and `userEmail` are caught too.
1720
+
1721
+ **Redacted by shape, under any field name:** JWTs, `Bearer`/`Basic` credentials, `sk_`/`pk_`/`rk_`/`snk_` live and test keys, email addresses, and card numbers that pass the Luhn check.
1477
1722
 
1478
1723
  **Add custom fields to redact:**
1479
1724
 
@@ -1492,19 +1737,119 @@ All request bodies are automatically scanned and sensitive fields are redacted:
1492
1737
  ```javascript
1493
1738
  // POST /api/users with body:
1494
1739
  {
1740
+ "username": "johnd",
1495
1741
  "email": "user@example.com",
1496
1742
  "password": "secret123",
1497
- "name": "John Doe"
1743
+ "plan": "pro"
1498
1744
  }
1499
1745
 
1500
1746
  // Captured in trace as:
1501
1747
  {
1502
- "email": "user@example.com",
1748
+ "username": "johnd",
1749
+ "email": "[REDACTED]",
1503
1750
  "password": "[REDACTED]",
1504
- "name": "John Doe"
1751
+ "plan": "pro"
1505
1752
  }
1506
1753
  ```
1507
1754
 
1755
+ Preview any payload with `npx securenow redact '<json>'`.
1756
+
1757
+ ---
1758
+
1759
+ ## Custom Security Events
1760
+
1761
+ Report what only your app knows (sign-ins, OTPs, password resets, MFA). Events feed brute-force, credential-stuffing and account-takeover detection, and alert rules can query them.
1762
+
1763
+ ```javascript
1764
+ const { track } = require('securenow/events');
1765
+ const { resolveClientIp } = require('securenow/resolve-ip');
1766
+
1767
+ app.post('/api/login', async (req, res) => {
1768
+ const ip = resolveClientIp(req); // same trusted-proxy rules as the firewall
1769
+ const user = await findUserByEmail(req.body.email);
1770
+ if (!user || !(await verifyPassword(user, req.body.password))) {
1771
+ track('auth.login.failure', {
1772
+ userId: user ? user.id : undefined,
1773
+ ip,
1774
+ userAgent: req.get('user-agent'),
1775
+ severity: 'warn',
1776
+ attributes: { reason: user ? 'bad_password' : 'unknown_user' },
1777
+ });
1778
+ return res.status(401).json({ error: 'Invalid email or password' });
1779
+ }
1780
+ track('auth.login.success', { userId: user.id, ip, userAgent: req.get('user-agent'), attributes: { method: 'password' } });
1781
+ res.json({ ok: true });
1782
+ });
1783
+ ```
1784
+
1785
+ `track(type, { userId, sessionId, ip, userAgent, severity, ts, attributes })` is fire-and-forget: it buffers up to 1000 events, sends batches of up to 100 every 2 seconds, and never throws. `flush()` returns a promise for scripts that exit right away. It works in any Node process that can resolve the runtime credentials, with or without the preload.
1786
+
1787
+ Recommended auth events: `auth.login.failure`, `auth.login.success`, `auth.otp.send.requested`, `auth.otp.send.result`, `auth.otp.verify.failure`, `auth.otp.verify.success`, `auth.password_reset.requested`, `auth.password_reset.completed`, `auth.mfa.challenge.failure`, `auth.mfa.challenge.success`, `auth.session.revoked`.
1788
+
1789
+ Check what arrives with `npx securenow telemetry fields --since 1h --json` (it lists custom event types). From a shell or a non-JS service: `npx securenow event send <type> --user <id> --ip <ip> --attrs k=v`.
1790
+
1791
+ ---
1792
+
1793
+ ## Session Revocation
1794
+
1795
+ Revoke a stolen session (or every session of a user) with `npx securenow revoke session <id>` / `revoke user <id>`; the SDK pulls active revocations every 30 seconds and rejects matching requests locally. It fails open: an outage never blocks sign-ins.
1796
+
1797
+ The session id is the SHA-256 of the Bearer token or of the first session cookie found (`__Secure-next-auth.session-token`, `__Host-next-auth.session-token`, `next-auth.session-token`, `connect.sid`, `session`, `sid`, plus `config.sessions.cookieNames` **(8.25.0+)**); the user id is the JWT `sub`.
1798
+
1799
+ **Zero code, every framework (8.25.0+)** — in `.securenow/runtime.json`:
1800
+
1801
+ ```json
1802
+ { "config": { "sessions": { "guard": true, "capture": true } } }
1803
+ ```
1804
+
1805
+ `guard` answers a revoked session or user with 401 and clears the session cookies; `capture` sends `session.seen` (deduplicated per session and IP every 5 minutes) for session-theft detection. It runs with `-r securenow/register`, Next.js, Nuxt and `securenow/firewall-only`, and needs the runtime API key.
1806
+
1807
+ **Express-style middleware** (Express, Connect, Polka, NestJS with the Express adapter, Feathers-Express):
1808
+
1809
+ ```js
1810
+ const sessions = require('securenow/sessions');
1811
+ // Emits session.seen (feeds impossible-travel / concurrent-session detection)
1812
+ // and rejects revoked sessions. Auto-detects NextAuth / connect.sid / JWT.
1813
+ app.use(sessions.guard());
1814
+ // ...or with your own ids:
1815
+ app.use(sessions.guard({ getSessionId: (req) => req.session?.id, getUserId: (req) => req.user?.id }));
1816
+ // ...or emit-only (detection without enforcement):
1817
+ app.use(sessions.capture());
1818
+ ```
1819
+
1820
+ **Anything else (8.25.0+)** — `sessions.check(req)` takes a Node request or a Web `Request` and returns `{ revoked, sessionId, userId }`; you return the 401:
1821
+
1822
+ ```js
1823
+ // Fastify
1824
+ fastify.addHook('onRequest', async (request, reply) => {
1825
+ if (sessions.check(request.raw).revoked) return reply.code(401).send({ error: 'session_revoked' });
1826
+ });
1827
+
1828
+ // Next.js route handler (Node.js runtime)
1829
+ import { check } from 'securenow/sessions';
1830
+ export async function GET(request) {
1831
+ if (check(request).revoked) return Response.json({ error: 'session_revoked' }, { status: 401 });
1832
+ return Response.json({ ok: true });
1833
+ }
1834
+ ```
1835
+
1836
+ Koa (`sessions.check(ctx.req)`), Hono (`sessions.check(c.req.raw)`) and h3 (`sessions.check(event.node.req)`) work the same way; examples in SKILL-API.md, section 5.10.
1837
+
1838
+ **Lower level:** `sessions.isRevoked({ sessionId, userId })`. Call `sessions.start()` once at boot when this is all you use.
1839
+
1840
+ ---
1841
+
1842
+ ## Deployment
1843
+
1844
+ The same credentials file works everywhere; only how it reaches the app changes. Recipes with full Dockerfiles, Kubernetes manifests and systemd units are in SKILL-API.md, section 6.
1845
+
1846
+ - **Docker:** `CMD ["node", "-r", "securenow/register", "src/index.js"]` with `WORKDIR` set to the app folder. Mount the production file at `/app/.securenow/credentials.json` or pass `SECURENOW_CREDENTIALS` (`docker run --env-file securenow.env` with the line printed by `--format env`). Keep `.securenow/runtime.json` and `admin.json` out of the image with a `.dockerignore`. Next.js standalone: the file goes next to `server.js`. Nuxt: in the folder you run `node .output/server/index.mjs` from.
1847
+ - **Kubernetes:** a Secret mounted as `/app/.securenow/credentials.json` (`subPath`), or `SECURENOW_CREDENTIALS` from a Secret.
1848
+ - **PM2:** `node_args: ['-r', require.resolve('securenow/register')]` in `ecosystem.config.cjs`, fork or cluster.
1849
+ - **systemd / VMs:** `WorkingDirectory` = the app folder; `ExecStart=/usr/bin/node -r securenow/register src/index.js`.
1850
+ - **Railway, Render, Heroku, Fly:** `SECURENOW_CREDENTIALS` as a secret variable.
1851
+ - **Vercel:** `SECURENOW_CREDENTIALS` in the project's environment; not tested end to end, firewall not verified on serverless functions.
1852
+
1508
1853
  ---
1509
1854
 
1510
1855
  ## Advanced Configuration
@@ -1515,16 +1860,14 @@ All request bodies are automatically scanned and sensitive fields are redacted:
1515
1860
  {
1516
1861
  "apiKey": "snk_live_...",
1517
1862
  "app": {
1518
- "key": "my-production-app",
1519
- "name": "my-production-app",
1520
- "instance": "http://collector.example.com:4318"
1863
+ "key": "<app-uuid>",
1864
+ "name": "my-production-app"
1521
1865
  },
1522
1866
  "config": {
1523
- "runtime": { "noUuid": true, "strict": true, "deploymentEnvironment": "production" },
1867
+ "runtime": { "strict": true, "deploymentEnvironment": "production" },
1524
1868
  "otel": {
1525
- "headers": { "x-securenow-app-key": "my-production-app" },
1526
- "logLevel": "info",
1527
- "disableInstrumentations": ["fs", "dns"]
1869
+ "logLevel": "warn",
1870
+ "disableInstrumentations": ["@opentelemetry/instrumentation-dns", "@opentelemetry/instrumentation-net"]
1528
1871
  },
1529
1872
  "logging": { "enabled": true },
1530
1873
  "capture": {
@@ -1532,15 +1875,20 @@ All request bodies are automatically scanned and sensitive fields are redacted:
1532
1875
  "maxBodySize": 20480,
1533
1876
  "sensitiveFields": ["internal_id", "session_key"]
1534
1877
  },
1878
+ "networking": { "trustedProxies": ["cloudflare"] },
1535
1879
  "firewall": {
1536
- "tcp": true,
1537
- "versionCheckInterval": 10,
1538
- "syncInterval": 300
1539
- }
1880
+ "tcp": false,
1881
+ "versionCheckInterval": 2,
1882
+ "syncInterval": 3600,
1883
+ "rateLimitStore": "redis://redis:6379"
1884
+ },
1885
+ "sessions": { "guard": true, "capture": true }
1540
1886
  }
1541
1887
  }
1542
1888
  ```
1543
1889
 
1890
+ `sessions` needs 8.25.0+. To send telemetry to your own collector instead of SecureNow (advanced; the SecureNow dashboard then sees nothing), set `config.otel.endpoint`.
1891
+
1544
1892
  ### Using Multiple Logger Instances
1545
1893
 
1546
1894
  ```javascript
@@ -1550,14 +1898,14 @@ const authLogger = getLogger('auth-service', '1.0.0');
1550
1898
  const dbLogger = getLogger('database', '1.0.0');
1551
1899
  const apiLogger = getLogger('api-handler', '1.0.0');
1552
1900
 
1553
- authLogger.emit({
1901
+ authLogger?.emit({
1554
1902
  severityNumber: 9,
1555
1903
  severityText: 'INFO',
1556
1904
  body: 'User authenticated',
1557
1905
  attributes: { userId: 123 },
1558
1906
  });
1559
1907
 
1560
- dbLogger.emit({
1908
+ dbLogger?.emit({
1561
1909
  severityNumber: 13,
1562
1910
  severityText: 'WARN',
1563
1911
  body: 'Slow query detected',
@@ -1579,23 +1927,26 @@ if (isLoggingEnabled()) {
1579
1927
 
1580
1928
  ### Programmatic Configuration
1581
1929
 
1582
- Use `.securenow/runtime.json` for local development and tests; CI/production should mount the generated runtime credentials file:
1930
+ Use `.securenow/runtime.json` for local development and tests; CI/production should mount the generated runtime credentials file. There is no configuration API: the preload reads the file (or `SECURENOW_CREDENTIALS`) at boot.
1583
1931
 
1584
1932
  ```javascript
1585
1933
  // Reads .securenow/runtime.json first, with legacy/generated runtime credentials as fallbacks.
1586
1934
  require('securenow/register');
1587
1935
  ```
1588
1936
 
1937
+ `require('securenow/app-config')` exposes the resolved values (`resolveAll()`, `resolveFirewallOptions()`, ...) for diagnostics.
1938
+
1589
1939
  ---
1590
1940
 
1591
1941
  ## TypeScript Support
1592
1942
 
1593
- SecureNow includes full TypeScript definitions.
1943
+ SecureNow ships type definitions for `securenow/tracing`, `securenow/events`, `securenow/sessions`, `securenow/nextjs`, `securenow/nuxt`, `securenow/rate-limits` and `securenow/telemetry-schema`.
1594
1944
 
1595
1945
  ### Type Definitions
1596
1946
 
1597
1947
  ```typescript
1598
1948
  import { getLogger, isLoggingEnabled, Logger, LogRecord } from 'securenow/tracing';
1949
+ import { track } from 'securenow/events';
1599
1950
 
1600
1951
  // Get a typed logger
1601
1952
  const logger: Logger | null = getLogger('my-service', '1.0.0');
@@ -1611,18 +1962,23 @@ if (logger) {
1611
1962
  action: 'login',
1612
1963
  },
1613
1964
  };
1614
-
1965
+
1615
1966
  logger.emit(logRecord);
1616
1967
  }
1617
1968
 
1618
1969
  // Check if enabled
1619
1970
  const enabled: boolean = isLoggingEnabled();
1971
+
1972
+ // Custom event
1973
+ track('auth.login.success', { userId: 'u_123', attributes: { method: 'password' } });
1620
1974
  ```
1621
1975
 
1976
+ Run TypeScript apps with `node -r securenow/register --import tsx src/index.ts`, `node -r securenow/register -r ts-node/register src/index.ts`, or the compiled output.
1977
+
1622
1978
  ### Next.js with TypeScript
1623
1979
 
1624
1980
  ```typescript
1625
- // instrumentation.ts
1981
+ // instrumentation.ts (src/instrumentation.ts with a src/ layout)
1626
1982
  export async function register() {
1627
1983
  if (process.env.NEXT_RUNTIME === 'nodejs') {
1628
1984
  try {
@@ -1637,16 +1993,21 @@ export async function register() {
1637
1993
  }
1638
1994
  ```
1639
1995
 
1640
- ```javascript
1641
- // next.config.js
1642
- const { withSecureNow } = require('securenow/nextjs-webpack-config');
1996
+ ```typescript
1997
+ // next.config.ts (Next.js 15+)
1998
+ import type { NextConfig } from 'next';
1643
1999
 
1644
- module.exports = withSecureNow({
1645
- reactStrictMode: true,
1646
- });
2000
+ const nextConfig: NextConfig = {
2001
+ serverExternalPackages: ['securenow'],
2002
+ outputFileTracingIncludes: {
2003
+ '/*': ['./node_modules/securenow/**/*'],
2004
+ },
2005
+ };
2006
+
2007
+ export default nextConfig;
1647
2008
  ```
1648
2009
 
1649
- `withSecureNow()` adds the server external package config and `outputFileTracingIncludes` for standalone/self-hosted builds.
2010
+ The older `withSecureNow()` wrapper from `securenow/nextjs-webpack-config` still exists, but it also adds a `webpack` hook; the plain config above is what `init` writes and what is tested.
1650
2011
 
1651
2012
  ### NestJS with TypeScript
1652
2013
 
@@ -1678,25 +2039,22 @@ npx securenow env
1678
2039
  npx securenow status --env local
1679
2040
  ```
1680
2041
 
1681
- The output should show an app key, runtime API key source, collector endpoint, and deployment environment from `.securenow/runtime.json` or the mounted runtime credentials file.
2042
+ The output should show an app key, runtime API key source, collector endpoint, and deployment environment from `.securenow/runtime.json` or the mounted runtime credentials file. `Service name` must be the app key UUID.
1682
2043
 
1683
- **Check 2: Verify OTLP collector is running**
2044
+ **Check 2: Look at the right environment**
1684
2045
 
1685
- ```bash
1686
- curl http://localhost:4318/v1/traces
1687
- # Should return 200 or 405 (method not allowed)
1688
- ```
2046
+ `npx securenow init` writes `deploymentEnvironment: "local"`, while the CLI and dashboard default to `production`. Use `--env local` (or `--env all`), or set the environment you expect.
1689
2047
 
1690
2048
  **Check 3: Run doctor and enable debug diagnostics**
1691
2049
 
1692
2050
  ```bash
1693
2051
  npx securenow doctor --json
1694
- OTEL_LOG_LEVEL=debug node -r securenow/register app.js
2052
+ npx securenow test-span
1695
2053
  ```
1696
2054
 
1697
- Doctor checks OTLP reachability and flags duplicate `@opentelemetry/api` versions that can silently leave tracing on the OpenTelemetry noop provider.
2055
+ Doctor probes traces, logs, the API and firewall sync, and flags duplicate `@opentelemetry/api` versions that can silently leave tracing on the OpenTelemetry noop provider. `test-span` should print `Span accepted (HTTP 200)`.
1698
2056
 
1699
- For a persistent setting, update credentials:
2057
+ For a persistent diagnostic setting, update credentials (or run once with `OTEL_LOG_LEVEL=debug`):
1700
2058
 
1701
2059
  ```json
1702
2060
  {
@@ -1708,12 +2066,14 @@ For a persistent setting, update credentials:
1708
2066
 
1709
2067
  Look for lines like:
1710
2068
  ```
1711
- [securenow] OTel SDK started -> http://localhost:4318/v1/traces
2069
+ [securenow] OTel SDK started → https://ingest.securenow.ai/v1/traces
1712
2070
  ```
1713
2071
 
2072
+ No `[securenow]` line at all means the process that serves HTTP never loaded the SDK: put the preload on the command that actually starts it (start script, Docker `CMD`, PM2 `node_args`, systemd `ExecStart`).
2073
+
1714
2074
  **Check 4: Verify initialization order**
1715
2075
 
1716
- SecureNow must be required BEFORE any other modules:
2076
+ SecureNow must be loaded BEFORE any other modules:
1717
2077
 
1718
2078
  ```javascript
1719
2079
  // Correct
@@ -1725,19 +2085,28 @@ const express = require('express');
1725
2085
  require('securenow/register');
1726
2086
  ```
1727
2087
 
2088
+ In ESM apps, use `node -r securenow/register` instead of an `import` in your code, and look for `[securenow] ESM loader hook auto-registered (module.register)` at startup (Node >= 20.6).
2089
+
2090
+ **Check 5: Wrong credentials?**
2091
+
2092
+ The startup line shows `credentials=<file>` or `credentials=env:SECURENOW_CREDENTIALS`. `SECURENOW_CREDENTIALS` wins over every file; a `runtime.json` beats a `credentials.production.json` in the same folder; a parent folder or `~/.securenow/` is used when the project has none. Remove the stray source.
2093
+
1728
2094
  ### Firewall Not Blocking IPs
1729
2095
 
1730
2096
  **Check 1: Is a runtime API key in `.securenow/runtime.json` or the mounted runtime credentials file?**
1731
2097
 
1732
2098
  ```bash
1733
2099
  npx securenow api-key show
2100
+ npx securenow env # "Firewall enabled", not "missing firewall API key"
1734
2101
  ```
1735
2102
 
1736
- **Check 2: Is the IP in the blocklist?**
2103
+ **Check 2: Is the IP in the blocklist, for this app and environment?**
1737
2104
 
1738
2105
  ```bash
1739
- npx securenow blocklist
1740
- npx securenow firewall test-ip 1.2.3.4
2106
+ npx securenow blocklist --env production
2107
+ npx securenow firewall test-ip 1.2.3.4 --path /admin --env production
2108
+ npx securenow firewall apps # firewall on for that environment?
2109
+ npx securenow firewall hits --ip 1.2.3.4
1741
2110
  ```
1742
2111
 
1743
2112
  **Check 3: Check startup logs for sync status**
@@ -1745,14 +2114,15 @@ npx securenow firewall test-ip 1.2.3.4
1745
2114
  You should see:
1746
2115
  ```
1747
2116
  [securenow] Firewall: ENABLED
1748
- [securenow] Firewall: synced 142 blocked IPs
2117
+ [securenow] Firewall: environment=production
2118
+ [securenow] Firewall: synced 142 global blocked IPs (138 exact + 4 CIDR ranges) and 0 scoped block rules
1749
2119
  ```
1750
2120
 
1751
- If you see `initial sync failed`, check the API URL and key.
2121
+ If you see `initial sync failed`, check the network and the key. `Firewall: environment=` must match the environment of the block.
1752
2122
 
1753
2123
  **Check 4: Wait for propagation**
1754
2124
 
1755
- After blocking an IP, it takes 10-15 seconds to propagate (one version-check interval).
2125
+ After blocking an IP, it takes up to one version-check interval (10 seconds by default) to propagate.
1756
2126
 
1757
2127
  **Check 5: Are you behind a proxy?**
1758
2128
 
@@ -1764,9 +2134,13 @@ proxy_set_header X-Real-IP $remote_addr;
1764
2134
  proxy_set_header X-Forwarded-Proto $scheme;
1765
2135
  ```
1766
2136
 
1767
- SecureNow trusts private, loopback, and same-host proxy peers automatically. For external load balancers or public proxy IPs, add them to `config.networking.trustedProxies` in `.securenow/runtime.json` or the mounted runtime credentials file: exact IPs, IPv4 ranges such as `"203.0.113.0/24"`, or `"cloudflare"` for Cloudflare's published edge ranges (8.21.0+). If no forwarded header reaches the app, SecureNow can detect the attack shape but cannot recover the real visitor IP from traces. Starter protection refuses block mode for an app whose traffic arrives through a proxy it doesn't trust, because a block would hit the proxy and everyone behind it.
2137
+ SecureNow trusts private, loopback, and same-host proxy peers automatically, and Unix-socket connections from 8.25.0. For external load balancers or public proxy IPs, add them to `config.networking.trustedProxies` in `.securenow/runtime.json` or the mounted runtime credentials file: exact IPs, IPv4 ranges such as `"203.0.113.0/24"`, or `"cloudflare"` for Cloudflare's published edge ranges (8.21.0+). If no forwarded header reaches the app, SecureNow can detect the attack shape but cannot recover the real visitor IP from traces. Starter protection refuses block mode for an app whose traffic arrives through a proxy it doesn't trust, because a block would hit the proxy and everyone behind it.
2138
+
2139
+ **Check 6: Allowlist or layers**
1768
2140
 
1769
- **Check 6: Using PM2?**
2141
+ An IP on the app's allowlist skips the blocklist. Layers 2 and 3 only see direct connections; behind a proxy, Layer 1 does the blocking. At Layer 1, IPv6 addresses and ranges match from 8.25.0 (before, IPv6 ranges never matched).
2142
+
2143
+ **Check 7: Using PM2?**
1770
2144
 
1771
2145
  Make sure `node_args: ['-r', require.resolve('securenow/register')]` is in your `ecosystem.config.cjs`. Without it, PM2 restarts skip the SDK entirely; with a bare `'-r securenow/register'`, cluster workers started from another folder (`pm2 resurrect` at boot) crash with `Cannot find module 'securenow/register'`.
1772
2146
 
@@ -1782,24 +2156,24 @@ Make sure `node_args: ['-r', require.resolve('securenow/register')]` is in your
1782
2156
  }
1783
2157
  ```
1784
2158
 
1785
- **Check 2: Verify console instrumentation is loaded**
2159
+ **Check 2: Verify the SDK is loaded**
1786
2160
 
1787
- ```javascript
1788
- require('securenow/register');
2161
+ ```bash
2162
+ node -r securenow/register app.js
1789
2163
  ```
1790
2164
 
1791
2165
  **Check 3: Check console output**
1792
2166
 
1793
2167
  You should see:
1794
2168
  ```
1795
- [securenow] Logging: ENABLED -> http://localhost:4318/v1/logs
2169
+ [securenow] Logging: ENABLED → https://ingest.securenow.ai/v1/logs
1796
2170
  ```
1797
2171
 
1798
- **Check 4: Verify OTLP logs endpoint**
2172
+ **Check 4: Query them**
1799
2173
 
1800
2174
  ```bash
1801
- curl http://localhost:4318/v1/logs
1802
- # Should return 200 or 405
2175
+ npx securenow logs --since 15m --env local
2176
+ npx securenow log send "manual test" --level info
1803
2177
  ```
1804
2178
 
1805
2179
  ### Request Body Not Captured
@@ -1820,10 +2194,15 @@ Body capture only works for:
1820
2194
  - `application/json`
1821
2195
  - `application/x-www-form-urlencoded`
1822
2196
  - `application/graphql`
2197
+ - `multipart/form-data` (metadata)
2198
+
2199
+ **Check 3: Does the route read its body?**
2200
+
2201
+ SecureNow records the body as the app reads it. A route without a body parser (Koa without `koa-bodyparser`, Polka without a parser) records none.
1823
2202
 
1824
- **Check 3: Check body size**
2203
+ **Check 4: Check body size**
1825
2204
 
1826
- Bodies larger than `config.capture.maxBodySize` are truncated:
2205
+ Bodies larger than `config.capture.maxBodySize` are recorded as `[TOO LARGE: <n> bytes]` instead of their content:
1827
2206
 
1828
2207
  ```json
1829
2208
  {
@@ -1833,6 +2212,10 @@ Bodies larger than `config.capture.maxBodySize` are truncated:
1833
2212
  }
1834
2213
  ```
1835
2214
 
2215
+ **Check 5: Next.js Edge runtime**
2216
+
2217
+ Routes with `export const runtime = 'edge'` and Edge middleware are not instrumented.
2218
+
1836
2219
  ### High Memory Usage
1837
2220
 
1838
2221
  **Option 1: Disable body capture**
@@ -1855,16 +2238,18 @@ Bodies larger than `config.capture.maxBodySize` are truncated:
1855
2238
  }
1856
2239
  ```
1857
2240
 
1858
- **Option 3: Disable specific instrumentations**
2241
+ **Option 3: Disable specific instrumentations** (full package names)
1859
2242
 
1860
2243
  ```json
1861
2244
  {
1862
2245
  "config": {
1863
- "otel": { "disableInstrumentations": ["fs", "dns", "net"] }
2246
+ "otel": { "disableInstrumentations": ["@opentelemetry/instrumentation-dns", "@opentelemetry/instrumentation-net"] }
1864
2247
  }
1865
2248
  }
1866
2249
  ```
1867
2250
 
2251
+ Keep `config.otel.middlewareSpans` off (the default): one span per middleware per request is most of the telemetry on a busy Express app.
2252
+
1868
2253
  ### Next.js Instrumentation Not Working
1869
2254
 
1870
2255
  **Check 1: Is `securenow` externalized?**
@@ -1878,15 +2263,15 @@ module.exports = {
1878
2263
  };
1879
2264
  ```
1880
2265
 
1881
- For Next.js < 15, add `securenow` to `experimental.serverComponentsExternalPackages` and enable `experimental.instrumentationHook`.
2266
+ For Next.js 14, the keys go under `experimental` together with `instrumentationHook: true` (see Option B above).
1882
2267
 
1883
2268
  **Check 2: Verify instrumentation file location**
1884
2269
 
1885
- `instrumentation.ts` or `instrumentation.js` must be in the project root (same level as `app/` or `pages/`).
2270
+ `instrumentation.ts` or `instrumentation.js` must sit next to `app/` or `pages/`: the project root, or `src/` when the project uses a `src/` layout.
1886
2271
 
1887
2272
  **Check 3: Check for OTel MODULE_NOT_FOUND errors**
1888
2273
 
1889
- If you see `MODULE_NOT_FOUND` for `@opentelemetry/*`, `./firewall`, or other SecureNow runtime modules, your `next.config.js` is missing the externalization or standalone output include. Run `npx securenow init`; if your config is custom, use the prompt it prints to merge the edit safely.
2274
+ If you see `MODULE_NOT_FOUND` for `@opentelemetry/*`, `./firewall`, or other SecureNow runtime modules, or every standalone request returns 500, your instrumentation file still uses `webpackIgnore` (written by `init` before 8.24.0) or `next.config.js` is missing the externalization or standalone output include. Run `npx securenow init`; if your config is custom, use the prompt it prints to merge the edit safely.
1890
2275
 
1891
2276
  **Check 4: Restart dev server**
1892
2277
 
@@ -1899,7 +2284,9 @@ npm run dev
1899
2284
 
1900
2285
  **Problem: Different service names for each worker**
1901
2286
 
1902
- **Solution: Set `config.runtime.noUuid`**
2287
+ **Solution: let `config.runtime.noUuid` stay automatic**
2288
+
2289
+ With `app.key` in the credentials, every worker reports under the app key. A per-worker suffix appears only when `noUuid` is set to `false` or no app key was found (check the `credentials=` startup line):
1903
2290
 
1904
2291
  ```json
1905
2292
  {
@@ -1909,8 +2296,6 @@ npm run dev
1909
2296
  }
1910
2297
  ```
1911
2298
 
1912
- This uses the same service name for all workers.
1913
-
1914
2299
  **Problem: Workers not instrumented / firewall not active**
1915
2300
 
1916
2301
  **Solution: Use node_args in PM2 config**
@@ -1927,7 +2312,11 @@ module.exports = {
1927
2312
  };
1928
2313
  ```
1929
2314
 
1930
- Without `node_args`, PM2 starts your script directly without the securenow preload, so neither tracing nor the firewall will be active. Use the absolute path: in cluster mode Node resolves a bare `-r securenow/register` from the PM2 daemon's folder, and the workers crash-loop when the daemon was started elsewhere (as `pm2 resurrect` does at boot).
2315
+ Without `node_args`, PM2 starts your script directly without the securenow preload, so neither tracing nor the firewall will be active. Use the absolute path: in cluster mode Node resolves a bare `-r securenow/register` from the PM2 daemon's folder, and the workers crash-loop when the daemon was started elsewhere (as `pm2 resurrect` does at boot). Rate limits count per worker unless `config.firewall.rateLimitStore` points at Redis.
2316
+
2317
+ ### Tests Sending Data
2318
+
2319
+ Test runs load the project's `.securenow` credentials and report to the real app. Run them with `SECURENOW_DISABLED=1 npm test`.
1931
2320
 
1932
2321
  ---
1933
2322
 
@@ -1942,7 +2331,7 @@ Do not hardcode configuration in code or deployment dashboards. Use `.securenow/
1942
2331
  const appKey = 'hardcoded-value';
1943
2332
 
1944
2333
  // Good: use .securenow/runtime.json
1945
- // { "app": { "key": "<uuid>", "instance": "https://ingest.securenow.ai" }, "apiKey": "snk_live_..." }
2334
+ // { "app": { "key": "<uuid>", "name": "my-api" }, "apiKey": "snk_live_..." }
1946
2335
  ```
1947
2336
 
1948
2337
  ### 2. Use Structured Logging
@@ -1956,7 +2345,7 @@ console.log('User 123 logged in');
1956
2345
  // Better -- structured attributes
1957
2346
  console.log('User logged in', {
1958
2347
  userId: 123,
1959
- email: 'user@example.com',
2348
+ plan: 'pro',
1960
2349
  timestamp: new Date().toISOString(),
1961
2350
  });
1962
2351
  ```
@@ -1980,18 +2369,21 @@ console.debug('Cache hit', { key: 'user:123' });
1980
2369
 
1981
2370
  ### 4. Don't Log Sensitive Data
1982
2371
 
1983
- Even though SecureNow automatically redacts common sensitive fields, avoid logging:
2372
+ Request bodies, headers and (from 8.25.0) log lines are redacted, but log redaction recognizes field names and value shapes, not meaning:
1984
2373
 
1985
2374
  ```javascript
1986
- // Bad
2375
+ // Bad: "hunter2" has no secret shape and no field name, so it is exported
2376
+ console.log(`Password for ${user.id} reset to hunter2`);
2377
+
2378
+ // Redacted (8.25.0+), but still better not logged
1987
2379
  console.log('Login attempt', {
1988
- email: 'user@example.com',
1989
- password: 'secret123', // Will be redacted, but don't log it!
2380
+ email: 'user@example.com', // -> [REDACTED]
2381
+ password: 'secret123', // -> [REDACTED]
1990
2382
  });
1991
2383
 
1992
2384
  // Good
1993
2385
  console.log('Login attempt', {
1994
- email: 'user@example.com',
2386
+ userId: user.id,
1995
2387
  timestamp: Date.now(),
1996
2388
  });
1997
2389
  ```
@@ -2009,51 +2401,47 @@ const dbLogger = getLogger('database', '1.0.0');
2009
2401
  const apiLogger = getLogger('api', '1.0.0');
2010
2402
  ```
2011
2403
 
2012
- ### 6. Disable Body Capture Outside Development
2404
+ ### 6. Keep Body Capture On, Tune Redaction
2405
+
2406
+ Detections read request bodies (SQL injection, XSS and command payloads often arrive in POST bodies). Rather than turning capture off, add your own field names:
2013
2407
 
2014
2408
  ```json
2015
2409
  {
2016
2410
  "config": {
2017
- "capture": { "body": false }
2411
+ "capture": { "sensitiveFields": ["national_id", "internal_token"] }
2018
2412
  }
2019
2413
  }
2020
2414
  ```
2021
2415
 
2416
+ ### 7. Emit Auth Events
2417
+
2418
+ `track()` the sign-in, OTP, reset and MFA outcomes (see [Custom Security Events](#custom-security-events)): they are what brute-force and account-takeover detection needs.
2419
+
2022
2420
  ---
2023
2421
 
2024
2422
  ## Supported Runtimes
2025
2423
 
2026
- - **Node.js:** 18+
2027
- - **Frameworks:** Express, Next.js, Nuxt 3, Fastify, NestJS, Koa, Hapi, and more
2028
- - **Databases:** PostgreSQL, MySQL, MongoDB, Redis
2029
- - **HTTP Clients:** axios, fetch, node-fetch, got, request
2030
- - **GraphQL:** Apollo Server, GraphQL.js
2031
- - **Message Queues:** Redis, BullMQ
2032
- - **And many more via OpenTelemetry auto-instrumentation**
2424
+ - **Node.js:** tested on Node 24.22 (CI: Node 24); use a patched 22, 24 or 26 release (see the security note). ESM apps need Node >= 20.6 for the automatic loader hook.
2425
+ - **Bun:** 1.3, for apps served through `node:http` (`Bun.serve` is not supported)
2426
+ - **Frameworks (tested):** Express 4.22 and 5.3, Fastify 5.12, NestJS 11.2, Koa 3.3, Hapi 21.4, Hono 4, Polka 0.5, Feathers 5.0, h3 1.15, Next.js 15.5 and 16.4, Nuxt 3.21 and 4.6; PM2 5.4 (fork and cluster). Older majors are expected to work but are not tested.
2427
+ - **Databases:** PostgreSQL, MySQL, MongoDB, Redis (OpenTelemetry auto-instrumentation)
2428
+ - **HTTP Clients:** Node `http`/`https` and the clients built on them, `fetch` (undici)
2429
+ - **Not supported:** Deno, edge runtimes (Cloudflare Workers, Vercel Edge), browser telemetry. Not tested: Vercel, AWS Lambda and other serverless platforms.
2033
2430
 
2034
2431
  ---
2035
2432
 
2036
2433
  ## Automatic Instrumentations
2037
2434
 
2038
- SecureNow automatically instruments:
2435
+ SecureNow loads the OpenTelemetry Node auto-instrumentations (`@opentelemetry/auto-instrumentations-node`), plus its own HTTP and MongoDB setup and route capture for Fastify (and, 8.25.0+, Hono, h3, Feathers, Polka and Next.js). Among them:
2039
2436
 
2040
2437
  - HTTP/HTTPS (incoming and outgoing)
2041
- - Express.js
2042
- - Fastify
2043
- - Koa
2044
- - Hapi
2045
- - Next.js
2046
- - PostgreSQL
2047
- - MySQL/MySQL2
2048
- - MongoDB
2049
- - Redis
2050
- - GraphQL
2051
- - gRPC
2052
- - DNS
2053
- - File System
2054
- - And 50+ more libraries
2055
-
2056
- No code changes needed!
2438
+ - Express.js, Koa, Hapi, NestJS
2439
+ - PostgreSQL, MySQL/MySQL2, MongoDB, Redis/ioredis
2440
+ - GraphQL, gRPC
2441
+ - DNS, net
2442
+ - And the rest of the OpenTelemetry auto-instrumentation set
2443
+
2444
+ Skip any of them with `config.otel.disableInstrumentations` (full package names). No code changes needed!
2057
2445
 
2058
2446
  ---
2059
2447
 
@@ -2067,53 +2455,54 @@ No code changes needed!
2067
2455
  +---------------------------+
2068
2456
  | SecureNow Library |
2069
2457
  | +---------------------+ |
2070
- | | Firewall Layer | | <-- Blocks IPs before they reach your app
2458
+ | | Firewall Layer | | <-- Blocks, rate-limits and challenges before your app
2071
2459
  | +---------------------+ |
2072
2460
  | | Auto-instrumentation | |
2073
2461
  | | Console wrapper | |
2074
2462
  | | Body capture | |
2463
+ | | Crash capture | |
2075
2464
  | +---------------------+ |
2076
2465
  +---------------------------+
2077
2466
  |
2078
2467
  +---------+---------+
2079
2468
  | |
2080
2469
  v v
2081
- +-----------+ +-----------+
2082
- | OTel SDK | | SecureNow |
2083
- | OTLP | | API |
2084
- | Exporters | | (blocklist|
2085
- +-----------+ | sync) |
2086
- | +-----------+
2087
- v
2088
- +-----------+
2089
- | Your OTLP |
2090
- | Backend |
2091
- +-----------+
2470
+ +-----------+ +-------------+
2471
+ | OTel SDK | | Firewall |
2472
+ | OTLP | | sync |
2473
+ | Exporters | | (blocklist, |
2474
+ +-----------+ | rules) |
2475
+ | +-------------+
2476
+ v |
2477
+ +--------------------------------+
2478
+ | SecureNow ingest gateway |
2479
+ | https://ingest.securenow.ai |
2480
+ +--------------------------------+
2092
2481
  ```
2093
2482
 
2094
2483
  ---
2095
2484
 
2096
2485
  ## Performance
2097
2486
 
2098
- - **Minimal overhead:** < 1% CPU and memory impact
2099
- - **Batch export:** Traces and logs are batched before sending
2487
+ - **Low overhead:** traces and logs are batched before export
2100
2488
  - **Async processing:** No blocking of application threads
2101
- - **Configurable:** Disable instrumentations you don't need
2102
- - **Firewall:** Sub-millisecond IP lookups using pre-compiled hash sets and sorted CIDR lists
2489
+ - **Configurable:** Disable instrumentations you don't need; middleware spans are off by default
2490
+ - **Firewall:** Sub-millisecond IP lookups using pre-compiled hash sets and CIDR lists, all in memory
2103
2491
  - **Smart sync:** Version-based polling with ETag/304 eliminates unnecessary data transfer
2492
+ - **Rate limits:** the firewall never waits on Redis when a shared store is configured
2104
2493
 
2105
2494
  ---
2106
2495
 
2107
2496
  ## Security
2108
2497
 
2109
- - **Automatic redaction** of sensitive fields (passwords, tokens, keys)
2498
+ - **Automatic redaction** of sensitive fields (passwords, tokens, keys, emails, card numbers, OTP codes) in captured headers and bodies
2110
2499
  - **Configurable** sensitive field patterns
2111
- - **No data stored** locally -- everything sent to your OTLP backend
2500
+ - **No data stored** locally -- telemetry is sent to the SecureNow ingest gateway
2112
2501
  - **Multi-layer firewall** -- blocks IPs at HTTP, TCP, OS, and cloud-edge levels
2113
- - **API keys** with granular scopes, IP allowlisting, and SHA-256 hashing
2114
- - **Allowlist support** -- trusted IPs are never blocked
2502
+ - **Runtime API keys** scoped to one app (`traces:write`, `logs:write`, `firewall:read`, `blocklist:read`, `allowlist:read`)
2503
+ - **Allowlist support** -- deny-by-default lock to known IPs
2115
2504
  - **Fail-open by default** -- network issues never accidentally block legitimate traffic
2116
- - **Open source** -- audit the code yourself
2505
+ - **Readable source** -- the package ships unminified; audit it in `node_modules/securenow`
2117
2506
 
2118
2507
  ---
2119
2508
 
@@ -2125,21 +2514,20 @@ ISC
2125
2514
 
2126
2515
  ## Support
2127
2516
 
2128
- - **Documentation:** [GitHub Docs](https://github.com/your-repo/securenow-npm/tree/main/docs)
2129
- - **Issues:** [GitHub Issues](https://github.com/your-repo/securenow-npm/issues)
2130
- - **Examples:** [GitHub Examples](https://github.com/your-repo/securenow-npm/tree/main/examples)
2517
+ - **Documentation:** [`SKILL-API.md`](./SKILL-API.md), [`SKILL-CLI.md`](./SKILL-CLI.md) and `TELEMETRY-SQL.md` in this package, `npx securenow help`, and [securenow.ai](https://securenow.ai)
2518
+ - **Contact:** [contact@securenow.ai](mailto:contact@securenow.ai)
2131
2519
 
2132
2520
  ---
2133
2521
 
2134
2522
  ## Changelog
2135
2523
 
2136
- See [CHANGELOG.md](CHANGELOG.md) for version history.
2524
+ `npx securenow version` prints the installed version. Release notes are kept in the CHANGELOG of the SecureNow source repository.
2137
2525
 
2138
2526
  ---
2139
2527
 
2140
2528
  ## Contributing
2141
2529
 
2142
- Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
2530
+ Questions, bug reports and suggestions: [contact@securenow.ai](mailto:contact@securenow.ai).
2143
2531
 
2144
2532
  ---
2145
2533