securenow 8.23.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/SKILL-CLI.md CHANGED
@@ -2,812 +2,442 @@
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
- Use the `securenow` CLI to perform security DevOps from the terminal: manage apps, investigate threats, control the firewall, analyze traces, handle false positives, and run instrumented Node.js processes. Every command supports `--json` for machine-readable output.
5
+ The `securenow` CLI runs SecureNow from the terminal: set up an app, see what is attacking it, investigate an address, block, rate-limit, challenge, revoke sessions, write and test detection rules, work the review queues, and run the MCP server for coding agents. It describes securenow 8.24.0; items marked **(8.25.0+)** need 8.25.0 or newer. Wiring the SDK into an app is covered by [SKILL-API.md](./SKILL-API.md).
6
6
 
7
- ## Installation
7
+ - `npx securenow help` lists every command; `npx securenow help <command>` shows its subcommands and flags. Both work offline: check a flag there before using it.
8
+ - Most read commands take `--json` for machine-readable output and `--app <key>` / `--env <environment>` to pick the target.
9
+ - Write commands act on production apps within about 10 seconds. Ask the person first; `--yes` (or `--force`) only skips the prompt after they agreed.
10
+ - Never print `.securenow/*.json` files or full `snk_live_` keys.
8
11
 
9
- ```bash
10
- # Install globally (recommended for CLI usage)
11
- npm install -g securenow
12
-
13
- # Or install per-project and use via npx
14
- npm install securenow@latest
15
- npx securenow <command>
16
- ```
17
-
18
- **Full parity with the SDK:** every capability the `securenow` Node SDK exposes has a CLI counterpart — redaction, CIDR matching, log/span emission, firewall preload, config inspection. If you can do it in code, you can do it from the terminal.
12
+ ## Before you start
19
13
 
20
- **MCP parity (v8.0+):** the same package also ships a local stdio MCP server for Codex, Claude, and other MCP clients:
14
+ ### Install
21
15
 
22
16
  ```bash
23
- npx securenow login
24
- codex mcp add securenow -- npx securenow mcp
25
- # or
26
- npx -p securenow securenow-mcp
27
- ```
28
-
29
- The MCP server reads admin/control-plane auth from `.securenow/admin.json` and SDK runtime app credentials from `.securenow/runtime.json`, with legacy `.securenow/credentials.json` still supported. Admin/global tools use admin auth; runtime-scoped read tools can use the runtime API key where its scopes allow it. Write tools require `confirm:true` plus a reason.
30
-
31
- **For agents, prefer the hosted MCP** (`https://api.securenow.ai/mcp`, OAuth sign-in, no install and always the current tools). With the local stdio server, warm the package once so the first connection doesn't time out on the download (`npx -y securenow@latest --version`), and give the client a longer connect timeout (Claude Code: `MCP_TIMEOUT=120000`).
32
-
33
- **Tool profiles** (stdio server): `securenow mcp --profile <name>` (or `SECURENOW_MCP_PROFILE=<name>`) serves only the tools one job needs, so the agent carries less context. Every profile keeps `securenow_auth_status`, `securenow_apps_list` and `securenow_apps_info`; calling a tool outside the profile returns an error naming the profile.
34
-
35
- | Profile | Tools | What's in it |
36
- |---|---|---|
37
- | `all` (default) | 112 | everything |
38
- | `investigator` | 58 | every read-only tool, plus forensics question/chat/confirm |
39
- | `detection` | 47 | `securenow_alert_rule*` (draft, create, test, run, runs, explain, versions, exclusions, request promotion…), traces, logs, forensics, IP intelligence, notifications list/get |
40
- | `responder` | 77 | notifications, human queue, alert reviews, blocklist, allowlist, trusted IPs, rate limits, challenges, automations, firewall, IP intelligence, plus rule get/runs/explain and traces/logs for context |
41
-
42
- ```bash
43
- claude mcp add securenow-investigator -- npx -y securenow@latest mcp --profile investigator
44
- codex mcp add securenow-detection -- npx securenow mcp --profile detection
17
+ npm install securenow@latest # in the project; run commands with npx securenow <command>
18
+ npm install -g securenow # or globally: securenow <command>
19
+ npx securenow version
45
20
  ```
46
21
 
47
22
  ### Authenticate
48
23
 
49
24
  ```bash
50
- securenow login # friendly flow: admin auth + app runtime connection
51
- securenow admin login # admin/control-plane CLI and MCP auth only
52
- securenow app connect # app/runtime SDK connection only
53
- securenow admin login --token <JWT> # headless / CI admin auth
54
- securenow whoami # verify both admin auth and runtime app status
55
- ```
56
-
57
- **Two-lane credentials (v8.0+):** admin/control-plane auth lives in `.securenow/admin.json`; SDK runtime app config and the runtime API key live in `.securenow/runtime.json`. `securenow login` can run both lanes for onboarding, but `securenow admin login` never replaces runtime app config and `securenow app connect` never replaces admin auth. Legacy combined `.securenow/credentials.json` files are still read.
58
-
59
- **Zero-config runtime flow:** the browser step lets the user pick (or create) an app. The CLI stores the app's **key (UUID)** and **name** in `.securenow/runtime.json`. The SDK sends traces/logs to the default SecureNow ingestion gateway, which routes by app key, so no env vars or per-instance collector URLs are required for local dev or production.
60
-
61
- **Default-on security (v8.0+):** after picking or creating the app, `securenow app connect` turns on that app's firewall toggle, mints a runtime API key with `traces:write + logs:write + firewall:read + blocklist:read + allowlist:read` scopes, and writes it into `.securenow/runtime.json`. Traces, logs, POST body capture, multipart metadata capture, and the firewall are enabled by default. No `SECURENOW_API_KEY` env var is needed. To add or rotate a key later without re-running app connect, use `securenow api-key create` or `securenow api-key set snk_live_...` (see [API Key Management](#api-key-management) below).
62
-
63
- Runtime credentials resolve in order: project `.securenow/runtime.json` -> legacy project `.securenow/credentials.json` -> project named runtime credentials -> global `.securenow/runtime.json` -> legacy global credentials -> global named runtime credentials. Admin auth resolves from `admin.json` first, then legacy `credentials.json`. Runtime config is credentials-json based; environment-variable fallbacks are not supported.
64
-
65
- The **runtime API key** should live in runtime credentials as `apiKey`; it authenticates telemetry ingestion and firewall sync.
66
-
67
- For CI / Docker / production, use `securenow credentials runtime --env production` to generate a tokenless runtime file, then mount/copy it as `.securenow/credentials.json`. Since v7.7.2, mounting the generated `.securenow/credentials.production.json` filename directly also works when `credentials.json` is absent.
68
-
69
- **Environment model:** use one SecureNow app key for local, preview, staging, and production. The credentials field `config.runtime.deploymentEnvironment` separates traces/logs/firewall/forensics by environment. CLI security commands default to `production`; pass `--env local`, `--env staging`, or `--env all` only when that scope is intentional.
70
-
71
- ### Integrate With Your App
72
-
73
- The CLI can also instrument any Node.js app at launch — no code changes:
74
-
75
- ```bash
76
- # Express, Fastify, NestJS, Koa, Hapi, raw Node.js
77
- securenow run src/index.js
78
-
79
- # Or via the Node preload flag
80
- node -r securenow/register src/index.js
81
- ```
82
-
83
- For Next.js, run the interactive scaffolding:
84
-
85
- ```bash
86
- securenow login
87
- securenow init
88
- ```
89
-
90
- This auto-detects your framework, creates the necessary `instrumentation.ts` and `next.config.js` changes, and reuses the app, instance, and runtime API key written by login or `app connect` to `.securenow/runtime.json`.
91
-
92
- ### Install This Skill in Cursor
93
-
94
- Save this file as `.cursor/skills/securenow-cli/SKILL.md` in your project. Your AI agent will auto-discover it whenever you ask about CLI commands, security investigations, or firewall management.
95
-
96
- ## Configuration
97
-
98
- Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-project):
99
-
100
- | File | Content |
101
- |------|---------|
102
- | `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
103
- | `~/.securenow/admin.json` | `token`, `email`, `expiresAt` for global admin/control-plane CLI and MCP auth |
104
- | `~/.securenow/runtime.json` | `apiKey`, `app`, `config` for global SDK runtime |
105
- | `.securenow/admin.json` | `token`, `email`, `expiresAt` for project-local admin/control-plane CLI and MCP auth |
106
- | `.securenow/runtime.json` | `apiKey`, `app`, `config`, `_securenow.explanations` for project-local SDK runtime |
107
- | `.securenow/credentials.json` | Legacy combined credentials; still read for backward compatibility |
108
- | `.securenow/credentials.<environment>.json` | Tokenless runtime credentials generated by `securenow credentials runtime --env <environment>`; read in a fixed order, not selected from env vars |
109
-
110
- **Credential 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`. Environment-variable fallbacks are not supported.
111
-
112
- **Runtime API key resolution (v8.0+):** project `.securenow/runtime.json` -> legacy project credentials -> project named runtime credentials in the fixed staging/production/preview/local/test/development/dev/prod order -> global runtime credentials -> legacy global credentials -> global named runtime credentials. Use `securenow app connect` for runtime setup or `securenow api-key set` to rotate a key without touching admin auth or env vars.
113
-
114
- ```bash
115
- securenow config set apiUrl https://api.securenow.ai
116
- securenow config set defaultApp my-app-key
117
- securenow config get # show all
118
- securenow config get defaultApp # show one
119
- securenow config path # print file paths + active auth source
25
+ npx securenow login # browser: admin auth + the app's runtime config (run from the project root)
26
+ npx securenow admin login # admin/control-plane auth only (CLI and MCP) [--token <TOKEN>] [--global]
27
+ npx securenow app connect # runtime only: pick/create the app, mint the runtime API key [--global]
28
+ npx securenow whoami # both lanes
29
+ npx securenow logout # clears admin auth; the runtime config stays [--global]
120
30
  ```
121
31
 
122
- Legacy CLI overrides still exist for operator automation, but runtime SDK config should stay in the credentials file.
123
-
124
- ## Global Flags
125
-
126
- | Flag | Short | Effect |
127
- |------|-------|--------|
128
- | `--json` | `-j` | JSON output (pipe-friendly) |
129
- | `--help` | | Show help for any command |
130
- | `--verbose` | `-v` | Verbose output |
131
- | `--force` | `-f` | Skip confirmations |
132
- | `--yes` | `-y` | Auto-confirm prompts |
133
-
134
- SecureNow-specific environment overrides are not supported; use `.securenow/runtime.json`, `.securenow/admin.json`, or `securenow config set`.
135
-
136
- ---
137
-
138
- ## Command Reference
139
-
140
- ### Run — Instrument Any Node.js App
141
-
142
- ```bash
143
- securenow run <script> # auto-detect CJS/ESM, inject OTel preload
144
- securenow run --watch src/index.js # pass Node flags through
145
- securenow run --inspect src/server.js --port 3000
146
- securenow run --firewall-only app.js # preload firewall only (no tracing overhead)
147
- securenow src/index.js # shorthand — auto-detected as "run"
148
- ```
32
+ Two lanes, two files: `.securenow/admin.json` (admin token for the CLI and MCP) and `.securenow/runtime.json` (app key, runtime API key `snk_live_...`, SDK config). `--global` writes to `~/.securenow/` instead of the project. Both files are secrets; the CLI adds them to `.gitignore` without ignoring the whole `.securenow/` folder. Never open an auth URL yourself: the CLI generates the callback.
149
33
 
