| name | desktop |
| description | Use for any desktop app task — planning, designing, implementing pages, components, API routes, and building. |
Desktop App
Stack
Electron + Vite + React frontend with a standalone Hono API server running on Bun.
| Layer | Tech | Location |
|---|
| Shell | Electron | packages/app/electron/ |
| Frontend | React + Vite + TanStack Query | packages/app/src/ |
| API | Hono (Bun) | packages/api/src/ |
| Database | SQLite via bun:sqlite + Drizzle ORM | packages/api/src/database/ |
| RPC | hono/client | packages/app/src/lib/api.ts |
Project Structure
.env ← all env vars (VITE_ = shared, rest = API-only)
packages/
api/
src/
index.ts ← Hono app, exports AppType, serves on port 3001
database/
index.ts ← drizzle + bun:sqlite (data/app.db, WAL mode)
schema.ts ← Drizzle table definitions
drizzle/ ← Generated SQL migrations
drizzle.config.ts ← Drizzle-kit config
package.json
app/
electron/
main.ts ← Electron main process
preload.ts ← Preload script
src/
main.tsx ← React entry, QueryClientProvider
App.tsx ← Root component
lib/
api.ts ← hc<AppType>(import.meta.env.VITE_API_URL)
index.css
index.html
vite.config.ts ← aliases @runable/api → ../api
package.json
Environment Variables
All env vars live in a single .env file at the project root.
| Prefix | Available to | How to access |
|---|
VITE_ | Desktop app (renderer) and API server | App: import.meta.env.VITE_* · API: process.env.VITE_* |
| No prefix | API server only | process.env.* (Bun auto-loads .env) |
The desktop app (Vite/Electron renderer) cannot see non-prefixed vars — Vite strips them at build time. Use this to keep secrets (DB credentials, auth secrets, API keys) out of the frontend.
Example .env:
# API-only (secret, never exposed to renderer)
BETTER_AUTH_SECRET=supersecretkey
DATABASE_URL=data/app.db
# Shared (available to both app and API)
VITE_API_URL=http://localhost:3001
Development
bun run dev
bun run dev:api
bun run dev:app
Database
Schema lives in packages/api/src/database/schema.ts. After changing it:
cd packages/api
bun run db:generate
bun run db:migrate
bun run db:studio
The SQLite database file is at packages/api/data/app.db (gitignored).
API Pattern
The API uses Hono with chained routes. The app consumes it via typed RPC client.
Adding a route in packages/api/src/index.ts:
const app = new Hono()
.use("/*", cors())
.get("/ping", (c) => c.json({ message: "pong" }))
.get("/users", async (c) => {
const result = await database.select().from(users);
return c.json(result);
});
export type AppType = typeof app;
Consuming from the app in packages/app/src/:
const res = await api.users.$get();
const data = await res.json();
Building
bun run build
bun run build:mac
bun run build:win
bun run build:linux
bun run build:all
Feature References
- Authentication — Better Auth setup for Bun + Electron
- AI Agent — AI SDK agent with tools and chat UI
- Payments — Autumn payments and usage tracking
- Email — Transactional emails with Resend
- Analytics — Privacy-friendly usage tracking
- Distribution — Icons, splash screens, store listings