goleo

module
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: MIT

README ΒΆ

Goleo

Build cross-platform desktop and mobile apps with a Go backend and a web UI.

Write your app logic in Go. Build your interface with any web framework β€” Vue, React, Svelte, or plain HTML. Ship one codebase to Windows, macOS, Linux, Android, iOS, and the web (PWA).

Goleo is in the Tauri / Wails class β€” a compiled backend driving the OS's native webview β€” but the backend is Go, and a single project targets desktop and mobile and PWA.

app.Bridge().Handle("greet", func(ctx context.Context, args json.RawMessage) (any, error) {
    var p struct{ Name string `json:"name"` }
    _ = json.Unmarshal(args, &p)
    return "Hello, " + p.Name + "!", nil
})
import { invoke } from '@goleo/bridge'
const msg = await invoke<string>('greet', { name: 'World' })   // β†’ "Hello, World!"

πŸ“– Developer Guide

Full step-by-step docs live in docs/guide/ β€” installation, project setup, building, packaging (icons + metadata + installers), deploying & auto-update, wiring up your app, RPC, native menus, system tray, and mobile (device dev + sideloading).

Why Goleo

  • πŸ–₯️ One codebase, six targets β€” Windows, macOS, Linux, Android, iOS, PWA.
  • 🐹 Real Go on the backend β€” full concurrency, the standard library, and the Go ecosystem for your app logic and system access.
  • 🎨 Any web frontend β€” Vue (default template), React, Svelte, vanilla β€” anything Vite builds.
  • πŸ”Œ Typed bridge β€” call Go from JS with invoke(), push events from Go with Emit(); generate TypeScript types for every command.
  • πŸ“¦ Small single binary β€” the frontend is embedded via //go:embed; no bundled Chromium (uses the OS webview).
  • πŸš€ Ships itself β€” installers, code signing/notarization, and a signed auto-updater built into the CLI.
  • πŸͺŸ Native desktop integration β€” multi-window, system tray, single-instance, launch-on-login, deep links, and a signal-based lifecycle.
  • πŸ”’ Secure by design β€” an opt-in runtime capability ACL and a hardened loopback bridge.
  • πŸ“± Device features β€” clipboard, filesystem, dialogs, share, camera, geolocation, battery, sensors, notifications, and more β€” native where it counts, graceful browser fallbacks elsewhere.

Quick start

# Install the CLI (npm, or `go install github.com/daforester/goleo/cli/goleo@latest`)
npm install -g @goleo/cli

# Scaffold a project (Vue + Vite + @goleo/bridge). Prompts for a template;
# add --demo for the full host-feature showcase. `npx @goleo/cli new` works too.
goleo new my-app
cd my-app
cd frontend && npm install && cd ..

# Live development (Go backend + Vite HMR)
goleo dev

# Build a single binary for the current platform
goleo build

Then edit backend/app/app.go (startup + feature wiring), backend/commands/ (your Go commands), and frontend/src/ (your UI). That's it.


Project layout

my-app/
β”œβ”€β”€ goleo.json              # project + bundle/publish config
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ app/app.go          # the one file you edit: config, command + feature registration
β”‚   β”œβ”€β”€ commands/           # your backend commands
β”‚   β”œβ”€β”€ main.go             # generated (desktop entry point) β€” do not edit
β”‚   └── gomobile/           # generated (mobile entry points) β€” do not edit
└── frontend/               # your web app (Vite; Vue by default, swappable)
    └── src/main.ts         # inits the bridge

main.go and the gomobile/ glue are regenerated on every build β€” all your logic lives in backend/app/app.go and backend/commands/.


The bridge β€” calling Go from your UI

Go β€” register a command:

app.Bridge().Handle("saveNote", func(ctx context.Context, args json.RawMessage) (any, error) {
    var note struct{ Title, Body string }
    if err := json.Unmarshal(args, &note); err != nil {
        return nil, err
    }
    // ...persist it...
    return map[string]string{"id": "note-1"}, nil
})

// Push an event to the frontend at any time:
app.Emit("sync:done", map[string]any{"count": 42})

TypeScript β€” invoke it and listen for events:

import { invoke, on } from '@goleo/bridge'

const { id } = await invoke<{ id: string }>('saveNote', { title: 'Hi', body: '...' })
const unsubscribe = on('sync:done', (data) => console.log('synced', data))

Run goleo generate types to produce frontend/src/goleo.d.ts with a fully typed invoke() for every built-in command.


Desktop capabilities

Configure in runtime.Config and call the bridge helpers from @goleo/bridge.

Multi-window, tray, and lifecycle:

a = runtime.New(runtime.Config{
    Title:            "My App",
    InProcessWindows: true,               // Windows: extra windows in-process (else child processes)
    SingleInstance:   true,               // a second launch focuses the running one
    URLScheme:        "myapp",            // register myapp:// deep links
    Background:       true,               // headless controller (window(s) on demand)
    Tray: &runtime.TrayConfig{            // optional system tray
        Tooltip: "My App",
        Items: []runtime.TrayItem{
            {Label: "Open", OnClick: func() { a.OpenWindow(runtime.WindowOptions{Path: "/"}) }},
            {Label: "Quit", OnClick: func() { a.Quit() }},
        },
    },
})
import { openWindow, quitApp, getInitialURL, onDeepLink,
         enableAutostart, checkForUpdate, applyUpdate } from '@goleo/bridge'

