diff --git a/README.md b/README.md
index 2864e7b..88ad3c4 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ While others try to _guess_ if a prompt is malicious (Semantic Security), Node9
**AIs are literal.** When you ask an agent to "Fix my disk space," it might decide to run `docker system prune -af`.
-
+
**With Node9, the interaction looks like this:**
diff --git a/examples/demo.ts b/examples/demo.ts
index 8876a05..381d938 100644
--- a/examples/demo.ts
+++ b/examples/demo.ts
@@ -1,4 +1,21 @@
-import { protect } from '../src/index';
+/**
+ * Node9 SDK — protect() example
+ *
+ * There are two ways Node9 protects you:
+ *
+ * 1. CLI Proxy (automatic) — Node9 wraps Claude Code / Gemini CLI at the
+ * process level and intercepts every tool call automatically. No code needed.
+ *
+ * 2. SDK / protect() (manual) — for developers building their own Node.js apps
+ * with an AI SDK (Anthropic, LangChain, etc.). Wrap any dangerous function
+ * with `protect()` and Node9 will intercept it before execution, showing a
+ * native approval popup and applying your security policy.
+ *
+ * Usage:
+ * npm install @node9/proxy
+ * npx ts-node examples/demo.ts
+ */
+import { protect } from '@node9/proxy';
import chalk from 'chalk';
async function main() {
@@ -6,16 +23,18 @@ async function main() {
console.log(chalk.green(`✅ Success: Database ${name} has been deleted.`));
};
- // Wrap the dangerous function
+ // Wrap the dangerous function — Node9 will intercept it before it runs
const secureDelete = protect('aws.rds.delete_database', deleteDatabase);
console.log(chalk.cyan("🤖 AI Agent: 'I am going to clean up the production DB...'"));
try {
+ // Node9 will show a native popup asking you to Allow / Block this action.
+ // If you click Block (or the policy denies it), an error is thrown.
await secureDelete('production-db-v1');
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
- console.log(chalk.yellow(`\n🛡️ Node9 caught it: ${msg}`));
+ console.log(chalk.yellow(`\n🛡️ Node9 blocked it: ${msg}`));
}
}
diff --git a/examples/node9.config.json.example b/examples/node9.config.json.example
index 12ea1f3..27dd30f 100644
--- a/examples/node9.config.json.example
+++ b/examples/node9.config.json.example
@@ -1,57 +1,96 @@
{
- "version": "1.0",
"settings": {
"mode": "standard",
- "failOpen": false
+ "environment": "production",
+ "autoStartDaemon": true,
+ "enableUndo": true,
+ "enableHookLogDebug": false,
+ "approvers": {
+ "native": true,
+ "browser": false,
+ "cloud": false,
+ "terminal": true
+ }
},
"policy": {
+ "sandboxPaths": ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
+
"dangerousWords": [
- "delete",
"drop",
- "remove",
- "rm",
- "rmdir",
- "terminate",
- "refund",
- "write",
- "update",
- "destroy",
+ "truncate",
"purge",
- "revoke",
"format",
- "truncate"
+ "destroy",
+ "terminate",
+ "revoke",
+ "docker",
+ "psql",
+ "rmdir",
+ "delete",
+ "alter",
+ "grant",
+ "rm"
],
+
"ignoredTools": [
"list_*",
"get_*",
"read_*",
"describe_*",
"read",
- "write",
- "edit",
- "multiedit",
"glob",
"grep",
"ls",
"notebookread",
- "notebookedit",
- "todoread",
- "todowrite",
"webfetch",
"websearch",
"exitplanmode",
"askuserquestion",
- "run_shell_command"
+ "agent",
+ "task*",
+ "toolsearch",
+ "mcp__ide__*",
+ "getDiagnostics"
+ ],
+
+ "toolInspection": {
+ "bash": "command",
+ "shell": "command",
+ "run_shell_command": "command",
+ "terminal.execute": "command",
+ "postgres:query": "sql",
+ "mcp__github__*": "command",
+ "mcp__redis__*": "query"
+ },
+
+ "rules": [
+ {
+ "action": "rm",
+ "allowPaths": [
+ "**/node_modules/**",
+ "dist/**",
+ "build/**",
+ ".next/**",
+ ".nuxt/**",
+ "coverage/**",
+ ".cache/**",
+ "tmp/**",
+ "temp/**",
+ "**/__pycache__/**",
+ "**/.pytest_cache/**",
+ "**/*.log",
+ "**/*.tmp",
+ ".DS_Store",
+ "**/yarn.lock",
+ "**/package-lock.json",
+ "**/pnpm-lock.yaml"
+ ]
+ }
]
},
+
"environments": {
- "production": {
- "requireApproval": true,
- "slackChannel": "#alerts-prod-security"
- },
- "development": {
- "requireApproval": true,
- "slackChannel": "#alerts-dev-sandbox"
- }
+ "production": { "requireApproval": true },
+ "development": { "requireApproval": false }
}
}
diff --git a/src/core.ts b/src/core.ts
index 7cfbf50..c7e5568 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -296,7 +296,6 @@ export function redactSecrets(text: string): string {
interface EnvironmentConfig {
requireApproval?: boolean;
- slackChannel?: string;
}
interface PolicyRule {
@@ -312,6 +311,7 @@ interface Config {
enableUndo?: boolean;
enableHookLogDebug?: boolean;
approvers: { native: boolean; browser: boolean; cloud: boolean; terminal: boolean };
+ environment?: string;
};
policy: {
sandboxPaths: string[];
@@ -866,8 +866,7 @@ export async function authorizeHeadless(
if (cloudEnforced) {
try {
- const envConfig = getActiveEnvironment(getConfig());
- const initResult = await initNode9SaaS(toolName, args, creds!, envConfig?.slackChannel, meta);
+ const initResult = await initNode9SaaS(toolName, args, creds!, meta);
if (!initResult.pending) {
return {
@@ -1213,6 +1212,7 @@ export function getConfig(): Config {
if (s.enableHookLogDebug !== undefined)
mergedSettings.enableHookLogDebug = s.enableHookLogDebug;
if (s.approvers) mergedSettings.approvers = { ...mergedSettings.approvers, ...s.approvers };
+ if (s.environment !== undefined) mergedSettings.environment = s.environment;
if (p.sandboxPaths) mergedPolicy.sandboxPaths.push(...p.sandboxPaths);
if (p.ignoredTools) mergedPolicy.ignoredTools.push(...p.ignoredTools);
@@ -1252,7 +1252,7 @@ function tryLoadConfig(filePath: string): Record | null {
}
function getActiveEnvironment(config: Config): EnvironmentConfig | null {
- const env = process.env.NODE_ENV || 'development';
+ const env = config.settings.environment || process.env.NODE_ENV || 'development';
return config.environments[env] ?? null;
}
@@ -1339,7 +1339,6 @@ async function initNode9SaaS(
toolName: string,
args: unknown,
creds: { apiKey: string; apiUrl: string },
- slackChannel?: string,
meta?: { agent?: string; mcpServer?: string }
): Promise<{
pending: boolean;
@@ -1358,7 +1357,6 @@ async function initNode9SaaS(
body: JSON.stringify({
toolName,
args,
- slackChannel,
context: {
agent: meta?.agent,
mcpServer: meta?.mcpServer,