Instalacion
Crear un proyecto nuevo, o sumar soul a uno que ya existe.
Requisitos#
- Node 20 o superior y una cuenta de Cloudflare (el plan gratuito basta).
- Acceso al repositorio privado de soul (una deploy key por proyecto).
- Un cliente OAuth de Google, para el login.
Crear un proyecto nuevo#
El CLI arma el proyecto completo: estructura, migraciones del core, wrangler.jsonc,
tests y el cableado de auth, tenancy y admin.
npx --package=git+ssh://git@github/jun-studio/soul.git#v4.2.0 soul create mi-saas
cd mi-saas
npm install
Base de datos y migraciones#
npx wrangler d1 create mi-saas-db
# pega el database_id en wrangler.jsonc (en el bloque raiz Y en cada env)
npx soul sync-migrations # copia las migraciones del core
npx wrangler d1 migrations apply mi-saas-db --local
sync-migrations copia las migraciones de soul a tu carpeta migrations/, donde conviven
con las tuyas. Nunca sobreescribe una que ya existe: una migracion copiada (y quizas
aplicada) es inmutable.
| Migracion | Que trae | Cuando |
|---|---|---|
0001_soul_core | users, orgs, org_members, subscriptions | Siempre |
0005_soul_ratelimit | tabla rate_limits | Si usas ratelimit |
0006_soul_users_email_index | indice en users(email) | Recomendada |
0007_soul_credentials | tabla soul_credentials | Si usas credentials |
Variables y secretos#
En desarrollo van en .dev.vars; en produccion, como secretos de Wrangler.
cp .dev.vars.example .dev.vars # completa los valores
# En produccion, uno por uno:
npx wrangler secret put SESSION_SECRET --env production
npx wrangler secret put GOOGLE_CLIENT_ID --env production
npx wrangler secret put GOOGLE_CLIENT_SECRET --env production
Levantar y desplegar#
npm run dev # local, en :8787
npm run deploy:production # soul build + wrangler deploy --env production
Despues del primer deploy, date superadmin a ti mismo:
npx wrangler d1 execute mi-saas-db --env production --remote \
--command "UPDATE users SET is_admin = 1 WHERE email = 'tu@email.com'"
Sumar soul a un proyecto existente#
Se instala como dependencia normal, apuntando a un tag:
npm install "@jun/soul@git+ssh://git@github/jun-studio/soul.git#v4.2.0"
Y en package.json queda fijado a esa version, para que cada proyecto suba cuando quiera:
{
"dependencies": {
"@jun/soul": "git+ssh://git@github/jun-studio/soul.git#v4.2.0",
"hono": "^4.12.26"
}
}
El archivo core.ts#
Las instancias compartidas viven en src/core.ts, no en index.ts. Es a proposito: los
modulos generados importan de core, y si vivieran en index.ts —que a su vez monta esos
modulos— habria un ciclo de imports.
import { createAuth } from "@jun/soul/auth";
import { createTenancy } from "@jun/soul/tenancy";
import { createAdmin } from "@jun/soul/admin";
import { createShell, ui } from "@jun/soul/ui";
import { PLANS, PERMISSIONS } from "./config";
export const { shell, errorPage } = createShell({
siteName: "Mi SaaS",
theme: { primary: "#2f6f4f", fontDisplay: "'Fraunces', serif" },
});
export const auth = createAuth({ errorPage });
export const tenancy = createTenancy({ errorPage, permissions: PERMISSIONS });
export const admin = createAdmin({
errorPage,
planNames: Object.keys(PLANS),
layout: (c, opts) => shell({ title: opts.title, body: opts.content }),
});
export { ui };
wrangler.jsonc minimo#
{
"name": "mi-saas",
"main": "src/index.ts",
"compatibility_date": "2026-01-01",
"vars": { "APP_URL": "http://localhost:8787", "SITE_NAME": "Mi SaaS" },
"d1_databases": [
{ "binding": "DB", "database_name": "mi-saas-db", "database_id": "..." }
],
"assets": { "directory": "./public" },
"env": {
"production": {
"name": "mi-saas",
"vars": { "APP_URL": "https://mi-saas.workers.dev", "SITE_NAME": "Mi SaaS" },
"d1_databases": [
{ "binding": "DB", "database_name": "mi-saas-db", "database_id": "..." }
],
"assets": { "directory": "./public" }
}
}
}
Estructura que genera el CLI#
mi-saas/
├── migrations/ 0001_soul_core.sql y las tuyas
├── src/
│ ├── core.ts instancias compartidas (auth, tenancy, admin, shell)
│ ├── config.ts PLANS y PERMISSIONS
│ ├── index.ts monta las rutas
│ ├── modules/ tu negocio (soul g module)
│ ├── pages/ paginas de portal o publicas (soul g page)
│ └── static.ts paginas prerenderizadas (soul g page --static)
├── test/
├── public/ salida de soul build (no se versiona)
└── wrangler.jsonc