synsema
ES Español

Un programa, tres apps — web, teléfono y escritorio desde el mismo archivo de Synsema

Una app de Synsema es un sitio web con HTTPS automático, una app instalable en el teléfono con push nativo y una app de escritorio en su propia ventana con tu ícono — desde un programa renderizado en el servidor y un módulo de API. Sin toolkit nativo, sin segundo código, sin tienda obligatoria.

La forma habitual de llegar a tres plataformas son tres bases de código, o un framework que envuelve un navegador y te pide que lo aprendas. Synsema toma el otro camino: la app es un programa renderizado en el servidor, y cada plataforma es una manera de distribuir el mismo programa. El sitio, la app del teléfono y la de escritorio comparten las rutas, las plantillas, las capacidades y los secretos, porque son un archivo con tres entradas.

Tres destinos, un archivo§

synsema init myapp --desktop && cd myapp                          # the scaffold: app.syn, desk.syn, api.syn, public/

synsema serve app.syn --domain app.example.com --tls-auto you@example.com   # the WEBSITE, with Let's Encrypt
                                                                  # the PHONE: the same site installs from the browser (PWA)
synsema build desk.syn -o desk --serve --no-console --icon public/icon.svg  # the DESKTOP app (Windows: desk.exe)
synsema build desk.syn -o desk --serve --icon public/icon.svg --bundle      # macOS .app / Linux folder + install.sh

El sitio es app.syn bajo serve: HTTPS con una bandera, la página renderizada en el servidor, cada ruta respondiendo también Markdown y JSON a los agentes. La app del teléfono es el mismo sitio: un manifiesto, un service worker y los íconos hacen que se instale en Android y en iOS con ícono en la pantalla de inicio, pantalla completa, un shell offline honesto y Web Push nativo — eso es lo que genera --pwa. La app de escritorio es desk.syn: la misma página y la misma API atadas a 127.0.0.1, abiertas como ventana de app del navegador que ya está en la máquina, con tu ícono en el ejecutable y sin consola; termina cuando se cierra la última ventana.

La misma API en todas partes§

La API vive una sola vez, en api.syn, como un grupo de rutas:

-- api.syn
export routes api
    route "GET /api/ping"
        give ok({"pong": true, "at": now()})
    route "POST /api/push/subscribe"
        rate_limit 10 per minute
        expect body {endpoint: text, keys: map}
        ...

Las dos entradas lo montan — mount api.api — y cada una declara las capacidades que las rutas necesitan (require net("fcm.googleapis.com"), require secret("VAPID_PRIVATE_KEY")): un módulo no lleva require, lo lleva la entrada. Los límites de tasa y los timeouts por ruta viajan con el grupo, así que el servidor público y la app de escritorio local hacen cumplir las mismas reglas. En una construcción con --sandbox o --cap-set, el techo viaja con el binario y no se puede levantar desde adentro.

Qué recibe cada destino, con honestidad§

Sitio webTeléfono (PWA)Escritorio
Cómo corresynsema serve en tu servidorel mismo servidor, instalado desde el navegadorun binario en la máquina del usuario
Íconofaviconícono de pantalla de inicio, del manifiestoel ícono del ejecutable (--icon), el de la ventana desde la página
Ventanapestaña del navegadorpantalla completa, sin cromo del navegadorventana de app del navegador, sin barra de direcciones ni consola
PushWeb Push nativo (Android; iOS una vez instalada, sobre HTTPS)las notificaciones del navegador y Web Push, mientras el proceso corre
Offlineel shell abre sin redel servidor es local
Ciclo de vidalo corrés vosel teléfono corre el navegadortermina al cerrar la última ventana, o a los 30 s sin ventana
VerificadoproducciónAndroid y Windows realesen vivo en Windows 11; macOS por CI en Apple Silicon; Linux por tests

Dónde se corta: no hay bandeja del sistema (no existe API web para eso), Firefox no tiene modo app (una pestaña), iOS necesita un certificado confiable para el service worker y el push, y un .app de macOS descargado sin Developer ID se encuentra con Gatekeeper como cualquier app sin firmar. Las tiendas son opcionales: la Microsoft Store acepta una PWA directamente, Play la acepta a través de una Trusted Web Activity y la App Store todavía quiere un envoltorio nativo — nada de lo cual el lenguaje pretende ocultar.

Por qué se sostiene§

Renderizar en el servidor significa que el teléfono y el escritorio muestran lo que decidió el servidor, no una copia de la lógica. La negociación de contenido significa que las mismas rutas son una API para agentes. Las capacidades deny-by-default significan que el binario de escritorio que le das a alguien solo toca lo que dicen sus líneas require, y los secretos quedan sellados dentro del proceso en todas las plataformas. Y synsema build significa que todo el conjunto — motor, programa, plantillas, public/ — es un archivo para regalar.

Probalo: instalá Synsema (0.6.19 o más nuevo), synsema init myapp --desktop, y después synsema serve app.syn para el sitio y synsema serve desk.syn para la ventana. Las dos guías — Tu app en el teléfono y Tu app en el escritorio — listan cada archivo que escribe el andamiaje y cada bandera, con qué está verificado en dónde.