Onyx is a self-hosted AI assistant that knows what is in your company's tools. It connects to more than 50 sources such as Google Drive, Slack, Confluence, Jira, SharePoint and GitHub, indexes them along with their permissions, and answers questions with citations, so people only get answers drawn from documents they could open anyway. On top of search it adds deep research, custom agents, web search, MCP actions, a code sandbox and voice.
It runs as Docker containers or on Kubernetes, works with hosted models (OpenAI, Anthropic, Gemini) or self-hosted ones (Ollama, vLLM, LiteLLM), and can be fully air-gapped. A Lite mode under 1 GB of memory gives you the chat interface and agents without document indexing. People reach it through web and desktop apps, a Slack or Discord bot, an MCP server for coding agents, and a Chrome extension.
The Community Edition is MIT-licensed; folders named ee hold the Enterprise Edition features (SSO, RBAC, analytics and similar) under a commercial licence. About 33,000 stars and 210 contributors, at v5.0.0. The full stack is heavy, with a search index, background workers and model servers, so plan a proper server for it.
- Repository: github.com/onyx-dot-app/onyx
- Licence: custom (Other)
- Language: Python. Stars: 33K. Forks: 4,646. Last push: Oct 26, 2026.
- Scan: safe, Oct 26, 2026, commit 616a4b1
Who it is for
Teams that want a ChatGPT-style assistant grounded in their own documents while keeping the data on their own infrastructure, and IT staff weighing self-hosted alternatives to enterprise AI search products.
Getting started
1. Install the Onyx CLI, which then walks you through a Docker deployment
curl -fsSL https://onyx.app/install_onyx.sh | bash2. Or deploy the lightweight Lite mode without prompts
curl -fsSL https://raw.githubusercontent.com/onyx-dot-app/onyx/main/deployment/docker_compose/install.sh | bash -s -- --lite --no-promptThe installer in the repository (deployment/docker_compose/install.sh) is only a bootstrap: it installs onyx-cli to ~/.local/bin (or /usr/local/bin as root), adds that folder to your PATH in your shell profile, and hands over to the CLI. Set DISABLE_TELEMETRY=true on the services if you do not want usage reporting.
Safety scan
We cloned onyx-dot-app/onyx at commit 616a4b1 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- The 40 secret hits are test fixtures (fake Slack tokens and private-key markers) and the same placeholder text, BEGIN PRIVATE KEY, in nine translation files, where it is the hint shown in an SSO key field. The 14 bare-IP URLs are SSRF tests checking that Onyx refuses them.
- Fleet telemetry is on by default. Per the project's deployment/fleet-telemetry.md, each deployment reports to telemetry.onyx.app every few minutes under a random deployment key, including query counts and timings, connector settings and counts, and signup email domains. Setting DISABLE_TELEMETRY=true on all services stops it.
- The six pattern hits are the curl | bash installer and comments about it. The script downloads onyx-cli from the project's GitHub releases, writes a PATH line to your shell profile, and only suggests sudo if it cannot write to the chosen folder.
- 101 advisories, none critical: the Python backend's uv.lock (55, 21 high, in aiohttp, fsspec and similar), the Tauri desktop app's Cargo.lock, and three Go modules for the CLI and tooling.
- 52 workflows. One uses pull_request_target, an ownership check for the ee folders, and it does not check out the pull request. All 272 third-party actions are pinned to a commit; Dependabot, CodeQL and a security policy are present.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 40 candidates found and read; see the notes above. |
| Suspicious code | 6 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 3 npm lifecycle scripts. 1 Cargo build script. 8 installer scripts (one fetches and runs a remote script; one edits your shell profile; one can call sudo) |
| Committed binaries | None. |
| CI workflows | 52 workflows. 1 uses pull_request_target, none check out the pull request head. 0 of 272 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often github.com, onyx.example, www.googleapis.com, identity.example.com. 14 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 101 advisories across 1,273 pinned packages: 0 critical, 28 high, 30 moderate, 13 low, 30 unrated. cli/go.mod: 46 packages, 5 advisories; desktop/src-tauri/Cargo.lock: 569 packages, 25 advisories; terraform-provider-onyx/go.mod: 56 packages, 10 advisories; tools/ods/go.mod: 166 packages, 16 advisories; uv.lock: 459 packages, 55 advisories. |
| Project hygiene | Has security policy, automated dependency updates, CodeQL, licence file, contributing guide. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (40, redacted)
Pattern hits (6)
| Where | Rule | Match |
|---|---|---|
| cli/cmd/deploy_install.go:80 | download-piped-to-shell | // newInstallOnyxCmd is the documented curl|bash entrypoint: a top-level alias |
| cli/internal/deploy/install/install.go:1026 | download-piped-to-shell | // "./install.sh --shutdown", which doesn't exist after curl|bash). Interactive |
| deployment/docker_compose/install.ps1:748 | download-piped-to-shell | $bashCmd = "curl -fsSL '$installUrl' | bash -s -- $($bashArgs -join ' ')" |
| deployment/docker_compose/install.sh:8 | download-piped-to-shell | # curl -fsSL https://raw.githubusercontent.com/onyx-dot-app/onyx/main/deployment/docker_compose/install.sh | bash |
| deployment/docker_compose/install.sh:9 | download-piped-to-shell | # curl -fsSL https://raw.githubusercontent.com/onyx-dot-app/onyx/main/deployment/docker_compose/install.sh | bash -s -- --lite --no-prompt |
| deployment/docker_compose/install.sh:385 | download-piped-to-shell | # `curl ... | bash` leaves stdin on the pipe, and the CLI only prompts when |
URLs to bare IP addresses (14)
npm lifecycle scripts (3)
mobile/package.jsonpreinstall:cd ../web/lib/shared && bun install && bun run buildweb/lib/opal/package.jsonprepare:bun run buildweb/lib/shared/package.jsonprepare:bun run build
Installer scripts (8)
- .github/scripts/run-cve-alerts.sh, 80 lines
- .zed/run-all-k8s.sh, 257 lines
- cli/internal/deploy/deployfiles/embedded/data/nginx/run-nginx.sh, 40 lines
- deployment/aws_ecs_fargate/cloudformation/deploy.sh, 193 lines
- deployment/aws_ecs_fargate/cloudformation/uninstall.sh, 77 lines
- deployment/data/nginx/run-nginx.sh, 40 lines
- deployment/docker_compose/install.ps1, 1,122 lines, fetches and runs a remote script; talks to desktop.docker.com, docs.docker.com, download.docker.com, github.com, raw.githubusercontent.com
- deployment/docker_compose/install.sh, 394 lines, uses sudo, edits your shell profile; talks to docs.onyx.app, github.com, raw.githubusercontent.com
Worst known vulnerabilities (24 of 101)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| RUSTSEC-2026-0194 | high | quick-xml@0.38.4 | Quadratic run time when checking a start tag for duplicate attribute names |
| RUSTSEC-2026-0195 | high | quick-xml@0.38.4 | Unbounded namespace-declaration allocation in `NsReader` enables memory-exhaustion denial of service |
| GHSA-3g6h-r5qx-f6hq | high | quinn-proto@0.11.16 | Peer minimum ACK delay can panic quinn-proto when ACK_FREQUENCY transmission is enabled |
| GHSA-qfwj-vfxf-92j2 | high | quinn-proto@0.11.16 | Stream reassembly memory-exhaustion guard in quinn-proto can be bypassed |
| GHSA-rg2x-37c3-w2rh | high | github.com/docker/docker@28.5.2+incompatible | Docker: Race condition in docker cp allows bind mount redirection to host path |
| GHSA-x86f-5xw2-fm2r | high | github.com/docker/docker@28.5.2+incompatible | Docker: `PUT /containers/{id}/archive` executes container binary on the host |
| GHSA-259r-337f-4rfw | high | github.com/klauspost/compress@1.18.6 | Compress/s2: Integer Overflow or Wraparound and Out-of-bounds Write in compress |
| GHSA-2g87-pp6c-29x5 | high | aiohttp@3.14.3 | aiohttp: Multiple instances of excessive or unbounded memory/CPU use |
| GHSA-5wr8-gcqf-gqf3 | high | aiohttp@3.14.3 | aiohttp server lacks an initial request/header timeout |
| GHSA-9xw5-w5jj-pvcg | high | aiohttp@3.14.3 | AIOHTTP: File descriptor exhaustion in multipart requests |
| GHSA-27vj-qcqg-25rc | high | fsspec@2025.10.0 | fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution |
| GHSA-r77w-qph3-7vf8 | high | graphql-core@3.2.8 | GraphQL-core: Denial of service via comment tokens bypassing the parser's max_tokens limit |
| GHSA-vj8h-fx38-h3vc | high | graphql-core@3.2.8 | GraphQL-core: Denial of service via quadratic complexity in the OverlappingFieldsCanBeMerged validation rule |
| GHSA-6c7f-9265-g8fw | high | pypdf@6.19.0 | pypdf: Possible long runtimes for repeated references to the same missing object |
| GHSA-gmh7-3g9r-mjh5 | high | pypdf@6.19.0 | pypdf: Possible long runtimes for large xref table header limits |
| GHSA-jr96-gvgh-j3r4 | high | pypdf@6.19.0 | pypdf: Possible long runtimes for repeated malformed cross-reference entries (Follow-up) |
| CVE-2026-4538 | high | torch@2.9.1 | |
| GHSA-63cw-57p8-fm3p | high | torch@2.9.1 | |
| GHSA-394h-qwmc-48pf | high | virtualenv@20.36.1 | virtualenv: Xonsh activation literals depend on the script decoding codec |
| GHSA-5vjq-rrrf-7h2q | high | virtualenv@20.36.1 | virtualenv activate runs commands from the environment name in zsh with PROMPT_SUBST |
| GHSA-6w3r-xr62-4xcp | high | virtualenv@20.36.1 | virtualenv: Bash prompt values can change meaning under older compatibility modes |
| GHSA-8rjx-v5ww-45pp | high | virtualenv@20.36.1 | virtualenv activation scripts run code from a path that contains a placeholder name |
| GHSA-94p9-xgh2-xp45 | high | virtualenv@20.36.1 | virtualenv: Downloaded seed wheels (pip/setuptools) are not integrity-checked before use |
| GHSA-c947-3pg5-gm8q | high | virtualenv@20.36.1 | virtualenv activate.fish runs commands from a path or prompt with a backslash before a quote |
Workflows worth a look
- .github/workflows/pr-ee-ownership-check.yml: pull_request_target
By the numbers
| Stars | 33K |
|---|---|
| Forks | 4,646 |
| Contributors | 210 |
| Commits | 11.2K |
| Open issues | 181 |
| Open pull requests | 387 |
| Releases | 392 |
| Latest release | v5.0.0 |
| Licence | custom |
| Main language | Python |
| Project age | 3 years |
| Last push | Oct 26, 2026 |
| Tracked files | 9,508 |
| Lines of code | 2M |
| Checkout size | 93 MB |
Lines by language: Python 987.1K, TypeScript 449.3K, JSON 266.5K, Go 104.3K, Markdown 76.6K, YAML 50.2K.
Questions
Is Onyx free?
The Community Edition is free and MIT-licensed, and covers search, chat, agents and actions. The Enterprise Edition adds features such as SSO, role-based access and analytics under a paid licence, and Onyx Cloud is a hosted version if you would rather not run it yourself.
Can Onyx run with a local model?
Yes. It supports self-hosted models through Ollama, vLLM and LiteLLM as well as the big hosted providers, and the embedding model can run locally too, which is what makes a fully air-gapped deployment possible.
What does Onyx need to run?
Docker or Kubernetes. The standard deployment runs several containers, including a search index, background workers and model servers, so give it a real server. Lite mode needs under 1 GB of memory but skips document indexing, so it is a chat interface rather than company search.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
