Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f04a4b7
chore: add semantic-release configuration
nadavis Mar 10, 2026
4526c7b
style: fix formatting in package.json
nadavis Mar 10, 2026
aad75a4
fix: expand codeKeys and increase fallback truncation in native popup
nadavis Mar 11, 2026
00e540a
feat: add local audit log and hook debug log to core
nadavis Mar 11, 2026
9915135
refactor: replace appendAuditModeEntry with appendLocalAudit
nadavis Mar 11, 2026
081a3a7
update readme
nadavis Mar 12, 2026
25955bf
feat: improve native popup with specific rule names and keyboard shor…
nadavis Mar 12, 2026
99520ae
docs: remove static image and expand video to full width in README
nadavis Mar 12, 2026
ced874c
docs: update demo video source in README
nadavis Mar 12, 2026
5e7b47a
docs: add .mp4 extension to demo video src
nadavis Mar 12, 2026
1359115
docs: replace video with gif for autoplay/loop support on GitHub
nadavis Mar 12, 2026
12a7372
docs: update demo gif asset URL
nadavis Mar 12, 2026
aaa8bab
docs: update demo gif asset URL
nadavis Mar 12, 2026
2b392c9
docs: update demo gif asset URL
nadavis Mar 12, 2026
30fb2eb
docs: update demo gif asset URL
nadavis Mar 12, 2026
5f2625b
Merge branch 'main' into dev
nadav-node9 Mar 12, 2026
a9e2069
ci: trigger fresh CI run to clear stale typecheck annotations
nadavis Mar 12, 2026
3f5f5b1
fix: restore missing return statement in buildPangoMessage lost durin…
nadavis Mar 12, 2026
eb4e63d
docs: update demo.ts with correct import and usage explanation
nadavis Mar 12, 2026
627c3db
fix: correct node9.config.json.example
nadavis Mar 12, 2026
a34ac4a
feat: add environment setting to config for explicit env selection
nadavis Mar 12, 2026
7a59bc7
style: fix prettier formatting in node9.config.json.example
nadavis Mar 12, 2026
2adcf0c
refactor: remove slackChannel from proxy config — managed via SaaS da…
nadavis Mar 12, 2026
38d2f6c
fix: remove unused envConfig variable left after slackChannel removal
nadavis Mar 12, 2026
7744f47
fix: add a new gif to readme
nadavis Mar 13, 2026
807aae7
Merge branch 'main' into dev
nadav-node9 Mar 13, 2026
0f1b6f4
feat: add node9 setup, doctor, explain, smart SQL check, and negotiat…
nadavis Mar 13, 2026
e73b56c
ci: build before running tests so integration tests find dist/cli.js
nadavis Mar 13, 2026
e2e49ac
fix: downgrade node9 PATH check to warning in doctor command
nadavis Mar 13, 2026
a99b460
Merge branch 'main' into dev
nadav-node9 Mar 13, 2026
addbed5
docs: add CLI reference section with doctor and explain commands
nadavis Mar 13, 2026
45ec2bb
fix: resolve merge conflict in README gif URL (keep main version)
nadavis Mar 13, 2026
91e96d0
style: run prettier on README
nadavis Mar 13, 2026
98225a2
Merge branch 'main' into dev
nadav-node9 Mar 13, 2026
86e7342
feat: add approval timeout (approvalTimeoutMs) to race engine
nadavis Mar 13, 2026
f69b095
feat: add node9 audit command
nadavis Mar 14, 2026
d53a366
feat: implement smart rules engine with hard-block verdict
nadavis Mar 14, 2026
3ae8546
docs: document smart rules, settings options, and approvalTimeoutMs
nadavis Mar 14, 2026
1a1e7a6
test: add 28 tests for smart rules engine
nadavis Mar 14, 2026
c750669
fix: close broken markdown link in Enterprise section
nadavis Mar 14, 2026
bd57c73
Merge branch 'main' into dev
nadav-node9 Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 142 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Rules are **merged additive**—you cannot "un-danger" a word locally if it was
"settings": {
"mode": "standard",
"enableUndo": true,
"approvalTimeoutMs": 30000,
"approvers": {
"native": true,
"browser": true,
Expand All @@ -144,13 +145,152 @@ Rules are **merged additive**—you cannot "un-danger" a word locally if it was
"toolInspection": {
"bash": "command",
"postgres:query": "sql"
}
},
"rules": [
{ "action": "rm", "allowPaths": ["**/node_modules/**", "dist/**"] },
{ "action": "push", "blockPaths": ["**"] }
],
"smartRules": [
{
"name": "no-delete-without-where",
"tool": "*",
"conditions": [
{ "field": "sql", "op": "matches", "value": "^(DELETE|UPDATE)\\s", "flags": "i" },
{ "field": "sql", "op": "notMatches", "value": "\\bWHERE\\b", "flags": "i" }
],
"verdict": "review",
"reason": "DELETE/UPDATE without WHERE — would affect every row"
}
]
}
}
```

### ⚙️ `settings` options

| Key | Default | Description |
| :------------------- | :----------- | :----------------------------------------------------------- |
| `mode` | `"standard"` | `standard` \| `strict` \| `audit` |
| `enableUndo` | `true` | Take git snapshots before every AI file edit |
| `approvalTimeoutMs` | `0` | Auto-deny after N ms if no human responds (0 = wait forever) |
| `approvers.native` | `true` | OS-native popup |
| `approvers.browser` | `true` | Browser dashboard (`node9 daemon`) |
| `approvers.cloud` | `true` | Slack / SaaS approval |
| `approvers.terminal` | `true` | `[Y/n]` prompt in terminal |

### 🧠 Smart Rules

Smart rules match on **raw tool arguments** using structured conditions — more powerful than `dangerousWords` or `rules`, which only see extracted tokens.

```json
{
"name": "curl-pipe-to-shell",
"tool": "bash",
"conditions": [{ "field": "command", "op": "matches", "value": "curl.+\\|.*(bash|sh)" }],
"verdict": "block",
"reason": "curl piped to shell — remote code execution risk"
}
```

**Fields:**

| Field | Description |
| :-------------- | :----------------------------------------------------------------------------------- |
| `tool` | Tool name or glob (`"bash"`, `"mcp__postgres__*"`, `"*"`) |
| `conditions` | Array of conditions evaluated against the raw args object |
| `conditionMode` | `"all"` (AND, default) or `"any"` (OR) |
| `verdict` | `"review"` (approval prompt) \| `"block"` (hard deny) \| `"allow"` (skip all checks) |
| `reason` | Human-readable explanation shown in the approval prompt and audit log |

**Condition operators:**

| `op` | Meaning |
| :------------ | :------------------------------------------------------------------ |
| `matches` | Field value matches regex (`value` = pattern, `flags` = e.g. `"i"`) |
| `notMatches` | Field value does not match regex |
| `contains` | Field value contains substring |
| `notContains` | Field value does not contain substring |
| `exists` | Field is present and non-empty |
| `notExists` | Field is absent or empty |

The `field` key supports dot-notation for nested args: `"params.query.sql"`.

**Built-in default smart rule** (always active, no config needed):

```json
{
"name": "no-delete-without-where",
"tool": "*",
"conditions": [
{ "field": "sql", "op": "matches", "value": "^(DELETE|UPDATE)\\s", "flags": "i" },
{ "field": "sql", "op": "notMatches", "value": "\\bWHERE\\b", "flags": "i" }
],
"verdict": "review",
"reason": "DELETE/UPDATE without WHERE clause — would affect every row in the table"
}
```

Use `node9 explain <tool> <args>` to dry-run any tool call and see exactly which smart rule (or other policy tier) would trigger.

---

## 🖥️ CLI Reference

| Command | Description |
| :---------------------------- | :------------------------------------------------------------------------------------ |
| `node9 setup` | Interactive menu — detects installed agents and wires hooks for you |
| `node9 addto <agent>` | Wire hooks for a specific agent (`claude`, `gemini`, `cursor`) |
| `node9 init` | Create default `~/.node9/config.json` |
| `node9 status` | Show current protection status and active rules |
| `node9 doctor` | Health check — verifies binaries, config, credentials, and all agent hooks |
| `node9 explain <tool> [args]` | Trace the policy waterfall for a given tool call (dry-run, no approval prompt) |
| `node9 undo [--steps N]` | Revert the last N AI file edits using shadow Git snapshots |
| `node9 check` | Called by agent hooks; evaluates a pending tool call and exits 0 (allow) or 1 (block) |

### `node9 doctor`

Runs a full self-test and exits 1 if any required check fails:

```
Node9 Doctor v1.2.0
────────────────────────────────────────
Binaries
✅ Node.js v20.11.0
✅ git version 2.43.0

