Ir al contenido

Instalar Heskala on-premise

Cómo instalar y correr Heskala on-premise: bajá el bundle desde GitHub Releases, ejecutá el instalador y en pocos minutos tenés tu ERP corriendo 100% en tu infraestructura.

Heskala en modo on-prem corre 100% en tu infraestructura: backend de Convex self-hosted, base SQLite y frontend servido como estático. La activación se hace con una licencia firmada (token ECDSA) que define qué módulos incluidos, cuántos usuarios y cuántas sucursales tenés.

  • Docker 20.10+ (con docker compose v2)
  • 2 GB de RAM disponibles
  • 10 GB de disco para los datos
  • Linux, macOS o Windows con WSL2
  • Conexión a internet (para bajar el bundle, las imágenes y validar la licencia la primera vez)

No necesitás acceso al código fuente ni a ninguna cuenta de GitHub. El bundle se descarga desde este sitio y las imágenes ghcr.io/heskala/frontend y ghcr.io/heskala/convex-deployer desde GHCR (ambas públicas).

Si solo querés levantarlo y empezar:

Ventana de terminal
curl -sL https://docs.heskala.com/downloads/heskala-onprem-0.8.0.tar.gz \
| tar -xz -C /tmp heskala-onprem-0.8.0 \
&& bash /tmp/heskala-onprem-0.8.0/install.sh

El bundle se hostea en este sitio (público) porque el repo de GitHub es privado y sus release assets no son accesibles sin auth. El one-liner funciona para cualquiera, sin cuenta ni token.

El script:

  • Verifica que Docker esté corriendo
  • Crea ~/heskala-onprem y copia el compose desde el bundle
  • Genera un INSTANCE_SECRET random
  • Te pregunta el INSTANCE_NAME (default: heskala-onprem)
  • Levanta los 4 servicios y espera al backend healthy
  • Genera el admin key y lo guarda en .admin-key (chmod 600)
  • Arranca el deployer, que sube las funciones y setea las env vars necesarias
  • Imprime las URLs y los próximos pasos

Para personalizar (directorio, RESEND_API_KEY, etc.):

Ventana de terminal
# Directorio custom
bash /tmp/heskala-onprem-0.8.0/install.sh /opt/heskala
# Con Resend habilitado
RESEND_API_KEY=re_xxx bash /tmp/heskala-onprem-0.8.0/install.sh
# Nombre de instancia custom
HESKALA_INSTANCE=mi-empresa bash /tmp/heskala-onprem-0.8.0/install.sh

Para upgrade a una versión más nueva, bajá el nuevo bundle y corré el install apuntando al directorio existente:

Ventana de terminal
curl -sL https://docs.heskala.com/downloads/heskala-onprem-0.8.0.tar.gz \
| tar -xz -C /tmp heskala-onprem-0.8.0 \
&& bash /tmp/heskala-onprem-0.8.0/install.sh /opt/heskala

El script detecta que el stack ya existe y solo hace docker compose pull + up -d sin perder datos.

Para ver qué versiones están disponibles para descargar: https://docs.heskala.com/downloads/

Todo se configura por variables de entorno en el .env (o pasándolas al install.sh). Las de correo y URL son las que más se ajustan:

VariablePara quéDefault
SITE_URLURL pública de tu instalación. Se usa en los links de los correos (invitaciones, reset de contraseña, etc.).https://erp.heskala.com
RESEND_API_KEYAPI key de Resend para enviar correos. Sin ella, los flujos de email quedan deshabilitados (el resto funciona igual).
RESEND_FROM_EMAILDirección del remitente. Debe usar un dominio verificado en tu cuenta de Resend; si no, Resend rechaza el envío.no-reply@erp.heskala.com
RESEND_FROM_NAMENombre visible del remitente.Heskala
Ventana de terminal
# En el .env de tu instalación:
SITE_URL=https://erp.micliente.com
RESEND_API_KEY=re_xxxxxxxx
RESEND_FROM_EMAIL=no-reply@micliente.com
RESEND_FROM_NAME=Mi Empresa

