LobeHub began as LobeChat, one of the best-looking ChatGPT-style interfaces on GitHub, and has grown into a workspace for agents. You build an agent by describing it, give it skills and MCP plugins from a library of more than 10,000, put several in a group to work on one task, schedule runs, and let them keep a structured, editable memory of how you work. It connects to OpenAI, Anthropic, Google and dozens of other providers, Ollama included, and the desktop app can drive coding agents such as Claude Code and Codex installed on your machine.
It is a Next.js and TypeScript code base of about 2.5 million lines (plus three million lines of JSON), built by LobeHub LLC with 357 contributors and 818 releases so far. You can use the hosted version at app.lobehub.com or self-host it with Docker. The licence is the LobeHub Community License: Apache-2.0 plus a condition that developing and distributing a derivative work needs a commercial licence.
- Repository: github.com/lobehub/lobehub
- Licence: custom (Other)
- Language: TypeScript. Stars: 82.8K. Forks: 15.9K. Last push: Sep 27, 2026.
- Scan: safe, Sep 27, 2026, commit 413ec82
Who it is for
Individuals and teams who want one good-looking interface for many model providers, people building multi-agent workflows without writing code, and self-hosters comfortable running a Postgres-backed Docker stack.
Getting started
1. Create a folder for the deployment
mkdir lobehub-db && cd lobehub-db2. Run the setup script, which downloads the Compose file and writes a .env with generated keys
bash <(curl -fsSL https://lobe.li/setup.sh)3. Start the stack
docker compose up -dThe short link redirects to docker-compose/setup.sh in the repository, 863 lines, which is run as is. It downloads the Compose file, SearXNG settings and an .env template from the project's GitHub and generates secrets such as the JWKS key; the only sudo in it is a printed hint about Docker group membership.
Safety scan
We cloned lobehub/lobehub at commit 413ec82 on Sep 27, 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.
- Five secret hits, none a leak: two test keys in unit tests and two example AWS keys in the environment-variable docs. The fifth deserves a note: docker-compose/production/grafana/init_data.json seeds Casdoor, the login server in that variant of the stack, with a fixed RSA private key for signing tokens. Anyone deploying it unchanged shares a signing key with everyone else who did, so generate a new certificate first. The default setup script uses a different Compose file without Casdoor.
- 17 pattern hits. Thirteen are curl | sh install commands for third-party coding agents (Amp, Claude Code, Cursor, Devin, Factory, Kimi, OpenCode, Qoder, xAI) and for Ollama, in the table the desktop app displays as install instructions when a tool is missing, and in its tests. The other four are three long prompt or generated files and a Telegram webhook test.
- Install hooks: the desktop app's postinstall rebuilds Electron native modules, a macOS notifications package compiles its native addon on install, and the root prepare script points Git at the repository's hooks. The two binaries are epub test fixtures, and the seven bare-IP URLs are tests for the filter that recognizes private addresses.
- The main app's pnpm lockfile is git-ignored, so the advisory check only covered the desktop app's lockfile: one critical (tar 6.2.1, a build-time archive library) and 54 high, mostly in XML and URL parsing libraries. The web app's resolved dependency versions cannot be checked from the repository.
- 32 workflows. Two use pull_request_target (release tagging and pull request assignment) and neither checks out the pull request; none of the 37 third-party actions is pinned to a commit. Renovate, a security policy and a code of conduct are present.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 5 candidates found and read; see the notes above. |
| Suspicious code | 17 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 3 npm lifecycle scripts. 10 installer scripts (one can call sudo) |
| Committed binaries | 2 executable or compiled objects committed; listed under the raw findings. |
| CI workflows | 32 workflows. 2 use pull_request_target, none check out the pull request head. 37 of 37 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often github.com, app.lobehub.com, cdn.example.com, lobehub.com. 7 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 88 advisories across 1,876 pinned packages: 1 critical, 54 high, 31 moderate, 2 low. apps/desktop/pnpm-lock.yaml: 1,876 packages, 88 advisories. |
| Project hygiene | Has security policy, automated dependency updates, licence file, contributing guide. Missing CodeQL. |
| 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 (5, redacted)
| Where | Rule | Match |
|---|---|---|
| apps/server/src/modules/ModelRuntime/index.test.ts:708 | private-key | -----B…--- (27 chars) |
| docker-compose/production/grafana/init_data.json:592 | private-key | -----B…--- (31 chars) |
| docs/self-hosting/environment-variables/model-provider.mdx:218 | aws-access-key | AKIA5S…WBR (20 chars) |
| docs/self-hosting/environment-variables/model-provider.zh-CN.mdx:216 | aws-access-key | AKIA5S…WBR (20 chars) |
| packages/env/src/__tests__/scm.test.ts:5 | private-key | -----B…--- (31 chars) |
Pattern hits (17)
| Where | Rule | Match |
|---|---|---|
| apps/server/src/services/bot/platforms/telegram/helpers.test.ts:82 | exfil-host | expect(url).toBe(`https://api.telegram.org/bot${BOT_TOKEN}/setWebhook`); |
| packages/builtin-skills/src/lobehub/manifest.ts:7 | very-long-line | 6978 chars |
| packages/heterogeneous-agents/src/config.test.ts:55 | download-piped-to-shell | install: { commands: ['curl https://cursor.com/install -fsS | bash'] }, |
| packages/heterogeneous-agents/src/config.test.ts:64 | download-piped-to-shell | 'curl -fsSL https://app.factory.ai/cli | sh', |
| packages/prompts/src/chains/goalManager.ts:23 | very-long-line | 3928 chars |
| packages/sdk/src/generated/sdk.gen.ts:5 | very-long-line | 11514 chars |
| packages/types/src/agent/heterogeneousAgent.ts:68 | download-piped-to-shell | 'curl -fsSL https://ampcode.com/install.sh | bash', |
| packages/types/src/agent/heterogeneousAgent.ts:97 | download-piped-to-shell | 'curl -fsSL https://claude.ai/install.sh | bash', |
| packages/types/src/agent/heterogeneousAgent.ts:171 | download-piped-to-shell | commands: ['curl https://cursor.com/install -fsS | bash'], |
| packages/types/src/agent/heterogeneousAgent.ts:199 | download-piped-to-shell | 'curl -fsSL https://app.factory.ai/cli | sh', |
| packages/types/src/agent/heterogeneousAgent.ts:228 | download-piped-to-shell | 'curl -fsSL https://cli.devin.ai/install.sh | bash', |
| packages/types/src/agent/heterogeneousAgent.ts:258 | download-piped-to-shell | 'curl -fsSL https://x.ai/cli/install.sh | bash', |
| packages/types/src/agent/heterogeneousAgent.ts:282 | download-piped-to-shell | 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash', |
| packages/types/src/agent/heterogeneousAgent.ts:310 | download-piped-to-shell | commands: ['curl -fsSL https://opencode.ai/install | bash'], |
| packages/types/src/agent/heterogeneousAgent.ts:356 | download-piped-to-shell | 'curl -fsSL https://qoder.com/install | bash', |
| src/components/OllamaSetupGuide/index.tsx:158 | download-piped-to-shell | curl -fsSL https://ollama.com/install.sh | sh |
| src/store/chat/slices/agentRun/actions/__tests__/heterogeneousAgentExecutor.test.ts:2232 | download-piped-to-shell (test/example) | installCommands: ['curl -fsSL https://claude.ai/install.sh | bash'], |
URLs to bare IP addresses (7)
| Where | Rule | Match |
|---|---|---|
| packages/utils/src/url.test.ts:247 | ip-literal-url | expect(isLocalOrPrivateUrl('http://172.15.0.1')).toBe(false); |
| packages/utils/src/url.test.ts:248 | ip-literal-url | expect(isLocalOrPrivateUrl('http://172.32.0.1')).toBe(false); |
| packages/utils/src/url.test.ts:260 | ip-literal-url | expect(isLocalOrPrivateUrl('http://8.8.8.8')).toBe(false); |
| packages/utils/src/url.test.ts:261 | ip-literal-url | expect(isLocalOrPrivateUrl('http://1.1.1.1')).toBe(false); |
| packages/utils/src/url.test.ts:262 | ip-literal-url | expect(isLocalOrPrivateUrl('http://192.167.1.1')).toBe(false); |
| packages/utils/src/url.test.ts:263 | ip-literal-url | expect(isLocalOrPrivateUrl('http://192.169.1.1')).toBe(false); |
| packages/utils/src/url.test.ts:289 | ip-literal-url | expect(isLocalOrPrivateUrl('http://256.256.256.256')).toBe(false); |
npm lifecycle scripts (3)
apps/desktop/package.jsonpostinstall:electron-builder install-app-depspackage.jsonprepare:git rev-parse --git-dir > /dev/null 2>&1 && git config core.hooksPath .githooks || truepackages/electron-mac-notifications/package.jsoninstall:node scripts/build-native.mjs
Installer scripts (10)
- .agents/acceptance/scripts/setup-auth.sh, 561 lines
- .agents/acceptance/scripts/setup-auth.test.sh, 198 lines
- .agents/skills/run-eval-harbor/scripts/bootstrap.sh, 67 lines
- .agents/skills/run-eval-harbor/scripts/run-smoke.sh, 128 lines
- .conductor/setup.sh, 108 lines
- apps/desktop/scripts/update-test/run-test.sh, 207 lines, uses sudo
- apps/desktop/scripts/update-test/setup.sh, 90 lines
- apps/desktop/scripts/update-test/start-server.sh, 78 lines
- docker-compose/setup.sh, 863 lines, uses sudo; talks to github.com, lobehub.com, raw.githubusercontent.com
- scripts/setup-test-postgres-db.sh, 22 lines
Committed binaries (2)
packages/file-loaders/test/fixtures/test.epub: JAR, 4 KBsrc/libs/document-loaders/loaders/epub/__tests__/demo.epub: JAR, 4 KB
Worst known vulnerabilities (24 of 88)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-23hp-3jrh-7fpw | critical | tar@6.2.1 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.8.13 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.8.13 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.8.13 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.8.13 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-x4fp-j954-r2f4 | high | @xmldom/xmldom@0.8.13 | xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.9.10 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-3px3-54cx-rmw9 | high | @xmldom/xmldom@0.9.10 | xmldom: Creation-time XML Name/QName validation is bypassable via an embedded line terminator, allowing injection on the… |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.9.10 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-6mj3-qw4j-hgrw | high | @xmldom/xmldom@0.9.10 | xmldom: HTML raw-text closing-tag case mismatch causes output amplification |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.9.10 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-g53g-w8rj-fmg7 | high | @xmldom/xmldom@0.9.10 | xmldom PI grammar regex ReDoS: quadratic backtracking on unterminated processing instructions |
| GHSA-vr34-hp96-76pp | high | @xmldom/xmldom@0.9.10 | xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.9.10 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-mh99-v99m-4gvg | high | brace-expansion@1.1.16 | brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash |
| GHSA-rgw5-rvv9-x895 | high | brace-expansion@1.1.16 | brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation |
| GHSA-mh99-v99m-4gvg | high | brace-expansion@2.1.2 | brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash |
| GHSA-rgw5-rvv9-x895 | high | brace-expansion@2.1.2 | brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation |
Workflows worth a look
- .github/workflows/auto-tag-release.yml: pull_request_target
- .github/workflows/claude-pr-assign.yml: pull_request_target
By the numbers
| Stars | 82.8K |
|---|---|
| Forks | 15.9K |
| Contributors | 357 |
| Commits | 14.1K |
| Open issues | 372 |
| Open pull requests | 117 |
| Releases | 818 |
| Latest release | v2.2.18 |
| Licence | custom |
| Main language | TypeScript |
| Project age | 3 years |
| Last push | Sep 27, 2026 |
| Tracked files | 17,495 |
| Lines of code | 6M |
| Checkout size | 241 MB |
Lines by language: JSON 3.2M, TypeScript 2.5M, Markdown 167.7K, YAML 34.8K, JavaScript 13K, Shell 8,283.
Questions
Is LobeHub free?
The self-hosted version is free to run, including commercially as a service, under the LobeHub Community License. What the licence restricts is developing and distributing a derivative work based on it, which needs a commercial licence from LobeHub LLC. The hosted version at app.lobehub.com is a separate service run by LobeHub, and model usage is billed by whichever providers you connect.
Can LobeHub use local models?
Yes. Ollama is a built-in provider with its own setup guide in the app, and an OpenAI-compatible server such as vLLM, LocalAI or llama.cpp's can be used by overriding the OpenAI base URL. Hosted providers need your own API keys. Note that the README's API key section recommends a third-party OpenAI reseller through a referral link; a reseller sees every prompt you send through it.
LobeHub or Open WebUI?
Both are self-hosted chat front ends for many providers. Open WebUI is the usual choice in front of Ollama, with document search and a Python plugin system, and installs as one container. LobeHub leans toward hosted providers and agents, with groups, schedules, memory and a skills library in a more designed interface, but its Docker stack needs Postgres and object storage. Check both licences before rebranding either.
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.