Configuration
✅ ~/.node9/config.json found and valid
✅ ~/.node9/credentials.json — cloud credentials found

Agent Hooks
✅ Claude Code — PreToolUse hook active
⚠️ Gemini CLI — not configured (optional)
⚠️ Cursor — not configured (optional)

────────────────────────────────────────
All checks passed ✅
```

### `node9 explain`

Dry-runs the policy engine and prints exactly which rule (or waterfall tier) would block or allow a given tool call — useful for debugging your config:

```bash
node9 explain bash '{"command":"rm -rf /tmp/build"}'
```

```
Policy Waterfall for: bash
──────────────────────────────────────────────
Tier 1 · Cloud Org Policy SKIP (no org policy loaded)
Tier 2 · Dangerous Words BLOCK ← matched "rm -rf"
Tier 3 · Path Block –
Tier 4 · Inline Exec –
Tier 5 · Rule Match –
──────────────────────────────────────────────
Verdict: BLOCK (dangerous word: rm -rf)
```

---

## 🖥️ CLI Reference
Expand Down Expand Up @@ -242,4 +382,4 @@ A corporate policy has locked this action. You must click the "Approve" button i
## 🏢 Enterprise & Compliance

Node9 Pro provides **Governance Locking**, **SAML/SSO**, and **VPC Deployment**.
Visit [node9.ai](https://node9.ai
Visit [node9.ai](https://node9.ai)
Loading
Loading