150
- Spawns `node --require securenow/register [--import otel/hook.mjs] <script>`. ESM detection uses nearest `package.json` `"type"` field or `.mjs`/`.cjs` extension. With `--firewall-only`, uses `securenow/firewall-only` instead (credentials-file firewall, no OpenTelemetry).
34
+ ### Pick the app and the environment
151
35
 
152
- ### Authentication
36
+ - One app key serves every environment. Traces, logs, firewall rules, forensics and alerts are scoped by `config.runtime.deploymentEnvironment` (`production`, `staging`, `preview`, `local`...).
37
+ - CLI security commands default to `production`. `npx securenow init` writes `local` for a development machine, so use `--env local` (or `--env all` where accepted) to see local traffic.
38
+ - `--app <key>` targets another app. `npx securenow apps default <key>` (or `npx securenow config set defaultApp <key>`) sets the default.
153
39
 
154
- ```bash
155
- securenow login # friendly flow: admin auth + app runtime connection
156
- securenow admin login # admin/control-plane CLI and MCP auth only
157
- securenow admin login --token <JWT> # headless / CI admin auth
158
- securenow app connect # app/runtime SDK connection only
159
- securenow logout # clear admin auth; runtime app config remains
160
- securenow logout --global # clear global admin auth only
161
- securenow whoami # show admin auth and runtime app status separately
162
- ```
40
+ ## Tasks
163
41
 
164
- ### Applications
42
+ ### Set up SecureNow in a project
165
43
 
166
44
  ```bash
167
- securenow apps # list all apps (default subcommand)
168
- securenow apps list # same as above
169
- securenow apps create <name> [--hosts h1,h2] [--instance <id>] # interactive instance picker
170
- securenow apps info <id> # show app details
171
- securenow apps delete <id> [--force] # delete an app
172
- securenow apps default <app-key> # set default app for all commands
173
- securenow apps discover [appId] [--domain example.com] # discover subdomains, add as apps
174
- securenow apps scan [--yes] # scan all app domains for new subdomains
45
+ npm install securenow@latest
46
+ npx securenow login
47
+ npx securenow init # [--env local] [--key snk_live_...] [--ts|--js] [--src|--root] [--force]
48
+ npx securenow doctor && npx securenow test-span
175
49
  ```
176
50
 
177
- ### API Key Management
51
+ `init` detects the framework, fills `.securenow/runtime.json`, writes the Next.js instrumentation file and config, and prints the start-script change for plain Node. The full wiring per framework is in SKILL-API.md.
178
52
 
179
- Manage the runtime API key stored in runtime credentials. Since v8.0 the app runtime flow writes app-scoped `snk_live_...` keys to `.securenow/runtime.json` by default, so no env var is required for local dev.
53
+ ### Production credentials
180
54
 
181
55
  ```bash
182
- securenow api-key create --name "CLI runtime" # mint + save a current-app runtime key
183
- securenow api-key create --app <key-or-id> # mint + save a runtime key scoped to another app
184
- securenow api-key set snk_live_xxxxxxxxxx # save to project ./.securenow/ (default)
185
- securenow api-key set snk_live_xxx --global # save to ~/.securenow/ instead
186
- securenow api-key show # print the masked current key + its source
187
- securenow api-key clear # remove just the API key (keeps session/app)
188
- securenow api-key clear --global # same, but from the global file
189
- securenow credentials runtime --env production # write .securenow/credentials.production.json for production secret-file deploys
56
+ npx securenow credentials runtime --env production # writes .securenow/credentials.production.json
57
+ npx securenow credentials runtime --env production --format env # prints SECURENOW_CREDENTIALS=<base64>
58
+ npx securenow credentials runtime --env staging --out deploy/securenow.json
59
+ npx securenow credentials runtime --env production --stdout # JSON to stdout
190
60
  ```
191
61
 
192
- The key must start with `snk_live_`. `securenow app connect` writes the key automatically; use `api-key create` when you have admin auth but no runtime key yet, or `api-key set` when you already have a key from the dashboard. `api-key create` defaults to the current app in `.securenow/runtime.json`, resolves that app UUID to the server app id, creates a one-app `runtime_app` key with `traces:write`, `logs:write`, `firewall:read`, `blocklist:read`, and `allowlist:read`, then stores the one-time plaintext key as `apiKey`.
62
+ The file carries the app key, the runtime API key and the SDK config, never the admin token. Mount it as `<app-root>/.securenow/credentials.json`, or store the `--format env` line as a secret environment variable (Vercel, Railway, Render, Heroku, Fly, Kubernetes). Both contain the runtime API key.
193
63
 
194
- ### Init — Project Setup
64
+ Runtime key only:
195
65
 
196
66
  ```bash
197
- securenow init [--env local] [--key <API_KEY>]
67
+ npx securenow api-key create --name "CLI runtime" # mint and save a key for the current app [--app <key-or-id>] [--global]
68
+ npx securenow api-key set snk_live_... # save a key you already have [--global]
69
+ npx securenow api-key show # masked key and its source
70
+ npx securenow api-key clear # remove the key only [--global]
198
71
  ```
199
72
 
200
- Auto-detects framework (Next.js, Nuxt, Express, Fastify, Koa, Hapi, Node) from `package.json`. Then:
201
- - **Credentials**: ensures `.securenow/runtime.json` exists with secure defaults and explanations
202
- - **Next.js**: creates `instrumentation.ts/js` with `securenow/nextjs` + `securenow/nextjs-auto-capture`, and adds `serverExternalPackages: ['securenow']` plus `outputFileTracingIncludes` when the config can be patched safely
203
- - **Existing Next.js files**: prints a Codex/Claude-ready merge prompt when instrumentation or config already exists or is too custom to safely patch
204
- - **Nuxt**: tells you to add `securenow/nuxt` to modules
205
- - **Node/Express/etc.**: suggests adding `-r securenow/register` to start script
206
- - Reuses `.securenow/runtime.json` written by login/app connect; production should mount/copy the tokenless runtime file generated by `securenow credentials runtime --env production`
207
-
208
- ### Dashboard & Status
73
+ ### Today's overview
209
74
 
210
75
  ```bash
211
- securenow today [--since 24h|7d] [--attackers 20] [--json] # the Overview: what happened, what SecureNow did, what needs you
212
- securenow attacker <ip> [--json] # why an address was flagged: verdict, signals, traffic vs normal, detections, timeline
213
- securenow status [--app <key>] [--env local|production|all] # dashboard overview
214
- securenow analytics [--app <key>] # response analytics
76
+ npx securenow today # what happened, what SecureNow did, what needs you [--since 24h|7d] [--attackers 8]
77
+ npx securenow status --env production # apps, protection, unread alerts [--app <key>] [--env local|production|all]
78
+ npx securenow analytics --app <key> # response-code analytics
79
+ npx securenow logs errors --since 1h # what is failing: errors grouped by message [--warn] [--limit 20]
215
80
  ```
216
81
 
217
- `today` is the dashboard's home in the terminal:
218
- - whether each app is covered (data flowing, firewall on, detection monitoring or blocking);
219
- - attack requests, attackers, what the firewall stopped and what got through;
220
- - the decisions waiting for you, the top attackers, and what SecureNow did (the agent's actions, automatic blocks,
221
- closed probes).
222
-
223
- `attacker <ip>` explains one address:
224
- - the verdict and score with the signals behind them, and its reputation;
225
- - where it is blocked;
226
- - its last 3 days of traffic against your apps' normal (error rate, share of traffic, scanner probes, attack
227
- payloads answered 2xx);
228
- - the detections it triggered and what happened to each, a timeline, and questions to paste into
229
- `securenow forensics chat`.
230
-
231
- MCP: `securenow_overview` and `securenow_attacker` (read).
232
-
233
- ---
82
+ `today` is the dashboard's Overview: whether each app is covered (data flowing, firewall on, detection monitoring or blocking), attack requests and attackers, what the firewall stopped and what got through, decisions waiting, top attackers, and what SecureNow did. MCP: `securenow_overview`.
234
83
 
235
- ### Traces
84
+ ### Investigate an IP
236
85
 
237
86
  ```bash
238
- securenow traces [--app <key>] [--env production] [--limit N] [--start ISO] [--end ISO]
239
- securenow traces list --app my-app --env production --limit 50
240
- securenow traces show <traceId> --env production # full trace detail with spans
241
- securenow traces analyze <traceId> --env production # AI-powered trace analysis
87
+ npx securenow attacker 203.0.113.9 # why it was flagged: verdict, signals, traffic vs normal, detections, timeline
88
+ npx securenow ip 203.0.113.9 # geo, ASN, reputation, verdict
89
+ npx securenow ip traces 203.0.113.9 --env production # its requests
90
+ npx securenow traces show <traceId> --env production # one request: spans, headers, body (redacted)
91
+ npx securenow traces analyze <traceId> # AI analysis of a trace
92
+ npx securenow firewall hits --ip 203.0.113.9 --since 24h # what the firewall did to it
93
+ npx securenow agent verify 203.0.113.9 # the double-check the agent runs before acting (alert moment + 3 days)
94
+ npx securenow forensics chat --app <key> "what did 203.0.113.9 do in the last 24 hours?"
242
95
  ```
243
96
 
244
- ### Logs
245
-
246
- ```bash
247
- securenow logs [--app <key>] [--env production] [--limit N] [--minutes M] [--level error|warn|info]
248
- securenow logs list --app my-app --env production --minutes 30 --level error
249
- securenow logs trace <traceId> --env production # logs correlated to a specific trace
250
- securenow logs errors --app my-app --since 6h [--warn] # what is failing: error lines grouped by message (ids/numbers normalized), counts, first/last seen, sample traces
251
- ```
252
-
253
- For "what's broken right now?", start with `logs errors` (MCP `securenow_logs_errors`): one call returns the distinct failures, not hundreds of raw lines.
254
-
255
- ### Notifications
256
-
257
- ```bash
258
- securenow notifications [--limit N] [--page P]
259
- securenow notifications list --limit 20
260
- securenow notifications read <id> # mark one as read
261
- securenow notifications read-all # mark all as read
262
- securenow notifications unread # unread count
263
- ```
97
+ `forensics chat` runs discovery queries, shows its SQL and ends each answer with findings; continue with `--conversation <id> "<follow-up>"`. `npx securenow forensics "<question>"` answers one question without memory. Trust the result rows over the summary. MCP: `securenow_attacker`, `securenow_ip_lookup`, `securenow_ip_traces`, `securenow_forensics_chat`.
264
98
 
265
- ### Two review queues
99
+ ### Triage notifications
266
100
 
267
- SecureNow decides most things alone (instant blocks, automations, IP investigations, self-pausing
268
- rules). What it won't decide alone waits in two queues. Work **Alert Review** first: one rule fix
269
- closes every run it caused and the Requires Human rows that came from the same noise.
101
+ SecureNow decides most things alone (instant blocks, automations, IP investigations, rules that pause themselves). What it won't decide waits in two queues; work **Alert Review** first, because one rule fix closes every run it caused.
270
102
 
271
103
  | Queue | Question | Decisions | CLI |
272
104
  |---|---|---|---|
