Deploy
Synsema viene como un único binario estático — sin runtime en el destino. El bloque serve queda dev-clean en el repo; los knobs de deploy son flags del CLI, así el mismo archivo corre local y en prod sin ediciones.
synsema serve app.syn # dev: :8080, HTTP plano, sin setup
synsema serve app.syn --port 443 --domain example.com,www.example.com --tls-auto admin@example.com # prod: HTTPS
Flags de serve
| Flag | Efecto |
|---|---|
--port N | Override de serve on N y concede serve(N). |
--domain d1,d2 | Dominios ACME (SAN). |
--tls-auto <email> | HTTPS automático (ACME) — es el switch dev↔prod; necesita un dominio. |
--tls-cert / --tls-key | TLS manual (mutuamente excluyente con --tls-auto). |
--bind <addr> | Dirección de bind (default 0.0.0.0). |
Precedencia: flag del CLI > cláusula del archivo > default. Sin --tls-auto → HTTP plano (dev); con --tls-auto → TLS (prod).
Config por entorno (mantené el repo dev-clean)
El archivo .syn no cambia entre tu laptop y el server, así que git pull en prod nunca genera conflictos. Todo lo que difiere vive fuera del código.
Knobs del server → flags del CLI. --port, --domain, --tls-auto (tabla de arriba). El flag gana sobre la cláusula del archivo.
Valores de la app → el entorno. Todo lo que leas con env("NAME", default) — p. ej. la URL pública canónica que alimenta tus tags canonical/OG/sitemap:
require env("SITE_URL")
let SITE be env("SITE_URL", "http://127.0.0.1:8080") -- default dev; prod lo pisa
Seteala en prod desde el entorno (Environment=SITE_URL=https://example.com en systemd, -e SITE_URL=… en Docker). La resolución es process env > .env > default del código, así que no hace falta editar el repo.
Knobs del runtime → el entorno del proceso (no .env). SYNSEMA_SERVE_WORKERS=N dimensiona el pool de workers del intérprete que atienden requests (default: uno por core, mín. 2). Subilo para handlers I/O-bound — más requests en vuelo, a costa de más RAM. A diferencia de los valores de la app, este knob configura el runtime mismo, así que se lee una sola vez, del entorno del proceso, antes de que arranque el primer server — Environment= en systemd, -e en Docker, export en una shell. Ponerlo en .env no hace nada: el .env sólo alimenta a env()/secret() dentro de tu programa.
¿Dos apps en un mismo host? Dale a cada una su puerto — el código queda idéntico: synsema serve api.syn --port 8081 y synsema serve admin.syn --port 8082. Como --port pisa serve on N y concede serve(N), no cambia nada en ninguno de los dos archivos.
HTTPS, paso a paso (certificado gratis y auto-renovable)
--tls-auto obtiene un certificado gratis de Let's Encrypt (ACME) y lo auto-renueva — sin certbot, sin cron. Qué necesitás:
1. Un dominio apuntando a la IP del server (un registro DNS A/AAAA).
2. Puertos 80 y 443 alcanzables — el 80 responde el desafío ACME una vez, después redirige al 443.
3. Correr con el dominio + un email de contacto:
synsema serve app.syn --port 443 --domain example.com,www.example.com --tls-auto you@example.com
Al primer arranque Synsema obtiene el certificado, sirve HTTPS en 443, redirige HTTP→HTTPS, y auto-renueva ~30 días antes del vencimiento (90 días). Los certificados se guardan (SYNSEMA_CERT_DIR → ~/.synsema/certs), así que un restart los recarga (sin re-emisión → sin rate-limit).
El challenge de ACME escucha en el puerto 80 (HTTP-01), y la CA tiene que poder
alcanzarlo desde internet. Si ese puerto ya está tomado —otro servidor, un mapeo de
contenedor, un sidecar— movelo con SYNSEMA_ACME_HTTP_PORT=8080 y redirigí el :80
externo hacia ahí. Como el resto de los knobs de runtime se lee del **entorno del
proceso**, no del .env.
¿Ya tenés un certificado? Usá --tls-cert cert.pem --tls-key key.pem (mutuamente excluyente con --tls-auto). Bajo systemd, dale al servicio un HOME/StateDirectory escribible (abajo) para que pueda guardar los certificados.
synsema daemon vs systemd — elegí uno
synsema daemon start app.syn— manager de background integrado (status/logs/stop/restart). Sin config de SO, pero sin arranque al boot ni restart al crashear. Bueno para dev / máquinas sin systemd.- systemd — supervisor del SO: arranque al boot (
enable),Restart=always, logs en journald. Usalo para producción.
[Service]
ExecStart=/usr/local/bin/synsema serve /opt/app/app.syn --port 443 --domain example.com --tls-auto admin@example.com
Restart=always
StateDirectory=synsema # HOME escribible para el fallback ~/.synsema/certs
Varios sitios en un mismo host (Synsema es su propio edge proxy)
Dos procesos no pueden bindear ambos :443, y no necesitás nginx/Caddy. Un proceso Synsema es el edge: termina TLS para cada dominio (un cert SAN) y enruta por Host a cada backend, que corre en HTTP plano en un puerto privado.
-- edge.syn — TLS + routing por Host para cada sitio del server
require serve(443)
require net("127.0.0.1") -- deny-by-default: el edge sólo habla con localhost
serve on 443
host "example.com"
route "GET /" -- raíz: /*path NO matchea "/"
proxy to "http://127.0.0.1:8080"
route "GET /*path"
proxy to "http://127.0.0.1:8080"
route "POST /*path"
proxy to "http://127.0.0.1:8080"
host "docs.example.com"
route "GET /"
proxy to "http://127.0.0.1:8791"
route "GET /*path"
proxy to "http://127.0.0.1:8791"
route "POST /*path"
proxy to "http://127.0.0.1:8791"
Corré el edge con un cert SAN para todos los dominios; cada backend en HTTP plano, sólo localhost, con su propio repo/versión/servicio:
synsema serve edge.syn --port 443 --domain example.com,docs.example.com --tls-auto admin@example.com
synsema serve app.syn --port 8080 --bind 127.0.0.1
synsema serve docs.syn --port 8791 --bind 127.0.0.1
- Gotcha de la raíz:
route "GET /*path"exige ≥1 segmento — no matchea/. Agregároute "GET /"también (por método) para que la home llegue al backend. - Por método:
routeliga método+path — declará cada método que reenvíes (GET, POST, …). proxy toreenvía status + content-type + body y los headers end-to-end del upstream (Location,Set-Cookie,Cache-Control,ETag, …), así redirects, cookies y caching funcionan a través del edge; los hop-by-hop se descartan.- Deploys independientes: reiniciás un backend sin tocar los otros; cada uno puede correr su propia versión de Synsema detrás del mismo edge.
Docker y Kubernetes
La imagen es solo el binario de Synsema — el mismo binario estático prebuilt que instala el sitio. El Dockerfile del repo lo baja de la release de GitHub y verifica su checksum (no compila); vos montás tu programa .syn en /app:
docker build -t synsema . # o --build-arg SYNSEMA_VERSION=v0.4.9
docker run -d --restart unless-stopped -e ANTHROPIC_API_KEY=sk-... \
-v "$PWD":/app -w /app -p 8080:8080 synsema serve app.syn
En K8s, command: ["synsema", "serve", "/app/app.syn"] e inyectá claves vía secretKeyRef. Los secretos/config vienen del environment en prod (override de .env) — ver Secretos.
Actualizar
Un server corriendo no se auto-actualiza. synsema update reemplaza el binario en disco; systemctl restart lo aplica. Los certs TLS persisten (guardados + auto-renovados), así que un restart los recarga — sin pegarle al rate-limit de Let's Encrypt.