⚠️ Si cambiás RESEND_API_KEY a tu propia cuenta pero no seteás RESEND_FROM_EMAIL, Resend intentará enviar desde no-reply@erp.heskala.com —un dominio que tu cuenta no tiene verificado— y fallará. Seteá siempre tu propio remitente con un dominio verificado. Del mismo modo, seteá SITE_URL para que los links de los correos no apunten a erp.heskala.com.

Si preferís hacerlo paso a paso o querés entender qué hace cada cosa:

Ventana de terminal
# 1. Descomprimí el bundle y entrá al directorio
tar -xzf heskala-onprem-0.8.0.tar.gz
cd heskala-onprem-0.8.0
cp .env.example .env
# Editá .env (INSTANCE_NAME, INSTANCE_SECRET, RESEND_API_KEY, etc.)
# 2. Generar el admin key (lo necesita el deployer para subir funciones)
docker compose up -d backend dashboard
docker compose exec backend ./generate_admin_key.sh
# Anotá el admin key, pegalo en .env como CONVEX_SELF_HOSTED_ADMIN_KEY
# 3. Levantar todo (backend, dashboard, deployer, frontend)
docker compose up -d
# El deployer corre una vez, sube las funciones y setea env vars, después termina.
# 4. Abrí http://localhost:3000
# El frontend se sirve desde el contenedor `frontend` (nginx),
# que internamente habla con el contenedor `backend` (Convex).

El compose levanta 4 servicios:

ServicioImagenPuerto hostFunción
backendghcr.io/get-convex/convex-backend3210Convex API (queries, mutations, auth, DB)
dashboardghcr.io/get-convex/convex-dashboard6791Panel admin interno (DB, logs, env vars)
frontendghcr.io/heskala/frontend3000App Heskala (UI del usuario final)
deployerghcr.io/heskala/convex-deployerDeploya las funciones de Convex y setea env vars (corre una vez)

El usuario navega a http://localhost:3000. El frontend (nginx) hace proxy de /api/* (queries, mutations, auth), /.well-known/* (JWKS de Convex Auth), /license/* y /download/* al backend internamente, así no hace falta exponer el puerto 3210. El bundle del frontend usa window.location.origin, no una URL hardcodeada al backend.

Antes de llegar al dashboard necesitás un token de licencia firmado (es un JWT ECDSA que el backend valida contra su clave pública embebida). El token define el tier, qué módulos se muestran en el sidebar, y los límites de seats y sucursales.

Camino 1: Pedilo desde el formulario público (recomendado)

Sección titulada «Camino 1: Pedilo desde el formulario público (recomendado)»

Andá a https://docs.heskala.com/solicitar-licencia/, completá el form con tus datos (nombre, correo, empresa, edición, cantidad de usuarios) y listo. Te llega el token firmado al mail en pocas horas (lo revisa Serlis y lo aprueba).

La licencia es de un solo uso: una vez que la activás en tu instalación on-prem, queda asociada a tu empresa. No se puede re-usar en otro servidor.

Si preferís no usar el formulario, mandanos un mail a licencias@heskala.com con:

  • Nombre y razón social
  • Email de contacto
  • Edición (onprem)
  • Cantidad de usuarios
  • Tipo (lifetime o subscription anual)

Camino 3: Compilar desde código fuente (dev / test)

Sección titulada «Camino 3: Compilar desde código fuente (dev / test)»

Si tenés el código fuente y la clave privada, podés generar tokens vos mismo:

Ventana de terminal
# Starter, 2 usuarios, 1 sucursal, lifetime
LICENSE_TEST_PRIVATE_KEY="$(cat /tmp/prod-private.txt)" \
npx tsx scripts/issue-test-license.ts \
--tier starter --seats 2 --cust "Mi Empresa" --type lifetime
# Pro, 5 usuarios, 10 sucursales, 1 año
LICENSE_TEST_PRIVATE_KEY="$(cat /tmp/prod-private.txt)" \
npx tsx scripts/issue-test-license.ts \
--tier pro --seats 5 --cust "Mi Empresa" --type annual --days 365
# All (todos los módulos), sin límite de sucursales, 3 usuarios, lifetime
LICENSE_TEST_PRIVATE_KEY="$(cat /tmp/prod-private.txt)" \
npx tsx scripts/issue-test-license.ts \
--tier all --seats 3 --cust "Mi Empresa" --type lifetime