273
- | Alert Review | "Is this rule right?" A rule fired far too wide, or on an IP treated as your own infrastructure; it usually pauses itself | tune the rule, keep it on, close/dismiss with a reason (never blocks or unblocks) | `securenow review` |
274
- | Requires Human | "What do we do about this IP or plan?" An investigated IP with a verdict SecureNow won't act on alone, or a proposed plan | block, false positive, close without blocking, approve/reject a plan | `securenow human` |
275
-
276
- ### Alert Review — is this rule right?
105
+ | Alert Review | "Is this rule right?" A rule fired far too wide, or on your own infrastructure; it usually paused itself | tune the rule, keep it on, close or dismiss with a reason (never blocks or unblocks) | `securenow review` |
106
+ | Requires Human | "What do we do about this IP or plan?" An investigated IP with a verdict SecureNow won't act on alone, or a proposed plan | block, false positive, close without blocking, approve or reject a plan | `securenow human` |
277
107
 
278
108
  ```bash
279
- securenow review # open runs grouped by root cause (rule + route + evidence shape)
280
- securenow review list --search lab-auth # one app (name or key), rule, route or IP
281
- securenow review show 1 # group 1: rule, route, IPs, context, its runs
282
- securenow review show <notificationId> # one run
283
- securenow review keep <notificationId> --reason "Correct detection" --yes # turn the rule back on, stop it auto-pausing, close the run
284
- securenow review close <notificationId> --outcome rule_tuning --reason "..." --yes
285
- securenow review dismiss <notificationId> --reason "Not worth acting on" --yes
286
- securenow review group 1 --outcome rule_tuning --reason "Guard added in v4" --preview # what would close
287
- securenow review group 1 --outcome rule_tuning --reason "Guard added in v4" --yes # close the whole group
288
- ```
289
-
290
- Outcomes: `rule_tuning`, `false_positive`, `clean`, `deferred`, `new_alert_rule`, `rate_limited`,
291
- `ambiguous`, `skipped`, `other`. Closing writes a decision report on the alert and changes no
292
- enforcement. Tune the rule first (`securenow alerts rules change <id> "..."`), then close its group.
293
- A mixed group (rows disagree on verdict or sender) needs `--allow-mixed` after reviewing every row.
294
-
295
- MCP parity: `securenow_alert_review_runs_list`, `securenow_alert_review_run_get`,
296
- `securenow_alert_review_run_keep_alive`, `securenow_alert_review_run_resolve`,
297
- `securenow_alert_review_run_dismiss`, `securenow_alert_review_run_apply_group_decision`; prompt `work_alert_review`.
298
-
299
- ### Requires Human — what do we do about this IP or plan?
109
+ npx securenow notifications list --limit 20 # [--search <rule or IP>] [--severity critical|high|medium|low] [--page N]
110
+ npx securenow notifications unread
111
+ npx securenow notifications read <id> # or: read-all
300
112
 
301
- Use this for the same work shown in **Requires Human**: AI has already grouped alerts, fetched traces, built a report, and left a final Block IP or False Positive decision.
113
+ npx securenow review # open runs grouped by root cause
114
+ npx securenow review show 1 # a group (row number or arc_ key) or one run (notification id)
115
+ npx securenow alerts rules change <rule-id> "ignore /healthz and raise the threshold to 20" # fix the rule first
116
+ npx securenow review group 1 --outcome rule_tuning --reason "Guard added" --preview
117
+ npx securenow review group 1 --outcome rule_tuning --reason "Guard added" --yes
118
+ npx securenow review keep <notificationId> --reason "Correct detection" --yes
119
+ npx securenow review dismiss <notificationId> --reason "Not worth acting on" --yes
302
120
 
303
- ```bash
304
- securenow human # list human decisions, most urgent first
305
- securenow human list --limit 20
306
- securenow human show 1 # inspect row 1 with AI report, investigation steps, proofs, trace links
307
- securenow human block 1 --yes --reason "AI evidence confirmed malicious"
308
- securenow human fp 1 --yes --reason "Scoped false positive after evidence review"
309
- securenow human close 1 --outcome deferred --reason "Scanner, nothing reachable; not worth a block" --yes # leaves the queue, nothing blocked
310
- securenow human action 1 --status rejected --yes --reason "Tuning guard is too broad"
311
- securenow human prompt 1 # print a Codex/Claude MCP prompt for this row
312
- securenow human work --limit 10 # list queue + print MCP runbook for deep queue work
121
+ npx securenow human # decisions waiting, most urgent first
122
+ npx securenow human show 1 # AI report, proofs, investigation steps, trace links
123
+ npx securenow human block 1 --reason "Confirmed SQL injection with 2xx answers" --yes
124
+ npx securenow human fp 1 --reason "Partner webhook, scoped to /api/hooks" --yes
125
+ npx securenow human close 1 --outcome deferred --reason "Scanner, nothing reachable" --yes
126
+ npx securenow human action 1 --status rejected --reason "Exclusion too broad" --yes
313
127
  ```
314
128
 
315
- MCP parity:
316
-
317
- - `securenow_human_actions_list`
318
- - `securenow_notifications_get`
319
- - `securenow_human_action_report`
320
- - `securenow_human_action_block`
321
- - `securenow_human_action_false_positive`
322
- - `securenow_human_action_close_no_write`
323
- - `securenow_human_case_action_update`
324
- - prompts: `investigate_human_action_row`, `work_human_actions`
325
-
326
- `human report` only adds a note: the row stays. `human close` removes it without enforcement.
327
- Approving a `tune_rule` or `create_exclusion` plan with `human action` records the decision and
328
- changes nothing: change the rule first, then approve. Only `approve_block` blocks.
329
-
330
- ### The autonomous agent works the queues for you
129
+ Review outcomes: `rule_tuning`, `false_positive`, `clean`, `deferred`, `new_alert_rule`, `rate_limited`, `ambiguous`, `skipped`, `other`. A group whose rows disagree needs `--allow-mixed` after reviewing every row. `human report` only adds a note (the row stays); `human close` removes the row without enforcement; approving a `tune_rule` or `create_exclusion` plan with `human action` only records the decision (change the rule first); only `approve_block` blocks.
331
130
 
332
- SecureNow runs an agent per organization that works both queues on its own: every few minutes when
333
- there is work, or on demand. Before any decision on an IP it runs the same double-check as
334
- `agent verify`: what that IP did around the alert and over the last 3 days on every app, its
335
- sign-ins, the trust lists and reputation. Its blocks only cover the alert's apps and expire. What it
336
- may do is set per kind of action: `off`, `shadow` (record what it would do, change nothing),
337
- `suggest` (leave the recommendation on the row for a person) or `act`.
131
+ Each notification carries an `ipInvestigations` array; act only on IPs whose own `status` is `open`:
338
132
 
339
- ```bash
340
- securenow agent # status: on/off, mode per action, what it did, agreement with people
341
- securenow agent on # or off (off also revokes a run in progress)
342
- securenow agent mode all shadow # start here: it records, you compare with your own decisions
343
- securenow agent mode block_ip act --yes # then hand over one kind of action at a time
344
- securenow agent activity --mode applied # every decision with its evidence
345
- securenow agent undo <id> --yes # unblock / reopen the row / roll the rule back
346
- securenow agent runs # patrols: status, tool calls, cost, summary
347
- securenow agent run # patrol now
348
- securenow agent verify 203.0.113.9 # the double-check, for any IP
349
- ```
133
+ | Status | Meaning | Action |
134
+ |---|---|---|
135
+ | `open` | No determination yet | Investigate, then decide |
136
+ | `false_positive` | Dismissed by an exclusion, a verified bot or a trusted IP | Skip |
137
+ | `blocked` | Already on the blocklist | Skip |
138
+ | `clean` | Cleared as benign | Skip |
350
139
 
351
- Action types: `close_row`, `false_positive`, `block_ip`, `review_close`, `keep_rule`, `rule_change`,
352
- `case_action`. The API enforces all of it, whatever the model decides: the agent's token only reaches
353
- the queue, verification and rule-tuning routes (never trust lists, the allowlist, unblocking,
354
- automations or prod promotion), it can't block without a verification that allows it, it only edits
355
- test-mode rules (prod rules get a proposal a person applies), and budgets cap each run and day. An
356
- action type that people keep undoing drops back to shadow by itself.
140
+ Prefer `metadata.matchedSubdetectors` over guessing which part of a rule fired from the title.
357
141
 
358
- ### Run the queues with your own agent
142
+ **False-positive exclusions** keep known-safe traffic from alerting. Keep them as narrow as the evidence (app, rule, path, method, status, user agent, body shape):
359
143
 
360
144
  ```bash
361
- claude "$(npx -y securenow@latest agent prompt patrol)" # read-only pass: a plan
362
- claude -p "$(npx -y securenow@latest agent prompt patrol --search my-app --confirm-writes)" --allowedTools "mcp__securenow" # unattended, acts on one app
145
+ npx securenow fp ai-fill --description "Stripe webhook POST to /api/stripe/webhook" --context '{"method":"POST","path":"/api/stripe/webhook"}'
146
+ npx securenow fp dry-run --conditions '[{"field":"path","operator":"starts_with","value":"/api/stripe/webhook"}]' # last 3 days
147
+ npx securenow fp create --conditions '[...]' --match-mode all --rule-scope any_rule --reason "Stripe webhook"
148
+ npx securenow fp create --path /api/events --method POST --path-safe standard --ua-safe standard --headers-safe standard --query-keys page,limit --reason "Event webhook"
149
+ npx securenow fp mark <notification-id> <ip> --rule-scope this_rule --reason "Known partner IP"
150
+ npx securenow alerts rules exclusions <rule-id> add --conditions '[...]' --reason "..." # one rule only
151
+ npx securenow fp list | show <id> | edit <id> --active false | delete <id> --yes
152
+ npx securenow fp test-body @request.json --conditions '[...]'
363
153
  ```
364
154
 
365
- `soc_patrol` works Alert Review then Requires Human, up to 10 groups and 10 rows per pass
366
- (`--max-alert-review-groups`, `--max-human-rows`). The API key's scopes are the hard limit: a
367
- Read Only key cannot change anything; acting needs `notifications:write`, `alerts:write` and
368
- `rate_limits:write`. Setup for Claude Code, Codex and Cursor: dashboard → Run With Your Agent.
155
+ Conditions are `{"field", "operator", "value"}`. Fields: `path`, `method`, `user_agent`, `status_code`, `request_body`, `request_headers`. Operators: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex`, `in`, `not_in`, and structural checks (`path_safe_values`, `query_safe_values`, `query_has_only_keys`, `ua_safe_values`, `headers_safe_values`, `headers_has_only_keys`, `body_safe_values`, `body_is_format`, `body_has_only_fields`, `json_has_only_keys`, `json_safe_values`, `json_valid`, `json_max_depth`, `json_schema_match`); prefer the `*_safe_values` checks, which only excuse a request whose value is clean. Match modes: `all`, `any`. Scopes: `this_rule` (only through `fp mark`), `specific_rules` (with `--target-rules id1,id2`), `all_existing`, `any_rule` (the `fp create` default). Presets for `--*-safe`: `standard`, `strict`.
369
156
 
370
- Write tools still require `confirm:true` plus a reason. False positives should stay restrictive to the app, alert rule, path, method/status, user-agent, body pattern, or other exact evidence the AI report supports. Notification IP investigations can include `metadata.matchedSubdetectors`; prefer that exact field over inferring which subdetector fired from the title or sample body.
371
-
372
- ### Alerts
373
-
374
- Before authoring any alert SQL, load the contract shipped by the installed package and compare
375
- it with fields actually observed for the selected application:
376
-
377
- ```bash
378
- securenow telemetry schema --json
379
- securenow telemetry schema logs --json
380
- securenow telemetry schema traces --json
381
- securenow telemetry schema events --json
382
- securenow telemetry fields --app <key> --env production --since 24h --source all --json
383
- ```
157
+ MCP: `securenow_alert_review_runs_list`, `securenow_alert_review_run_get`, `securenow_alert_review_run_keep_alive`, `securenow_alert_review_run_resolve`, `securenow_alert_review_run_dismiss`, `securenow_alert_review_run_apply_group_decision`, `securenow_human_actions_list`, `securenow_human_action_report`, `securenow_human_action_block`, `securenow_human_action_false_positive`, `securenow_human_action_close_no_write`, `securenow_human_case_action_update`; prompts `work_alert_review`, `work_human_actions`, `investigate_human_action_row`.
384
158
 
385
- `telemetry schema` is offline and versioned with the SDK. `telemetry fields` requires admin
386
- auth and telemetry access; it returns names, map scopes, data types, occurrence counts, and
387
- custom event types, but never customer attribute values. Absence means "not observed in this
388
- window," not "unsupported." Use only documented fields, and label a rule `READY` only when
389
- its fields were observed and `alerts rules validate` succeeds. Otherwise label it
390
- `NEEDS_INSTRUMENTATION`, `NEEDS_BASELINE`, or `NEEDS_CUSTOMER_DECISION` as appropriate.
159
+ ### Block an IP
391
160
 
392
161
  ```bash
