¶ ⚠️ Obligado cumplimiento — Estándares de aplicaciones
Toda aplicación creada o auditada con este workflow debe cumplir los Estándares obligatorios de aplicaciones antes de deploy.
Items críticos (severidad 🔴) son bloqueantes:
- CR-1: Sin secrets en código commiteado
- CR-2:
.enven.gitignorey nunca commiteado- CR-3: Sin SQL injection (queries parametrizadas)
- CR-4: Sin ejecución de comandos con input de usuario sin validar
- CR-5: Endpoints destructivos con autenticación
El agente auditor verifica estos items automáticamente. Si falla cualquier crítico, el deploy queda bloqueado hasta corregirlo.
Workflow para tu asistente IA (Claude Code, Codex u otro con acceso a shell, red y ficheros). Cubre tanto crear apps nuevas (Ruta A) como auditar/migrar apps existentes (Ruta B — Vercel, otro servidor, solo local, etc.).
Tu IA debería leer este workflow directamente desde la wiki via Wiki MCP:
read_page("workflow/create-app")
Si Wiki MCP no está configurado, la IA puede leerlo via WebFetch a https://wiki.pampl.ing/en/workflow/create-app. La copia local del workflow en ~/.claude/create_app.md o ~/.codex/workflows/create_app.md es opcional y solo útil para trabajar offline.
Las variables
<ENTRE_ANGULOS>se leen del fichero global del desarrollador (~/.claude/CLAUDE.mdo~/.codex/AGENTS.md). Las variablesNOMBRE-APPyNOMBRE-REPOlas proporciona el usuario.
En repositorios nuevos generar fichero de instrucciones de proyecto según las herramientas que use el equipo:
CLAUDE.mdAGENTS.mdSERVER_IP = 192.168.1.10
COOLIFY_URL = http://192.168.1.10:8000
COOLIFY_SERVER = p9oujt6wvwz90l3zgzd1vexy
DEPLOY_KEY_UUID = lay870h6dq9p25wvj4la8otn
BITBUCKET_WS = pampling
APP_DOMAIN = pampl.ing
Nota — Estos UUIDs son del Root team de Coolify.
COOLIFY_SERVERyDEPLOY_KEY_UUIDno son credenciales personales: son identificadores de recursos compartidos del Root team (servidor físico y deploy key SSH). Por eso son constantes para todos los desarrolladores que trabajan en ese team.El
COOLIFY_PROJECTque se elige en el Paso 0.3 también es del Root team.Cuando Pampling gestione varios teams (por ejemplo
IA,Marketing,Producción), estos UUIDs dejarán de ser constantes globales: cada team tendrá los suyos. Habrá que refactorizar este workflow para que los UUIDs se resuelvan según el team activo del usuario, no como constantes hardcodeadas.
ANTES de ejecutar nada, preguntar al usuario:
¿Es una app NUEVA o una app EXISTENTE?
1. Nueva → seguir Ruta A
2. Existente → seguir Ruta B (auditoría primero)
¿Dónde está ahora la app?
- Solo en local (sin repo)
- Repo en Bitbucket
- Repo en GitHub
- Desplegada en Vercel
- Desplegada en otro servidor / hosting
- Repo en otro sitio
¿Tiene base de datos?
- No
- PostgreSQL existente (¿dónde?)
- Nueva PostgreSQL dedicada en Coolify
- Otra base de datos
¿Tiene dominio actual?
¿Hay variables de entorno o secrets existentes?
¿Qué quieres hacer?
- Migrar a Coolify
- Mantener deploy actual y solo documentarla
- Crear entorno paralelo en Coolify para pruebas
- Solo crear repo o instrucciones, sin deploy
"¿Cómo se llama la app? (minúsculas, sin espacios; ej:
analytics,inventario)"
Guardar como NOMBRE-APP. Por convención, el subdominio será https://NOMBRE-APP.pampl.ing y el repositorio se llamará igual salvo que el usuario pida otro nombre.
curl -s -u "<BITBUCKET_EMAIL>:<BITBUCKET_API_TOKEN>" \
"https://api.bitbucket.org/2.0/workspaces/pampling/projects?pagelen=100" \
| python -c "
import sys, json
for p in json.load(sys.stdin).get('values', []):
print(f\" - {p['key']}: {p['name']}\")
"
"¿En qué proyecto de Bitbucket creo el repositorio? Por defecto ALL PROJECTS (key
ALL). Otros:IA, etc."
Guardar como BITBUCKET_PROJECT_KEY.
curl -s -H 'Authorization: Bearer <COOLIFY_TOKEN>' \
http://192.168.1.10:8000/api/v1/projects \
| python -c "
import sys, json
for p in json.load(sys.stdin):
print(f\" - {p['uuid']}: {p['name']}\")
"
"¿En qué proyecto de Coolify despliego? Por defecto IA PROJECTS (
e11g6dohjsbfsucm9f2r3h9k). Si quieres otro o crear uno nuevo, dímelo."
Guardar como COOLIFY_PROJECT. Para crear uno nuevo:
curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{"name": "NOMBRE_PROYECTO"}' \
http://192.168.1.10:8000/api/v1/projects
"¿Qué stack? Por defecto: Python + FastAPI + PostgreSQL."
Si es distinto, adaptar A.3. Lo importante es que Nixpacks lo detecte (requirements.txt, package.json, go.mod, etc).
=== Configuración de la app ===
Nombre: NOMBRE-APP
URL producción: https://NOMBRE-APP.pampl.ing
Repo Bitbucket: pampling/NOMBRE-APP (proyecto: <BITBUCKET_PROJECT_KEY>)
Proyecto Coolify: <nombre>
Stack: <stack>
"¿Procedo?"
curl -s -X POST \
-u "<BITBUCKET_EMAIL>:<BITBUCKET_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"scm": "git",
"is_private": true,
"description": "Descripción de NOMBRE-APP",
"project": {"key": "<BITBUCKET_PROJECT_KEY>"}
}' \
https://api.bitbucket.org/2.0/repositories/pampling/NOMBRE-REPO
Añadir deploy key de Coolify al repo:
PUBKEY=$(curl -s \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
http://192.168.1.10:8000/api/v1/security/keys/lay870h6dq9p25wvj4la8otn \
| python -c "import sys,json; print(json.load(sys.stdin)['public_key'])")
curl -s -X POST \
-u "<BITBUCKET_EMAIL>:<BITBUCKET_API_TOKEN>" \
-H "Content-Type: application/json" \
-d "{\"key\": \"$PUBKEY\", \"label\": \"Coolify Deploy\"}" \
https://api.bitbucket.org/2.0/repositories/pampling/NOMBRE-REPO/deploy-keys
PG_PASSWORD=$(python -c "import secrets; print(secrets.token_urlsafe(48))")
DB_RESPONSE=$(curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"project_uuid": "<COOLIFY_PROJECT>",
"environment_name": "production",
"server_uuid": "p9oujt6wvwz90l3zgzd1vexy",
"name": "NOMBRE-APP-postgresql",
"postgres_user": "nombreapp_user",
"postgres_password": "'$PG_PASSWORD'",
"postgres_db": "nombreapp_db",
"image": "postgres:16-alpine",
"is_public": false
}' \
http://192.168.1.10:8000/api/v1/databases/postgresql)
DB_UUID=$(echo $DB_RESPONSE | python -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
DB_INTERNAL_URL=$(echo $DB_RESPONSE | python -c "import sys,json; print(json.load(sys.stdin)['internal_db_url'])")
# Arrancar
curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
http://192.168.1.10:8000/api/v1/databases/$DB_UUID/start
Verificar running:healthy:
curl -s -H 'Authorization: Bearer <COOLIFY_TOKEN>' \
http://192.168.1.10:8000/api/v1/databases/$DB_UUID \
| python -c "import sys,json; print(json.load(sys.stdin).get('status'))"
(Opcional) Exponer puerto público para dev local: ver detalle en la página Workflow crear app v3 anterior, sigue siendo válido.
Adaptar al stack. Recomendado Python + FastAPI:
mkdir -p C:\Users\User\repositorio\NOMBRE-APP\backend\routers
mkdir -p C:\Users\User\repositorio\NOMBRE-APP\frontend\css
mkdir -p C:\Users\User\repositorio\NOMBRE-APP\frontend\js
Ficheros base:
Procfile: web: uvicorn backend.main:app --host 0.0.0.0 --port 8000requirements.txt: fastapi, uvicorn, psycopg2-binarybackend/main.py con health check /api/healthbackend/database.py con helpers query(), query_one(), execute()frontend/index.html mínimoAPP_UUID=$(curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"project_uuid": "<COOLIFY_PROJECT>",
"environment_name": "production",
"server_uuid": "p9oujt6wvwz90l3zgzd1vexy",
"type": "private-deploy-key",
"name": "NOMBRE-APP",
"git_repository": "git@bitbucket.org:pampling/NOMBRE-REPO.git",
"git_branch": "main",
"build_pack": "nixpacks",
"base_directory": "/",
"ports_exposes": "8000",
"private_key_uuid": "lay870h6dq9p25wvj4la8otn"
}' \
http://192.168.1.10:8000/api/v1/applications/private-deploy-key \
| python -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
curl -s -X PATCH \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{"fqdn": "https://NOMBRE-APP.pampl.ing"}' \
http://192.168.1.10:8000/api/v1/applications/$APP_UUID
SSL automático via Let's Encrypt.
curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{"key": "DATABASE_URL", "value": "'$DB_INTERNAL_URL'", "is_preview": false}' \
http://192.168.1.10:8000/api/v1/applications/$APP_UUID/envs
Repetir para cada variable adicional.
Seguir la estructura: https://wiki.pampl.ing/en/setup-personal/organizacion-md
Crear en la raíz del repo el fichero o ficheros que correspondan a las herramientas del equipo:
| Herramienta del equipo | Fichero a generar |
|---|---|
| Solo Claude Code | CLAUDE.md |
| Solo Codex | AGENTS.md |
| Ambos | Los dos |
Contenido (sin secretos):
Crear también CLAUDE.local.md o AGENTS.local.md o .env con las connection strings reales (gitignored).
.gitignore debe incluir:
CLAUDE.local.md
AGENTS.local.md
.env
cd C:\Users\User\repositorio\NOMBRE-APP
git init
git remote add origin git@bitbucket.org:pampling/NOMBRE-REPO.git
git add .
git commit -m "Initial commit — NOMBRE-APP scaffold"
git push -u origin main
curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-d '{}' \
http://192.168.1.10:8000/api/v1/applications/$APP_UUID/restart
Verificar (~90s) que el status es running:healthy.
=== NOMBRE-APP creada ===
Repositorio: git@bitbucket.org:pampling/NOMBRE-REPO.git
Proyecto Bitbucket: <BITBUCKET_PROJECT_KEY>
Proyecto Coolify: <nombre>
URL: https://NOMBRE-APP.pampl.ing
DB: nombreapp_db (UUID: $DB_UUID)
Ficheros: CLAUDE.md / AGENTS.md (commiteados), .local.md (gitignored)
Pedir al usuario:
Si no está clonado, clonar en ~/repositorio/NOMBRE-APP.
Inspeccionar el repo (sin modificar):
package.json, requirements.txt, pyproject.toml, go.mod, Cargo.toml, composer.json, etc.)pip install -r requirements.txt, npm install, composer install, etc.package.json, Procfile, Makefile, READMEpytest, jest, vitest, etc.eslint, ruff, black, etc.npm run build, tsc, next build, etc.Mostrar al usuario lo detectado y preguntar si falta algún comando relevante.
Buscar pistas:
vercel.json, .vercel/ → Vercelnetlify.toml → NetlifyDockerfile, docker-compose.yml → Docker (puede ser cualquier servidor).coolify o referencias a Coolify → Coolify.github/workflows/bitbucket-pipelines.ymlSi no se detecta, preguntar al usuario:
"¿Dónde está desplegada actualmente? Vercel / Coolify / servidor manual / sin deploy / otro"
.env.example, .env.sample, .env.templateprocess.env.X, os.environ['X'], os.getenv('X') en el códigoMostrar al usuario el inventario:
Variables detectadas:
DATABASE_URL (valor desconocido)
STRIPE_API_KEY (valor desconocido)
NEXT_PUBLIC_API_URL (valor desconocido)
Preguntar dónde tiene los valores actuales (si están en Vercel, en .env local, en password manager, etc.).
Buscar:
DATABASE_URL, MONGO_URI, REDIS_URL, etc.migrations/, prisma/, alembic/)Preguntar:
"¿Quieres mantener la BD actual o migrar a una PostgreSQL nueva en Coolify?"
Mostrar al usuario un menú:
He auditado la app. Opciones:
1. SOLO DOCUMENTAR
Mantener deploy actual, generar CLAUDE.md/AGENTS.md de proyecto.
No migrar nada.
2. MIGRAR A COOLIFY
Crear repo en Bitbucket (si está en otro sitio), crear app en Coolify,
configurar BD nueva, redirigir dominio.
3. ENTORNO PARALELO EN COOLIFY (staging/preview)
Mantener producción donde está, crear copia en Coolify para pruebas.
4. SOLO CREAR REPO/INSTRUCCIONES
Mover/clonar a Bitbucket, generar instrucciones, sin deploy.
Saltar a Ruta A desde el paso A.0.2 (proyecto Bitbucket), reutilizando lo ya detectado:
Saltar al paso A.7 (generar CLAUDE.md/AGENTS.md de proyecto) con la información detectada.
Crear app en Coolify pero con dominio temporal (<NOMBRE>-staging.pampl.ing) y BD aparte. Documentar ambos entornos.
Crear repo en Bitbucket si no existe, push del código, generar instrucciones. Sin Coolify.
=== Auditoría de NOMBRE-APP ===
Stack detectado: <stack>
Deploy actual: <plataforma>
BD: <descripción>
Dominio actual: <dominio>
Variables: <N> detectadas
Acción elegida: <acción>
git push origin main
curl -s -X POST \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
-d '{}' \
http://192.168.1.10:8000/api/v1/applications/<APP_UUID>/restart
curl -s \
-H 'Authorization: Bearer <COOLIFY_TOKEN>' \
http://192.168.1.10:8000/api/v1/applications/<APP_UUID> \
| python -c "import sys,json; print(json.load(sys.stdin).get('status'))"
| Acción | Método | URL |
|---|---|---|
| Listar apps | GET | /api/v1/applications |
| Detalle app | GET | /api/v1/applications/ |
| Actualizar app | PATCH | /api/v1/applications/ |
| Listar env vars | GET | /api/v1/applications/{uuid}/envs |
| Añadir env var | POST | /api/v1/applications/{uuid}/envs |
| Borrar env var | DELETE | /api/v1/applications/{uuid}/envs/ |
| Deploy/restart | POST | /api/v1/applications/{uuid}/restart |
| Logs app | GET | /api/v1/applications/{uuid}/logs |
| Acción | Método | URL |
|---|---|---|
| Listar DBs | GET | /api/v1/databases |
| Crear PostgreSQL | POST | /api/v1/databases/postgresql |
| Detalle DB | GET | /api/v1/databases/ |
| Actualizar DB | PATCH | /api/v1/databases/ |
| Arrancar DB | POST | /api/v1/databases/{uuid}/start |
| Parar DB | POST | /api/v1/databases/{uuid}/stop |
| Borrar DB | DELETE | /api/v1/databases/ |
| Acción | Método | URL |
|---|---|---|
| Listar proyectos | GET | /api/v1/projects |
| Crear proyecto | POST | /api/v1/projects |
| Deploy keys | GET | /api/v1/security/keys |
| Detalle key | GET | /api/v1/security/keys/ |
Base URL: http://192.168.1.10:8000 · Header: Authorization: Bearer <COOLIFY_TOKEN>
Si la app necesita leer documentación de la wiki en runtime (catálogos de páginas, búsquedas, lectura de markdown), usar el Wiki MCP — expone también un API REST sin auth:
GET http://wiki-mcp:8000/api/pages?tag=<tag> # listar páginas
GET http://wiki-mcp:8000/api/search?q=<query> # búsqueda
GET http://wiki-mcp:8000/api/pages/<path> # leer página (markdown crudo)
GET http://wiki-mcp:8000/api/navigation # estructura del sidebar
GET http://wiki-mcp:8000/api/tags # tags con conteo
URL desde Internet (si la app la expone fuera de la red Docker): https://wiki-mcp.pampl.ing/api/....
Docs interactivas: https://wiki-mcp.pampl.ing/docs