Ir al contenido

Cuentas por edición

Cómo se crean y aprovisionan las cuentas/empresas en Heskala según la edición de distribución (SaaS, Desktop, On-prem).

EdiciónBackendControl de accesoAlta de cuenta
SaaSla nube de Heskala (Convex)company_subscriptions + Dodo (trial/pago)autoservicio web (hoy vía waitlist)
Desktopla nube (Tauri apunta a la misma nube)igual que SaaS (misma cuenta)solo login; la cuenta/billing se crea en la web
On-premel del cliente (self-host)licencia firmada (mods/seats) + heartbeatbootstrap in-app del primer admin
  • Backend: EDITION (convex/lib/config/core.ts), de process.env.EDITION. Default saas.
  • Frontend: getEdition() / isOnPremEdition() (lib/edition/edition.ts), de import.meta.env.VITE_EDITION.
  • Desktop runtime: isDesktopApp() (lib/edition/is-desktop.ts), por window.isTauri.
  • Dueño / primer usuario: crea la cuenta y aprovisiona empresa + plan/licencia (onboarding).
  • Miembro: lo invita un admin; entra por link de activación (/activate) o first-time-login (OTP).
landing (/) → "crear cuenta" → /waitlist
login → /auth/sign-in
autenticado sin empresa → OnboardingWizard → setupCompanyFromOnboarding
→ empresa + company_subscriptions del plan default (trial Dodo)

Misma cuenta/backend que SaaS. La app es login-only:

app abre en / → redirect a /auth/sign-in (detecta desktop)
sign-in: "¿No tienes cuenta?" → link externo a heskala.com
autenticado sin empresa → NoCompanyDesktopCard (gestionar en la web)
app abre → LicenseGate pide token
licencia válida + base SIN usuarios → /setup (crear primer admin)
→ OnboardingWizard → setupCompanyFromOnboarding(licenseToken)
rama on-prem: enabledModules = mods, limits.maxUsers = seats, lifetime
admin invita usuarios → link /activate?token=... (sin SMTP)

Ver Probar on-prem localmente.

Dos fuentes según la edición, un mismo chokepoint en la UI:

  • SaaS/Desktop: subscription.enabledModules + limits.maxUsers (plan/Dodo).
  • On-prem: payload.mods / payload.seats del token firmado, expuestos por useLicense().

El gating ocurre en hooks/usePermissions.tscan(action, module) filtra los módulos fuera del tier (salvo ALWAYS_ENABLED_MODULES). El sidebar y los RouteGuard* cascadean. Acceso por URL a un módulo fuera del tier → pantalla “módulo no incluido”.

convex/system/activation/: generateActivationLink (super_admin o company_admin), getActivationStatus (pública) y activateAccount (action, fija la contraseña con modifyAccountCredentials). UI: ruta /activate.

Para “autenticado SIN empresa”:

super_admin → acceso
on-prem → OnboardingWizard (tier de licencia)
desktop → NoCompanyDesktopCard (gestionar en la web)
saas → /waitlist
ÁreaArchivos
Ediciónconvex/lib/config/core.ts, lib/edition/edition.ts, lib/edition/is-desktop.ts
Authconvex/auth.ts
Routingapp/routes/_home/index.tsx, .../_public/auth/sign-in.tsx, .../_public/setup.tsx, .../_public/activate.tsx
Gatecomponents/auth/auth-guard-combined.tsx
Onboardingconvex/system/onboarding/*, components/onboarding/onboarding-wizard.tsx, hooks/useOnboarding.ts
Licencialib/licensing/*, convex/system/licensing/*
Invitaciónconvex/system/activation/*, components/users/user-create-dialog.tsx