393
- securenow alerts # list alert rules (default)
394
- securenow alerts rules # list alert rules (columns: Status, Applications, Schedule)
395
- securenow alerts rules from-text \ # describe a detection in English; checked draft, then create (test mode)
396
- "one IP failing 10 sign-ins in 5 minutes" \
397
- --app <key> [--env production] # picks instant or periodic; --instant/--periodic, --draft-only, --yes
398
- securenow alerts rules create --name "Secrets probe" --apps <key> \
399
- --conditions '[{"field":"path","operator":"contains","value":"/.env"}]' # an instant rule, no SQL
400
- securenow alerts rules validate \ # dry-run a candidate query BEFORE creating any rule
401
- --sql @rule.sql --app <key> # no rule id needed — auto-hosts on an existing (system) rule
402
- securenow alerts rules create \ # create a custom detection rule from inline SQL
403
- --name "Auth: magic-link brute force" \
404
- --sql @rule.sql \ # SQL, @file, or - for stdin; scope apps with __USER_APP_KEYS__
405
- --apps key1,key2 \ # or --applications-all
406
- --severity high --schedule "*/15 * * * *" \
407
- --nlp "single IP flooding /api/auth/signin or /api/auth/callback"
408
- # create auto dry-runs + rolls back (deletes) on query failure; --no-validate to skip
409
- securenow alerts rules show <id> # one rule; JSON: --json
410
- securenow alerts rules set-sql <id> --sql @new.sql # replace a CUSTOM rule's SQL in place (alias: update <id> --sql)
411
- securenow alerts rules delete <id> --yes # delete a custom rule (system rules: disable instead)
412
- securenow alerts rules update <id> --applications-all # all current & future apps
413
- securenow alerts rules update <id> --apps key1,key2 # explicit app keys only
414
- securenow alerts rules test <id> --wait # dry-run an existing rule's query (default; --mode live inserts 6 synthetic attack requests)
415
- securenow alerts rules change <id> "raise the threshold to 20" # rewrite a rule from English: checked, shown before/after, applied as a new version
416
- securenow alerts rules runs <id> [--since 1h] # what each run saw and why it did or didn't notify (+ next scheduled run)
417
- securenow alerts rules run <id> # run a periodic rule now and print the run (--no-wait to just queue it)
418
- securenow alerts rules why <id> --ip <ip> [--since 1h] # why the rule did or didn't alert on one address: verdict + findings
419
- securenow alerts rules quality <id> # was the rule right to fire? 14-day precision, alerts labeled wrong/unclear and why, what detection learning changed
420
- securenow alerts rules dry-run-query <id> --sql @candidate.sql --app <key> --wait # candidate test against a specific host rule
421
- securenow alerts rules tune-query <id> --sql @candidate.sql --reason "Preserve exploit detector, remove benign broad match" --apply-globally --yes
422
- securenow alerts rules exclusions <id> list # embedded rule exclusions
423
- securenow alerts channels # list alert channels (Slack, email, etc.)
424
- securenow alerts history [--limit N] # past triggered alerts
162
+ npx securenow blocklist add 203.0.113.42 --env production --duration 24h --reason "credential stuffing"
163
+ npx securenow blocklist add 203.0.113.0/24 --route /admin --mode prefix --method ALL --reason "admin probing"
164
+ npx securenow firewall test-ip 203.0.113.42 --path /admin --method GET --env production # would it be blocked?
165
+ npx securenow blocklist list --env production # [--status active|removed] [--search 203.0.] [--approval-status pending]
166
+ npx securenow blocklist unblock <id> --reason "false alarm after review" # stops enforcement, keeps the history
167
+ npx securenow blocklist approve <pending-id> --yes # or: reject <pending-id> (records a scoped no-block decision)
168
+ npx securenow blocklist stats
425
169
  ```
426
170
 
427
- **Authoring loop that won't strand a broken rule:** `validate --sql @rule.sql --app <key>`
428
- (dry-run before anything exists) → `create …` (which itself dry-runs and auto-rolls-back on
429
- failure) → if you later need to fix it, `set-sql <id> --sql @rule.sql` (custom rules) or
430
- `delete <id>`. Detection `.sql` files may start with `--`/`/* */` comments. Custom-rule SQL is
431
- editable; **system**-rule SQL changes go through `tune-query … --apply-globally`. Pick the
432
- right tenant-scope column per table: logs (`signoz_logs.distributed_logs_v2`) use
433
- `resources_string['service.name'] IN (__USER_APP_KEYS__)`, traces
434
- (`signoz_traces.distributed_signoz_index_v3`) use `` `resource_string_service$$name` IN (__USER_APP_KEYS__) ``.
435
-
436
- `alerts rules create` flags: `--name` (required); one of `--sql <sql|@file|->` or `--query-mapping-id <id>`; one of `--apps k1,k2` or `--applications-all`; optional `--description`, `--nlp` (plain-English intent), `--category`, `--severity critical|high|medium|low`, `--schedule <cron>` (default `*/15 * * * *`), `--throttle-minutes N` / `--no-throttle`, `--execution-mode scheduled|instant|hybrid`, `--channel id1,id2` (defaults to your in-app SecureNow channel). Detection SQL must scope app keys via the `__USER_APP_KEYS__` placeholder and select an `ip` column for per-IP aggregation/remediation: create and set-sql refuse SQL with no ip column (it would never notify) unless `--allow-no-ip`. A new periodic rule runs once right away. Requires `alerts:write`.
437
-
438
- **Throttle = one alert per rule AND address** (8.17): an address alerted on within the window (default 15 min) is left out of the next runs' notifications, and other addresses still alert. Alert storms keep one alert per rule per window.
439
-
440
- **When a rule stays quiet,** don't guess: `alerts rules runs <id>` shows every run's outcome — `notified`, `no_rows`, `throttled`, `suppressed_blocked` (already blocked, often by another rule's instant block), `suppressed_trusted`, `no_ip` (rows without an ip column), `storm`, `auto_resolved` (dismissed by an exclusion, trusted IP or verified bot), `skipped`, `error` — and which addresses were left out and why. `alerts rules why <id> --ip <ip>` ends in a verdict such as "Detected at 15:06:31 but not notified: 203.0.113.50 was already blocked by instant rule X". `--yes` on `from-text` refuses to create a rule whose check matches your data right now (it would alert at once, usually a bad baseline); review the rows, then `--yes --force` if they are a real attack.
441
-
442
- MCP parity for quiet rules: `securenow_alert_rule_runs` (run history), `securenow_alert_rule_run_now` (write, confirm) and `securenow_alert_rule_explain` (`ip`, `since`: verdict + findings).
443
-
444
- **Agents don't promote rules; they ask.** When a test-mode rule has proved itself, an agent runs `securenow alerts rules request-promotion <id> --reason "..." --evidence "..."` (MCP `securenow_alert_rule_request_promotion`). A person sees it on the rules page or with `securenow human promotions` and runs `securenow human approve-promotion <request-id> --yes` (the rule goes to prod, recorded as a rule version naming the approver) or `deny-promotion <request-id> --reason "..."`. API keys and MCP tokens can ask but not decide.
445
-
446
- MCP parity for noisy alert-rule reviews:
447
-
448
- - `securenow_alert_rule_get`
449
- - `securenow_alert_rule_candidate_test` dry-runs a full candidate SQL query without saving it.
450
- - `securenow_alert_rule_test_result` polls the dry-run.
451
- - `securenow_alert_rule_query_update` updates the shared public query mapping behind a system rule for all customer copies. It is admin-only, requires `confirm:true`, `applyGlobally:true`, `reason`, and SQL that keeps `__USER_APP_KEYS__` tenant scoping.
452
- - `securenow_alert_rule_instant_update` patches Instant rule conditions/config with stale-write guards. Fetch the rule first, pass `expectedRuleVersion` plus `expectedCurrentInstantHash`, use operations such as `remove_condition` / `add_condition` / `update_condition`, and set `applyGlobally:true` for system rules. The API runs seeded before/after checks and returns benign samples removed plus attack samples still matching. If global tuning is denied, it returns a structured admin handoff with the exact patch and missing permission.
453
- - `securenow_alert_rule_exclusion_add` remains the last-resort customer-specific path; it supports restrictive conditions plus `matchMode` and should not be used to hide a generic system-rule bug.
454
-
455
- For system-rule tuning, dry-run the candidate SQL first for scheduled rules, or use `dryRun:true` on `securenow_alert_rule_instant_update` for Instant rules. Save only when the guard preserves attack detection. Good guards add exact exploit tokens, dangerous schemes, matched subdetectors, sensitive path/status evidence, malicious user agents, or repeat thresholds; bad guards simply suppress a noisy path.
171
+ `--route` (aliases `--path`, `--pattern`) with `--mode prefix|exact|regex` and `--method` makes a scoped block; without them the block is global. `blocklist remove` is an alias of `unblock`. Running apps enforce it within about 10 seconds. In the app (layer 1), ranges are IPv4 CIDRs and IPv6 is matched by exact address.
456
172
 
457
- ---
458
-
459
- ### IP Intelligence
460
-
461
- ```bash
462
- securenow ip <ip-address> # lookup (geo, ASN, threat score, reputation)
463
- securenow ip lookup <ip-address> # same as above
464
- securenow ip traces <ip-address> --env production # traces originating from this IP
465
- ```
466
-
467
- ### Forensics — Natural Language Security Queries
173
+ **Allowlist** is deny-by-default: once any active entry exists for an app and environment, only listed IPs get through. Use it only to lock an app to known IPs, with the person's approval. **Trusted IPs** are the safe way to mark known infrastructure (monitors, office VPN, your servers): alerts on them are suppressed and nobody is restricted.
468
174
 
469
175
  ```bash
