Security & Data Sovereignty
askLenny is designed for environments where data cannot leave the network. Here is exactly what does and does not cross your perimeter โ and which container is responsible.
Row data: never read by the engine
The Rust graph engine only stores schema metadata. The Python app layer executes SQL and returns results to the browser โ no row data is ever stored or forwarded.
Passwords: never stored
connectors.yaml references environment variable names only. Actual passwords are injected via Docker Compose and exist only in the app container process memory.
Graph engine: fully isolated
The Rust engine container has no external network access. It cannot reach your databases, the internet, or any LLM. It responds only to the Python app container.
Container trust boundaries
Frontend Container
:5173
Outbound connections
- โPython app layer API only
Holds in memory / disk
- ยทNo credentials
- ยทNo database access
- ยทNo persistent storage
App Layer Container
:8000 (loopback)
Outbound connections
- โSource databases (INFORMATION_SCHEMA + SELECT)
- โLLM endpoint (configurable)
- โRust engine (:3030 internal)
Holds in memory / disk
- ยทDB credentials (env vars)
- ยทconnectors.yaml
- ยทTemporary query results
Graph Engine Container
None
Outbound connections
- โNo external connections whatsoever
Holds in memory / disk
- ยทSchema graph (.dat files)
- ยทVector embeddings
- ยทNo credentials
What leaves your network
Every category of data askLenny handles โ where it goes and which container handles it.
| Data | Leaves network? |
|---|---|
| Table names | Yes โ to AI endpoint |
| Column names + SQL types | Yes โ to AI endpoint |
| Natural-language question | Yes โ to AI endpoint |
| Schema context (markdown) | Yes โ to AI endpoint |
| Row data (SELECT results) | Never |
| Database passwords | Never |
| Schema graph / embeddings | Never |
| Query history / results | Never |
Controlling the AI endpoint
Only the Python app container makes LLM calls โ for description generation during enrichment and for SQL generation at query time. All other containers are isolated from external networks.
Point LLM_BASE_URL at any OpenAI-compatible server. Use a local model and zero bytes leave your perimeter.
Cloud LLM (default)
GEMINI_API_KEY + remote endpointSchema names + questions leave (app container only)
Self-hosted Ollama
LLM_BASE_URL=http://ollama:11434/v1Zero bytes leave โ full on-prem
Private Azure OpenAI
LLM_BASE_URL=https://corp.openai.azure.comData stays within your Azure tenant
Network isolation
Exposed ports
- :5173Frontend dashboard โ expose only to internal network or VPN. Never to the public internet.
- :8000App layer API โ bound to 127.0.0.1 by default; browser talks to it via the Compose network.
- :3030Rust engine โ NOT exposed. Accessible only from the app container over the internal Docker bridge.
askLenny never calls home
- โNo telemetry or usage reporting
- โNo licence check or phone-home
- โNo package downloads at runtime โ all dependencies in the image
- โMIT licensed โ audit the source code on GitHub
Known limitations
Being honest about what askLenny does not currently do.
No dashboard authentication
The web UI on port 5173 has no login screen. Run it behind a VPN, internal network boundary, or a reverse proxy with auth. Do not expose it to the public internet.
No application-layer encryption at rest
The .dat files in the engine volume are unencrypted binary. Apply host-level disk encryption (BitLocker, LUKS) if required by your policy.
AI provider privacy policy applies
When using a cloud LLM (Gemini, OpenAI, etc.), your schema names and questions are subject to that provider's data terms. Use a self-hosted model to eliminate this entirely.
SQL execution uses the read account
The same database account used for INFORMATION_SCHEMA discovery also executes user queries. Scope permissions to SELECT on the tables you want queryable.