Open-source AI endpoint monitoring for shadow AI, ghost AI, and unmanaged model usage.
PatronAI helps security, platform, and AI governance teams discover unknown AI endpoints, detect abandoned AI assets, and monitor model usage across applications, agents, cloud services, and developer environments.
It ingests VPC Flow Logs, Packetbeat, and Zeek telemetry, normalises to OCSF, and matches against a deny-all list of 70+ AI providers. A code-scanning layer (Marauder Scan) detects AI framework imports (LangChain, CrewAI, AutoGen, 40+ others), MCP server registrations, and hardcoded API keys at git commit time. An on-prem AI chat assistant (LFM2.5-1.2B-Thinking via llama.cpp) answers questions over your findings β data never leaves your environment.
Single Docker container. Multi-cloud. Grafana dashboards pre-built and provisioned on first boot. Apache 2.0.
| Problem | PatronAI response |
|---|---|
| Shadow AI β employees connecting to AI services that IT never approved | Network layer matches traffic to 70+ AI providers; fires alert within one scan cycle |
| Ghost AI β abandoned models, forgotten API keys, stale integrations | Code layer detects framework imports, API key literals, and MCP server configs in every commit |
| Blind spots on developer laptops | Hook agent scans pip/npm/brew packages, running processes, browser history, IDE plugins every 30 min |
| "We don't know what AI our teams are using" | AI Inventory view aggregates per-owner, per-category, per-provider β clickable mind map |
| Compliance audit trail | R6 Compliance report: SHA-256 hash of all findings for a period, immutable, one-click PDF |
- Outbound calls to 70+ AI provider domains (OpenAI, Anthropic, Cohere, Mistral, Groq, Stability AI, Replicate, Together AI, HuggingFace, Bedrock, Vertex AI, Azure OpenAI, β¦)
- Calls to any domain on the customer deny list (
config/unauthorized_custom.csv)
- AI framework imports β LangChain, LlamaIndex, CrewAI, AutoGen, Haystack, DSPy, Semantic Kernel, Instructor, Guidance, and 40+ others
- MCP server registrations β reads Claude Desktop, Cursor, Continue, Cline JSON
config files; fires
MCP_CONFIG_CHANGEDwhen a config changes - Agent workflows β n8n, Flowise, langflow JSON/YAML files sitting on disk
- Hardcoded API keys β OpenAI
sk-proj-, Anthropicsk-ant-, HuggingFacehf_, and generic bearer token patterns - Vector databases β Chroma, FAISS, LanceDB, Qdrant, Milvus files in home caches or repos
| Surface | What is checked |
|---|---|
| Packages | pip / npm / brew / choco / winget β AI-package patterns |
| Processes | ps aux / tasklist β n8n, Ollama, Cursor, LM Studio, etc. |
| Browser history | Safari Β· Chrome Β· Firefox Β· Edge Β· Brave Β· Arc Β· Opera Β· Vivaldi |
| IDE plugins | VS Code, Cursor, vscode-server, all JetBrains IDEs |
| Containers | docker ps -a image names + last 500 log lines |
| Shell history | bash, zsh, fish, PowerShell ConsoleHost_history |
flowchart TD
subgraph edge["Edge β developer devices"]
hook["Git pre-commit hook\n(Marauder Scan)"]
scan["Endpoint scan\nevery 30 min"]
hb["Heartbeat\nevery 5 min"]
end
subgraph net["Network layer"]
vpc["VPC Flow Logs"]
pb["Packetbeat / Zeek"]
end
subgraph s3["S3 β single source of truth"]
ocsf["ocsf/ raw events"]
findings["findings/ YYYY/MM/DD/"]
config["config/\nprovider lists + settings"]
end
subgraph platform["PatronAI β single Docker container"]
scanner["Scanner engine\nNormalise β Match β Alert"]
dash["Streamlit UI\nExec Β· Manager Β· Support Β· Home"]
grafana["Grafana\ndashboards"]
llm["LFM2.5-1.2B-Thinking\nchat LLM (llama.cpp)"]
end
hook -->|OCSF event| ocsf
scan -->|OCSF event| ocsf
hb -->|OCSF event| ocsf
vpc -->|OCSF event| ocsf
pb -->|OCSF event| ocsf
ocsf --> scanner
config --> scanner
scanner --> findings
scanner -->|SNS / webhook| alerts["Alerts\n(SNS Β· Slack Β· PagerDuty)"]
findings --> dash
findings --> grafana
dash <-.->|AI chat Β· tool calls| llm
Data never leaves your cloud account. All telemetry writes to your own S3 bucket; the scanner reads from S3 every scan cycle; no third-party cloud plane.
Marauder Scan is PatronAI's discovery layer for hidden AI usage. It maps AI activity across source code, configuration files, MCP definitions, API calls, provider domains, network telemetry, and deployed endpoints to help teams find shadow AI, ghost AI assets, and unmanaged model usage.
In short: PatronAI is the platform; Marauder Scan is the mapping and discovery engine inside it.
Before you start β 3 things to know:
- Email-only login. PatronAI has no password field. Add your email to
ALLOWED_EMAILSin.envand that becomes your login credential.- First-boot LLM download. On first
docker compose up, PatronAI auto-downloads LFM2.5-1.2B-Thinking (~750 MB) into a Docker volume. The dashboard opens immediately; AI chat activates once the download finishes (~3-5 min on a typical EC2 connection).- SNS confirmation email. If you use
prereqs.sh, AWS sends a subscription confirmation toADMIN_EMAILS. You must click it β if you miss it, alert emails are silently dropped with no error logged.
Requires: Docker Desktop + an existing S3 bucket (or LocalStack).
git clone https://github.com/giggsoinc/patronai.git
cd patronai/ghost-ai-scanner
cp .env.example .env
# Open .env and fill in the 5 REQUIRED lines:
# PATRONAI_BUCKET, COMPANY_NAME, COMPANY_SLUG, ADMIN_EMAILS, ALLOWED_EMAILS
docker compose up -d
open http://localhost:8501 # macOS β or navigate in any browserLog in with any email you added to ALLOWED_EMAILS. No password required.
Run unit tests first (optional, ~40 s, no Docker needed):
pip install -r requirements.txt
cd .. && pytest ghost-ai-scanner/tests/unit/ -q
# β 380 passedFull local guide: docs/quickstart-local.md
Run all commands from the repo root (patronai/).
# Step 1 β Deploy EC2, transfer code, install Docker + LLM
bash deploy_to_ec2.sh
# Step 2 β SSH in when the script finishes, then run interactive setup
# (creates S3 bucket, SNS topic, IAM role + policy, VPC Flow Logs, writes .env)
bash prereqs.sh
# Step 3 β Start the stack (3 containers: patronai, grafana, nginx)
docker compose up -d
# Step 4 β Populate ENI metadata cache (run once after first deploy)
docker exec patronai python3 scripts/refresh_eni_cache.py| Surface | URL |
|---|---|
| PatronAI UI | http://<ec2-ip>/ |
| Grafana | http://<ec2-ip>/grafana/ |
# Teardown β removes EC2, S3, SNS, IAM, VPC Flow Log
bash teardown.sh| Variable | Required | Default | Purpose |
|---|---|---|---|
PATRONAI_BUCKET |
Yes | β | S3 bucket name (preferred name) |
MARAUDER_SCAN_BUCKET |
Yes | β | Legacy alias for PATRONAI_BUCKET; accepted if PATRONAI_BUCKET is not set |
COMPANY_NAME |
No | β | Shown in UI header |
ADMIN_EMAILS |
Yes | β | Comma-separated admin emails |
CLOUD_PROVIDER |
No | aws |
aws / gcp / azure |
ALERT_SNS_ARN |
No | β | SNS topic ARN for alerts |
SCAN_INTERVAL_SECS |
No | 300 |
Scan cycle frequency |
LOOKBACK_MINUTES |
No | 60 |
First-boot lookback window |
AWS_REGION |
No | us-east-1 |
AWS region |
PUBLIC_HOST |
No | β | EC2 public IP or DNS (no protocol) |
STRICT_MIN_RULES |
No | 50 |
Minimum provider rules before degraded alert |
INCLUDE_CLASSIFIER |
No | 0 |
1 bakes LFM2.5-1.2B-Thinking GGUF (~750 MB) into the image |
LLM_PROVIDER |
No | openai_compat |
LLM backend for AI chat: openai_compat or anthropic |
LLM_BASE_URL |
No | http://localhost:8080 |
Base URL for OpenAI-compatible LLM endpoint |
Bucket variable:
PATRONAI_BUCKETis the canonical name going forward.MARAUDER_SCAN_BUCKETis accepted as a backward-compatible alias (existing deployments do not need to change). Both are passed throughdocker-compose.yml.
All settings are also writable from the Streamlit Settings tab. Values persist
to s3://{bucket}/config/settings.json and apply within one scan cycle.
PatronAI applies a 5-type ENI denylist before normalisation to eliminate noise:
| Type | Match | Reason |
|---|---|---|
| EFS mount target | Description starts with "EFS mount target" |
Storage protocol β all rows NODATA |
| NAT Gateway | InterfaceType = nat_gateway |
Aggregates traffic β masks real src |
| VPC Endpoint | InterfaceType = vpc_endpoint |
AWS PrivateLink only |
| Load Balancer | Description starts with "ELB " |
Inbound forwarder |
| Lambda idle ENI | Description starts with "AWS Lambda VPC ENI" |
Idle NODATA floods |
Only ENIs where RequesterManaged=False and OwnerId matches the customer
account are normalised. ENI metadata cached in S3, refreshed every 6 hours.
Cache miss = fail open β unknown ENIs are never silently dropped.
Add new ENI types without code changes: config/eni_denylist.yaml.
| File | Who edits | Purpose |
|---|---|---|
config/unauthorized.csv |
Giggso baseline (read-only) | 70+ network-side AI providers |
config/unauthorized_custom.csv |
Customer, via UI | Additions; (domain, port) collisions: custom wins |
config/unauthorized_code.csv |
Giggso baseline | 90+ code-side AI framework patterns |
config/unauthorized_code_custom.csv |
Customer, via UI | Code-side additions |
config/authorized.csv |
Customer, via UI | Suppress alerts for approved endpoints |
Lists reload on every scan cycle β no container restart needed. Invalid rows are
rejected at write time by src/matcher/rule_model.py (same validation runs in the UI
and the scanner).
Discovered AI tools queue β the Provider Lists tab surfaces novel AI domains that hit the network in the last 7 days ranked by event count. Admins promote to deny list (one click) or dismiss.
Findings from the code layer are aggregated into the Manager β AI INVENTORY tab:
- MCP servers β name, command, args, env-var keys (no values)
- Agent workflows β n8n, Flowise, langflow files on developer machines
- Scheduled agents β cron / launchd entries mentioning AI keywords
- Vector databases β Chroma, FAISS, LanceDB, Qdrant, Milvus
- Tool registrations β
@tool/@function_tooldecorator counts (no source shipped)
All findings pass through the secret redactor before upload. Findings that still contain secrets after redaction are dropped entirely.
Click any owner in the AI INVENTORY tab β AI Asset Mind Map (radial graph, drill filter on click) and Asset Map (treemap: Owner β Repo β Category β Asset).
Admins generate personalised installers from Settings β Deploy Agents:
- Enter recipient name, email, and platform (Mac / Linux / Windows).
- Optionally set per-user authorised tool domains.
- Click Generate & Send β renders OTP-locked
.sh+.ps1, builds macOS DMG and Windows EXE on EC2 (no Mac or Windows machine needed), emails a 48-hour presigned download link + 6-digit OTP to the recipient. - Status updates from
PENDINGβINSTALLEDas agents check in. - Update any user's tool whitelist without reinstalling: Whitelist button in table.
Install (Mac/Linux):
curl -fsSL "<presigned-link-from-email>" | bash
# Enter 6-digit OTP when promptedDiagnose:
bash ~/.patronai/diagnose.sh # macOS / Linux
powershell -File ~/.patronai/diagnose.ps1 # Windowss3://{bucket}/
βββ config/
β βββ authorized.csv customer allow list
β βββ unauthorized.csv Giggso baseline deny (overwritten on deploy)
β βββ unauthorized_custom.csv customer additions (survives rebuilds)
β βββ unauthorized_code.csv code-layer baseline
β βββ unauthorized_code_custom.csv code-layer customer additions
β βββ HOOK_AGENTS/
β βββ catalog.json index of all provisioned agents
β βββ {token}/
β βββ meta.json recipient metadata + expiry
β βββ setup_agent.sh macOS/Linux installer
β βββ setup_agent.ps1 Windows installer
β βββ status.json install status (PENDING β INSTALLED)
βββ ocsf/YYYY/MM/DD/ incoming OCSF normalised events
βββ ocsf/agent/scans/{token}/ latest endpoint scan per agent
βββ findings/YYYY/MM/DD/
β βββ critical.jsonl
β βββ high.jsonl
β βββ medium.jsonl
β βββ unknown.jsonl
βββ summary/daily/ pre-aggregated stats (dashboards read from here)
βββ chat/{sha256(email)[:16]}/{view}/YYYY-MM-DD.jsonl AI chat history
| View | Who sees it | Content |
|---|---|---|
| Home | Everyone | Welcome, how-it-works, docs, AI chat |
| Exec | All roles | KPI metrics, Sankey, provider exposure map |
| Manager | Admin / Support | AI Inventory mind map, Risk table, Log export |
| Support | Admin / Support | Rules health, fleet, code signals, pipeline |
| Reports | Admin / Support | 7 PDF report types (R1βR7) |
Two pre-built dashboards provisioned on first boot:
- Exec β AI governance: bubble chart, world map, MCP topology, risk heatmap
- Manager β Inventory, risks, raw OCSF browser, alerts
Every dashboard view includes a persistent π€ Ask PatronAI side panel. Powered by llama.cpp running LiquidAI LFM2.5-1.2B-Thinking (Q4_K_M, ~750 MB) on the same EC2 host β no data leaves your environment. Backed by per-user / per-tenant hourly S3 rollups so answers cite real data with S3 paths, not LLM hallucinations. BM25 retrieval over the HTML+MD docs answers product/how-to questions.
Supports tool calls for 10 analytics + help functions:
get_summary_stats Β· get_top_risky_users Β· get_user_risk_profile Β·
query_findings Β· get_fleet_status Β· get_shadow_ai_census Β·
get_recent_activity Β· compare_periods Β· get_help Β· refresh_docs
Pluggable LLM: set LLM_PROVIDER=anthropic or point LLM_BASE_URL at any
OpenAI-compatible endpoint (Ollama, Groq, Together AI, LM Studio). API keys
read from env vars or AWS Parameter Store (/patronai/llm/*).
MCP server (scripts/patronai_mcp_server.py) β exposes the same 8 analytics tools to
Claude Desktop and any MCP-compatible agent over SSH stdio (V1, no HTTP port).
All outbound email goes through one module β src/notify/email.py β
which is the single SES call site for the codebase. Any new feature
that needs to send email imports from here:
from notify.email import send, send_welcome, send_agent_otp, send_alert, ensure_verifiedThree convenience wrappers compose subject + body for each domain:
| Trigger | Wrapper | Sent to | Where in UI |
|---|---|---|---|
| Admin adds a new user | send_welcome(...) |
The new user | Settings β Users β Add User |
| Admin generates an agent install package | send_agent_otp(...) |
The recipient typed in the form | Settings β Deploy Agents β Generate |
| On-demand action-item alert | send_alert(recipients, events) |
ALERT_RECIPIENTS env var (comma-separated) |
Manager β Risks β tick rows β β Send Alert Email |
All three call notify.email.send() underneath, which (a) resolves
SES_SENDER_EMAIL (or legacy PATRONAI_FROM_EMAIL) β falls back to
patronai@<company>.com with a WARN log; (b) calls ensure_verified()
on every recipient unless auto_verify=False so SES sandbox mode
unblocks itself as new users are added; (c) logs the boto3 ErrorCode
on failure so the next time something breaks you can grep notify.email
and read the cause.
For background SNS / webhook alerts on HIGH and CRITICAL findings see
ALERT_SNS_ARN, TRINITY_WEBHOOK_URL, and LOGANALYZER_WEBHOOK_URL
in ghost-ai-scanner/.env.example.
SES sandbox warning: new SES accounts can send only to verified recipients (200/day cap). The
notify.email.send()path auto-verifies recipients, but they still have to click AWS's verification email before subsequent sends to them succeed. Long-term fix: request SES production access in the AWS Console β SES β Account dashboard. ~24h approval, removes the sandbox constraint entirely.
Where to find things. This index is the source of truth β keep it up to date when you add or move modules.
Companion artifacts (machine-readable):
CODE_MAP.csvβ every file withpath, layer, purpose, key_exports, depends_on, notes. Greppable, AI-tool-ingestable.Roadmap.csvβ backlog: P0/P1/P2/OPS items + done log withid, title, category, priority, effort, status, owner, target_release, notes.docs/codebase-tree.htmlβ printable visual tree of the same content (open in browser).
patronai/
βββ README.md # this file
βββ SECURITY.md # vulnerability reporting policy
βββ SECURITY_CONSIDERATIONS.md # production hardening checklist
βββ docs/
β βββ quickstart-local.md # local Docker setup
β βββ github-metadata.md
β βββ launch-issues.md
β βββ archive/ # superseded HTML guides (Mac agent guide is here)
βββ ghost-ai-scanner/ # the actual product
βββ main.py # container entrypoint β spawns scanner / chat / rollup / streamlit threads
βββ docker-compose.yml # 3 services: scanner, grafana, nginx
βββ Dockerfile / Dockerfile.grafana
βββ requirements.txt # all Python deps, license-pinned
βββ iam-policy.json # AWS IAM policy for the runtime role
β
βββ src/ # business logic
β βββ bootstrap.py # validate_env, build_store, load_settings
β βββ threads.py # scanner_loop, alerter_backlog, url_refresh_loop, streamlit_proc
β βββ code_analyser.py # AMBIGUOUS-snippet classifier (llama-cli subprocess)
β βββ code_fallback.py # regex fallback when classifier is offline
β βββ rule_health.py # validate merged rule counts
β β
β βββ normalizer/ # event flattening + provider-name normalisation
β β βββ schema.py # OCSF flat schema definition
β β βββ agent_explode.py # agent payload β flat findings
β β βββ provider_names.py # raw provider β human name (e.g. claude.ai β "Anthropic Claude")
β β
β βββ matcher/ # network-side rule engine
β βββ alerter/dispatcher.py # SNS + Trinity webhook fan-out
β βββ ingestor/ # S3-walk β pipeline β findings store
β β
β βββ store/ # S3 persistence layer (BlobIndexStore)
β β βββ base_store.py # SigV4-forced boto3 client
β β βββ findings_store.py # findings/YYYY/MM/DD/{sev}.jsonl
β β βββ agent_store.py # OTP-gated agent installer packages
β β βββ users_store.py # users/users.json β RBAC source of truth
β β βββ β¦ # cursor, dedup, identity, report, summary, settings
β β
β βββ jobs/ # background workers spawned from main.py
β β βββ hourly_rollup.py # findings β per-user / per-tenant dimension rollups
β β βββ docs_refresh.py # mtime-watch the docs RAG index
β β
β βββ query/ # read-side helpers for chat tools
β β βββ rollup_reader.py # parallel S3 GETs + dimension-aware merge + LRU
β β
β βββ chat/ # LLM agent (the "brain") β used by widget AND MCP server
β β βββ engine.py # tool-call loop
β β βββ tools.py # 8 analytics tools
β β βββ tools_schema.py # JSON schemas
β β βββ prompts.py # system prompt builder
β β βββ help.py # get_help + refresh_docs
β β βββ docs_index.py # BM25 over docs/**/*.{md,html}
β β βββ history.py # S3 chat-history persistence + lifecycle policy
β β βββ llm/ # provider-agnostic transport
β β βββ __init__.py # factory: env / SSM β client
β β βββ base.py # LLMClient ABC
β β βββ openai_compat.py # llama.cpp / Ollama / OpenAI / Groq
β β βββ anthropic.py # Anthropic Messages API
β β
β βββ notify/ # SINGLE email surface for the codebase
β βββ __init__.py # public re-exports
β βββ email.py # send() / send_welcome / send_agent_otp / send_alert / ensure_verified
β
βββ dashboard/ # Streamlit UI (entry: ghost_dashboard.py)
β βββ ghost_dashboard.py
β βββ auth.py / auth_gate.py # email-allowlist auth (SSO is roadmap)
β βββ ui/
β βββ chat/widget.py # ONLY chat code in dashboard/ β Streamlit panel
β βββ manager_tab_*.py # Manager view tabs (inventory, risks, actions, β¦)
β βββ exec_tab_*.py / support_tab_*.py
β βββ tabs/users.py # Users RBAC CRUD β calls notify.email.send_welcome
β βββ tabs/deploy_agents.py # Agent OTP packages β calls render_agent_package
β βββ β¦
β
βββ scripts/ # operator scripts (NOT business logic)
β βββ setup.sh # interactive first-run setup (creates AWS infra + .env)
β βββ start.sh # safe `docker compose up` wrapper (env-shadow guard, STS verify)
β βββ prefetch_model.sh # populate /models named volume before container start
β βββ deploy_to_ec2.sh # SCP code β EC2 + remote install
β βββ render_agent_package.py # OTP + presigned URL + DMG/EXE builder; calls notify.email.send_agent_otp
β βββ patronai_mcp_server.py # FastMCP exposure of chat tools to Claude Desktop / Cursor
β βββ β¦
β
βββ config/ # default rules + provider lists (CSV / YAML)
βββ agent/ # hook-agent fragment templates (sh + ps1)
βββ grafana/ Β· nginx/ # provisioning + reverse-proxy
βββ tests/ # 397 unit tests + integration suite
βββ docs/ # active product docs (HTML + MD)
βββ architecture_chat_mcp.html # chat + MCP architecture deep-dive
βββ user_guide.html # end-user dashboard guide
βββ patronai-agent-{linux,windows}-guide.html
βββ chat-rollups.md # how the per-tenant hourly rollups work
βββ vpc_flow_filtering.md
βββ archive/ # superseded versions of older guides
One-rule guideline: if you're about to write a boto3.client("ses")
call, stop and use notify.email instead. If your new feature needs
something notify.email doesn't have, extend that module β don't fork.
# Unit tests only (~40 seconds, no Docker/LocalStack needed)
cd ghost-ai-scanner
pytest tests/unit/ -q
# β 380 passed
# Full suite including integration
bash scripts/run_regression.sh # unit + integration + docker build
bash scripts/run_regression.sh --unit-only380 tests across 38 files covering normaliser, matcher, alerter, code engine, agent delivery, endpoint scan, fleet heartbeat, chat tools, pipeline health, and more.
CLOUD_PROVIDER=aws # default
CLOUD_PROVIDER=gcp # implements providers/gcp/ adapter
CLOUD_PROVIDER=azure # implements providers/azure/ adapter| Lock-in level | Services |
|---|---|
| HIGH | VPC Flow Logs, IAM, CloudTrail, EC2 describe |
| MEDIUM | S3, SNS, Parameter Store β config change only to swap |
| NONE | Docker, Grafana, Streamlit, Polars β zero change |
Four scenarios not fully detectable (documented for transparency):
- Fully offline local inference β model on disk, data copied via USB. The endpoint scan detects the installed package and running process, but not the data movement itself.
- Personal cloud accounts β personal AWS/GCP with personal credit card. Out of scope for the network layer.
- Web UI manual copy-paste β partially mitigated by browser history check.
- Personal laptop outside VPN β unmanaged device; accepted organisation risk.
| Item | Status |
|---|---|
| LocalStack single-command demo | Planned |
| Azure AD / Entra ID identity binding | Planned |
| SIEM export (Splunk / Elastic / Sentinel) | Planned |
| AWS Bedrock provider detection | Planned |
| Azure OpenAI endpoint detection | Planned |
| GitHub Actions SARIF output | Planned |
| Terraform provider for infrastructure | Planned |
| V2 MCP server β HTTP transport + IAM auth | Planned |
| Provider deny-list community contributions | Open for PRs |
Contributions are welcome! See CONTRIBUTING.md for:
- Local setup and test instructions
- Coding standards (150 LOC cap per file, type hints, try/except on every external call)
- How to add a new AI provider to the deny list
- PR checklist
Quick contribution path β add a new AI provider:
# 1. Add a row to config/unauthorized.csv or config/unauthorized_code.csv
# 2. The rule_model validates automatically β no code change needed
# 3. Submit a PR with evidence (URL, API pattern, risk category)Please do not open a public GitHub issue for security vulnerabilities.
Report privately: security@giggso.com
See SECURITY.md for the full disclosure policy and supported versions.
Apache License 2.0 β Β© 2026 Giggso Inc / Ravi Venugopal
Open-source dependencies:
| Library | License |
|---|---|
| Streamlit | Apache 2.0 |
| boto3 | Apache 2.0 |
| requests | Apache 2.0 |
| Polars | MIT |
| Plotly | MIT |
| Grafana | AGPL v3 |
| Packetbeat | Apache 2.0 |
| Zeek | BSD |
| ReportLab | BSD |
| weasyprint | BSD |
| fastmcp | MIT |
| bcrypt | Apache 2.0 |