470
- securenow forensics "show me all SQL injection attempts in the last 24h" --env production
471
- securenow forensics query "top 10 IPs by blocked requests" --app my-app --env production
472
- securenow forensics chat --app my-app --env production # interactive forensics chat session (with memory)
473
- securenow forensics chat --app my-app "which accounts had failed logins then a success?" # one question, prints the conversation id
474
- securenow forensics chat --conversation <id> "what else did those IPs do?" # follow up: the chat remembers the earlier answers
475
- securenow forensics conversations # list conversations (dashboard, CLI and agent ones)
476
- securenow forensics conversations <id> # show one: questions, SQL, findings, rows
477
- securenow forensics library # view saved/template queries
176
+ npx securenow allowlist add 198.51.100.7 --app <key> --env production --label "Office" --reason "Admin app locked to office"
177
+ npx securenow allowlist list | remove <id> | stats
178
+ npx securenow trusted add 198.51.100.20 --label "Uptime monitor" # [--app <key>] [--env <env>]
179
+ npx securenow trusted list | remove <id>
478
180
  ```
479
181
 
480
- The chat runs discovery queries before answering, shows its SQL, and ends each answer with findings: what the result rows
481
- show. A follow-up sees the earlier questions, their findings, and the IPs and trace IDs in their results. MCP:
482
- `securenow_forensics_chat` (pass `conversationId` to continue), `securenow_forensics_conversations_list`,
483
- `securenow_forensics_conversation_get`. `securenow forensics "<q>"` and `securenow_forensics_query` answer one question
484
- with no memory.
182
+ Default automations block by risk score (95+ for 7 days, 90–94 for 72 hours, 85–89 for 24 hours): `npx securenow automation list`, `automation defaults --yes`, `automation show <id>`, `automation dry-run <id>`, `automation execute <id> --yes`, `automation create|update|delete`.
485
183
 
486
- ---
184
+ MCP: `securenow_blocklist_add`, `securenow_blocklist_unblock`, `securenow_firewall_test_ip`, `securenow_allowlist_*`, `securenow_trusted_*`.
487
185
 
488
- ### Firewall
186
+ ### Rate-limit a route
489
187
 
490
188
  ```bash
491
- securenow firewall # show status (default)
492
- securenow firewall status --app <key> --env production # app/env toggle, sync time, blocked count
493
- securenow firewall test-ip <ip> --app <key> --env local # check if IP would be blocked
189
+ npx securenow ratelimit add --route /api/login --method POST --limit 5 --window 1m --env production --reason "login brute force"
190
+ npx securenow ratelimit add --route /api/ --limit 100 --window 1m --header x-api-key # per API key value
191
+ npx securenow ratelimit add --route /app --limit 300 --window 1m --cookie session # per session cookie value
192
+ npx securenow ratelimit add --route /api/export --limit 50 --window 1h --key-by global # one bucket for everyone
193
+ npx securenow ratelimit add 203.0.113.42 --limit 30 --window 1m --duration 24h # one IP or CIDR
194
+ npx securenow ratelimit parse "5 per minute per IP on POST /api/login for 2h" # draft only
195
+ npx securenow ratelimit from-text "5 per minute per IP on POST /api/login for 2h" --yes # create
196
+ npx securenow ratelimit test 203.0.113.42 --path /api/login --method POST --env production
197
+ npx securenow ratelimit list | show <id> | enable <id> | disable <id> | remove <id> --reason "..."
494
198
  ```
495
199
 
496
- **Zero-config setup:** running `securenow app connect` enables the selected app's firewall toggle, auto-mints a runtime API key (scoped `traces:write + logs:write + firewall:read + blocklist:read + allowlist:read`), and writes it to `.securenow/runtime.json` after the app is selected. No `SECURENOW_API_KEY` env var needed. If the user already has a key, `securenow api-key set snk_live_...` achieves the same thing; if they only have admin auth, `securenow api-key create` mints and saves a fresh runtime key. See [the landing firewall page](https://securenow.ai/firewall) for an overview.
497
-
498
- ### Starter Protection — Rules Every App Gets
200
+ `--mode exact|prefix|regex` (default `prefix`), `--method` (default `ALL`), `--window 30s|1m|1h`, `--duration 24h|7d` (expiry), `--key-by ip|global|header|cookie` (`--header <name>` or `--cookie <name>` imply it). Header and cookie values are hashed; requests without them count per IP. Over the limit the app answers 429 with `Retry-After`. Rules are per environment and default to `production`; use `--env local` to try one on a local app. Counters live in each process unless the app sets `config.firewall.rateLimitStore` to a Redis URL. `rate-limit` is an alias of `ratelimit`. MCP: `securenow_rate_limit_parse`, `securenow_rate_limit_create_from_text`, `securenow_rate_limit_create`, `securenow_rate_limit_test`.
499
201
 
500
- An app gets the starter pack when it is created or first sends traffic: about 20 rules (SQL injection, XSS, traversal, command and template injection, Log4Shell, Spring4Shell, metadata SSRF, CRLF, prototype pollution, `.env`/`.git`/backup probes, exploit kits, attack-tool user agents, body payloads, failed sign-ins, 404 scanning). They start in **monitor** mode: they alert, nothing is blocked by them. In **block** mode the 14 block-ready rules block the address for 6 hours on their exploit signatures (URL, headers, user agent; bodies and the scheduled rules only alert).
202
+ ### Challenge bots
501
203
 
502
204
  ```bash
503
- securenow protect # status of the current app: mode, rules, alerts, ready to block?
504
- securenow protect status --all --json # every app
505
- securenow protect enable --app <key> # turn it on (monitor mode)
506
- securenow protect block --app <key> # a person turns blocking on; refused unless the app sees real client IPs
507
- securenow protect monitor --app <key> # back to alerts only
508
- securenow protect disable --app <key> # off; stays off until someone turns it on
509
- securenow protect learn --app <key> # detection learning now (it also runs hourly): label alerts, measure precision, learn safe exclusions
510
- securenow protect status --rules # adds each rule's 14-day precision (right / right + wrong)
205
+ npx securenow challenge add --route /login --clearance 30m --env production --reason "credential stuffing wave"
206
+ npx securenow challenge add 203.0.113.0/24 --route /signup --difficulty 16 --escalate-to-block --fail-threshold 10 --block-ttl-hours 24
207
+ npx securenow challenge test 203.0.113.9 --path /login --method GET --env production
208
+ npx securenow challenge list | show <id> | enable <id> | disable <id> | remove <id> --reason "..."
209
+ npx securenow firewall hits --action captcha_challenged --since 1h
511
210
  ```
512
211
 
513
- Only a person signed in (`securenow admin login`) can turn blocking on; API keys, MCP connectors and the agent get a 403. Blocking is refused when more than 20% of the app's traffic reaches it through a proxy the SDK doesn't trust: add the proxy to `config.networking.trustedProxies` (IPs, IPv4 ranges or `"cloudflare"`). Tune a noisy starter rule like any rule (exclusions, `alerts rules update`); pack updates never overwrite a rule a person edited.
212
+ Browsers get an invisible proof of work (`--difficulty` in leading zero bits, 4–28, default 14: about a second or less) and a pass for `--clearance` (default 30m). Clients that don't accept HTML get a 403 JSON `challenge_required`, so don't challenge API routes used by non-browser clients. MCP: `securenow_challenge_create`, `securenow_challenge_list`, `securenow_challenge_update`, `securenow_challenge_remove`.
514
213
 
515
- **Detection learning** keeps the starter rules precise per app. Every alert gets a label: was the rule right to fire? A blocked address counts as right, a person's false-positive call as wrong, and an LLM judge (accounts with AI access) labels the rest from the requests and the address's last day of traffic; a failed one-off probe (`/.env` answered 404) is closed as right but not worth action. Precision = right / (right + wrong) over 14 days, target 95%. A rule below it with 3+ wrong alerts gets one exclusion for the legitimate traffic, applied only when it matches the wrong alerts and none of the right ones, can't be pinned to an IP, and still lets every reference attack through even when injected into the excused request; otherwise the rule is flagged for a person. A block-ready rule under 95% (10+ labeled alerts) keeps the app from being ready to block. MCP: `securenow_alert_rule_quality`, `securenow_protection_learn`.
516
-
517
- ### Blocklist — Block Malicious IPs
214
+ ### Revoke a stolen session
518
215
 
519
216
  ```bash
520
- securenow blocklist # list blocked IPs
521
- securenow blocklist list
522
- securenow blocklist add <ip> --app <key> --env production --reason "Brute force"
523
- securenow blocklist add <ip> --route /admin* --mode prefix --method ALL --reason "Admin probing"
524
- securenow blocklist unblock <id> --reason "False alarm after review"
525
- securenow blocklist remove <id> # compatibility alias for unblock
526
- securenow blocklist list --status removed # audit retained unblocks
527
- securenow blocklist stats # block counts, top reasons
217
+ npx securenow revoke session <session-id> --reason "impossible travel" --duration 24h --app <key> # [--env <env>]
218
+ npx securenow revoke user <user-id> --reason "account takeover" # every session of that user; default expiry 7d
219
+ npx securenow revoke list # [--type session|user] [--status active|restored]
220
+ npx securenow revoke restore <revocation-id> --reason "verified with the user"
528
221
  ```
529
222
 
530
- Scoped blocks use the same model as rate limits: `--route`/`--path`/`--pattern`
531
- sets `pathPattern`, `--mode`/`--path-mode` chooses `prefix`, `exact`, or
532
- `regex`, and `--method` limits enforcement to one HTTP method or `ALL`.
533
-
534
- Unblock stops firewall enforcement but preserves the block report, history, and
535
- unblock audit fields. Reblocking the same IP later creates a fresh active block
536
- without erasing the previous investigation trail.
223
+ The app enforces it within 30 seconds, through one of: `"sessions": { "guard": true }` in its runtime config **(8.25.0+)**, `sessions.guard()` middleware, or `sessions.check(req)` **(8.25.0+)** (SKILL-API.md section 5.10). The session id is the SHA-256 of the Bearer token or session cookie, as it appears in `session.id` on traces and `session.seen` events; the user id is the JWT `sub` or what the app reports. There is no MCP tool for revocation: use this command.
537
224
 
538
- MCP exposes legacy pending-block cleanup separately from current Requires Human
539
- work:
540
-
541
- - `securenow_blocklist_unblock`
542
- - `securenow_blocklist_remove` (compatibility alias for unblock)
543
- - `securenow_blocklist_pending_list`
544
- - `securenow_blocklist_pending_approve`
545
- - `securenow_blocklist_pending_reject`
546
- - `securenow_blocklist_pending_bulk_approve`
547
- - `securenow_blocklist_pending_bulk_reject`
548
- - prompt: `cleanup_legacy_pending_blocks`
549
-
550
- ### Rate Limits - Soft Remediation
225
+ ### Create an alert rule from English and test it
551
226
 
552
227
  ```bash
