https://github.com/kvnpetit/tynd
Desktop apps in TypeScript. Small native binaries, zero-codegen typed RPC, no bridge language to learn.
https://github.com/kvnpetit/tynd
bun cross-platform desktop desktop-app electron-alternative framework rust tauri-alternative typescript wails-alternative webview
Last synced: 12 days ago
JSON representation
Desktop apps in TypeScript. Small native binaries, zero-codegen typed RPC, no bridge language to learn.
- Host: GitHub
- URL: https://github.com/kvnpetit/tynd
- Owner: kvnpetit
- License: apache-2.0
- Created: 2026-04-16T07:25:14.000Z (16 days ago)
- Default Branch: main
- Last Pushed: 2026-04-20T04:30:37.000Z (12 days ago)
- Last Synced: 2026-04-20T06:29:08.919Z (12 days ago)
- Topics: bun, cross-platform, desktop, desktop-app, electron-alternative, framework, rust, tauri-alternative, typescript, wails-alternative, webview
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@tynd/cli
- Size: 1.56 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Notice: NOTICE
Awesome Lists containing this project
README
# **Tynd**
### Desktop apps in TypeScript. One language, native binary, no glue code.
[](https://github.com/kvnpetit/tynd/actions/workflows/ci.yml)
[](./LICENSE)
[](https://www.npmjs.com/package/@tynd/cli)
[](https://www.npmjs.com/package/@tynd/core)
[](https://www.npmjs.com/package/@tynd/cli)
[](https://github.com/kvnpetit/tynd/stargazers)
[](https://www.typescriptlang.org)
[](https://bun.sh)
[](./CONTRIBUTING.md)
[](https://github.com/kvnpetit/tynd/commits/main)
[](https://github.com/kvnpetit/tynd/issues)
**Native window. Zero network. TypeScript end-to-end.**
Write your backend and frontend in TypeScript, ship a small native binary β no extra language to learn, no codegen step.
```bash
bunx @tynd/cli create my-app
```
**-> [Getting Started in 5 minutes](./GETTING_STARTED.md)**
---
## β¨ At a glance
- π¦ **TypeScript top to bottom.** Backend, frontend, IPC, config β same language, no codegen.
- 𧬠**Two runtime modes, one API.** Start with `lite` (~6.5 MB native binary, no extra runtime needed). Switch to `full` with one config line when you need Bun's JIT or native-binding npm packages.
- π **Native window, zero network.** No HTTP server, no loopback TCP port, no firewall prompt. Frontend and IPC ride a native custom scheme.
- π§° **27 OS APIs, identical in both modes** β `fs` (+ watcher), `sql` (embedded SQLite), `http`, `websocket`, `terminal` (real PTY), `compute` (blake3/sha/CSPRNG), `dialog`, `tray`, `menu` (accelerators + checkbox/radio), `notification`, `clipboard` (image + HTML), `shell`, `process`, `sidecar`, `singleInstance` (with argv forwarding + deep links), `shortcuts` (system-wide hotkeys), `keyring` (OS-encrypted secrets), `autolaunch` (start at boot), `store`, `updater` (signed auto-update with Ed25519), `workers`, `app` (name/version/exit/relaunch), `os` / `path`, `tyndWindow` (multi-window + events), plus typed emitters and streaming RPC.
- β‘ **Zero-copy binary IPC.** Multi-MB payloads (`fs.readBinary`, `compute.hash`, β¦) skip JSON/base64 entirely β roughly **5-10Γ faster** than the usual webview-framework binary path.
- π¦ **First-class installers.** `tynd build --bundle` emits `.app` / `.dmg` / `.deb` / `.rpm` / `.AppImage` / NSIS `.exe` / `.msi`. Installer tools auto-download on first build β no manual setup. Built-in code signing (`signtool` on Windows, `codesign` + optional notarization on macOS).
- π‘οΈ **Security defaults.** Auto-injected CSP on every HTML response, OS-backed secret storage via `keyring`, Ed25519-signed auto-updater so tampered binaries are rejected.
- π **Streaming RPC that doesn't flinch.** Async-generator backend handlers stream to the frontend with per-chunk flow control + batched DOM updates β 10k+ yields/sec without freezing the UI.
- π¨ **Framework-agnostic.** React, Vue, Svelte, Solid, Preact, Lit, Angular β anything that outputs a pure SPA.
---
## π§ Why Tynd
- **TypeScript backend, no codegen.** The frontend types backend calls from `typeof backend` β no `.d.ts` generation, no IDL, no schema file.
- **Native OS webview.** The final binary doesn't ship a browser β it uses WebView2 / WKWebView / WebKitGTK, so you inherit the OS's paint loop, font stack, and accessibility for free.
- **Two modes, one API.** `lite` for the smallest binary (~6.5 MB, embedded JS engine). `full` when you need Bun's JIT or native-binding npm packages. Switch via one config line β every OS API works the same in both.
- **Zero network IPC.** RPC and assets never touch TCP β no loopback port, no firewall prompt on first launch.
See [COMPARISON.md](./COMPARISON.md) for the full Tynd vs Tauri / Wails / Electron matrix (39 categories, 500+ rows).
---
## π§ͺ How it works
```
TypeScript backend Native OS window
ββββββββββββββββββββββββββ βββββββββββββββββββββββββ
export async function greet() βββ IPC β await api.greet("Alice")
events.emit("ready", payload) βββ push ββΊ api.on("ready", handler)
β
βΌ
tynd-full β your TypeScript runs on Bun, wrapped in a native host
tynd-lite β your TypeScript runs inside the native host, no extra runtime
```
**Zero network.** Frontend assets and IPC ride a native custom scheme β no HTTP server, no loopback port, no firewall prompt. Multi-MB binary payloads (`fs.readBinary`, `fs.writeBinary`, `compute.hash`, β¦) skip JSON entirely via a dedicated binary channel.
### Two runtime modes
| | `lite` | `full` |
|---|---|---|
| JS runtime | embedded interpreter β ships inside the native binary | Bun, packed into the native binary |
| Hot JS speed | interpreter β fine for IPC glue, slower on tight loops | **Bun JIT β often 10-100Γ faster on CPU-bound JS** |
| IPC overhead | in-process, no serialization hop | one serialization hop (OS pipe) |
| `fs` / `http` / `websocket` / `sql` / `compute` / `terminal` / β¦ | β same API | β same API |
| JS-level `fetch` / `Bun.file` / `bun:sqlite` | β (use the Tynd API) | β |
| Pure-JS npm packages | β (bundled) | β |
| npm with native bindings | β | β |
| Binary size | smaller (~6.5 MB) | larger (~44 MB, Bun compressed) |
| Startup | faster (everything in-process) | slower (spawns Bun) |
-> See [`RUNTIMES.md`](RUNTIMES.md) for the full comparison (APIs, performance, detection, examples).
---
## π Requirements
**[Bun](https://bun.sh) is required for app developers.** Tynd is a Bun-first framework β the CLI, the dev server, and the full runtime all run on Bun. Node.js is not supported as a replacement.
```bash
# macOS / Linux / WSL
curl -fsSL https://bun.sh/install | bash
# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"
```
**End users of your built app need nothing** β whichever runtime mode you picked is already packed into the distributed binary.
---
## π Quick start
```bash
bunx tynd create my-app
cd my-app
bun run dev
```
With a specific framework / runtime:
```bash
bunx tynd create my-app --framework react --runtime full
bunx tynd create my-app --framework vue --runtime lite
```
---
## π οΈ CLI
```bash
tynd create [name] # scaffold a new project (interactive if no args)
--framework react|vue|svelte|solid|preact|lit|angular
--runtime full|lite
tynd dev # start app in development mode (HMR)
tynd start # classic JS build (frontend + backend) then run (no HMR)
tynd build # bundle backend + frontend -> single binary
--bundle [targets] # + installers: app, dmg, deb, rpm, appimage, nsis, msi (or "all")
tynd init # add tynd to an existing project
tynd clean # remove build artifacts (.tynd/cache, release/)
tynd validate # check config and project structure
tynd upgrade # upgrade @tynd/cli and @tynd/core to latest
tynd info # show environment info (Bun version, WebView, paths)
tynd keygen # generate an Ed25519 keypair for the auto-updater
tynd sign # sign a file with an updater private key
```
---
## π¨ Supported frameworks
| Framework | Scaffold | Build | Fast Refresh (HMR) |
|---|---|---|---|
| React | β
Vite `react-ts` | β
| β OK; breaks if React Compiler is enabled |
| Vue / Svelte / Solid / Preact | β
Vite `-ts` | β
| β
|
| Lit | β
Vite `lit-ts` | β
| β» Full reload only β Web Components by design |
| Angular | β
Angular CLI | β
| β» Full reload by default (opt-in HMR via `ng serve --hmr`) |
`tynd init` also detects existing **Vite**, **CRA**, **Angular CLI**, **Parcel**, **Rsbuild**, and **Webpack** setups.
**Blocked (SSR):** Next.js, Nuxt, SvelteKit, Remix, Gatsby, Blitz.js, RedwoodJS, SolidStart, Angular Universal, Analog, Qwik City, Astro, TanStack Start, Vike. Use the SPA variant instead (plain Svelte vs SvelteKit, plain Solid vs SolidStart, β¦).
See [`FRAMEWORKS.md`](FRAMEWORKS.md) for the full matrix, per-framework notes, output-dir rules, and the React Compiler workaround.
---
## π Project structure
```
my-app/
βββ tynd.config.ts β project config
βββ package.json
βββ backend/
β βββ main.ts β backend entry β app.start() here
βββ src/ β frontend source (React / Vue / Svelteβ¦)
βββ main.tsx
```
---
## π§© API
Tynd exposes three surfaces β backend module, typed frontend RPC, and direct OS APIs:
| Surface | Import | Purpose |
|---|---|---|
| **Backend** | `@tynd/core` | `app.start`, `app.onReady`, `app.onClose`, `createEmitter` |
| **Frontend RPC** | `@tynd/core/client` | `createBackend()` β typed proxy |
| **OS APIs** | `@tynd/core/client` | `app`, `dialog`, `tyndWindow`, `monitors`, `menu`, `clipboard`, `shell`, `notification`, `tray`, `process`, `fs`, `shortcuts`, `keyring`, `autolaunch`, `store`, `updater`, `os`, `path`, `http`, `websocket`, `sql`, `sidecar`, `terminal`, `compute`, `workers`, `singleInstance` |
| **Web APIs** | `@tynd/core/client` | `fetch`, `WebSocket`, `EventSource`, `crypto`, `URL`, `Blob`, `FormData`, `AbortController`, `TextEncoder`, β¦ (re-exports for `import * as tynd`) |
Short example:
```typescript
// backend/main.ts
import { app, createEmitter } from "@tynd/core"
export const events = createEmitter<{ ready: { message: string } }>()
export async function greet(name: string): Promise { return `Hello, ${name}!` }
app.start({
frontendDir: import.meta.dir + "/../dist",
window: { title: "My App", width: 1200, height: 800, center: true },
})
```
```typescript
// src/App.tsx
import { createBackend, fs, process, terminal } from "@tynd/core/client"
import type * as backend from "../backend/main"
const api = createBackend()
const msg = await api.greet("Alice") // string β
await fs.writeText("data.json", JSON.stringify(state))
const { stdout } = await process.exec("git", { args: ["status"] })
```
**Full reference:** [API.md](./API.md) β every method, signature, and example.
---
## βοΈ tynd.config.ts
```typescript
import type { TyndConfig } from "@tynd/cli"
export default {
runtime: "full", // "full" | "lite"
backend: "backend/main.ts", // backend entry file
frontendDir: "dist", // built frontend assets (relative to project root)
icon: "public/favicon.svg", // optional β auto-detected; SVG recommended
window: {
title: "My App",
width: 1200,
height: 800,
center: true,
},
} satisfies TyndConfig
```
### `TyndConfig` fields
| Field | Default | Description |
|---|---|---|
| `runtime` | `"full"` | `"full"` or `"lite"` β see [RUNTIMES.md](RUNTIMES.md) |
| `backend` | `"backend/main.ts"` | Backend entry file |
| `frontendDir` | `"frontend"` | Built frontend output directory |
| `frontendEntry` | β | Simple TS/JS entry (no framework) β auto-bundled by tynd |
| `devUrl` | auto | Dev server URL override |
| `devCommand` | auto | Dev server start command override |
| `icon` | auto | App icon path β auto-detected from `public/favicon.{ico,png,svg}` |
| `binaryArgs` | β | Extra args passed to the `tynd-full` / `tynd-lite` binary |
| `window` | β | Default window options (title, width, height, center) |
**Icon auto-detection order** (SVG first β single source of truth renders pixel-perfect at every size):
`public/{favicon,icon,logo}.svg` -> `public/{favicon,icon,logo}.{ico,png}` -> `assets/icon.{svg,png,ico}` -> `icon.{svg,png,ico}`. One source feeds every output β each build produces a multi-size ICO (16/32/48/256) for Windows, a multi-entry ICNS (32/128/256/512/1024) for macOS `.app`, and a full hicolor PNG tree (16..512) for `.deb` / `.rpm` / `.AppImage`. PNG sources degrade to single-size; `.ico` sources pass through to Windows bundles and are skipped (with a warning) for macOS/Linux. Set `icon` explicitly to override.
---
## πͺ WebView runtime
| OS | WebView | Pre-installed? |
|---|---|---|
| Windows 10/11 | WebView2 (Edge Chromium) | β
|
| macOS | WKWebView | β
|
| Linux | WebKitGTK 4.1 | β οΈ `sudo apt install libwebkit2gtk-4.1-0` |
---
## ποΈ Building from source (contributors only)
App authors don't need this section β `@tynd/host`'s postinstall downloads pre-built binaries automatically. If you want to build the native host yourself:
```bash
# Requirements
rustup install stable
# Linux only
sudo apt install libgtk-3-dev libwebkit2gtk-4.1-dev \
libjavascriptcoregtk-4.1-dev libsoup-3.0-dev \
libxdo-dev
# Build
cargo build --release -p tynd-full
cargo build --release -p tynd-lite
```
### Repo layout
```
packages/
βββ host-rs/ β native host library (window + IPC + OS APIs)
βββ full/ β tynd-full binary (packs Bun inside the host)
βββ lite/ β tynd-lite binary (embeds a lightweight JS engine)
βββ host/ β @tynd/host (npm: postinstall downloads pre-built binaries)
βββ core/ β @tynd/core (TypeScript: app, createEmitter, client API)
βββ cli/ β @tynd/cli (TypeScript: tynd create/dev/build/info)
```
---
## π Documentation
**-> [tynd.kvnpetit.com/docs](https://tynd.kvnpetit.com/docs)** β live versioned docs site.
| Doc | When to read it |
|---|---|
| [GETTING_STARTED.md](./GETTING_STARTED.md) | First project β scaffold, dev, build, ship in 5 min |
| [API.md](./API.md) | Every backend / frontend / OS API with signatures + examples |
| [RUNTIMES.md](./RUNTIMES.md) | `lite` vs `full` β what each exposes, when to pick which |
| [FRAMEWORKS.md](./FRAMEWORKS.md) | Per-framework matrix (React, Vue, Svelte, Solid, Angular, Preact, Lit) |
| [COMPARISON.md](./COMPARISON.md) | Tynd vs Tauri / Wails / Electron across 39 categories |
| [SIGNING.md](./SIGNING.md) | Code signing + notarization workflows per OS |
| [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) | Common errors: missing binary, WebView2, GTK deps, Gatekeeper, etc. |
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Dev loop, commit style, PR process |
| Per-package READMEs | [@tynd/core](./packages/core/README.md) Β· [@tynd/cli](./packages/cli/README.md) Β· [@tynd/host](./packages/host/README.md) |
| Working examples | [playground/full](./playground/full/README.md) (LLM chatbot, full runtime) Β· [playground/example](./playground/example/README.md) (minimal demo, lite runtime) |
| [docs/](./docs/README.md) | Next.js 16 + Nextra 4 docs site (landing + versioned docs). Static export, deploys to Cloudflare Pages. `bun --cwd docs run dev` |