Skip to main content

Architecture Overview

High-Level Flow

Dashed nodes are optional hooks, installed with install --optional.

Cursor limitation

postToolUse and afterAgentResponse are observe-only. Cursor processes tool outputs and displays AI responses before these hooks fire — violations are logged and warnings emitted, but content cannot be blocked or retracted. See Cursor Limitation for details.

Module Map

ModulePurpose
src/config.tsLoad and validate airs-config.json with env var resolution
src/airs-client.tsSDK wrapper with circuit breaker integration
src/scanner.tsScan orchestration, DLP masking, UX block messages
src/code-extractor.tsExtract code blocks from AI responses
src/tool-name-parser.tsParse MCP:server:tool format tool names
src/content-limits.tsConfigurable skip/truncate thresholds (applied by the scanner to all directions)
src/logger.tsStructured JSON Lines logging with rotation
src/circuit-breaker.tsFailure tracking, cooldown bypass, automatic recovery
src/dlp-masking.tsPer-service enforcement actions (block/mask/allow)
src/log-rotation.tsLog file rotation at 10MB threshold
src/types.tsTypeScript interfaces for config, Cursor API, AIRS
src/hooks/before-submit-prompt.tsCursor beforeSubmitPrompt entry point (can block)
src/hooks/before-mcp-execution.tsCursor beforeMCPExecution entry point (can block)
src/hooks/post-tool-use.tsCursor postToolUse entry point (observe-only; optional MCP output sanitization)
src/hooks/after-agent-response.tsCursor afterAgentResponse entry point (observe-only)
src/hooks/before-shell-execution.tsCursor beforeShellExecution entry point (optional, can block)
src/hooks/after-shell-execution.tsCursor afterShellExecution entry point (optional, observe-only)
src/hooks/before-read-file.tsCursor beforeReadFile entry point (optional, can block)
src/hooks/before-tab-file-read.tsCursor beforeTabFileRead entry point (optional, can block)
src/hooks/subagent-start.tsCursor subagentStart entry point (optional, can block)
src/hooks/after-mcp-execution.tsCursor afterMCPExecution entry point (optional, observe-only)
scripts/lib/hook-registry.tsRegistry of core + optional hooks driving install/uninstall/verify/doctor

Request Lifecycle

Prompt Scan (beforeSubmitPrompt)

  1. Cursor pipes { prompt, user_email, ... } as JSON to stdin
  2. Hook loads config, initializes logger
  3. Scanner sends prompt to AIRS via SDK (prompt content key)
  4. Circuit breaker gates the request (bypass if open)
  5. AIRS returns verdict + detections
  6. If enforce mode and verdict is block: output { "continue": false, "user_message": "..." }
  7. If observe or verdict is allow: output { "continue": true }

MCP Tool Scan (beforeMCPExecution — can block)

  1. Cursor pipes { tool_name, tool_input, ... } as JSON to stdin
  2. Hook loads config, initializes logger
  3. Content limits check: if input exceeds max_scan_bytes, skip scan (fail-open)
  4. Scanner sends tool input to AIRS via tool_event content key using profiles.tool
  5. Circuit breaker gates the request (bypass if open)
  6. AIRS returns verdict + detections
  7. If enforce mode and verdict is block: output { "continue": false, "user_message": "..." }
  8. If observe or verdict is allow: output { "continue": true }

Tool Output Scan (postToolUse — observe-only)

  1. Cursor pipes { tool_name, tool_input, tool_output, ... } as JSON to stdin (after tool already executed)
  2. Hook loads config, initializes logger, parses tool name
  3. Route by tool type:
    • MCP:* → scan input+output as tool_event
    • Bash → scan output as response
    • Write / Edit → scan new content as prompt (DLP)
    • Skip list → no scan
  4. Content limits applied: truncate or skip oversized content
  5. If violation detected: log to audit trail + emit warning (cannot block — observe-only)

Response Scan (afterAgentResponse — observe-only)

  1. Cursor pipes { text, ... } as JSON to stdin (after the response is already displayed)
  2. Hook loads config, initializes logger
  3. Code extractor splits response into natural language + code blocks
  4. Scanner sends both to AIRS (response + code_response content keys)
  5. code_response triggers WildFire/ATP malicious code detection
  6. If violation detected: log to audit trail + emit warning (cannot block — observe-only)
  7. If clean: output { "permission": "allow" }

Build Modes

ModeCommandHook ExecutionStartup
Productionnode dist/hooks/*.jsPrecompiled JS~800ms
Developmentnpx tsx src/hooks/*.tsJIT TypeScript~2.5s

The install-hooks script points at compiled JS by default. See Contributing for the development workflow.

Optional Hook Scans

The optional hooks reuse the same lifecycle with different content routing:

  • beforeShellExecution → command text as tool_event (profiles.tool), deny + exit 2 on enforce-mode block
  • afterShellExecution → terminal output as response (profiles.response), observe-only
  • beforeReadFile / beforeTabFileRead → file contents as prompt (profiles.prompt, DLP), deny on enforce-mode block
  • subagentStart → task text as prompt, deny on enforce-mode block
  • afterMCPExecution → tool input + result JSON as tool_event, observe-only
  • postToolUse with sanitize_mcp_output: true → flagged MCP output replaced via updated_mcp_tool_output