553
- securenow ratelimit list --env production
554
- securenow ratelimit add 203.0.113.42 --route /api/login --limit 2 --window 1m --duration 24h
555
- securenow ratelimit parse "rate limit 203.0.113.42 on POST /api/login to 2 attempts per minute"
556
- securenow ratelimit from-text "rate limit /api/login for this IP to 2 attempts per minute for 24h" --yes
557
- securenow ratelimit test 203.0.113.42 --path /api/login --method POST
228
+ npx securenow alerts rules from-text "one IP failing 10 sign-ins in 5 minutes" --app <key> --draft-only # see the checked draft
229
+ npx securenow alerts rules from-text "one IP failing 10 sign-ins in 5 minutes" --app <key> # create it (asks)
230
+ npx securenow alerts rules test <rule-id> --wait # dry run on real data (default)
231
+ npx securenow alerts rules test <rule-id> --mode live --wait # inserts 6 synthetic attack requests first
232
+ npx securenow alerts rules run <rule-id> # run a periodic rule now [--no-wait]
233
+ npx securenow alerts rules runs <rule-id> --since 1h # each run: notified, no_rows, throttled, suppressed_blocked, ...
234
+ npx securenow alerts rules why <rule-id> --ip 203.0.113.50 --since 2h
235
+ npx securenow alerts rules change <rule-id> "raise the threshold to 20" # rewrite from English as a new version
236
+ npx securenow alerts rules quality <rule-id> # 14-day precision, wrong alerts and why
237
+ npx securenow alerts rules promote <rule-id> --yes # a person: test → prod (demote: back to test)
238
+ npx securenow alerts rules request-promotion <rule-id> --reason "..." --evidence @evidence.md # what an agent does instead
558
239
  ```
559
240
 
560
- MCP parity:
241
+ `from-text` picks an instant rule (each request, as it arrives) or a periodic one (SQL on a schedule); force it with `--instant` or `--periodic`, and set `--name`, `--severity`, `--env` (default: every environment). New rules start in **test mode**: they alert, and no instant block, automation or agent acts on them. With `--yes`, `from-text` refuses a rule whose check matches your data right now (it would alert at once, usually a bad baseline); `--yes --force` creates it anyway when the rows are a real attack. A person approves promotion requests with `npx securenow human promotions` and `human approve-promotion <request-id> --yes` (or `deny-promotion <request-id> --reason "..."`); API keys and MCP tokens can ask but not decide.
561
242
 
562
- - `securenow_rate_limit_parse`
563
- - `securenow_rate_limit_create_from_text`
243
+ When a rule stays quiet, `alerts rules runs` names the outcome of every run (`notified`, `no_rows`, `throttled`, `suppressed_blocked`, `suppressed_trusted`, `no_ip`, `storm`, `auto_resolved`, `skipped`, `error`) and `alerts rules why` ends in a verdict such as "Detected at 15:06:31 but not notified: 203.0.113.50 was already blocked". The throttle sends one alert per rule and address per window (default 15 minutes).
564
244
 
565
- ### Automation Rules
245
+ **SQL rules.** Read the contract first and check the fields the app really sends:
566
246
 
567
247
  ```bash
568
- securenow automation # list blocklist automation rules
569
- securenow automation defaults --yes # ensure built-in default risk-score rules
570
- securenow automation show <id>
571
- securenow automation dry-run <id> --limit 500
572
- securenow automation execute <id> --yes
248
+ npx securenow telemetry schema --json # offline: tables, timestamps, tenant scope, event mapping, templates [logs|traces|events]
249
+ npx securenow telemetry fields --app <key> --env production --since 24h --json # names, types, counts, event types (never values)
250
+ npx securenow alerts rules validate --sql @rule.sql --app <key> # dry-run before creating anything
251
+ npx securenow alerts rules create --name "Auth: magic-link brute force" --sql @rule.sql --apps <key> \
252
+ --severity high --schedule "*/15 * * * *" --nlp "single IP flooding /api/auth/signin"
253
+ npx securenow alerts rules create --name "Secrets probe" --apps <key> \
254
+ --conditions '[{"field":"path","operator":"contains","value":"/.env"}]' # an instant rule, no SQL
255
+ npx securenow alerts rules set-sql <rule-id> --sql @rule.sql # replace a custom rule's SQL
256
+ npx securenow alerts rules delete <rule-id> --yes
573
257
  ```
574
258
 
575
- Built-in default automation is enabled by default for all apps/environments and
576
- uses the canonical `riskScore`: 95-100 blocks for 7 days, 90-94 blocks for 72h,
577
- 85-89 blocks for 24h, and scores below 85 stay in investigation/review unless
578
- the customer adds a stricter custom rule. Raw SecureNow IPDB confidence remains
579
- supporting evidence, not the primary automation score.
259
+ Scope app keys with `__USER_APP_KEYS__` (logs: `resources_string['service.name'] IN (__USER_APP_KEYS__)`; traces: `` `resource_string_service$$name` IN (__USER_APP_KEYS__) ``) and return an `ip` column (SQL without one is refused unless `--allow-no-ip`: it would never notify). `create` dry-runs the query and deletes the rule again if it fails (`--no-validate` skips that); a new periodic rule runs once right away. Label a rule `READY` only when its fields were observed and `validate` succeeded, otherwise `NEEDS_INSTRUMENTATION` (the app must send a `track()` event), `NEEDS_BASELINE` or `NEEDS_CUSTOMER_DECISION`. Details: `node_modules/securenow/TELEMETRY-SQL.md`. System rules are tuned with `alerts rules tune-query <id> --sql @candidate.sql --reason "..." --apply-globally --yes` (admin), after `alerts rules dry-run-query <id> --sql @candidate.sql --app <key> --wait`.
580
260
 
581
- ### Allowlist — Restrict to Known IPs
261
+ MCP: `securenow_alert_rule_draft`, `securenow_alert_rule_create`, `securenow_alert_rule_test`, `securenow_alert_rule_runs`, `securenow_alert_rule_run_now`, `securenow_alert_rule_explain`, `securenow_alert_rule_request_promotion`, `securenow_alert_rule_candidate_test`, `securenow_alert_rule_instant_update`, `securenow_alert_rule_quality`.
582
262
 
583
- Dangerous production mode: IP Allowlist is deny-by-default. Once any active
584
- allowlist entry exists for an app/environment, only listed IPs can reach it and
585
- all other IPs are blocked. Do not use allowlist to mark an IP trusted, suppress
586
- false positives, or clean up investigations. Use `securenow trusted add` for
587
- known-safe monitors, office/VPN traffic, or trusted bypass cases.
263
+ ### Starter protection
588
264
 
589
265
  ```bash
590
- securenow allowlist # list allowed IPs
591
- securenow allowlist list
592
- securenow allowlist add <ip> --app <key> --env local --label "Office" --reason "Corporate VPN"
593
- securenow allowlist remove <id>
594
- securenow allowlist stats
266
+ npx securenow protect # the current app: mode, rules, alerts, ready to block? [--all] [--rules] [--json]
267
+ npx securenow protect enable --app <key> # monitor mode: rules alert, nothing is blocked
268
+ npx securenow protect block --app <key> # a signed-in person only: block-ready rules block for 6 hours
269
+ npx securenow protect monitor --app <key> # back to alerts only
270
+ npx securenow protect disable --app <key> # off until someone turns it on [--yes]
271
+ npx securenow protect learn --app <key> # detection learning now (it also runs hourly)
595
272
  ```
596
273
 
597
- ### Trusted IPs
274
+ Every app gets about 20 starter rules in monitor mode when it is created or first sends traffic. `protect block` is refused for API keys, MCP connectors and the agent, and while more than 20% of the app's traffic arrives through a proxy the SDK doesn't trust (fix with `config.networking.trustedProxies`). Detection learning labels each alert (right, wrong, unclear), keeps each rule's 14-day precision per app and adds a safe exclusion only when it excuses the wrong alerts and none of the right ones. MCP: `securenow_protection_status`, `securenow_protection_learn`.
598
275
 
599
- Trusted IPs are the safe bypass/suppression mechanism for known infrastructure
600
- and do not enable deny-by-default allowlist mode.
276
+ ### Let the autonomous agent work the queues
601
277
 
602
278
  ```bash
603
- securenow trusted # list trusted IPs
604
- securenow trusted list
605
- securenow trusted add <ip> [--label "CloudFlare edge"]
606
- securenow trusted remove <id>
279
+ npx securenow agent # status: on/off, mode per action, what it did, agreement with people
280
+ npx securenow agent on # or: off (also revokes a run in progress)
281
+ npx securenow agent mode all shadow # record what it would do, change nothing
282
+ npx securenow agent mode block_ip act --yes # then hand over one kind of action at a time
283
+ npx securenow agent activity --mode applied # every decision with its evidence [--action block_ip] [--page N]
284
+ npx securenow agent undo <id> --yes # unblock, reopen the row, or roll the rule back
285
+ npx securenow agent runs # patrols: status, tool calls, cost, summary
286
+ npx securenow agent run # patrol now
607
287
  ```
608
288
 
609
- ---
610
-
611
- ### False Positive Management
289
+ Actions: `close_row`, `false_positive`, `block_ip`, `review_close`, `keep_rule`, `rule_change`, `case_action` (or `all`); modes: `off`, `shadow`, `suggest`, `act`. The API enforces the limits whatever the model decides: the agent's token reaches only the queue, verification and rule-tuning routes, it can't block without a verification that allows it, and an action type that people keep undoing drops back to shadow by itself.
612
290
 
613
- The `fp` command manages exclusion rules that prevent known-safe traffic from triggering security alerts.
291
+ Your own harness instead: `npx securenow agent prompts` lists the runbooks; `npx securenow agent prompt patrol` prints one (`patrol`, `alert-review`, `human-queue`; `--confirm-writes` for the variant that may act, `--search`, `--limit`, `--max-alert-review-groups`, `--max-human-rows`):
614
292
 
615
293
  ```bash
616
- securenow fp # list all exclusion rules
617
- securenow fp list
618
- securenow fp show <id> # rule details
619
- securenow fp delete <id> [--yes]
620
-
621
- # Create exclusion rules
622
- securenow fp create \
623
- --conditions '[{"field":"path","operator":"starts_with","value":"/api/health"}]' \
624
- --match-mode all \
625
- --rule-scope any_rule \
626
- --reason "Health check endpoint"
627
-
628
- # Shorthand safe-value presets
629
- securenow fp create \
630
- --path /api/events \
631
- --method POST \
632
- --path-safe standard \
633
- --ua-safe standard \
634
- --headers-safe standard \
635
- --query-keys page,limit \
636
- --headers-keys host,content-type \
637
- --reason "Event webhook"
638
-
639
- # Edit an existing rule
640
- securenow fp edit <id> [--active true|false] [--conditions '[...]']
641
-
642
- # Test conditions against a request body
643
- securenow fp test-body '{"user":"admin"}' --conditions '[{"field":"request_body","operator":"contains","value":"\"user\":\"admin\""}]'
644
- securenow fp test-body @request.json --conditions '[...]'
645
-
646
- # Dry-run conditions against the last 3 days of live traces
647
- securenow fp dry-run --conditions '[{"field":"path","operator":"starts_with","value":"/api/webhook"}]'
648
-
649
- # AI-generate exclusion conditions from a description
650
- securenow fp ai-fill --description "Stripe webhook POST to /api/stripe/webhook" \
651
- --context '{"method":"POST","path":"/api/stripe/webhook"}'
652
-
653
- # Mark an IP as false positive on a specific notification
654
- securenow fp mark <notification-id> <ip> \
655
- [--conditions '[...]'] \
656
- [--reason "Known partner IP"] \
657
- [--rule-scope this_rule|specific_rules|all_existing|any_rule] \
658
- [--target-rules id1,id2]
294
+ claude -p "$(npx -y securenow@latest agent prompt patrol --search my-app --confirm-writes)" --allowedTools "mcp__securenow"
659
295
  ```
660
296
 
661
- **Condition shape:** `{"field": ..., "operator": ..., "value": ...}` (the key is `operator`, not `op`).
662
-
663
- **Condition fields:** `path`, `method`, `user_agent`, `status_code`, `request_body`, `request_headers`.
664
-
665
- **Operators:** `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex`, `in`, `not_in`, plus structural checks — `path_safe_values`, `query_safe_values`, `query_has_only_keys`, `ua_safe_values`, `headers_safe_values`, `headers_has_only_keys`, `body_safe_values`, `body_is_format`, `body_has_only_fields`, `json_has_only_keys`, `json_safe_values`, `json_valid`, `json_max_depth`, `json_schema_match`. Prefer the `*_safe_values` checks over `contains`: they only exclude a request whose value is clean.
666
-
667
- **Match modes:** `all` (AND logic), `any` (OR logic).
668
-
669
- **Rule scopes:** `this_rule` (single alert rule — only via `fp mark`; for a rule-only exclusion without a notification use `securenow alerts rules exclusions <rule-id> add --conditions '[...]'`), `specific_rules` (comma-separated IDs via `--target-rules`), `all_existing` (all current rules), `any_rule` (all current and future rules; the `fp create` default).
670
-
671
- **Safe-value presets:** `standard` or `strict`. These auto-generate conditions that whitelist common safe patterns for paths, query strings, user-agents, and headers.
672
-
673
- ---
674
-
675
- ### Instances
297
+ ### Set up MCP
676
298
 
677
299
  ```bash
