Skip to content

Commit 21b1f22

Browse files
authored
Merge pull request #25 from nadeem4/feature/enh-001-sandboxed-execution
feat(core): Implement Sandboxed Execution (ENH-001)
2 parents 316df25 + 9f6b775 commit 21b1f22

14 files changed

Lines changed: 627 additions & 253 deletions

File tree

README.md

Lines changed: 68 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,100 @@
1-
# NL2SQL Platform
1+
# Enterprise NL2SQL Engine
22

3-
An enterprise-grade **Natural Language to SQL** engine built on an Agentic Graph Architecture.
3+
> **A Production-Grade Natural Language to SQL Engine built on the principles of Zero Trust and Deterministic Execution.**
44
5-
## 🚀 Overview
5+
This platform treats "Text-to-SQL" not as a prompt engineering problem, but as a **Distributed Systems** problem. It replaces fragile one-shot generation with a robust, compiled pipeline that bridges the gap between Unstructured Intention (User Language) and Structured Execution (SQL Databases).
66

7-
This platform transforms complex natural language questions into safe, optimized, and executable SQL queries across multiple database engines (PostgreSQL, MySQL, MSSQL, SQLite). It uses a **Directed Cyclic Graph** (LangGraph) to orchestrate planning, validation, generation, and self-correction.
7+
---
88

9-
### Key Features
9+
## 🏗️ System Topology
1010

11-
* **🛡️ Security First**: Strict AST Validation, **Intent Analysis** (Jailbreak Detection), RBAC Policies, and Read-Only enforcement.
12-
* **🧠 Agentic Reasoning**: Self-correcting nodes that fix SQL errors automatically.
13-
* **🔌 Polyglot**: First-class support for Postgres, MySQL, MSSQL, and SQLite.
14-
* **⚡ Smart Routing**: Decomposes complex queries into sub-queries for multi-datasource environments.
15-
* **🔄 Reliability**: Built-in **Exponential Backoff** and **Circuit Breakers** to handle transient failures gracefully.
11+
The architecture is composed of three distinct planes, ensuring separation of concerns and failure isolation.
1612

17-
## 🏁 Quick Demo
13+
### 1. The Control Plane (The Graph)
1814

19-
Explore the platform's capabilities with our interactive setup wizard. You can choose between **Lite Mode** (in-memory, no deps) or **Docker Mode** (real databases).
15+
**Responsibility**: Reasoning, Planning, and Orchestration.
2016

21-
### 1. Lite Mode (Fastest) uses SQLite
17+
* **Agentic Graph**: Implemented as a Directed Cyclic Graph (LangGraph) to enable "Refinement Loops". If a plan fails validation, the system self-corrects.
18+
* **State Management**: Deterministic state transitions ensure auditability and reproducibility of every decision.
2219

23-
Perfect for a standardized, local environment without needing Docker.
20+
### 2. The Security Plane (The Firewall)
2421

25-
```bash
26-
nl2sql setup --demo
27-
```
22+
**Responsibility**: Invariants Enforcement.
2823

