securenow 6.0.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONSUMING-APPS-GUIDE.md +455 -0
- package/NPM_README.md +2029 -0
- package/README.md +297 -40
- package/SKILL-API.md +634 -0
- package/SKILL-CLI.md +454 -0
- package/cidr.js +83 -0
- package/cli/apps.js +585 -0
- package/cli/auth.js +280 -0
- package/cli/client.js +115 -0
- package/cli/config.js +173 -0
- package/cli/diagnostics.js +387 -0
- package/cli/firewall.js +100 -0
- package/cli/fp.js +638 -0
- package/cli/init.js +201 -0
- package/cli/monitor.js +440 -0
- package/cli/run.js +148 -0
- package/cli/security.js +980 -0
- package/cli/ui.js +386 -0
- package/cli/utils.js +127 -0
- package/cli.js +466 -455
- package/console-instrumentation.js +147 -136
- package/docs/ALL-FRAMEWORKS-QUICKSTART.md +1377 -455
- package/docs/API-KEYS-GUIDE.md +233 -0
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/AUTO-BODY-CAPTURE.md +1 -1
- package/docs/AUTO-SETUP-SUMMARY.md +331 -0
- package/docs/AUTO-SETUP.md +4 -4
- package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
- package/docs/BODY-CAPTURE-FIX.md +261 -0
- package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
- package/docs/CHANGELOG-NEXTJS.md +1 -35
- package/docs/COMPLETION-REPORT.md +408 -0
- package/docs/CUSTOMER-GUIDE.md +16 -16
- package/docs/EASIEST-SETUP.md +5 -5
- package/docs/ENVIRONMENT-VARIABLES.md +880 -652
- package/docs/EXPRESS-BODY-CAPTURE.md +13 -12
- package/docs/EXPRESS-SETUP-GUIDE.md +719 -720
- package/docs/FINAL-SOLUTION.md +335 -0
- package/docs/FIREWALL-GUIDE.md +426 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +410 -0
- package/docs/INDEX.md +22 -4
- package/docs/LOGGING-GUIDE.md +701 -708
- package/docs/LOGGING-QUICKSTART.md +234 -255
- package/docs/NEXTJS-BODY-CAPTURE-COMPARISON.md +323 -0
- package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
- package/docs/NEXTJS-GUIDE.md +14 -14
- package/docs/NEXTJS-QUICKSTART.md +1 -1
- package/docs/NEXTJS-SETUP-COMPLETE.md +795 -0
- package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
- package/docs/NUXT-GUIDE.md +166 -0
- package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
- package/docs/REDACTION-EXAMPLES.md +1 -1
- package/docs/REQUEST-BODY-CAPTURE.md +19 -10
- package/docs/SOLUTION-SUMMARY.md +312 -0
- package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
- package/examples/README.md +6 -6
- package/examples/instrumentation-with-auto-capture.ts +1 -1
- package/examples/nextjs-env-example.txt +2 -2
- package/examples/nextjs-instrumentation.js +1 -1
- package/examples/nextjs-instrumentation.ts +1 -1
- package/examples/nextjs-with-logging-example.md +6 -6
- package/examples/nextjs-with-options.ts +1 -1
- package/examples/test-nextjs-setup.js +1 -1
- package/firewall-cloud.js +212 -0
- package/firewall-iptables.js +139 -0
- package/firewall-only.js +38 -0
- package/firewall-tcp.js +74 -0
- package/firewall.js +720 -0
- package/free-trial-banner.js +174 -0
- package/nextjs-auto-capture.js +199 -207
- package/nextjs-middleware.js +186 -181
- package/nextjs-webpack-config.js +88 -53
- package/nextjs-wrapper.js +158 -158
- package/nextjs.d.ts +1 -1
- package/nextjs.js +639 -647
- package/nuxt-server-plugin.mjs +423 -0
- package/nuxt.d.ts +60 -0
- package/nuxt.mjs +75 -0
- package/package.json +186 -164
- package/postinstall.js +6 -6
- package/register.d.ts +1 -1
- package/register.js +39 -4
- package/resolve-ip.js +77 -0
- package/tracing.d.ts +2 -1
- package/tracing.js +295 -34
- package/web-vite.mjs +239 -156
- package/LICENSE +0 -15
|
@@ -1,455 +1,1377 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
###
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
```bash
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
```javascript
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
body: '
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
1
|
+
# SecureNow — Complete Guide for Every Node.js Framework
|
|
2
|
+
|
|
3
|
+
Protect any Node.js app in minutes. This guide covers **installation, CLI commands, the forensics chat, and IP blocking** for all 11 supported frameworks.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Prerequisites](#prerequisites)
|
|
10
|
+
2. [Install & Create Your App](#step-1--install--create-your-app)
|
|
11
|
+
3. [Set Environment Variables](#step-2--set-environment-variables)
|
|
12
|
+
4. [Plug SecureNow into Your Framework](#step-3--plug-securenow-into-your-framework)
|
|
13
|
+
- [Express.js](#expressjs)
|
|
14
|
+
- [Fastify](#fastify)
|
|
15
|
+
- [Koa](#koa)
|
|
16
|
+
- [NestJS](#nestjs)
|
|
17
|
+
- [Hapi](#hapi)
|
|
18
|
+
- [h3 (UnJS / Nitro)](#h3-unjs--nitro)
|
|
19
|
+
- [Polka](#polka)
|
|
20
|
+
- [Micro / Raw HTTP](#micro--raw-http)
|
|
21
|
+
- [Hono](#hono)
|
|
22
|
+
- [Feathers](#feathers)
|
|
23
|
+
- [Next.js](#nextjs)
|
|
24
|
+
5. [Verify It Works](#step-4--verify-it-works)
|
|
25
|
+
6. [CLI Command Reference](#step-5--cli-command-reference)
|
|
26
|
+
7. [Forensics Chat — Ask Questions in Plain English](#step-6--forensics-chat--ask-questions-in-plain-english)
|
|
27
|
+
8. [Block & Manage IPs + Firewall](#step-7--block--manage-ips)
|
|
28
|
+
9. [Monitor, Detect & Respond](#step-8--monitor-detect--respond)
|
|
29
|
+
10. [PM2 / Docker Deployment](#deployment)
|
|
30
|
+
11. [Compatibility Matrix](#compatibility-matrix)
|
|
31
|
+
12. [Troubleshooting](#troubleshooting)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
| Requirement | Details |
|
|
38
|
+
|-------------|---------|
|
|
39
|
+
| **Node.js** | v16 or later |
|
|
40
|
+
| **npm** | v7 or later |
|
|
41
|
+
| **Account** | Free trial at [app.securenow.ai](https://app.securenow.ai) (no credit card) |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Step 1 — Install & Create Your App
|
|
46
|
+
|
|
47
|
+
### 1a. Install the package
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install securenow
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 1b. Authenticate the CLI
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx securenow login
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This opens your browser. Log in with your SecureNow account and the CLI receives a session token automatically.
|
|
60
|
+
|
|
61
|
+
**Alternative — token-based login (CI / headless servers):**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx securenow login --token <YOUR_TOKEN>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Get your CLI token from [app.securenow.ai/dashboard/settings](https://app.securenow.ai/dashboard/settings).
|
|
68
|
+
|
|
69
|
+
### 1c. Create an application
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx securenow apps create my-app
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The CLI returns your **app key** and **instance URL**. Copy them — you need both in the next step.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Name my-app
|
|
79
|
+
Key my-app
|
|
80
|
+
Instance Free Trial (https://freetrial.securenow.ai:4318)
|
|
81
|
+
|
|
82
|
+
Add to your .env.local:
|
|
83
|
+
SECURENOW_APPID=my-app
|
|
84
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 1d. Set it as the default app (optional)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx securenow config set defaultApp my-app
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This lets you run CLI commands like `securenow traces` without passing `--app` every time.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Step 2 — Set Environment Variables
|
|
98
|
+
|
|
99
|
+
Create a `.env` file in your project root:
|
|
100
|
+
|
|
101
|
+
```env
|
|
102
|
+
SECURENOW_APPID=my-app
|
|
103
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
104
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
105
|
+
SECURENOW_NO_UUID=1
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### All Environment Variables
|
|
109
|
+
|
|
110
|
+
| Variable | Purpose | Default |
|
|
111
|
+
|----------|---------|---------|
|
|
112
|
+
| `SECURENOW_APPID` | Your app key (from `securenow apps create`) | **Required** |
|
|
113
|
+
| `SECURENOW_INSTANCE` | OTLP collector endpoint | `https://freetrial.securenow.ai:4318` |
|
|
114
|
+
| `SECURENOW_LOGGING_ENABLED` | Auto-forward all `console.*` calls as OTLP logs | `0` |
|
|
115
|
+
| `SECURENOW_NO_UUID` | Keep `service.name` equal to your app key (no UUID suffix) | `0` |
|
|
116
|
+
| `SECURENOW_CAPTURE_BODY` | Capture request/response bodies in traces | `0` |
|
|
117
|
+
| `SECURENOW_MAX_BODY_SIZE` | Max captured body size in bytes | `10240` |
|
|
118
|
+
| `SECURENOW_SENSITIVE_FIELDS` | Extra field names to auto-redact (comma-separated) | — |
|
|
119
|
+
| `SECURENOW_TRUSTED_PROXIES` | Comma-separated proxy IPs for X-Forwarded-For | — |
|
|
120
|
+
| `SECURENOW_DISABLE_INSTRUMENTATIONS` | OTel instrumentation packages to skip (comma-separated) | — |
|
|
121
|
+
| `SECURENOW_HIDE_BANNER` | Hide the free-trial testing banner | `0` |
|
|
122
|
+
| `SECURENOW_STRICT` | Exit if `APPID` is missing in PM2 cluster mode | `0` |
|
|
123
|
+
| `OTEL_LOG_LEVEL` | OTel diagnostic level (`debug`, `info`, `warn`, `error`, `none`) | `none` |
|
|
124
|
+
|
|
125
|
+
### Production Example
|
|
126
|
+
|
|
127
|
+
```env
|
|
128
|
+
SECURENOW_APPID=my-app-prod
|
|
129
|
+
SECURENOW_INSTANCE=https://collector.yourcompany.com:4318
|
|
130
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
131
|
+
SECURENOW_NO_UUID=1
|
|
132
|
+
SECURENOW_CAPTURE_BODY=0
|
|
133
|
+
SECURENOW_TRUSTED_PROXIES=10.0.0.1,10.0.0.2
|
|
134
|
+
NODE_ENV=production
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Step 3 — Plug SecureNow into Your Framework
|
|
140
|
+
|
|
141
|
+
There are two ways to initialize SecureNow. Both work with every framework.
|
|
142
|
+
|
|
143
|
+
**Option A — Zero code changes (recommended)**
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
node -r securenow/register app.js
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Option B — Add one line to your entry file**
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
require('securenow/register'); // Must be the very first line
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Pick **one** method. Both do the same thing. Below are complete, copy-paste examples for each framework.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### Express.js
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm install securenow express
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// app.js
|
|
167
|
+
'use strict';
|
|
168
|
+
require('securenow/register');
|
|
169
|
+
const express = require('express');
|
|
170
|
+
|
|
171
|
+
const app = express();
|
|
172
|
+
app.use(express.json());
|
|
173
|
+
|
|
174
|
+
app.get('/health', (req, res) => {
|
|
175
|
+
res.json({ status: 'ok' });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
app.post('/tasks', (req, res) => {
|
|
179
|
+
const { title } = req.body;
|
|
180
|
+
if (!title) return res.status(400).json({ error: 'title is required' });
|
|
181
|
+
console.log('Created task:', title);
|
|
182
|
+
res.status(201).json({ id: '1', title });
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
app.listen(3000, () => {
|
|
186
|
+
console.log('Express app running on port 3000');
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
node app.js
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| Feature | Supported |
|
|
195
|
+
|---------|-----------|
|
|
196
|
+
| Traces | Yes |
|
|
197
|
+
| Logs | Yes |
|
|
198
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Fastify
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm install securenow fastify
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// app.js
|
|
210
|
+
'use strict';
|
|
211
|
+
require('securenow/register');
|
|
212
|
+
const Fastify = require('fastify');
|
|
213
|
+
|
|
214
|
+
const fastify = Fastify({ logger: true });
|
|
215
|
+
|
|
216
|
+
fastify.get('/health', async () => {
|
|
217
|
+
return { status: 'ok' };
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
fastify.post('/tasks', {
|
|
221
|
+
schema: {
|
|
222
|
+
body: { type: 'object', required: ['title'], properties: { title: { type: 'string' } } }
|
|
223
|
+
}
|
|
224
|
+
}, async (request) => {
|
|
225
|
+
const { title } = request.body;
|
|
226
|
+
console.log('Created task:', title);
|
|
227
|
+
return { id: '1', title };
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
fastify.listen({ port: 3000 }, (err) => {
|
|
231
|
+
if (err) { fastify.log.error(err); process.exit(1); }
|
|
232
|
+
console.log('Fastify app running on port 3000');
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
node app.js
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Feature | Supported |
|
|
241
|
+
|---------|-----------|
|
|
242
|
+
| Traces | Yes |
|
|
243
|
+
| Logs | Yes |
|
|
244
|
+
| Body Capture | **No** — set `SECURENOW_CAPTURE_BODY=0` (stream conflict) |
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### Koa
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npm install securenow koa @koa/router koa-bodyparser
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
// app.js
|
|
256
|
+
'use strict';
|
|
257
|
+
require('securenow/register');
|
|
258
|
+
const Koa = require('koa');
|
|
259
|
+
const Router = require('@koa/router');
|
|
260
|
+
const bodyParser = require('koa-bodyparser');
|
|
261
|
+
|
|
262
|
+
const app = new Koa();
|
|
263
|
+
const router = new Router();
|
|
264
|
+
|
|
265
|
+
router.get('/health', (ctx) => {
|
|
266
|
+
ctx.body = { status: 'ok' };
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
router.post('/tasks', (ctx) => {
|
|
270
|
+
const { title } = ctx.request.body;
|
|
271
|
+
if (!title) { ctx.status = 400; ctx.body = { error: 'title is required' }; return; }
|
|
272
|
+
console.log('Created task:', title);
|
|
273
|
+
ctx.status = 201;
|
|
274
|
+
ctx.body = { id: '1', title };
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
app.use(bodyParser());
|
|
278
|
+
app.use(router.routes());
|
|
279
|
+
app.use(router.allowedMethods());
|
|
280
|
+
|
|
281
|
+
app.listen(3000, () => {
|
|
282
|
+
console.log('Koa app running on port 3000');
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
node app.js
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
| Feature | Supported |
|
|
291
|
+
|---------|-----------|
|
|
292
|
+
| Traces | Yes |
|
|
293
|
+
| Logs | Yes |
|
|
294
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### NestJS
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npm install securenow
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
NestJS uses TypeScript. Create an `instrument.js` file in your project root to load SecureNow before anything else:
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
// instrument.js
|
|
308
|
+
require('securenow/register');
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Your NestJS entry file stays unchanged:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// src/main.ts
|
|
315
|
+
import 'reflect-metadata';
|
|
316
|
+
import { NestFactory } from '@nestjs/core';
|
|
317
|
+
import { Module, Controller, Get, Post, Body } from '@nestjs/common';
|
|
318
|
+
|
|
319
|
+
@Controller()
|
|
320
|
+
class AppController {
|
|
321
|
+
@Get('health')
|
|
322
|
+
health() {
|
|
323
|
+
return { status: 'ok' };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@Post('tasks')
|
|
327
|
+
create(@Body() body: { title: string }) {
|
|
328
|
+
console.log('Created task:', body.title);
|
|
329
|
+
return { id: '1', title: body.title };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@Module({ controllers: [AppController] })
|
|
334
|
+
class AppModule {}
|
|
335
|
+
|
|
336
|
+
async function bootstrap() {
|
|
337
|
+
const app = await NestFactory.create(AppModule);
|
|
338
|
+
await app.listen(3000);
|
|
339
|
+
console.log('NestJS app running on port 3000');
|
|
340
|
+
}
|
|
341
|
+
bootstrap();
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Development (ts-node):**
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
node -r ./instrument.js -r ts-node/register src/main.ts
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Production (compiled):**
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
node -r ./instrument.js dist/main.js
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Add both to your `package.json`:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"scripts": {
|
|
361
|
+
"start:dev": "node -r ./instrument.js -r ts-node/register src/main.ts",
|
|
362
|
+
"start": "node -r ./instrument.js dist/main.js"
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
| Feature | Supported |
|
|
368
|
+
|---------|-----------|
|
|
369
|
+
| Traces | Yes |
|
|
370
|
+
| Logs | Yes |
|
|
371
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
### Hapi
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
npm install securenow @hapi/hapi
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// app.js
|
|
383
|
+
'use strict';
|
|
384
|
+
require('securenow/register');
|
|
385
|
+
const Hapi = require('@hapi/hapi');
|
|
386
|
+
|
|
387
|
+
const init = async () => {
|
|
388
|
+
const server = Hapi.server({ port: 3000, host: '0.0.0.0' });
|
|
389
|
+
|
|
390
|
+
server.route({
|
|
391
|
+
method: 'GET', path: '/health',
|
|
392
|
+
handler: () => ({ status: 'ok' })
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
server.route({
|
|
396
|
+
method: 'POST', path: '/tasks',
|
|
397
|
+
options: { payload: { parse: true, allow: 'application/json' } },
|
|
398
|
+
handler: (request, h) => {
|
|
399
|
+
const { title } = request.payload || {};
|
|
400
|
+
if (!title) return h.response({ error: 'title is required' }).code(400);
|
|
401
|
+
console.log('Created task:', title);
|
|
402
|
+
return h.response({ id: '1', title }).code(201);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
await server.start();
|
|
407
|
+
console.log('Hapi app running on port 3000');
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
init().catch((err) => { console.error(err); process.exit(1); });
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
node app.js
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
| Feature | Supported |
|
|
418
|
+
|---------|-----------|
|
|
419
|
+
| Traces | Yes |
|
|
420
|
+
| Logs | Yes |
|
|
421
|
+
| Body Capture | **No** — set `SECURENOW_CAPTURE_BODY=0` (payload stream conflict) |
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
### h3 (UnJS / Nitro)
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npm install securenow h3
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
```javascript
|
|
432
|
+
// app.js
|
|
433
|
+
'use strict';
|
|
434
|
+
require('securenow/register');
|
|
435
|
+
const { createApp, createRouter, defineEventHandler, readBody, setResponseStatus, toNodeListener } = require('h3');
|
|
436
|
+
const http = require('http');
|
|
437
|
+
|
|
438
|
+
const app = createApp();
|
|
439
|
+
const router = createRouter();
|
|
440
|
+
|
|
441
|
+
router.get('/health', defineEventHandler(() => {
|
|
442
|
+
return { status: 'ok' };
|
|
443
|
+
}));
|
|
444
|
+
|
|
445
|
+
router.post('/tasks', defineEventHandler(async (event) => {
|
|
446
|
+
const body = await readBody(event);
|
|
447
|
+
if (!body?.title) { setResponseStatus(event, 400); return { error: 'title is required' }; }
|
|
448
|
+
console.log('Created task:', body.title);
|
|
449
|
+
setResponseStatus(event, 201);
|
|
450
|
+
return { id: '1', title: body.title };
|
|
451
|
+
}));
|
|
452
|
+
|
|
453
|
+
app.use(router);
|
|
454
|
+
|
|
455
|
+
http.createServer(toNodeListener(app)).listen(3000, () => {
|
|
456
|
+
console.log('h3 app running on port 3000');
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
node app.js
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
| Feature | Supported |
|
|
465
|
+
|---------|-----------|
|
|
466
|
+
| Traces | Yes |
|
|
467
|
+
| Logs | Yes |
|
|
468
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
### Polka
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
npm install securenow polka
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Polka has no built-in body parser, so add a simple middleware:
|
|
479
|
+
|
|
480
|
+
```javascript
|
|
481
|
+
// app.js
|
|
482
|
+
'use strict';
|
|
483
|
+
require('securenow/register');
|
|
484
|
+
const polka = require('polka');
|
|
485
|
+
|
|
486
|
+
function jsonBody(req, res, next) {
|
|
487
|
+
if (req.method === 'GET' || req.method === 'DELETE') return next();
|
|
488
|
+
let data = '';
|
|
489
|
+
req.on('data', chunk => { data += chunk; });
|
|
490
|
+
req.on('end', () => {
|
|
491
|
+
try { req.body = JSON.parse(data); } catch { req.body = {}; }
|
|
492
|
+
next();
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function sendJson(res, status, body) {
|
|
497
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
498
|
+
res.end(JSON.stringify(body));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
polka()
|
|
502
|
+
.use(jsonBody)
|
|
503
|
+
.get('/health', (req, res) => sendJson(res, 200, { status: 'ok' }))
|
|
504
|
+
.post('/tasks', (req, res) => {
|
|
505
|
+
const { title } = req.body;
|
|
506
|
+
if (!title) return sendJson(res, 400, { error: 'title is required' });
|
|
507
|
+
console.log('Created task:', title);
|
|
508
|
+
sendJson(res, 201, { id: '1', title });
|
|
509
|
+
})
|
|
510
|
+
.listen(3000, () => {
|
|
511
|
+
console.log('Polka app running on port 3000');
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
node app.js
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
| Feature | Supported |
|
|
520
|
+
|---------|-----------|
|
|
521
|
+
| Traces | Yes |
|
|
522
|
+
| Logs | Yes |
|
|
523
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
### Micro / Raw HTTP
|
|
528
|
+
|
|
529
|
+
For apps using the bare `http` module:
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
npm install securenow
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
```javascript
|
|
536
|
+
// app.js
|
|
537
|
+
'use strict';
|
|
538
|
+
require('securenow/register');
|
|
539
|
+
const http = require('http');
|
|
540
|
+
|
|
541
|
+
function sendJson(res, status, body) {
|
|
542
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
543
|
+
res.end(JSON.stringify(body));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function readBody(req) {
|
|
547
|
+
return new Promise((resolve) => {
|
|
548
|
+
let data = '';
|
|
549
|
+
req.on('data', chunk => { data += chunk; });
|
|
550
|
+
req.on('end', () => {
|
|
551
|
+
try { resolve(JSON.parse(data)); } catch { resolve({}); }
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async function handler(req, res) {
|
|
557
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
558
|
+
|
|
559
|
+
if (url.pathname === '/health' && req.method === 'GET') {
|
|
560
|
+
return sendJson(res, 200, { status: 'ok' });
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (url.pathname === '/tasks' && req.method === 'POST') {
|
|
564
|
+
const body = await readBody(req);
|
|
565
|
+
if (!body.title) return sendJson(res, 400, { error: 'title is required' });
|
|
566
|
+
console.log('Created task:', body.title);
|
|
567
|
+
return sendJson(res, 201, { id: '1', title: body.title });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
sendJson(res, 404, { error: 'Not found' });
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
http.createServer(handler).listen(3000, () => {
|
|
574
|
+
console.log('HTTP app running on port 3000');
|
|
575
|
+
});
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
node app.js
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
| Feature | Supported |
|
|
583
|
+
|---------|-----------|
|
|
584
|
+
| Traces | Yes |
|
|
585
|
+
| Logs | Yes |
|
|
586
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
### Hono
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
npm install securenow hono @hono/node-server
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Hono uses ESM, so load SecureNow via the `-r` flag (do **not** use `require()` in `.mjs` files):
|
|
597
|
+
|
|
598
|
+
```javascript
|
|
599
|
+
// app.mjs
|
|
600
|
+
import { serve } from '@hono/node-server';
|
|
601
|
+
import { Hono } from 'hono';
|
|
602
|
+
|
|
603
|
+
const app = new Hono();
|
|
604
|
+
|
|
605
|
+
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
606
|
+
|
|
607
|
+
app.post('/tasks', async (c) => {
|
|
608
|
+
const body = await c.req.json();
|
|
609
|
+
if (!body.title) return c.json({ error: 'title is required' }, 400);
|
|
610
|
+
console.log('Created task:', body.title);
|
|
611
|
+
return c.json({ id: '1', title: body.title }, 201);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
serve({ fetch: app.fetch, port: 3000 }, () => {
|
|
615
|
+
console.log('Hono app running on port 3000');
|
|
616
|
+
});
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
node -r securenow/register app.mjs
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
| Feature | Supported |
|
|
624
|
+
|---------|-----------|
|
|
625
|
+
| Traces | Yes |
|
|
626
|
+
| Logs | Yes |
|
|
627
|
+
| Body Capture | **No** — set `SECURENOW_CAPTURE_BODY=0` (stream conflict) |
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
### Feathers
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
npm install securenow @feathersjs/feathers @feathersjs/express @feathersjs/errors
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Feathers uses Express as its transport, so the setup is identical:
|
|
638
|
+
|
|
639
|
+
```javascript
|
|
640
|
+
// app.js
|
|
641
|
+
'use strict';
|
|
642
|
+
require('securenow/register');
|
|
643
|
+
const feathers = require('@feathersjs/feathers');
|
|
644
|
+
const express = require('@feathersjs/express');
|
|
645
|
+
const errors = require('@feathersjs/errors');
|
|
646
|
+
|
|
647
|
+
class TaskService {
|
|
648
|
+
constructor() { this.tasks = []; this.nextId = 1; }
|
|
649
|
+
|
|
650
|
+
async find() { return this.tasks; }
|
|
651
|
+
|
|
652
|
+
async create(data) {
|
|
653
|
+
if (!data.title) throw new errors.BadRequest('title is required');
|
|
654
|
+
const task = { id: String(this.nextId++), title: data.title };
|
|
655
|
+
this.tasks.push(task);
|
|
656
|
+
console.log('Created task:', task.id);
|
|
657
|
+
return task;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const app = express(feathers());
|
|
662
|
+
app.use(express.json());
|
|
663
|
+
app.configure(express.rest());
|
|
664
|
+
|
|
665
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
666
|
+
app.use('/tasks', new TaskService());
|
|
667
|
+
app.use(express.errorHandler());
|
|
668
|
+
|
|
669
|
+
app.listen(3000, () => {
|
|
670
|
+
console.log('Feathers app running on port 3000');
|
|
671
|
+
});
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
node app.js
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
| Feature | Supported |
|
|
679
|
+
|---------|-----------|
|
|
680
|
+
| Traces | Yes |
|
|
681
|
+
| Logs | Yes |
|
|
682
|
+
| Body Capture | Yes — set `SECURENOW_CAPTURE_BODY=1` |
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
### Next.js
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
npm install securenow
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Create `instrumentation.ts` (or `.js`) in your project root (or `src/` if you use that layout):
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
// instrumentation.ts
|
|
696
|
+
export async function register() {
|
|
697
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
698
|
+
await import('securenow/register');
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Add to `.env.local`:
|
|
704
|
+
|
|
705
|
+
```env
|
|
706
|
+
SECURENOW_APPID=my-nextjs-app
|
|
707
|
+
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
|
|
708
|
+
SECURENOW_LOGGING_ENABLED=1
|
|
709
|
+
SECURENOW_NO_UUID=1
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Or scaffold it automatically:**
|
|
713
|
+
|
|
714
|
+
```bash
|
|
715
|
+
npx securenow init
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
This creates both `instrumentation.ts` and `.env.local` for you.
|
|
719
|
+
|
|
720
|
+
```bash
|
|
721
|
+
npm run dev
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
| Feature | Supported |
|
|
725
|
+
|---------|-----------|
|
|
726
|
+
| Traces | Yes |
|
|
727
|
+
| Logs | Yes |
|
|
728
|
+
| Body Capture | Yes |
|
|
729
|
+
|
|
730
|
+
See the [full Next.js guide](./NEXTJS-SETUP-COMPLETE.md) for App Router, middleware, and server actions.
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Step 4 — Verify It Works
|
|
735
|
+
|
|
736
|
+
After starting your app, you should see this in the console:
|
|
737
|
+
|
|
738
|
+
```
|
|
739
|
+
[securenow] OTel SDK started → https://freetrial.securenow.ai:4318/v1/traces
|
|
740
|
+
[securenow] 📋 Logging: ENABLED → https://freetrial.securenow.ai:4318/v1/logs
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Send a test request:
|
|
744
|
+
|
|
745
|
+
```bash
|
|
746
|
+
curl http://localhost:3000/health
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
Then check traces from the CLI:
|
|
750
|
+
|
|
751
|
+
```bash
|
|
752
|
+
npx securenow traces --app my-app
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
Traces and logs should appear within seconds.
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Step 5 — CLI Command Reference
|
|
760
|
+
|
|
761
|
+
The SecureNow CLI is your terminal command center. Below is every command organized by workflow.
|
|
762
|
+
|
|
763
|
+
### Authentication
|
|
764
|
+
|
|
765
|
+
| Command | What It Does |
|
|
766
|
+
|---------|-------------|
|
|
767
|
+
| `securenow login` | Opens browser to authenticate (global session) |
|
|
768
|
+
| `securenow login --token <T>` | Authenticate with a token (for CI/CD or headless servers) |
|
|
769
|
+
| `securenow login --local` | Save credentials to this project only (per-project session) |
|
|
770
|
+
| `securenow logout` | Clear stored credentials |
|
|
771
|
+
| `securenow logout --local` | Clear project-local credentials only |
|
|
772
|
+
| `securenow whoami` | Show current session (email, API URL, auth source, expiry, default app) |
|
|
773
|
+
|
|
774
|
+
**Per-project credentials:** Use `--local` to maintain separate logins for different projects on the same machine. The CLI resolves credentials in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
|
|
775
|
+
|
|
776
|
+
### App Management
|
|
777
|
+
|
|
778
|
+
| Command | What It Does |
|
|
779
|
+
|---------|-------------|
|
|
780
|
+
| `securenow apps list` | List all your applications |
|
|
781
|
+
| `securenow apps create <name>` | Create a new app (interactive instance picker) |
|
|
782
|
+
| `securenow apps create <name> --hosts api.example.com` | Create with host binding |
|
|
783
|
+
| `securenow apps info <id>` | Show app details + env variables |
|
|
784
|
+
| `securenow apps delete <id>` | Delete an app |
|
|
785
|
+
| `securenow apps default <key>` | Set default app for CLI commands |
|
|
786
|
+
| `securenow apps discover --domain example.com` | Discover subdomains and add as apps |
|
|
787
|
+
| `securenow apps scan --yes` | Scan all app domains for new subdomains |
|
|
788
|
+
|
|
789
|
+
### Observe — Traces & Logs
|
|
790
|
+
|
|
791
|
+
| Command | What It Does |
|
|
792
|
+
|---------|-------------|
|
|
793
|
+
| `securenow traces` | List recent traces |
|
|
794
|
+
| `securenow traces --app my-app --limit 50` | Filtered trace list |
|
|
795
|
+
| `securenow traces show <traceId>` | Show all spans in a trace |
|
|
796
|
+
| `securenow traces analyze <traceId>` | AI-powered security analysis of a trace |
|
|
797
|
+
| `securenow logs` | List recent logs |
|
|
798
|
+
| `securenow logs --app my-app --minutes 30 --level ERROR` | Filtered logs |
|
|
799
|
+
| `securenow logs trace <traceId>` | Show all logs for a specific trace |
|
|
800
|
+
| `securenow analytics --app my-app` | Response code breakdown (2xx/3xx/4xx/5xx) |
|
|
801
|
+
| `securenow status` | Dashboard overview (apps, alerts, protection status) |
|
|
802
|
+
|
|
803
|
+
### Detect & Respond — Alerts, Notifications, False Positives
|
|
804
|
+
|
|
805
|
+
| Command | What It Does |
|
|
806
|
+
|---------|-------------|
|
|
807
|
+
| `securenow alerts rules` | List alert rules |
|
|
808
|
+
| `securenow alerts rules show <id>` | One rule (all-apps vs explicit keys) |
|
|
809
|
+
| `securenow alerts rules update <id> --applications-all` | All current & future apps |
|
|
810
|
+
| `securenow alerts rules update <id> --apps k1,k2` | Explicit app keys only |
|
|
811
|
+
| `securenow alerts channels` | List alert channels (email, webhook, Slack) |
|
|
812
|
+
| `securenow alerts history --limit 50` | View past triggered alerts |
|
|
813
|
+
| `securenow notifications` | List notifications |
|
|
814
|
+
| `securenow notifications unread` | Show unread count |
|
|
815
|
+
| `securenow notifications read <id>` | Mark one as read |
|
|
816
|
+
| `securenow notifications read-all` | Mark all as read |
|
|
817
|
+
| `securenow fp` / `fp list` | List FP exclusion rules |
|
|
818
|
+
| `securenow fp show <id>` | Rule detail (conditions, scope, match mode) |
|
|
819
|
+
| `securenow fp create --conditions '[...]'` | Create raw exclusion rule |
|
|
820
|
+
| `securenow fp create --path /api/events --method POST --path-safe standard` | Safe-value preset helper |
|
|
821
|
+
| `securenow fp edit <id> [--active true\|false] [--conditions '[...]']` | Edit existing rule |
|
|
822
|
+
| `securenow fp delete <id> [--yes]` | Delete rule |
|
|
823
|
+
| `securenow fp test-body '<json>' --conditions '[...]'` | Test conditions against a payload |
|
|
824
|
+
| `securenow fp dry-run --conditions '[...]'` | Dry-run against last 3 days of live traces |
|
|
825
|
+
| `securenow fp ai-fill --description "..."` | AI-generate exclusion conditions |
|
|
826
|
+
| `securenow fp mark <notification-id> <ip>` | Mark an IP as FP on a notification in one shot |
|
|
827
|
+
|
|
828
|
+
### Investigate — IP Intelligence & Forensics
|
|
829
|
+
|
|
830
|
+
| Command | What It Does |
|
|
831
|
+
|---------|-------------|
|
|
832
|
+
| `securenow ip <ip-address>` | Full IP intelligence report (country, ISP, abuse score, risk factors) |
|
|
833
|
+
| `securenow ip traces <ip-address>` | Show all traces from a specific IP |
|
|
834
|
+
| `securenow forensics "<query>"` | **Chat with your data** — natural language to SQL (see below) |
|
|
835
|
+
| `securenow forensics library` | View saved forensic queries |
|
|
836
|
+
| `securenow api-map` | List all discovered API endpoints |
|
|
837
|
+
| `securenow api-map stats` | API map statistics |
|
|
838
|
+
|
|
839
|
+
### Remediation — Blocklist, Allowlist & Trusted IPs
|
|
840
|
+
|
|
841
|
+
| Command | What It Does |
|
|
842
|
+
|---------|-------------|
|
|
843
|
+
| `securenow blocklist` | List all blocked IPs |
|
|
844
|
+
| `securenow blocklist add <ip>` | Block an IP address |
|
|
845
|
+
| `securenow blocklist add <cidr> --reason "brute force"` | Block a CIDR range with reason |
|
|
846
|
+
| `securenow blocklist remove <id>` | Unblock an IP |
|
|
847
|
+
| `securenow blocklist stats` | Blocklist statistics |
|
|
848
|
+
| `securenow allowlist` | List allowed IPs (restrict mode) |
|
|
849
|
+
| `securenow allowlist add <ip> --label "office" --reason "corporate VPN"` | Allow an IP |
|
|
850
|
+
| `securenow allowlist remove <id>` | Remove from allowlist |
|
|
851
|
+
| `securenow trusted` | List trusted IPs |
|
|
852
|
+
| `securenow trusted add <ip> --label "office"` | Add a trusted IP |
|
|
853
|
+
| `securenow trusted remove <id>` | Remove a trusted IP |
|
|
854
|
+
|
|
855
|
+
### Firewall Runtime
|
|
856
|
+
|
|
857
|
+
| Command | What It Does |
|
|
858
|
+
|---------|-------------|
|
|
859
|
+
| `securenow firewall status` | Active layers, sync time, blocked count |
|
|
860
|
+
| `securenow firewall test-ip <ip>` | Check if an IP would be blocked |
|
|
861
|
+
| `securenow run --firewall-only <script>` | Preload firewall **without** OTel tracing (zero overhead) |
|
|
862
|
+
|
|
863
|
+
### Telemetry from the Shell
|
|
864
|
+
|
|
865
|
+
Mirrors the SDK's `getLogger()` and tracing APIs for scripts, cron, and CI.
|
|
866
|
+
|
|
867
|
+
| Command | What It Does |
|
|
868
|
+
|---------|-------------|
|
|
869
|
+
| `securenow log send "<msg>" [--level info\|warn\|error] [--attrs k=v]` | Emit an OTLP log record |
|
|
870
|
+
| `securenow test-span [<name>]` | Emit a test span to verify collector connectivity |
|
|
871
|
+
|
|
872
|
+
### Utilities
|
|
873
|
+
|
|
874
|
+
SDK helpers surfaced as CLI commands.
|
|
875
|
+
|
|
876
|
+
| Command | What It Does |
|
|
877
|
+
|---------|-------------|
|
|
878
|
+
| `securenow redact '<json>' [--fields f1,f2]` | Redact sensitive fields (accepts `@file.json`) |
|
|
879
|
+
| `securenow cidr match <ip> <cidrs>` | IP vs. CIDR list (exit 0 hit / 2 miss) |
|
|
880
|
+
| `securenow cidr parse <cidr>` | Parse CIDR — network, broadcast, mask, size |
|
|
881
|
+
| `securenow env [--json]` | Show resolved config (service name, endpoints, env vars) |
|
|
882
|
+
| `securenow doctor [--json]` | Probe OTLP + API endpoints, check config sanity |
|
|
883
|
+
|
|
884
|
+
### Settings & Config
|
|
885
|
+
|
|
886
|
+
| Command | What It Does |
|
|
887
|
+
|---------|-------------|
|
|
888
|
+
| `securenow config set <key> <value>` | Set a config value |
|
|
889
|
+
| `securenow config get [key]` | Get a config value (or show all) |
|
|
890
|
+
| `securenow config path` | Show config + credentials file paths |
|
|
891
|
+
| `securenow instances` | List ClickHouse instances |
|
|
892
|
+
| `securenow instances test <id>` | Test an instance connection |
|
|
893
|
+
| `securenow version` | Show CLI version |
|
|
894
|
+
|
|
895
|
+
### Global Flags
|
|
896
|
+
|
|
897
|
+
Every command supports:
|
|
898
|
+
|
|
899
|
+
| Flag | Effect |
|
|
900
|
+
|------|--------|
|
|
901
|
+
| `--json` | Output raw JSON (pipe to `jq`, scripts, etc.) |
|
|
902
|
+
| `--help` | Show help for any command |
|
|
903
|
+
| `--app <key>` | Override the default app for this command |
|
|
904
|
+
|
|
905
|
+
---
|
|
906
|
+
|
|
907
|
+
## Step 6 — Forensics Chat — Ask Questions in Plain English
|
|
908
|
+
|
|
909
|
+
The `securenow forensics` command lets you **ask security questions in plain English**. SecureNow translates your question to SQL, runs it against your ClickHouse traces database, and returns the results.
|
|
910
|
+
|
|
911
|
+
### How to Use
|
|
912
|
+
|
|
913
|
+
```bash
|
|
914
|
+
npx securenow forensics "your question here"
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### Example Queries
|
|
918
|
+
|
|
919
|
+
**Find suspicious activity:**
|
|
920
|
+
|
|
921
|
+
```bash
|
|
922
|
+
npx securenow forensics "show me all 401 responses in the last hour"
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**Hunt for attackers:**
|
|
926
|
+
|
|
927
|
+
```bash
|
|
928
|
+
npx securenow forensics "which IPs sent the most requests in the last 24 hours"
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
**Find slow endpoints:**
|
|
932
|
+
|
|
933
|
+
```bash
|
|
934
|
+
npx securenow forensics "show the slowest API endpoints this week"
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
**Investigate a specific IP:**
|
|
938
|
+
|
|
939
|
+
```bash
|
|
940
|
+
npx securenow forensics "show all requests from 185.220.101.1 in the last 7 days"
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**Error analysis:**
|
|
944
|
+
|
|
945
|
+
```bash
|
|
946
|
+
npx securenow forensics "count 500 errors per endpoint in the last 24 hours"
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Brute force detection:**
|
|
950
|
+
|
|
951
|
+
```bash
|
|
952
|
+
npx securenow forensics "find IPs that sent more than 100 POST requests to /login today"
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
**Data exfiltration patterns:**
|
|
956
|
+
|
|
957
|
+
```bash
|
|
958
|
+
npx securenow forensics "show requests with response bodies larger than 1MB"
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### How It Works
|
|
962
|
+
|
|
963
|
+
1. You type a question in English
|
|
964
|
+
2. SecureNow's AI converts it to a SQL query
|
|
965
|
+
3. The query runs against your ClickHouse traces database
|
|
966
|
+
4. Results are displayed as a table in your terminal
|
|
967
|
+
|
|
968
|
+
The CLI shows you both the generated SQL and the results:
|
|
969
|
+
|
|
970
|
+
```
|
|
971
|
+
Generated SQL
|
|
972
|
+
|
|
973
|
+
SELECT ClientIP, count() as cnt FROM traces
|
|
974
|
+
WHERE Timestamp > now() - INTERVAL 1 HOUR AND ResponseStatusCode = 401
|
|
975
|
+
GROUP BY ClientIP ORDER BY cnt DESC LIMIT 20
|
|
976
|
+
|
|
977
|
+
Results (5 rows)
|
|
978
|
+
|
|
979
|
+
ClientIP cnt
|
|
980
|
+
185.220.101.1 342
|
|
981
|
+
45.134.26.8 128
|
|
982
|
+
...
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### Save & Reuse Queries
|
|
986
|
+
|
|
987
|
+
View your saved query library:
|
|
988
|
+
|
|
989
|
+
```bash
|
|
990
|
+
npx securenow forensics library
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
### Output as JSON
|
|
994
|
+
|
|
995
|
+
Pipe forensic results to scripts or other tools:
|
|
996
|
+
|
|
997
|
+
```bash
|
|
998
|
+
npx securenow forensics "top 10 IPs by request count today" --json | jq '.result'
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
---
|
|
1002
|
+
|
|
1003
|
+
## Step 7 — Block & Manage IPs
|
|
1004
|
+
|
|
1005
|
+
SecureNow gives you full IP lifecycle management — investigate, block, trust, and audit.
|
|
1006
|
+
|
|
1007
|
+
### Investigate an IP
|
|
1008
|
+
|
|
1009
|
+
Before blocking, look up the IP's intelligence:
|
|
1010
|
+
|
|
1011
|
+
```bash
|
|
1012
|
+
npx securenow ip 185.220.101.1
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
Output:
|
|
1016
|
+
|
|
1017
|
+
```
|
|
1018
|
+
IP Intelligence: 185.220.101.1
|
|
1019
|
+
|
|
1020
|
+
Country Germany (DE)
|
|
1021
|
+
ISP Tor Exit Node
|
|
1022
|
+
Usage Type Hosting
|
|
1023
|
+
Abuse Score 100/100
|
|
1024
|
+
Malicious Yes
|
|
1025
|
+
Bot Yes
|
|
1026
|
+
Total Reports 4,521
|
|
1027
|
+
|
|
1028
|
+
Risk Factors
|
|
1029
|
+
• Known Tor exit node
|
|
1030
|
+
• High abuse confidence score
|
|
1031
|
+
• Associated with brute force attacks
|
|
1032
|
+
|
|
1033
|
+
Attack Types
|
|
1034
|
+
• SSH Brute Force
|
|
1035
|
+
• Web Application Attack
|
|
1036
|
+
• Port Scanning
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
### See What That IP Did
|
|
1040
|
+
|
|
1041
|
+
```bash
|
|
1042
|
+
npx securenow ip traces 185.220.101.1
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
Shows all traced requests from that IP — method, status code, URL, duration, and time.
|
|
1046
|
+
|
|
1047
|
+
### Block an IP
|
|
1048
|
+
|
|
1049
|
+
```bash
|
|
1050
|
+
npx securenow blocklist add 185.220.101.1 --reason "tor exit node, brute force"
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
Block a CIDR range:
|
|
1054
|
+
|
|
1055
|
+
```bash
|
|
1056
|
+
npx securenow blocklist add 185.220.101.0/24 --reason "malicious subnet"
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
Block with an expiration:
|
|
1060
|
+
|
|
1061
|
+
```bash
|
|
1062
|
+
npx securenow blocklist add 45.134.26.8 --reason "rate limiting" --duration 24h
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
### View All Blocked IPs
|
|
1066
|
+
|
|
1067
|
+
```bash
|
|
1068
|
+
npx securenow blocklist
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
Output:
|
|
1072
|
+
|
|
1073
|
+
```
|
|
1074
|
+
ID IP/CIDR Reason Source Added Expires
|
|
1075
|
+
abc123... 185.220.101.1 tor exit node manual 2 hours ago permanent
|
|
1076
|
+
def456... 185.220.101.0/24 malicious subnet manual 1 hour ago permanent
|
|
1077
|
+
ghi789... 45.134.26.8 rate limiting manual 30 min ago 2024-01-16
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
### Unblock an IP
|
|
1081
|
+
|
|
1082
|
+
```bash
|
|
1083
|
+
npx securenow blocklist remove abc123
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### Blocklist Statistics
|
|
1087
|
+
|
|
1088
|
+
```bash
|
|
1089
|
+
npx securenow blocklist stats
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
Shows total active blocks, removed blocks, manual vs automated counts, and active automation rules.
|
|
1093
|
+
|
|
1094
|
+
### Trust an IP (Whitelist)
|
|
1095
|
+
|
|
1096
|
+
Trusted IPs bypass security detections:
|
|
1097
|
+
|
|
1098
|
+
```bash
|
|
1099
|
+
npx securenow trusted add 203.0.113.50 --label "office VPN"
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
List trusted IPs:
|
|
1103
|
+
|
|
1104
|
+
```bash
|
|
1105
|
+
npx securenow trusted
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
Remove a trusted IP:
|
|
1109
|
+
|
|
1110
|
+
```bash
|
|
1111
|
+
npx securenow trusted remove <id>
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
### Full Investigation → Block Workflow
|
|
1115
|
+
|
|
1116
|
+
Here is the typical workflow for handling a suspicious IP:
|
|
1117
|
+
|
|
1118
|
+
```bash
|
|
1119
|
+
# 1. Check forensics for anomalies
|
|
1120
|
+
npx securenow forensics "IPs with more than 50 failed login attempts today"
|
|
1121
|
+
|
|
1122
|
+
# 2. Pick a suspicious IP and investigate
|
|
1123
|
+
npx securenow ip 185.220.101.1
|
|
1124
|
+
|
|
1125
|
+
# 3. See exactly what it did
|
|
1126
|
+
npx securenow ip traces 185.220.101.1
|
|
1127
|
+
|
|
1128
|
+
# 4. Block it
|
|
1129
|
+
npx securenow blocklist add 185.220.101.1 --reason "brute force login attempts"
|
|
1130
|
+
|
|
1131
|
+
# 5. Verify it's blocked
|
|
1132
|
+
npx securenow blocklist
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
### Enforce the Blocklist on Your App (Firewall)
|
|
1136
|
+
|
|
1137
|
+
Once you've built a blocklist, enforce it at your application layer — automatically, with zero code changes:
|
|
1138
|
+
|
|
1139
|
+
```bash
|
|
1140
|
+
# Add your API key to .env
|
|
1141
|
+
SECURENOW_API_KEY=snk_live_abc123...
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
Restart your app. The firewall syncs the blocklist every 60 seconds and blocks matching IPs with a 403 response:
|
|
1145
|
+
|
|
1146
|
+
```
|
|
1147
|
+
[securenow] Firewall: ENABLED
|
|
1148
|
+
[securenow] Firewall: Layer 1 (HTTP 403) active
|
|
1149
|
+
[securenow] Firewall: synced 142 blocked IPs
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
Enable additional layers for defense in depth:
|
|
1153
|
+
|
|
1154
|
+
```bash
|
|
1155
|
+
# TCP-level blocking (zero bytes sent back)
|
|
1156
|
+
SECURENOW_FIREWALL_TCP=1
|
|
1157
|
+
|
|
1158
|
+
# OS-level blocking (iptables/nftables, Linux only)
|
|
1159
|
+
SECURENOW_FIREWALL_IPTABLES=1
|
|
1160
|
+
|
|
1161
|
+
# Cloud WAF blocking (Cloudflare, AWS WAF, GCP Cloud Armor)
|
|
1162
|
+
SECURENOW_FIREWALL_CLOUD=cloudflare
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
Check firewall status:
|
|
1166
|
+
|
|
1167
|
+
```bash
|
|
1168
|
+
npx securenow firewall status
|
|
1169
|
+
npx securenow firewall test-ip 185.220.101.1
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
See the [Firewall Guide](FIREWALL-GUIDE.md) for the full reference.
|
|
1173
|
+
|
|
1174
|
+
---
|
|
1175
|
+
|
|
1176
|
+
## Step 8 — Monitor, Detect & Respond
|
|
1177
|
+
|
|
1178
|
+
### Daily Monitoring
|
|
1179
|
+
|
|
1180
|
+
```bash
|
|
1181
|
+
# Quick overview of all your apps
|
|
1182
|
+
npx securenow status
|
|
1183
|
+
|
|
1184
|
+
# Check for unread alerts
|
|
1185
|
+
npx securenow notifications unread
|
|
1186
|
+
|
|
1187
|
+
# List open security issues
|
|
1188
|
+
npx securenow issues --status open
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
### Respond to an Issue
|
|
1192
|
+
|
|
1193
|
+
```bash
|
|
1194
|
+
# Read the full issue detail (includes AI analysis)
|
|
1195
|
+
npx securenow issues show <id>
|
|
1196
|
+
|
|
1197
|
+
# If it's resolved, mark it
|
|
1198
|
+
npx securenow issues resolve <id>
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
### AI Trace Analysis
|
|
1202
|
+
|
|
1203
|
+
Let SecureNow's AI analyze a suspicious trace for security issues:
|
|
1204
|
+
|
|
1205
|
+
```bash
|
|
1206
|
+
npx securenow traces analyze <traceId>
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
Returns a summary, risk level, specific security issues found, and recommended actions.
|
|
1210
|
+
|
|
1211
|
+
### Set Up Alerts
|
|
1212
|
+
|
|
1213
|
+
Configure alert rules and channels from the [dashboard](https://app.securenow.ai/dashboard), then monitor from the CLI:
|
|
1214
|
+
|
|
1215
|
+
```bash
|
|
1216
|
+
# List your alert rules
|
|
1217
|
+
npx securenow alerts rules
|
|
1218
|
+
|
|
1219
|
+
# Show one rule / set application scope (all apps vs explicit keys)
|
|
1220
|
+
npx securenow alerts rules show <rule-id>
|
|
1221
|
+
npx securenow alerts rules update <rule-id> --applications-all
|
|
1222
|
+
npx securenow alerts rules update <rule-id> --apps key1,key2
|
|
1223
|
+
|
|
1224
|
+
# List alert channels (email, Slack, webhook)
|
|
1225
|
+
npx securenow alerts channels
|
|
1226
|
+
|
|
1227
|
+
# View alert history
|
|
1228
|
+
npx securenow alerts history --limit 20
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
1233
|
+
## Deployment
|
|
1234
|
+
|
|
1235
|
+
### PM2 Setup (All Frameworks)
|
|
1236
|
+
|
|
1237
|
+
```javascript
|
|
1238
|
+
// ecosystem.config.cjs
|
|
1239
|
+
module.exports = {
|
|
1240
|
+
apps: [{
|
|
1241
|
+
name: 'my-app',
|
|
1242
|
+
script: './app.js',
|
|
1243
|
+
node_args: '-r securenow/register',
|
|
1244
|
+
env: {
|
|
1245
|
+
SECURENOW_APPID: 'my-app',
|
|
1246
|
+
SECURENOW_INSTANCE: 'https://freetrial.securenow.ai:4318',
|
|
1247
|
+
SECURENOW_LOGGING_ENABLED: '1',
|
|
1248
|
+
SECURENOW_NO_UUID: '1',
|
|
1249
|
+
SECURENOW_CAPTURE_BODY: '1',
|
|
1250
|
+
PORT: 3000,
|
|
1251
|
+
}
|
|
1252
|
+
}]
|
|
1253
|
+
};
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
```bash
|
|
1257
|
+
pm2 start ecosystem.config.cjs
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
**NestJS (TypeScript):**
|
|
1261
|
+
|
|
1262
|
+
```javascript
|
|
1263
|
+
{
|
|
1264
|
+
name: 'my-nestjs-app',
|
|
1265
|
+
script: 'dist/main.js',
|
|
1266
|
+
node_args: '-r ./instrument.js',
|
|
1267
|
+
env: { /* same as above */ }
|
|
1268
|
+
}
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
**Hono (ESM `.mjs`):**
|
|
1272
|
+
|
|
1273
|
+
```javascript
|
|
1274
|
+
{
|
|
1275
|
+
name: 'my-hono-app',
|
|
1276
|
+
script: 'app.mjs',
|
|
1277
|
+
node_args: '-r securenow/register',
|
|
1278
|
+
env: {
|
|
1279
|
+
SECURENOW_CAPTURE_BODY: '0', // Required for Hono
|
|
1280
|
+
/* ... other vars ... */
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
### Docker
|
|
1286
|
+
|
|
1287
|
+
```dockerfile
|
|
1288
|
+
FROM node:20-alpine
|
|
1289
|
+
WORKDIR /app
|
|
1290
|
+
COPY package*.json ./
|
|
1291
|
+
RUN npm install
|
|
1292
|
+
COPY . .
|
|
1293
|
+
|
|
1294
|
+
ENV SECURENOW_APPID=my-app
|
|
1295
|
+
ENV SECURENOW_INSTANCE=https://collector:4318
|
|
1296
|
+
ENV SECURENOW_LOGGING_ENABLED=1
|
|
1297
|
+
ENV SECURENOW_NO_UUID=1
|
|
1298
|
+
|
|
1299
|
+
EXPOSE 3000
|
|
1300
|
+
CMD ["node", "-r", "securenow/register", "app.js"]
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
---
|
|
1304
|
+
|
|
1305
|
+
## Compatibility Matrix
|
|
1306
|
+
|
|
1307
|
+
| Framework | Traces | Logs | Body Capture | Init Method | Notes |
|
|
1308
|
+
|-----------|--------|------|--------------|-------------|-------|
|
|
1309
|
+
| Express | Yes | Yes | Yes | `require()` or `-r` | Fully compatible |
|
|
1310
|
+
| Fastify | Yes | Yes | **No** | `require()` or `-r` | Set `SECURENOW_CAPTURE_BODY=0` |
|
|
1311
|
+
| Koa | Yes | Yes | Yes | `require()` or `-r` | Needs `koa-bodyparser` |
|
|
1312
|
+
| NestJS | Yes | Yes | Yes | `instrument.js` + `-r ./instrument.js` | Create `instrument.js` with `require('securenow/register')` |
|
|
1313
|
+
| Hapi | Yes | Yes | **No** | `require()` or `-r` | Set `SECURENOW_CAPTURE_BODY=0` |
|
|
1314
|
+
| h3 | Yes | Yes | Yes | `require()` or `-r` | Uses `toNodeListener()` |
|
|
1315
|
+
| Polka | Yes | Yes | Yes | `require()` or `-r` | Needs manual body parser |
|
|
1316
|
+
| Micro/HTTP | Yes | Yes | Yes | `require()` or `-r` | Raw `http.createServer` |
|
|
1317
|
+
| Hono | Yes | Yes | **No** | `-r` flag only (ESM) | Set `SECURENOW_CAPTURE_BODY=0` |
|
|
1318
|
+
| Feathers | Yes | Yes | Yes | `require()` or `-r` | Express transport |
|
|
1319
|
+
| Next.js | Yes | Yes | Yes | `instrumentation.ts` | Use `securenow init` |
|
|
1320
|
+
|
|
1321
|
+
---
|
|
1322
|
+
|
|
1323
|
+
## Troubleshooting
|
|
1324
|
+
|
|
1325
|
+
### Traces not appearing
|
|
1326
|
+
|
|
1327
|
+
1. Verify `SECURENOW_APPID` and `SECURENOW_INSTANCE` are set
|
|
1328
|
+
2. Set `SECURENOW_NO_UUID=1` so the dashboard matches your app key exactly
|
|
1329
|
+
3. Enable debug logging: `OTEL_LOG_LEVEL=debug node -r securenow/register app.js`
|
|
1330
|
+
|
|
1331
|
+
### Logs not appearing
|
|
1332
|
+
|
|
1333
|
+
1. Confirm `SECURENOW_LOGGING_ENABLED=1` is set
|
|
1334
|
+
2. Check that startup output shows `📋 Logging: ENABLED`
|
|
1335
|
+
3. Any `console.log` / `console.error` in your app automatically becomes an OTLP log record
|
|
1336
|
+
|
|
1337
|
+
### ESM apps (.mjs / "type": "module")
|
|
1338
|
+
|
|
1339
|
+
Use `NODE_OPTIONS="-r securenow/register"` or `node -r securenow/register app.mjs`.
|
|
1340
|
+
Do **not** add `require('securenow/register')` inside `.mjs` files.
|
|
1341
|
+
|
|
1342
|
+
### Body capture crashes / empty payloads
|
|
1343
|
+
|
|
1344
|
+
Set `SECURENOW_CAPTURE_BODY=0` for Fastify, Hapi, and Hono. These frameworks use custom stream handling that conflicts with the body capture hook.
|
|
1345
|
+
|
|
1346
|
+
### CLI says "Not logged in"
|
|
1347
|
+
|
|
1348
|
+
```bash
|
|
1349
|
+
npx securenow login
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
Or re-authenticate with a token:
|
|
1353
|
+
|
|
1354
|
+
```bash
|
|
1355
|
+
npx securenow login --token <YOUR_TOKEN>
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
Or set the env var directly:
|
|
1359
|
+
|
|
1360
|
+
```bash
|
|
1361
|
+
SECURENOW_TOKEN=<YOUR_JWT> npx securenow whoami
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
### CLI says "Session expired"
|
|
1365
|
+
|
|
1366
|
+
Tokens expire after a set period. Re-run `securenow login` to get a fresh session. Use `securenow whoami` to check which credential source is active.
|
|
1367
|
+
|
|
1368
|
+
---
|
|
1369
|
+
|
|
1370
|
+
## Complete Documentation
|
|
1371
|
+
|
|
1372
|
+
- [Express Guide](./EXPRESS-SETUP-GUIDE.md)
|
|
1373
|
+
- [Next.js Guide](./NEXTJS-SETUP-COMPLETE.md)
|
|
1374
|
+
- [Logging Guide](./LOGGING-GUIDE.md)
|
|
1375
|
+
- [Environment Variables](./ENVIRONMENT-VARIABLES.md)
|
|
1376
|
+
- [Body Capture](./REQUEST-BODY-CAPTURE.md)
|
|
1377
|
+
- [NPM README](../NPM_README.md)
|