678
- securenow instances # list ClickHouse instances
679
- securenow instances list
680
- securenow instances test <id> # test connection
300
+ npx securenow login # the local server uses this auth
301
+ claude mcp add securenow -- npx securenow mcp # Claude Code
302
+ codex mcp add securenow -- npx securenow mcp # Codex
303
+ claude mcp add --transport http securenow https://api.securenow.ai/mcp # hosted server, OAuth sign-in
681
304
  ```
682
305
 
683
- ---
684
-
685
- ### Telemetry — Emit Logs and Spans From the Shell
686
-
687
- 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.
688
-
689
- ```bash
690
- # Send a structured log record to the OTLP collector
691
- securenow log send "Deployment completed" --level info --attrs version=1.2.3,service=api
692
- securenow log send "Backup failed" --level error --attrs host=db-01
306
+ Cursor, `.cursor/mcp.json`:
693
307
 
694
- # Emit a test span to verify the collector accepts OTLP traffic
695
- securenow test-span
696
- securenow test-span "ci.smoke-test" # custom span name
308
+ ```json
309
+ { "mcpServers": { "securenow": { "command": "npx", "args": ["securenow", "mcp"] } } }
697
310
  ```
698
311
 
699
- Both commands use the default SecureNow ingestion gateway plus optional advanced `config.otel.*` overrides from `.securenow/runtime.json` or legacy runtime credentials, and return non-zero on HTTP errors so CI/cron can detect failures.
312
+ - The local stdio server is `npx securenow mcp` (also the `securenow-mcp` bin). Admin tools use `.securenow/admin.json`; app-scoped reads can use `.securenow/runtime.json`. Warm the package once (`npx -y securenow@latest --version`) so the first connection doesn't time out on the download, and give the client a longer connect timeout (Claude Code: `MCP_TIMEOUT=120000`).
313
+ - The hosted server `https://api.securenow.ai/mcp` needs no install and always has the current tools: OAuth sign-in with a SecureNow account (or `Authorization: Bearer <API key>`). The SecureNow plugin for Claude Code and Codex bundles it with skills (`/plugin marketplace add securenow-ai/claude-plugin`, then `/plugin install securenow@securenow`).
314
+ - Read tools are annotated read-only; every write tool refuses to run without `confirm: true` and a non-empty `reason`. Resources: the installed SKILL-API.md, SKILL-CLI.md and NPM_README.md, the project config, the tool catalog. Prompts: `secure_project_setup`, `verify_firewall_default_on`, `investigate_ip`, `investigate_human_action_row`, `work_human_actions`, `work_alert_review`, `soc_patrol`, `agent_patrol`, `cleanup_legacy_pending_blocks`, `configure_default_automation`.
315
+ - Profiles serve only the tools one job needs (`npx securenow mcp --profile <name>` or `SECURENOW_MCP_PROFILE=<name>`). Every profile keeps `securenow_auth_status`, `securenow_apps_list` and `securenow_apps_info`; a tool outside the profile returns an error naming it.
700
316
 
701
- ### Utilities — Redaction, CIDR, Diagnostics
317
+ | Profile | Tools | Contents |
318
+ |---|---|---|
319
+ | `all` (default) | 125 | everything |
320
+ | `investigator` | 65 | every read-only tool, plus forensics question/chat/confirm |
321
+ | `detection` | 53 | alert rules (draft, create, test, run, runs, explain, versions, exclusions, request promotion...), traces, logs, forensics, IP intelligence, notifications |
322
+ | `responder` | 82 | notifications, both queues, blocklist, allowlist, trusted IPs, rate limits, challenges, automations, firewall, IP intelligence, plus rule get/runs/explain and traces/logs |
702
323
 
703
- The `redact` command also hides OTP credentials named `code`, `oneTimeCode`, `verificationCode`, and `passcode` (case/separator variants included), while preserving `status_code` and `error_code`. SDK capture uses the same defaults.
324
+ ```bash
325
+ claude mcp add securenow-investigator -- npx -y securenow@latest mcp --profile investigator
326
+ ```
704
327
 
705
- These mirror the SDK exports (`redactSensitiveData`, `createMatcher`, `parseCidr`) so agents can validate behavior without writing Node.
328
+ ### Emit an event, a log or a span from a script
706
329
 
707
330
  ```bash
708
- # Redact sensitive fields (password, token, card, ssn, etc.)
709
- securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
710
- securenow redact @request.json --fields internal_id,sessionHash
711
-
712
- # CIDR operations — match an IP against a list, or parse a range
713
- securenow cidr match 10.0.0.5 10.0.0.0/24,192.168.0.0/16
714
- securenow cidr parse 10.0.0.0/24 # network, broadcast, mask, size
715
-
716
- # Show resolved config (service name, endpoints, credentials, firewall layers)
717
- securenow env # human-readable
718
- securenow env --json # pipe to jq
719
-
720
- # End-to-end diagnostic: probe OTLP + API endpoints, check config
721
- securenow doctor # exits 0 if healthy, 1 otherwise
722
- securenow doctor --json
331
+ npx securenow event send auth.login.failure --user u_123 --ip 203.0.113.10 --attrs reason=bad_password,method=password
332
+ npx securenow event send auth.otp.send.result --session s_1 --level warn --attrs status=failed --env staging
333
+ npx securenow log send "Backup failed" --level error --attrs host=db-01 # --level trace|debug|info|warn|error|fatal
334
+ npx securenow test-span ci.smoke-test --env production
723
335
  ```
724
336
 
725
- The `redact` command accepts a JSON string or `@path/to/file.json` and layers your `--fields` flag on top of `DEFAULT_SENSITIVE_FIELDS`. Exit code from `cidr match` is `0` if the IP matches the list, `2` otherwise — scriptable.
337
+ `event send` posts one custom event to the `/v1/events` ingest with the runtime credentials (`--user`, `--session`, `--ip`, `--user-agent`, `--level`, `--attrs k=v,k=v`, `--env`): the same event `track()` sends from Node, for cron jobs, CI or non-JS services. `log send` and `test-span` exit non-zero on HTTP errors, so CI can catch failures.
726
338
 
727
- ---
728
-
729
- ## Workflow Examples for Agentic AI
730
-
731
- ### Investigate a Security Alert
339
+ ### Diagnose a setup
732
340
 
733
341
  ```bash
734
- securenow notifications list --limit 5 --json
735
- # IMPORTANT: parse the ipInvestigations array for each notification — see statuses below
736
- securenow ip <attacker-ip> --json
737
- securenow ip traces <attacker-ip> --json
738
- securenow traces show <trace-id> --json
739
- securenow traces analyze <trace-id> --json
740
- # Decision: block the IP
741
- securenow blocklist add <attacker-ip> --reason "Automated: SQL injection detected"
342
+ npx securenow env # resolved service name, environment, endpoints, firewall (no secrets) [--json]
343
+ npx securenow doctor # probes traces, logs, API and firewall sync; exit 1 on failure [--json]
344
+ npx securenow test-span # "Span accepted (HTTP 200)"
345
+ npx securenow firewall status --env production # the app's firewall state, layers, blocklist
346
+ npx securenow firewall apps # every app with its firewall on/off per environment
347
+ npx securenow firewall enable --app <key> --env production # or: disable
348
+ npx securenow config path # config and credential files in use
742
349
  ```
743
350
 
744
- #### Notification IP Investigation Statuses
745
-
746
- Each notification contains an `ipInvestigations` array. **Every IP has its own `status` field** that you MUST check before taking action or raising flags. Do not rely only on top-level notification fields — always cross-reference each IP's individual investigation status.
747
-
748
- | Status | Meaning | Action |
749
- |--------|---------|--------|
750
- | `open` | Still needs review — no determination yet | Investigate: run `securenow ip <ip>`, check traces, decide to block or dismiss |
751
- | `false_positive` | Trusted/dismissed by exclusion rule, verified bot, or trusted IP list | **Skip** — greyed out in UI, no action needed |
752
- | `blocked` | Already on the user's blocklist | **Skip** — already handled |
753
- | `clean` | Pipeline analyzed and cleared as benign | **Skip** — verified safe |
754
-
755
- **Critical workflow rule:** When investigating notifications, iterate `ipInvestigations[]` and **only flag or act on IPs with `status: "open"`**. IPs marked `false_positive`, `blocked`, or `clean` have already been triaged — do not re-flag them.
756
-
757
- ### Triage and Suppress a False Positive
351
+ ### Threat-modeling prompts
758
352
 
759
353
  ```bash
760
- securenow notifications list --json
761
- # Identify a false positive notification
762
- securenow fp ai-fill --description "Stripe webhook calls to /api/stripe/webhook"
763
- # Review the suggested conditions, then create the rule
764
- securenow fp create --conditions '<ai-suggested-conditions>' --rule-scope any_rule --reason "Stripe webhook"
765
- # Or directly mark the notification's IP as FP
766
- securenow fp mark <notification-id> <ip> --rule-scope this_rule --reason "Known Stripe IP"
354
+ npx securenow threat-model --list
355
+ npx securenow threat-model authentication --out threat-prompt.md # then: "Follow threat-prompt.md"
356
+ npx securenow threat-model authentication | claude -p
767
357
  ```
768
358
 
769
- ### Onboard a New Application
359
+ Categories include `authentication`, `authorization`, `injection`, `api-security`, `rate-limits-and-abuse`, `webhooks`, `ai-llm-features` and more (`--list`).
770
360
 
771
- ```bash
772
- securenow apps create my-new-app --hosts api.example.com,app.example.com
773
- securenow apps default my-new-app
774
- securenow login
775
- securenow init
776
- securenow run src/index.js
777
- securenow status --json
778
- ```
779
-
780
- ### Security Posture Check
361
+ ### Utilities
781
362
 
782
363
  ```bash
783
- securenow status --json
784
- securenow firewall status --json
785
- securenow blocklist stats --json
786
- securenow forensics "summarize all attacks in the last 7 days"
364
+ npx securenow redact '{"user":"alice","password":"s3cret","otpCode":"123456","status_code":200}' # [--fields f1,f2] or @file.json
365
+ npx securenow cidr match 10.0.0.5 10.0.0.0/8,192.168.1.0/24 # exit 0 = match, 2 = no match
366
+ npx securenow cidr parse 10.0.0.0/24 # network, broadcast, mask, size
787
367
  ```
788
368
 
789
- ### Discover Attack Surface
790
-
791
- ```bash
792
- securenow apps discover --domain example.com
793
- securenow apps scan --yes
794
- ```
369
+ `redact` uses the same rules as SDK capture (field names, OTP names matched exactly so `status_code` stays, secret-shaped values).
795
370
 
796
- ---
371
+ ## Command reference
797
372
 