await openWindow({ path: '/settings', width: 600, height: 400 })
onDeepLink((url) => route(url))               // myapp:// links while running
await enableAutostart()                       // launch on login
if ((await checkForUpdate()).available) await applyUpdate()
Capability Config / API
Additional windows App.OpenWindow Β· openWindow/closeWindow/listWindows Β· WindowOptions.ExitOnClose
System tray Config.Tray + Config.Background
Single instance Config.SingleInstance β†’ app:secondInstance event
Launch on login enableAutostart / disableAutostart / isAutostartEnabled
Deep links Config.URLScheme β†’ getInitialURL + onDeepLink
Graceful quit App.Quit() Β· quitApp()
Auto-update runtime.RegisterUpdater + checkForUpdate/applyUpdate

Device features & storage

Register the ones you use in backend/app/app.go; the CLI auto-detects them for mobile builds. Each has a TS wrapper with a browser fallback, so PWA/dev degrade gracefully.

runtime.RegisterDesktopFeatures(a.Bridge())  // clipboard, dialogs, filesystem
runtime.RegisterStore(a.Bridge())            // persistent key/value store
runtime.RegisterShare(a.Bridge())            // native share sheet
runtime.RegisterClipboard(a.Bridge())
runtime.RegisterCamera(a.Bridge())           // + geolocation, battery, sensors, vibration, nfc, ble…
import { storeSet, storeGet, share, readText } from '@goleo/bridge'
await storeSet('theme', 'dark')
const theme = await storeGet<string>('theme')
await share({ title: 'Goleo', url: 'https://example.com' })

Available: clipboard Β· dialogs Β· filesystem Β· key/value store Β· share Β· camera Β· geolocation Β· battery Β· sensors Β· vibration Β· wake-lock Β· NFC Β· Bluetooth (BLE) Β· notifications Β· background Β· push.


Security β€” capability ACL

Bridge access is permissive by default; set a Policy to switch to deny-by-default and scope sensitive plugins (Tauri-style, enforced centrally on every invoke):

app.SetPolicy(&runtime.Policy{
    Allow:   []string{"goleo:store*", "greet"},   // exact or "prefix*"; core info commands always allowed
    FSRoots: []string{"/home/me/app-data"},        // scope filesystem access
})

The loopback bridge is also hardened in production (loopback-only bind, origin allow-list, per-launch token).


Distribution & auto-update

The CLI takes you from build to a self-updating installer:

goleo build --bundle             # native installer: .msi/NSIS Β· .dmg Β· .deb/.rpm
goleo build --bundle --publish   # + write an ed25519-signed update manifest
goleo generate updater-key       # keypair for signing updates

Code signing/notarization are env-driven (GOLEO_WIN_CERT, GOLEO_MAC_IDENTITY, GOLEO_APPLE_ID, …) so secrets stay out of the repo and CI can inject them. The in-app updater verifies the signed manifest before applying an update.


CLI reference

Command What it does
goleo new <name> Scaffold a new project
goleo dev Dev mode β€” Go backend + Vite HMR
goleo dev pwa Frontend-only PWA dev (no Go backend)
goleo build [target] Build for current/windows/linux/darwin/android/ios/pwa
goleo build --bundle Also produce a native installer
goleo build --publish Also write the signed update manifest
goleo emulate android Build + run on a connected Android emulator/device
goleo generate types Generate goleo.d.ts typed bridge bindings
goleo generate updater-key Generate an ed25519 update-signing keypair

Platform support

Desktop app Mobile app PWA Native webview
Windows βœ… β€” βœ… WebView2 (cgo-free)
macOS βœ… β€” βœ… WKWebView (cgo-free)
Linux βœ… β€” βœ… WebKitGTK (cgo-free)
Android β€” βœ… βœ… system WebView (gomobile)
iOS β€” βœ… βœ… WKWebView (gomobile)

All three desktop builds are fully cgo-free and cross-compile from one machine β€” the glaze binding (WKWebView / WebKitGTK / WebView2 via purego) drives the OS webview with no C toolchain in the loop. Mobile builds use gomobile; iOS requires macOS + Xcode.


How it works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   invoke / events    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Web UI (OS webview)    β”‚ ◄──────────────────► β”‚  Go backend + Bridge   β”‚
β”‚  @goleo/bridge          β”‚  WebSocket Β· HTTP    β”‚  your commands + feats β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  Β· native bind       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Dev: Vite serves the UI with HMR and proxies to the Go backend.
  • Production: the frontend is embedded in the Go binary (//go:embed) and served from a hardened loopback server; the OS webview loads it.
  • Mobile: a gomobile .aar/.xcframework runs the same Go backend inside the platform's native WebView.

Deeper docs: AGENTS.md (architecture), docs/roadmap.md (masterplan + status), SPIKES.md (feasibility findings), docs/comparison.md (vs Tauri v2 & Wails v3).


Status

Feature-complete and shipping-ready on all targets via the paths above. The desktop webview is now cgo-free on all three OSes (macOS / Linux / Windows via glaze); see the roadmap for what's next.

License

MIT

Directories ΒΆ

Path Synopsis
cli
cmd
goleo command
autostart
Package autostart registers/unregisters an app to launch on login.
Package autostart registers/unregisters an app to launch on login.
deeplink
Package deeplink registers a custom URL scheme (myapp://) so links launch or wake the app.
Package deeplink registers a custom URL scheme (myapp://) so links launch or wake the app.
fs
nfc
singleinstance
Package singleinstance enforces a single running instance of an app and forwards a later launch's args to the primary (e.g.
Package singleinstance enforces a single running instance of an app and forwards a later launch's args to the primary (e.g.
store
Package store is a simple persistent key/value store backed by a JSON file in the app data directory.
Package store is a simple persistent key/value store backed by a JSON file in the app data directory.
updater
Package updater is a desktop auto-update client.
Package updater is a desktop auto-update client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL