synsema

Turn a server-rendered site into an installable app (PWA) with native push

synsema init --pwa gives any Synsema site a manifest, a service worker, icons and Web Push. It installs on Android, iOS and desktop with a home-screen icon and full screen — nothing native, no store.

An installable web app needs three things a normal site does not have: a manifest, a service worker and a few lines in <head>. Synsema scaffolds all of it, and the push notifications on top, so a site that renders on the server becomes an app on the phone without a native client.

Scaffold it§

synsema init myapp --pwa && cd myapp
synsema serve app.syn

You get public/manifest.webmanifest, public/sw.js (caches the shell, never /api/, never a failed response), public/app.js (registers the worker, shows the install button, subscribes to push), an SVG icon with the PNG sizes generated from it, the API in api.syn (an export routes group with the push routes and their per-route rate limits, mounted by app.syn — since 0.6.19), and a one-off push_keys.syn that prints your VAPID key pair for .env.

The server side is the serve you already know:

require serve(8080)
require file.read("index.html")

serve on 8080
    static "/" from "./public" cache "1h"     -- sw.js must live at /sw.js (scope "/")
    route "GET /"
        give render("index.html", {"title": "My app"})

.webmanifest is served as application/manifest+json, which is what makes browsers accept it.

Push, end to end§

The page reads the public key from GET /api/push/config, subscribes, and posts the subscription to POST /api/push/subscribe. Sending is one builtin:

let r be push_send(sub, {"title": "Order shipped", "body": "Arrives Tuesday"}, {"vapid": keys})
when r["gone"]
    forget_subscription(sub)      -- 404/410: the browser dropped it

Declare one require net(...) per push service you deliver to (FCM, Windows, Mozilla, Apple), and keep the send behind requires auth or inside a cron — never in a public route.

Where it installs§

Android and desktop Chromium show the install prompt; iOS installs from Share → Add to Home Screen and gets push once installed, on a trusted certificate. That last part is one flag — see Automatic HTTPS in one flag. Firefox has no install or app mode: there the site works as a site.

And on the desktop, as a real app§

The same files are a desktop app: synsema init --desktop adds desk.syn, which serves the same page and the same api.syn on 127.0.0.1, opens it as an app window of the browser already on the machine and quits when the last window closes. synsema build desk.syn -o desk --serve --no-console --icon public/icon.svg makes it a .exe with your icon (a .app on macOS, a launcher folder on Linux with --bundle). How it works, and what each OS does with it, is in Ship your web app as a desktop app.

Already have a site? Run synsema init --pwa inside it: nothing of yours is overwritten, the factory files land beside yours. Details and the iOS notes are in the docs, and you can try the language first at try.synsema.org.