798
- ## Output Parsing
373
+ `securenow help <command>` is authoritative. Subcommand flags not listed by `help` are shown in its usage lines below.
799
374
 
800
- All commands support `--json` for structured output. When piping to other tools or parsing programmatically, always use `--json`. Table output is the default for human readability.
375
+ | Command | Subcommands | Key flags |
376
+ |---|---|---|
377
+ | `run` | `run [node-flags] [--firewall-only] <script> [args]`; `securenow <script.js>` is a shorthand | `--firewall-only`, Node flags pass through (`--watch`, `--inspect`) |
378
+ | `login` | | `--token`, `--global` |
379
+ | `logout` | | `--global` |
380
+ | `whoami` | | |
381
+ | `admin` | `login` (default), `logout` | `--token`, `--global` |
382
+ | `app` | `connect` (default) | `--global` |
383
+ | `credentials` | `runtime` (default) | `--env` (default `production`), `--out`, `--stdout`, `--format env` |
384
+ | `api-key` | `create`, `set <key>`, `clear`, `show` (default) | `create`: `--name`, `--app <key-or-id>`, `--preset runtime_app`, `--global` |
385
+ | `init` | | `--env` (default `local`), `--key snk_live_...`, `--ts` (`--typescript`), `--js` (`--javascript`), `--src`/`--root` (Next.js file folder; default `src/` when `app/` or `pages/` is there), `--force` (replace the instrumentation file, keeping a `.bak`) |
386
+ | `apps` | `list` (default), `create <name>`, `info <id>`, `delete <id>`, `default <key>`, `discover [appId]`, `scan` | `create`: `--hosts h1,h2`, `--instance <id>`; `delete`: `--force`; `discover`: `--domain`; `scan`: `--yes` |
387
+ | `status` | | `--app`, `--env local\|production\|all` |
388
+ | `today` | | `--since 1h..7d` (default 24h), `--attackers <n>` (default 8), `--json` |
389
+ | `attacker` | `attacker <ip>` | `--json` |
390
+ | `traces` | `list` (default), `show <traceId>`, `analyze <traceId>` | `--app`, `--env`, `--limit`, `--start`, `--end` |
391
+ | `logs` | `list` (default), `trace <traceId>`, `errors` | `--app`, `--env`, `--since 30m\|6h\|2d`, `--minutes`, `--level trace\|debug\|info\|warn\|error\|fatal`, `--limit`, `--start`, `--end`; `errors`: `--warn` |
392
+ | `analytics` | | `--app` |
393
+ | `protect` | `status` (default), `enable`, `disable`, `block`, `monitor`, `learn` | `--app`, `--all`, `--rules`, `--yes` |
394
+ | `agent` | `status` (default), `on`, `off`, `mode <action> <mode>`, `activity`, `undo <id>`, `runs`, `run`, `verify <ip>`, `prompt <name>`, `prompts` | `--action`, `--mode`, `--page`, `--notification`, `--yes`, `--confirm-writes`, `--limit`, `--search`, `--max-alert-review-groups`, `--max-human-rows` |
395
+ | `human` | `list` (default), `show`, `block`, `fp`, `close`, `report`, `action`, `prompt`, `work`, `promotions`, `approve-promotion`, `deny-promotion` | `--reason`, `--outcome`, `--summary`, `--evidence`, `--yes`, `--search`, `--limit`, `--page`, `--status`, `--note`, `--conditions`, `--rule-scope`, `--create-exclusion`, `--missing-proof` |
396
+ | `review` | `list` (default), `show`, `keep`, `close`, `dismiss`, `group` | `--outcome`, `--reason`, `--summary`, `--preview`, `--allow-mixed`, `--search`, `--yes` |
397
+ | `notifications` | `list` (default), `read <id>`, `read-all`, `unread` | `list`: `--limit`, `--page`, `--search`, `--severity` |
398
+ | `alerts` | `rules` (default), `channels`, `history` | `history`: `--limit` |
399
+ | `alerts rules` | `list` (default), `from-text`, `change`, `create`, `show`, `update`, `duplicate`, `promote`, `demote`, `set-sql`, `validate`, `dry-run-query`, `tune-query`, `delete`, `test`, `run`, `runs`, `why`, `quality`, `exclusions <id> [list\|add\|delete]`, `versions`, `version`, `rollback`, `request-promotion` | `--app`, `--apps`, `--applications-all`, `--env`, `--instant`, `--periodic`, `--draft-only`, `--name`, `--sql`, `--conditions`, `--severity`, `--schedule`, `--throttle-minutes`, `--no-throttle`, `--channel`, `--allow-no-ip`, `--no-validate`, `--mode dry_run\|live` (test) / `test\|prod` (list, update), `--status`, `--wait`, `--ip`, `--since`, `--limit`, `--reason`, `--evidence`, `--apply-globally`, `--yes`, `--force` |
400
+ | `fp` | `list` (default), `show`, `create`, `edit`, `delete`, `test-body`, `dry-run`, `ai-fill`, `mark <notification-id> <ip>` | `--conditions`, `--match-mode`, `--rule-scope`, `--target-rules`, `--path`, `--method`, `--path-safe`, `--query-safe`, `--query-keys`, `--ua-safe`, `--headers-safe`, `--headers-keys`, `--reason`, `--description`, `--context`, `--active` |
401
+ | `ip` | `ip <address>` (lookup), `lookup`, `traces <ip>` | `traces`: `--app`, `--env` |
402
+ | `forensics` | `forensics "<question>"`, `query`, `chat`, `conversations [<id>]`, `library` | `--app`, `--env`, `chat`: `--conversation <id>`, `--yes` |
403
+ | `firewall` | `status` (default), `apps`, `enable`, `disable`, `test-ip <ip>`, `hits` | `--app`, `--env`; `test-ip`: `--path`, `--method`; `hits`: `--since`, `--action blocked\|rate_limited\|captcha_challenged`, `--source`, `--rule`, `--ip`, `--limit` |
404
+ | `automation` | `list` (default), `defaults`, `show`, `create`, `update`, `dry-run`, `execute`, `delete` | `--name`, `--conditions`, `--actions`, `--logic`, `--status`, `--body`, `--file`, `--app`, `--applications-all`, `--env`, `--environments-all`, `--limit`, `--sample-limit`, `--yes`; `defaults`: `--force-enable` |
405
+ | `ratelimit` (alias `rate-limit`) | `list` (default), `add [ip]`, `parse`, `from-text`, `show`, `test <ip>`, `enable`, `disable`, `remove` | `--route`/`--path`, `--mode`, `--method`, `--limit`, `--window`, `--duration`, `--key-by`, `--header`, `--cookie`, `--key-name`, `--app`, `--env`, `--reason`, `--text` |
406
+ | `challenge` | `list` (default), `add [ip]`, `show`, `test <ip>`, `enable`, `disable`, `remove` | `--route`/`--path`, `--mode`, `--method`, `--difficulty` (4–28, default 14), `--clearance` (default 30m), `--duration`, `--escalate-to-block`, `--fail-threshold` (10), `--block-ttl-hours` (24), `--app`, `--env`, `--reason` |
407
+ | `blocklist` | `list` (default), `add <ip>`, `unblock <id>`, `remove <id>`, `approve <id>`, `reject <id>`, `stats` | `--route`/`--path`/`--pattern`, `--mode`/`--path-mode`, `--method`, `--duration`, `--app`, `--env`, `--reason`, `--status`, `--approval-status`, `--search`, `--view`, `--page`, `--limit` |
408
+ | `revoke` | `list` (default), `session <id>`, `user <id>`, `restore <id>` | `--reason`, `--duration` (default 7d), `--app`, `--env`, `--type`, `--status` |
409
+ | `allowlist` | `list` (default), `add <ip>`, `remove <id>`, `stats` | `--app` (comma-separated), `--env`, `--label`, `--reason`, `--page`, `--limit` |
410
+ | `trusted` | `list` (default), `add <ip>`, `remove <id>` | `--app`, `--env`, `--label` |
411
+ | `telemetry` | `schema` (default) `[all\|logs\|traces\|events]`, `fields` | `--app`, `--env`, `--since` (max 30d), `--source`, `--limit` (1–500), `--json` |
412
+ | `log` | `send <message>` (default) | `--level`, `--attrs`, `--env` |
413
+ | `event` | `send <type>` (default) | `--user`, `--session`, `--ip`, `--user-agent`, `--level`, `--attrs`, `--env` |
414
+ | `test-span` | `test-span [<name>]` | `--env` |
415
+ | `threat-model` | `threat-model <category>` | `--list`, `--out` |
416
+ | `redact` | `redact '<json>'` or `@file` | `--fields` |
417
+ | `cidr` | `match <ip> <cidrs>`, `parse <cidr>` | |
418
+ | `doctor` | | `--json` |
419
+ | `env` | | `--json` |
420
+ | `mcp` | | `--profile all\|investigator\|detection\|responder` |
421
+ | `instances` | `list` (default), `test <id>` | |
422
+ | `config` | `get [key]`, `set <key> <value>`, `path` | keys: `apiUrl`, `appUrl`, `defaultApp` |
423
+ | `version` | | |
424
+
425
+ ## Flags, environment and errors
801
426
 
802
- ## Error Handling
427
+ | Flag | Short | Effect |
428
+ |---|---|---|
429
+ | `--json` | `-j` | JSON output for scripts and agents |
430
+ | `--help` | | Help for the command |
431
+ | `--yes` | `-y` | Confirm a write without the prompt |
432
+ | `--force` | `-f` | Same as `--yes` on most write commands |
803
433
 
804
- | Exit code / Error | Meaning | Recovery |
805
- |------------------|---------|----------|
806
- | `Session expired` | Admin JWT expired | `securenow admin login` (or `admin login --global` for shared admin auth) |
807
- | `Not logged in` | No admin token found | `securenow admin login`; runtime `.securenow/runtime.json` is unrelated |
808
- | `Access denied (403)` | Insufficient permissions | Check the user's role in the account/organization |
809
- | `HTTP 402` | The 14-day trial ended and the account has no active subscription (everything, including the firewall, is paused) | Subscribe in the dashboard: $99 per app per month, every feature included |
810
- | `Cannot connect` | API unreachable | Check `securenow config get apiUrl` or network |
811
- | `Unknown command` | Typo or unrecognized command | `securenow help` |
434
+ Environment variables the CLI reads: `NO_COLOR` (plain output), `SECURENOW_MCP_PROFILE` (MCP tool profile), `SECURENOW_CREDENTIALS` (runtime credentials, used by `env`, `doctor`, `test-span`, `log send`, `event send`). The admin token always comes from `admin.json` (`npx securenow admin login`).
812
435
 
813
- SecureNow-specific environment overrides are intentionally unsupported for troubleshooting; use explicit CLI config files and normal terminal logs.
436
+ | Error | Meaning | Recovery |
437
+ |---|---|---|
438
+ | `Session expired` | The admin token expired | `npx securenow admin login` |
439
+ | `Not logged in` | No admin token found | `npx securenow admin login` (the runtime file is a separate lane) |
440
+ | `Access denied (403)` | The user's role or the key's scopes don't allow it | Check the role in the organization or the API key's scopes |
441
+ | `HTTP 402` | The 14-day trial ended and the account has no subscription; everything, the firewall included, is paused | Subscribe in the dashboard ($99 per app per month, every feature) |
442
+ | `Cannot connect` | API unreachable | Network, or `npx securenow config get apiUrl` |
443
+ | `Unknown command` | Typo | `npx securenow help` |