29-
### 2. Docker Mode (Full Fidelity) uses Postgres
24+
* **Valid-by-Construction**: The LLM *never* executes SQL directly. It generates an **Abstract Syntax Tree (AST)**.
25+
* **Static Analysis**: The [Validator Node](docs/core/nodes.md#4-logical-validator) enforces **Row-Level Security (RLS)** and type safety on the AST *before* compilation.
26+
* **Intent Classification**: Upstream detection of adversarial prompts (Jailbreaks/Injections).
3027

31-
Spins up real orchestrator and database containers for a production-like test.
28+
### 3. The Data Plane (The Sandbox)
3229

33-
```bash
34-
nl2sql setup --demo --docker
35-
```
30+
**Responsibility**: Semantic Search and Execution.
3631

37-
## 🛠️ Installation
32+
* **Blast Radius Isolation**: SQL Drivers (ODBC/C-Ext) run in a dedicated **[Sandboxed Process Pool](docs/architecture/decisions/ADR-001_sandboxed_execution.md)**. A segfault in a driver kills a disposable worker, not the Agent.
33+
* **Partitioned Retrieval**: The [Orchestrator](docs/core/indexing.md) uses Partitioned MMR to inject only relevant schema context, preventing context window overflow.
3834

39-
This is a monorepo. To develop or run the platform from source:
35+
---
4036

41-
### Prerequisites
42-
43-
* Python 3.10+
44-
* Docker & Docker Compose (optional, for Integration Tests)
37+
## 📐 Architectural Invariants
4538

46-
### Setup
39+
| Invariant | Rationale | Mechanism |
40+
| :--- | :--- | :--- |
41+
| **No Unvalidated SQL** | Prevent Hullucinations & Data Leaks | All plans pass through `LogicalValidator` (AST) + `PhysicalValidator` (Dry Run) before execution. |
42+
| **Zero Shared State** | Crash Safety | Execution happens in isolated processes; no shared memory with the Control Plane. |
43+
| **Fail-Fast** | Reliability | Circuit Breakers and Strict Timeouts prevent cascading failures (Retry Storms). |
44+
| **Determinism** | Debuggability | Temperature-0 generation + Strict Typing (Pydantic) for all LLM outputs. |
4745

48-
1. **Clone and Install**:
46+
---
4947

50-
```bash
51-
git clone https://github.com/nadeem4/nl2sql.git
52-
cd nl2sql
53-
54-
# Create virtual environment
55-
python -m venv venv
56-
source venv/bin/activate # or .\venv\Scripts\activate on Windows
57-
58-
# Install Core and CLI
59-
pip install -e packages/core
60-
pip install -e packages/adapter-sdk
61-
pip install -e packages/cli
62-
pip install -e packages/adapters/postgres # Install specific adapters as needed
63-
```
48+
## 🚀 Quick Start
6449

65-
2. **Verify Installation**:
50+
### Prerequisites
6651

67-
```bash
68-
nl2sql --help
69-
```
52+
* Python 3.10+
53+
* Docker (Optional, for full integration environment)
7054

71-
## 🏗️ Architecture
55+
### 1. Installation
7256

73-
The system is composed of specialized Neural Nodes:
57+
```bash
58+
git clone https://github.com/nadeem4/nl2sql.git
59+
cd nl2sql
7460

75-
1. **Semantic Analysis**: Intent classification and entity extraction.
76-
2. **Decomposer (Router)**: Splits complex queries and routes them to the correct datasource.
77-
3. **Planner**: Generates a database-agnostic Abstract Syntax Tree (AST).
78-
4. **Validator**: Enforces security policies and logical correctness on the AST.
79-
5. **Generator**: Compiles AST to dialect-specific SQL.
80-
6. **Executor**: Runs the query in a sandboxed environment.
81-
7. **Refiner**: Self-corrects errors by analyzing stack traces and feedback.
82-
8. **Aggregator**: Synthesizes results from multiple sub-queries.
61+
# Set up environment
62+
python -m venv venv
63+
source venv/bin/activate
8364

84-
See [Architecture Documentation](docs/core/architecture.md) for details.
65+
# Install Core Engine & CLI
66+
pip install -e packages/core
67+
pip install -e packages/cli
68+
pip install -e packages/adapter-sdk
69+
```
8570

86-
## 📚 Documentation
71+
### 2. Run Demo (Lite Mode)
8772

88-
Full documentation is available in the `docs/` directory.
73+
Boot the engine with an in-memory SQLite database (No Docker required).
8974

9075
```bash
91-
pip install -r requirements-docs.txt
92-
mkdocs serve
76+
nl2sql setup --demo
9377
```
9478

95-
## 📂 Repository Structure
79+
---
80+
81+
## 📚 Technical Documentation
9682

97-
* `packages/core`: The core graph engine, nodes, and state management.
98-
* `packages/cli`: Command-line interface tool.
99-
* `packages/adapter-sdk`: SDK for building custom database adapters.
100-
* `configs/`: Configuration files (Policies, Datasources).
101-
* `docs/`: MkDocs source files.
83+
* **[System Architecture](docs/core/architecture.md)**: Deep dive into the Control, Security, and Data planes.
84+
* **[Component Reference](docs/core/nodes.md)**: Detailed specs for Planner, Validator, Executor, etc.
85+
* **[Security Model](docs/safety/security.md)**: Defense-in-depth strategy against prompt injection and unauthorized access.
86+
* **[ADR-001: Sandboxed Execution](docs/architecture/decisions/ADR-001_sandboxed_execution.md)**: Decision record for the Process Pool architecture.
87+
88+
---
89+
90+
## 📦 Repository Structure
91+
92+
```text
93+
packages/
94+
├── core/ # The Engine (Graph, State, Logic)
95+
├── cli/ # Terminal Interface & Ops Tools
96+
├── adapter-sdk/ # Interface Contract for new Databases
97+
└── adapters/ # Official Dialects (Postgres, MSSQL, MySQL)
98+
configs/ # Runtime Configuration (Policies, Prompts)
99+
docs/ # Architecture & Operations Manual
100+
```

docs/architecture/decisions/ADR-001_sandboxed_execution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ class ExecutionResponse(BaseModel):
9898

9999
## 6. Recommendation
100100

101-
**Phase 1: Option A (Multiprocessing)**
101+
### Phase 1: Option A (Multiprocessing)
102102

103103
* It solves the critical **Blast Radius** immediately.
104104
* It separates **Indexing** from **Execution** via distinct pools.
105105
* It is "Service-Ready": The `ExecutionRequest` protocol makes migrating to Option B trivial later.
106106

107-
**Phase 2: Option B (Dedicated Service)**
107+
### Phase 2: Option B (Dedicated Service)
108108

109109
* Migrate only when we need to scale Execution independently of the Agent or require strict network segmentation.

docs/core/architecture.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,4 @@ The platform is designed to be cloud-agnostic and operationally robust.
7474

7575
* **[Environments](environment.md)**: Configuration management via `.env` files.
7676
* **[Adapters](../adapters/index.md)**: The connectivity layer that abstracts specific database dialects (Postgres, MSSQL, etc.).
77+
* **[Sandboxed Execution](../architecture/decisions/ADR-001_sandboxed_execution.md)**: Isolated process pools for executing untrusted SQL and performing heavy schema introspection.

docs/core/indexing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ We index two main types of documents: **Schema** and **Examples**.
1010

1111
Table schemas are "flattened" into a rich text representation before embedding.
1212

13+
* **Sandboxed Introspection**: Schema fetching is offloaded to the **Indexing Process Pool** ensuring that connecting to slow/unstable databases does not block the Orchestrator's main event loop.
1314
* **Format**: `Table: {name} (Alias: {alias}). Columns: {col_desc}. Primary Key: {pk}. Foreign Keys: {fk}.`
1415
* **Offline Aliasing**: To prevent conflicts, tables are assigned canonical aliases (e.g., `sales_db_t1`) during indexing.
1516
* **Rich Metadata (via Adapter)**:

docs/core/nodes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ The pipeline is composed of specialized **Nodes**. Each node performs a single r
4545

4646
**Responsibility**: Execution Readiness.
4747

48-
* **Dry Run**: Executes `EXPLAIN` or equivalent to verify syntax validity without running the query.
49-
* **Performance**: Checks row cost estimates against `row_limit`.
48+
* **Dry Run (Sandboxed)**: Executes `EXPLAIN` or equivalent inside the **Execution Sandbox** to verify syntax validity without crashing the main process.
49+
* **Performance (Sandboxed)**: Checks row cost estimates against `row_limit` in the sandbox.
5050

5151
## 7. Executor Node
5252

5353
**Responsibility**: Sandboxed Execution.
5454

55-
* **Logic**: Connects to the database using the specific Adapter and executes the query.
55+
* **Logic**: Offloads the query execution to the **Execution Process Pool** (`nl2sql.common.sandbox`). This isolates the main application from driver segfaults (e.g., OOMs, C-extension panics).
5656
* **Safety**: Read-only connection enforcement.
5757

5858
## 8. Refiner Node

packages/adapter-sqlalchemy/src/nl2sql_sqlalchemy_adapter/adapter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def __init__(self, datasource_id: str = None, datasource_engine_type: str = None
4040
if self.statement_timeout_ms:
4141
self.execution_options["timeout"] = self.statement_timeout_ms / 1000.0
4242

43+
self.connection_args = connection_args
4344
self.connection_string = self.construct_uri(connection_args)
4445

4546
self.engine: Engine = None
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Sandbox Manager for isolating unsafe operations (SQL Execution, Indexing).
3+
4+
This module manages two separate ProcessPoolExecutors:
5+
1. Execution Pool: Low-latency, for user-facing query execution.
6+
2. Indexing Pool: High-throughput, for background schema fetching.
7+
8+
It implements the Singleton pattern to ensure pools are reused across requests.
9+
"""
10+
from __future__ import annotations
11+
12+
import atexit
13+
from concurrent.futures import ProcessPoolExecutor
14+
from typing import Optional
15+
16+
from nl2sql.common.logger import get_logger
17+
from nl2sql.common.settings import settings
18+
19+
logger = get_logger("sandbox_manager")
20+
21+
class SandboxManager:
22+
"""Manages global process pools for sandboxed execution."""
23+
24+
_exec_pool: Optional[ProcessPoolExecutor] = None
25+
_index_pool: Optional[ProcessPoolExecutor] = None
26+
27+
@classmethod
28+
def get_execution_pool(cls) -> ProcessPoolExecutor:
29+
"""Returns the process pool for latency-sensitive execution tasks."""
30+
if cls._exec_pool is None:
31+
workers = settings.sandbox_exec_workers
32+
logger.info(f"Initializing Execution Sandbox with {workers} workers.")
33+
cls._exec_pool = ProcessPoolExecutor(
34+
max_workers=workers,
35+
initializer=cls._init_worker,
36+
initargs=("execution",)
37+
)
38+
return cls._exec_pool
39+
40+
@classmethod
41+
def get_indexing_pool(cls) -> ProcessPoolExecutor:
42+
"""Returns the process pool for throughput-heavy indexing tasks."""
43+
if cls._index_pool is None:
44+
workers = settings.sandbox_index_workers
45+
logger.info(f"Initializing Indexing Sandbox with {workers} workers.")
46+
cls._index_pool = ProcessPoolExecutor(
47+
max_workers=workers,
48+
initializer=cls._init_worker,
49+
initargs=("indexing",)
50+
)
51+
return cls._index_pool
52+
53+
@staticmethod
54+
def _init_worker(pool_type: str):
55+
"""Initializer run once per worker process startup.
56+
57+
Sets a distinct process name for debugging and prepares the worker execution environment.
58+
"""
59+
import multiprocessing
60+
p = multiprocessing.current_process()
61+
p.name = f"SandboxWorker-{pool_type}-{p.name}"
62+
63+
@classmethod
64+
def shutdown(cls):
65+
"""Shuts down all sandbox pools waiting for pending tasks to complete."""
66+
if cls._exec_pool:
67+
logger.info("Shutting down Execution Sandbox...")
68+
cls._exec_pool.shutdown(wait=True)
69+
cls._exec_pool = None
70+
71+
if cls._index_pool:
72+
logger.info("Shutting down Indexing Sandbox...")
73+
cls._index_pool.shutdown(wait=True)
74+
cls._index_pool = None
75+
76+
atexit.register(SandboxManager.shutdown)
77+
78+
def get_execution_pool() -> ProcessPoolExecutor:
79+
"""Helper accessor for execution pool.
80+
81+
Returns:
82+
ProcessPoolExecutor: The shared execution pool instance.
83+
"""
84+
return SandboxManager.get_execution_pool()
85+
86+
def get_indexing_pool() -> ProcessPoolExecutor:
87+
"""Helper accessor for indexing pool.
88+
89+
Returns:
90+
ProcessPoolExecutor: The shared indexing pool instance.
91+
"""
92+
return SandboxManager.get_indexing_pool()

packages/core/src/nl2sql/common/settings.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ class Settings(BaseSettings):
4545
description="Global timeout in seconds for pipeline execution."
4646
)
4747

48+
sandbox_exec_workers: int = Field(
49+
default=4,
50+
validation_alias="SANDBOX_EXEC_WORKERS",
51+
description="Max workers for latency-sensitive execution pool."
52+
)
53+
54+
sandbox_index_workers: int = Field(
55+
default=2,
56+
validation_alias="SANDBOX_INDEX_WORKERS",
57+
description="Max workers for throughput-heavy indexing pool."
58+
)
59+
4860
model_config = SettingsConfigDict(
4961
env_file=".env",
5062
env_file_encoding="utf-8",

0 commit comments

Comments
 (0)