# SecureNow CLI — Agent Skill

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.

## Installation

```bash
# Install globally (recommended for CLI usage)
npm install -g securenow

# Or install per-project and use via npx
npm install securenow
npx securenow <command>
```

**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.

### Authenticate

```bash
securenow login             # opens browser OAuth + app picker; writes ./.securenow/credentials.json (project-local by default)
securenow login --global    # save to ~/.securenow/ instead (shared across projects)
securenow login --token <JWT>   # headless / CI login (get token from dashboard Settings)
securenow whoami            # verify session (shows email, app, auth source)
```

**Zero-config flow (v7+):** the browser step lets the user pick (or create) an app. The CLI stores the app's **key (UUID)**, **name**, and **instance URL** in `.securenow/credentials.json`. The SDK reads this file at boot and sends traces/logs to the right app bucket — **no env vars required for local dev**.

**Firewall onboarding (v7.1+):** after picking the app, `securenow login` asks "Enable the Firewall?" in the browser. If you accept, the dashboard mints an API key with `firewall:read + blocklist:read + allowlist:read` scopes and the CLI writes it into `.securenow/credentials.json` automatically — no `SECURENOW_API_KEY` env var needed. To add or rotate a key later without re-running login, use `securenow api-key set snk_live_...` (see [API Key Management](#api-key-management) below).

Credentials resolve in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.

The **firewall API key** resolves in a slightly different order: `SECURENOW_API_KEY` env var (only if it starts with `snk_live_`) → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`. An env var that isn't the `snk_live_` format is ignored, so the file can still win.

For CI / Docker / production, set env vars directly (always win over the file): `SECURENOW_APPID=<uuid>`, `SECURENOW_INSTANCE=<url>`, `SECURENOW_API_KEY=snk_live_<...>`.

### Integrate With Your App

The CLI can also instrument any Node.js app at launch — no code changes:

```bash
# Express, Fastify, NestJS, Koa, Hapi, raw Node.js
securenow run src/index.js

# Or via the Node preload flag
node -r securenow/register src/index.js
```

For Next.js, run the interactive scaffolding:

```bash
securenow init --key snk_live_...
```

This auto-detects your framework and creates the necessary `instrumentation.ts`, `next.config.js` changes, and writes your API key to `.env.local`.

### Install This Skill in Cursor

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.

## Configuration

Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-project):

| File | Content |
|------|---------|
| `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
| `~/.securenow/credentials.json` | `token`, `email`, `expiresAt` (global) |
| `.securenow/credentials.json` | `token`, `email`, `expiresAt` (project-local, use `login --local`) |

**Credential resolution order:** `SECURENOW_TOKEN` env var → `.securenow/credentials.json` (project) → `~/.securenow/credentials.json` (global).

**Firewall API key resolution (v7.1+):** `SECURENOW_API_KEY` env var (only honored if it starts with `snk_live_`) → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`. Use `securenow api-key set` to write the key to the credentials file without touching env vars.

```bash
securenow config set apiUrl https://api.securenow.ai
securenow config set defaultApp my-app-key
securenow config get                     # show all
securenow config get defaultApp          # show one
securenow config path                    # print file paths + active auth source
```

Environment overrides: `SECURENOW_TOKEN` (JWT), `SECURENOW_API_URL`, `SECURENOW_APP_URL`, `SECURENOW_APP` (default app key).

## Global Flags

| Flag | Short | Effect |
|------|-------|--------|
| `--json` | `-j` | JSON output (pipe-friendly) |
| `--help` | | Show help for any command |
| `--verbose` | `-v` | Verbose output |
| `--force` | `-f` | Skip confirmations |
| `--yes` | `-y` | Auto-confirm prompts |

Debug mode: `SECURENOW_DEBUG=1 securenow <cmd>` prints stack traces on errors.

---

## Command Reference

### Run — Instrument Any Node.js App

```bash
securenow run <script>                       # auto-detect CJS/ESM, inject OTel preload
securenow run --watch src/index.js           # pass Node flags through
securenow run --inspect src/server.js --port 3000
securenow run --firewall-only app.js         # preload firewall only (no tracing overhead)
securenow src/index.js                       # shorthand — auto-detected as "run"
```

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 (dotenv + firewall, no OpenTelemetry).

### Authentication

```bash
securenow login                   # browser-based OAuth (stores in global ~/.securenow/)
securenow login --token <JWT>     # headless / CI login
securenow login --local           # save credentials to project .securenow/ (per-project session)
securenow logout                  # clear active credentials (local if present, else global)
securenow logout --local          # clear project-local credentials only
securenow whoami                  # show email, user ID, API URL, auth source, expiry, default app
```

### Applications

```bash
securenow apps                               # list all apps (default subcommand)
securenow apps list                          # same as above
securenow apps create <name> [--hosts h1,h2] [--instance <id>]   # interactive instance picker
securenow apps info <id>                     # show app details
securenow apps delete <id> [--force]         # delete an app
securenow apps default <app-key>             # set default app for all commands
securenow apps discover [appId] [--domain example.com]  # discover subdomains, add as apps
securenow apps scan [--yes]                  # scan all app domains for new subdomains
```

### API Key Management

Manage the firewall API key stored in the credentials file. Since v7.1.0 the firewall reads `snk_live_...` keys from `.securenow/credentials.json` so no env var is required.

```bash
securenow api-key set snk_live_xxxxxxxxxx    # save to project ./.securenow/ (default)
securenow api-key set snk_live_xxx --global  # save to ~/.securenow/ instead
securenow api-key show                       # print the masked current key + its source
securenow api-key clear                      # remove just the API key (keeps session/app)
securenow api-key clear --global             # same, but from the global file
```

The key must start with `snk_live_`. `securenow login` with firewall enabled writes the key automatically; use `api-key set` when you already have a key from the dashboard, or to rotate it later.

### Init — Project Setup

```bash
securenow init [--key <API_KEY>]
```

Auto-detects framework (Next.js, Nuxt, Express, Fastify, Koa, Hapi, Node) from `package.json`. Then:
- **Next.js**: creates `instrumentation.ts/js`, suggests `withSecureNow()` in `next.config`
- **Nuxt**: tells you to add `securenow/nuxt` to modules
- **Node/Express/etc.**: suggests adding `-r securenow/register` to start script
- Writes `SECURENOW_API_KEY` to `.env.local` or `.env` if `--key` provided

### Dashboard & Status

```bash
securenow status [--app <key>]               # dashboard overview
securenow analytics [--app <key>]            # response analytics
```

---

### Traces

```bash
securenow traces [--app <key>] [--limit N] [--start ISO] [--end ISO]
securenow traces list --app my-app --limit 50
securenow traces show <traceId>              # full trace detail with spans
securenow traces analyze <traceId>           # AI-powered trace analysis
```

### Logs

```bash
securenow logs [--app <key>] [--limit N] [--minutes M] [--level error|warn|info]
securenow logs list --app my-app --minutes 30 --level error
securenow logs trace <traceId>               # logs correlated to a specific trace
```

### Notifications

```bash
securenow notifications [--limit N] [--page P]
securenow notifications list --limit 20
securenow notifications read <id>            # mark one as read
securenow notifications read-all             # mark all as read
securenow notifications unread               # unread count
```

### Alerts

```bash
securenow alerts                             # list alert rules (default)
securenow alerts rules                       # list alert rules (columns: Status, Applications, Schedule)
securenow alerts rules show <id>            # one rule; JSON: --json
securenow alerts rules update <id> --applications-all   # all current & future apps
securenow alerts rules update <id> --apps key1,key2     # explicit app keys only
securenow alerts channels                    # list alert channels (Slack, email, etc.)
securenow alerts history [--limit N]         # past triggered alerts
```

---

### IP Intelligence

```bash
securenow ip <ip-address>                    # lookup (geo, ASN, threat score, reputation)
securenow ip lookup <ip-address>             # same as above
securenow ip traces <ip-address>             # traces originating from this IP
```

### Forensics — Natural Language Security Queries

```bash
securenow forensics "show me all SQL injection attempts in the last 24h"
securenow forensics query "top 10 IPs by blocked requests" --app my-app
securenow forensics chat --app my-app        # interactive forensics chat session
securenow forensics library                  # view saved/template queries
```

### API Map

```bash
securenow api-map                            # list discovered API endpoints
securenow api-map list                       # same
securenow api-map stats                      # endpoint statistics
```

---

### Firewall

```bash
securenow firewall                           # show status (default)
securenow firewall status                    # layers, sync time, blocked count, API key info
securenow firewall test-ip <ip>              # check if IP would be blocked
```

**Zero-config setup (v7.1+):** running `securenow login` and opting into "Enable the Firewall?" in the browser auto-mints an API key (scoped `firewall:read + blocklist:read + allowlist:read`) and writes it to the credentials file. No `SECURENOW_API_KEY` env var needed. If the user already has a key, `securenow api-key set snk_live_...` achieves the same thing. See [the landing firewall page](https://securenow.ai/firewall) for an overview.

### Blocklist — Block Malicious IPs

```bash
securenow blocklist                          # list blocked IPs
securenow blocklist list
securenow blocklist add <ip> [--reason "Brute force"]
securenow blocklist remove <id>
securenow blocklist stats                    # block counts, top reasons
```

### Allowlist — Restrict to Known IPs

```bash
securenow allowlist                          # list allowed IPs
securenow allowlist list
securenow allowlist add <ip> [--label "Office"] [--reason "Corporate VPN"]
securenow allowlist remove <id>
securenow allowlist stats
```

### Trusted Proxies

```bash
securenow trusted                            # list trusted IPs
securenow trusted list
securenow trusted add <ip> [--label "CloudFlare edge"]
securenow trusted remove <id>
```

---

### False Positive Management

The `fp` command manages exclusion rules that prevent known-safe traffic from triggering security alerts.

```bash
securenow fp                                 # list all exclusion rules
securenow fp list
securenow fp show <id>                       # rule details
securenow fp delete <id> [--yes]

# Create exclusion rules
securenow fp create \
  --conditions '[{"field":"http.target","op":"starts_with","value":"/api/health"}]' \
  --match-mode all \
  --rule-scope any_rule \
  --reason "Health check endpoint"

# Shorthand safe-value presets
securenow fp create \
  --path /api/events \
  --method POST \
  --path-safe standard \
  --ua-safe standard \
  --headers-safe standard \
  --query-keys page,limit \
  --headers-keys host,content-type \
  --reason "Event webhook"

# Edit an existing rule
securenow fp edit <id> [--active true|false] [--conditions '[...]']

# Test conditions against a request body
securenow fp test-body '{"user":"admin"}' --conditions '[{"field":"body.user","op":"eq","value":"admin"}]'
securenow fp test-body @request.json --conditions '[...]'

# Dry-run conditions against the last 3 days of live traces
securenow fp dry-run --conditions '[{"field":"http.target","op":"starts_with","value":"/api/webhook"}]'

# AI-generate exclusion conditions from a description
securenow fp ai-fill --description "Stripe webhook POST to /api/stripe/webhook" \
  --context '{"method":"POST","path":"/api/stripe/webhook"}'

# Mark an IP as false positive on a specific notification
securenow fp mark <notification-id> <ip> \
  [--conditions '[...]'] \
  [--reason "Known partner IP"] \
  [--rule-scope this_rule|specific_rules|all_existing|any_rule] \
  [--target-rules id1,id2]
```

**Condition fields:** `http.target`, `http.method`, `http.url`, `http.user_agent`, `http.request.header.*`, `body.*`, `http.status_code`, `net.peer.ip`, and more.

**Operators:** `eq`, `neq`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex`, `in`, `not_in`, `exists`, `not_exists`, `gt`, `lt`, `gte`, `lte`.

**Match modes:** `all` (AND logic), `any` (OR logic).

**Rule scopes:** `this_rule` (single alert rule), `specific_rules` (comma-separated IDs via `--target-rules`), `all_existing` (all current rules), `any_rule` (all current and future rules).

**Safe-value presets:** `standard` or `strict`. These auto-generate conditions that whitelist common safe patterns for paths, query strings, user-agents, and headers.

---

### Instances

```bash
securenow instances                          # list ClickHouse instances
securenow instances list
securenow instances test <id>                # test connection
```

---

### Telemetry — Emit Logs and Spans From the Shell

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.

```bash
# Send a structured log record to the OTLP collector
securenow log send "Deployment completed" --level info --attrs version=1.2.3,service=api
securenow log send "Backup failed" --level error --attrs host=db-01

# Emit a test span to verify the collector accepts OTLP traffic
securenow test-span
securenow test-span "ci.smoke-test"          # custom span name
```

Both commands use the resolved `SECURENOW_INSTANCE` / `OTEL_EXPORTER_OTLP_*` endpoints. Honors `OTEL_EXPORTER_OTLP_HEADERS` for API-key auth. Returns non-zero on HTTP errors so CI/cron can detect failures.

### Utilities — Redaction, CIDR, Diagnostics

These mirror the SDK exports (`redactSensitiveData`, `createMatcher`, `parseCidr`) so agents can validate behavior without writing Node.

```bash
# Redact sensitive fields (password, token, card, ssn, etc.)
securenow redact '{"user":"alice","password":"s3cret","card":"4242"}'
securenow redact @request.json --fields internal_id,sessionHash

# CIDR operations — match an IP against a list, or parse a range
securenow cidr match 10.0.0.5 10.0.0.0/24,192.168.0.0/16
securenow cidr parse 10.0.0.0/24             # network, broadcast, mask, size

# Show resolved config (service name, endpoints, env vars, firewall layers)
securenow env                                # human-readable
securenow env --json                         # pipe to jq

# End-to-end diagnostic: probe OTLP + API endpoints, check config
securenow doctor                             # exits 0 if healthy, 1 otherwise
securenow doctor --json
```

The `redact` command accepts a JSON string or `@path/to/file.json`, layers your `--fields` flag on top of `DEFAULT_SENSITIVE_FIELDS`, and also honors `SECURENOW_SENSITIVE_FIELDS` from the env. Exit code from `cidr match` is `0` if the IP matches the list, `2` otherwise — scriptable.

---

## Workflow Examples for Agentic AI

### Investigate a Security Alert

```bash
securenow notifications list --limit 5 --json
# IMPORTANT: parse the ipInvestigations array for each notification — see statuses below
securenow ip <attacker-ip> --json
securenow ip traces <attacker-ip> --json
securenow traces show <trace-id> --json
securenow traces analyze <trace-id> --json
# Decision: block the IP
securenow blocklist add <attacker-ip> --reason "Automated: SQL injection detected"
```

#### Notification IP Investigation Statuses

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.

| Status | Meaning | Action |
|--------|---------|--------|
| `open` | Still needs review — no determination yet | Investigate: run `securenow ip <ip>`, check traces, decide to block or dismiss |
| `false_positive` | Trusted/dismissed by exclusion rule, verified bot, or trusted IP list | **Skip** — greyed out in UI, no action needed |
| `blocked` | Already on the user's blocklist | **Skip** — already handled |
| `clean` | Pipeline analyzed and cleared as benign | **Skip** — verified safe |

**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.

### Triage and Suppress a False Positive

```bash
securenow notifications list --json
# Identify a false positive notification
securenow fp ai-fill --description "Stripe webhook calls to /api/stripe/webhook"
# Review the suggested conditions, then create the rule
securenow fp create --conditions '<ai-suggested-conditions>' --rule-scope any_rule --reason "Stripe webhook"
# Or directly mark the notification's IP as FP
securenow fp mark <notification-id> <ip> --rule-scope this_rule --reason "Known Stripe IP"
```

### Onboard a New Application

```bash
securenow apps create my-new-app --hosts api.example.com,app.example.com
securenow apps default my-new-app
securenow init --key snk_live_abc123...
securenow run src/index.js
securenow status --json
```

### Security Posture Check

```bash
securenow status --json
securenow firewall status --json
securenow blocklist stats --json
securenow api-map stats --json
securenow forensics "summarize all attacks in the last 7 days"
```

### Discover Attack Surface

```bash
securenow apps discover --domain example.com
securenow apps scan --yes
securenow api-map list --json
securenow api-map stats --json
```

---

## Output Parsing

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.

## Error Handling

| Exit code / Error | Meaning | Recovery |
|------------------|---------|----------|
| `Session expired` | JWT expired | `securenow login` (or `login --local`) |
| `Not logged in` | No token found | `securenow login` or set `SECURENOW_TOKEN` env var |
| `Access denied (403)` | Insufficient plan or permissions | Upgrade plan or check user role |
| `Cannot connect` | API unreachable | Check `SECURENOW_API_URL` or network |
| `Unknown command` | Typo or unrecognized command | `securenow help` |

Set `SECURENOW_DEBUG=1` for full stack traces on any error.