El script imprime el token a stdout. Copialo desde un archivo (no del terminal) para evitar caracteres invisibles:

Ventana de terminal
npx tsx scripts/issue-test-license.ts --tier starter --seats 2 \
--cust "Mi Empresa" --type lifetime > /tmp/licencia.txt
cat /tmp/licencia.txt | pbcopy # macOS
# xclip -selection clipboard < /tmp/licencia.txt # Linux

La LICENSE_TEST_PRIVATE_KEY tiene que corresponder a la clave pública embebida en el frontend (lib/licensing/public-key.ts). Si el token lo firmás con una clave distinta, el backend lo rechaza con firma_no_valida.

El token es un string largo que empieza con lic_. Pegalo en la pantalla de /setup de tu instalación on-prem. Si te aparece firma_no_valida, mirá “Problemas comunes” más abajo (casi siempre es un caracter invisible en el portapapeles).

La imagen ghcr.io/get-convex/convex-backend es una imagen runtime: viene sin tus funciones pre-cargadas, sin env vars, sin JWT keys. En SaaS, Convex Cloud deploya todo automáticamente; en self-hosted, hay que hacerlo manualmente.

El deployer resuelve esto: es un sidecar que:

  1. Espera a que backend esté healthy
  2. Lee el CONVEX_SELF_HOSTED_ADMIN_KEY de .env
  3. Corre npx convex deploy para subir todas las funciones
  4. Genera un par de claves RSA con jose y las setea como JWT_PRIVATE_KEY y JWKS (necesarias para Convex Auth)
  5. Setea EDITION=onprem, LICENSE_PUBLIC_KEY (baked en la imagen del deployer, debe coincidir con la del frontend) y otras env vars críticas
  6. Termina (restart: "no")

Después de que el deployer completa, el backend ya tiene todo lo necesario para que el frontend se conecte y arranque la primera activación.

El primer acceso con base vacía te lleva directo a /setup. En un solo submit:

  1. Se crea la cuenta del administrador.
  2. Se aprovisiona la empresa, la branch principal, el rol admin con full-access y la subscription con los módulos y límites del token.
  3. Redirección a /dashboard.

No hay wizard de pasos en on-prem: el tier, los módulos y los límites ya vienen firmados en la licencia — no hay nada que el usuario tenga que elegir.

El token de licencia define tres campos clave:

CampoSignificado
modsLista de módulos habilitados (clientes, ventas, facturas, etc.)
seatsMáximo de usuarios (0 = ilimitado)
mbMáximo de sucursales (0 o ausente = ilimitado)

Tres tiers disponibles:

TierMódulosseatsmb (sucursales)
starter47 módulos (sistema + comercial + fiscal completo)configurable1
pro47 módulos (igual que starter)configurable10
alltodos los disponibles en onprem (~64)configurablesin límite

Starter y Pro comparten los mismos módulos: ambos incluyen todo el fiscal (facturación, exoneraciones, anulaciones, notas crédito/débito, correlativos SAR). La diferencia entre ellos son los límites de usuarios y sucursales, no los features.

Para los ejemplos de cómo emitir un token con cada tier, mirá Obtener tu token de licencia más arriba.

