MOBIGO

GO AT THE CORE OF YOUR MOBILE APP

Write a Go web app. Ship it as a phone app. No server. No JavaScript framework.

v0.1 ALPHA

Zero to phone

One command makes an app. One runs it in your browser with live reload. One puts it on a phone.

No Gradle files to edit, no Java to write, no JavaScript framework to learn. The CLI finds your Android toolchain, compiles your Go with gomobile, wraps it in a Capacitor shell and installs it.

From mobigo new to the app running on an emulator: about 20 seconds.

■ ■ ■~/code
$ mobigo new hello
▸ Creating hello
▸ Fetching dependencies
 
$ cd hello && mobigo dev
mobigo dev: Hello (bridge) on http://127.0.0.1:8080/
 
$ mobigo run android
▸ Generating Go glue
▸ Compiling Go for Android (gomobile bind)
▸ Bundling web assets
▸ Building APK (Gradle)
▸ Built app-debug.apk (13.8 MB)
▸ Launching Hello
[bridge] GET / -> 200
[bridge] POST /tap -> 200

How it works

  1. You write an ordinary Go web app: net/http, html/template, htmx attributes, a little Alpine for UI-only bits.
  2. On the phone, mobigo.js catches every htmx request and hands it to Go as one message. No socket, no server, no port.
  3. Your handler runs on the device: SQLite, crypto, validation, all in Go. It answers with HTML.
  4. Need the phone itself? Set an event from Go (mobigo.Haptic(w, …)) and the native plugin fires.
■ ■ ■STACK.TXT
■ ■ ■app/app.go
func New(env mobigo.Env) (*mobigo.App, error) {
    mux := http.NewServeMux()

    mux.HandleFunc("POST /tap", func(w http.ResponseWriter, r *http.Request) {
        taps := counter.Add(1)      // runs on the phone
        mobigo.Haptic(w, mobigo.HapticLight)
        mobigo.Toast(w, "Nice tap!")
        templates.ExecuteTemplate(w, "taps", taps)
    })

    return &mobigo.App{Handler: mux, Static: static}, nil
}

It's just Go

A MoBiGo app is an http.Handler. Use the standard library, chi, or whatever router you like.

The same handler runs in three places: your browser during mobigo dev, the phone, and your tests.

app_test.go
func TestTap(t *testing.T) {
    phone := mobigotest.New(t, New)
    res := phone.Post("/tap", nil)
    // same bridge as the device
}

Why MoBiGo

NO NETWORK HOP

Go runs on the device. Taps are answered in-process, and the app works offline by default.

ONE CODEBASE

Share packages between your phone app and your server. Write a business rule once.

HTML OVER THE WIRE

No JSON client models to keep in sync. Go renders; htmx swaps.

TESTS THAT MEAN IT

mobigotest drives your app through the real bridge, in plain go test.

NATIVE VIA EVENTS

Haptics, camera and more through Capacitor plugins, triggered from Go with one call.

LIVE RELOAD

mobigo dev rebuilds on save and the page reloads itself.

Real apps, real phones

Keeper: a 2FA app showing six-digit codes for AWS, GitHub and Stripe with countdown timers
KEEPER.APK
The starter app: Hello from MoBiGo, running on android/arm64, with a tap counter
HELLO.APK

KEEPER: A 2FA APP IN GO

The flagship example. Time-based codes (RFC 6238), SQLite storage, and QR scanning, where the camera frames are decoded by Go on the phone.

About 530 lines of Go, plus 130 lines of browser-only JS for the countdown, clipboard and camera.

HELLO: THE STARTER

What mobigo new gives you: a page, a button, a counter Go saves to disk, a toast and a haptic tap, and a test.

Screenshots: Android 16 emulator, tinted for this screen.

Roadmap

  • Go runtime + in-process bridge
  • CLI: new · dev · build · run · doctor
  • Android
  • Test client through the bridge
  • Push from Go to the page
  • Files over the bridge
  • Secure storage (Keychain / Keystore)
  • Component kit (templ)
  • Release builds + signing

Get it

PRE-RELEASEMoBiGo is in early alpha. The public repository opens soon; this is where it will live.
INSTALL
$ go install mobigo.codenexis.com/cmd/mobigo@latest
$ mobigo doctor
$ mobigo new hello

Needs Go, Node, Java 21 and the Android SDK. mobigo doctor tells you what's missing. Full setup →