Una vez en el dashboard:

  • Sidebar: solo aparecen los módulos habilitados en tu licencia.
  • URL a un módulo fuera del tier: redirige a “módulo no incluido”.
  • Usuarios: invitá hasta el límite de seats; el siguiente queda bloqueado.
  • Sucursales: solo se pueden crear hasta mb branches.
  • Sin suscripción en la UI: el panel de Suscripción no aparece en on-prem — la licencia se gestiona fuera del producto.
  • Código propietario excluido de las imágenes: las imágenes ghcr.io/heskala/frontend y ghcr.io/heskala/convex-deployer se construyen con la lógica de negocio de los módulos premium (operaciones, reportes) y la emisión de licencias removida antes de compilar. Tu servidor corre el producto, pero no recibe ese código fuente.
  • Seguridad “honor system” (solo datos en runtime): la licencia firmada se valida al boot y en cada heartbeat. Un usuario con acceso root al servidor puede modificar los valores de su propia base (módulos habilitados, límites) directamente, ya que la firma no protege los datos en runtime. Esto se resuelve en próximas versiones con heartbeat activo contra un servidor central.
  • Plan C (v0.6.0+): la imagen del frontend en ghcr.io/heskala/frontend es pública y se rebuilda en cada release tag. El docker-compose.yml del bundle ya la apunta al tag del release, así que con docker compose pull && docker compose up -d actualizás.
  • Multi-arch (v0.7.0+): la imagen se publica como manifest list con variantes para linux/amd64 y linux/arm64. El pull es nativo en Mac M1/M2/M3/M4, servidores Linux amd64, AWS Graviton, Oracle Ampere y Raspberry Pi — sin emulación.

”firma_no_valida” al activar la licencia

Sección titulada «”firma_no_valida” al activar la licencia»

Si el servidor responde firma_no_valida aunque tu token “se ve” bien, es muy probable que tenga un char invisible o modificado (zero-width space, BOM, soft-hyphen, etc.). El cliente ya limpia esos chars al pegar, pero a veces el clipboard introduce uno antes. Soluciones:

  • Si el token lo recibiste por mail/chat, pegalo primero en un editor plano (TextEdit, VS Code) y de ahí al input, para que no arrastre formato ni caracteres invisibles.
  • Si persiste, pedile a Heskala que te reenvíe el token (puede haberse cortado o alterado al copiarlo).

docker compose pull falla al bajar frontend o deployer

Sección titulada «docker compose pull falla al bajar frontend o deployer»

Si el pull de ghcr.io/heskala/frontend o ghcr.io/heskala/convex-deployer falla con denied / unauthorized / manifest unknown, es porque ese paquete de GHCR quedó privado (GHCR publica los paquetes privados por defecto). Ambas imágenes deben estar en visibilidad Public:

  1. Entrá a https://github.com/orgs/heskala/packages/container/<frontend|convex-deployer>/settings
  2. Danger Zone → Change visibility → Public (una por imagen).

Verificá desde una máquina sin login:

Ventana de terminal
docker logout ghcr.io
docker pull ghcr.io/heskala/frontend:latest
docker pull ghcr.io/heskala/convex-deployer:latest

Una vez públicas, las versiones que publiques después siguen siendo públicas (no hay que repetirlo por release).

El frontend se queda cargando y no llega al dashboard

Sección titulada «El frontend se queda cargando y no llega al dashboard»
  • Confirmá que los servicios estén arriba: docker compose ps (backend healthy, frontend running).
  • El deployer corre una vez en la primera instalación y sube las funciones al backend. Revisá que haya terminado bien: docker compose logs deployer. Si ves errores o HTTP actions not enabled, volvé a correrlo: docker compose up -d deployer.
  • Revisá los logs del backend: docker compose logs -f backend.

El sidebar muestra módulos que no deberían estar

Sección titulada «El sidebar muestra módulos que no deberían estar»

Si cambiaste el tier de la licencia y el sidebar sigue mostrando los módulos del tier anterior, el token quedó cacheado en el localStorage del browser. Limpiá:

  1. DevTools (F12) → Application → Local Storage → http://localhost:3000
  2. Borrá todas las keys que empiezan con heskala.license.
  3. Hard refresh (Cmd+Shift+R)
  4. Pegá el nuevo token

Cambié el token pero el backend no lo reconoce

Sección titulada «Cambié el token pero el backend no lo reconoce»

Cada token está firmado para una instalación/versión. Si actualizaste a una versión nueva y el token deja de validar (firma_no_valida), puede ser que esa versión haya rotado la clave de licencia: pedile a Heskala un token nuevo para tu instalación. Antes de pegar el nuevo, limpiá el viejo del localStorage (ver arriba).

Para reportar problemas o pedir nuevas features del flujo on-prem, contactanos por los canales habituales.