Capítulo publicar de la guía Neblla — galería pública y modelo de cuotas. El contrato base está en core.txt; el criterio de cuándo usar Neblla, en empezar.txt. # Publicar y cuotas ## El borrador, pre y pro Todo cambio, desde el dashboard o el MCP (ficheros, código de servidor, módulos, salas, tema, IA, tablas…), cae primero en el **borrador** de la app (el *draft*), que no ve nadie. Termina de una de dos maneras: - **Publicar** («Publicar» en el panel, arriba o en Publicar › Borrador y publicada; por MCP `neblla_promote({ appId, action: 'publish' })`): manda el borrador **entero y de una vez** al entorno elegido. Así varios ficheros salen juntos y nadie ve un `client.js` nuevo con un `server/room.js` viejo. **Un agente llama a `publish` al acabar** un cambio; si no, el cambio sigue esperando. - **Descartar** («Descartar», o `action: 'discard'`): lo tira, y la app sigue como estaba. **Nada se publica solo.** Mientras se edita, el borrador vive en la memoria de Neblla; a los **5 minutos sin cambios** se guarda en la base de datos y se sigue viendo hasta que se publica o se descarta: arriba en el panel («N cambios sin publicar», con «Publicar»), en `neblla_get_app` (`draft: { count, files: [{ path, op }], settings, savedAt, dropped }`, y `env.target`) y en `neblla_promote_read({ action: 'status' })` o `'diff'`. Las lecturas del MCP (`neblla_get_file`, `neblla_list_files`) y del panel ven el borrador, para que un cambio encadene sobre el anterior, y la vista previa del editor (y «Tu borrador ↗») enseña pre con el borrador encima: solo el dev la ve así. Si Neblla cambia su esquema y un cambio del borrador ya no encaja (p. ej. un ajuste que ya no existe), se quita del borrador y el panel y `neblla_get_app` dicen cuál (`dropped`). Publicar no gasta créditos. Una app enlazada a su repo (`neblla_git`) sigue mandando sus ficheros a su rama, como siempre; sus ajustes sí pasan por el borrador. Cada app tiene dos entornos con el mismo código, y **un entorno elegido** (`target`), que es un ajuste de la app: **pro por defecto**. - **pro** — lo que usan sus usuarios en `play.neblla.com/`. Con pro elegido, cada publicación llega a los usuarios **en el acto** (y es una versión más del historial): una app nueva tiene su enlace funcionando desde el primer momento. - **pre** — una copia privada para probar con calma: un enlace privado (`play.neblla.com/pre//`, que da el dashboard con «Abrir pre ↗» o `neblla_get_app_url`; el enlace abre pre durante 10 minutos y el navegador que lo abre sigue dentro 4 horas), sus propios datos y Stripe de prueba (abajo). No sale en buscadores ni en la galería. Gasta la cuota de la app como cualquier uso. **«Pasar a pre»** (Publicar › Borrador y publicada, o `neblla_promote({ appId, action: 'to_pre' })`): desde ahí cada publicación va **solo a pre**. Pre empieza siendo lo que tiene pro. **«Subir a pro»** (`action: 'promote'`) lleva pre entero a pro de una vez y el elegido vuelve a pro; **«Descartar pre»** (`action: 'reset'`) vuelve a pro sin subir pre (pre vuelve a ser lo que tiene pro). Ejemplo: John hace su ajedrez con su IA y `play.neblla.com/ajedrez` ya juega; para rehacer la tienda pulsa «Pasar a pre», la prueba con la tarjeta 4242 y pulsa «Subir a pro». **Los datos de pre.** Por defecto pre tiene **los suyos**: sus propios usuarios, guardados, tablas, buzón y comunidad, vacíos al empezar y separados de los de pro. El dev puede cambiarlo a **los de pro** en el panel (Publicar › Borrador y publicada) o con `neblla_pre_data`, y entonces pre lee y escribe los datos de verdad de sus usuarios (lo que haga su código, lo hace de verdad). Las salas son siempre de pre, para que quien prueba no comparta nunca una sala en marcha con los usuarios. Al cambiarlo, a quien haya hecho login en pre se le hace logout. En el panel, el selector «Datos de: publicada · borrador» enseña los usuarios, mensajes, salas y comunidad de uno u otro. Con la base de datos propia del dev (`neblla_byodb`), los datos de pre van por defecto a la misma base, separados por su id, y se mudan con ella; el dev puede darle a pre otra base suya (Funciones › Datos, «Entorno: borrador», o `neblla_byodb` con `env: 'pre'`) y luego devolverla a la de la app (`follow: true`). **Las compras en pre.** Pre vende los productos de la app (hay un solo catálogo) y, por defecto, lo hace en el **modo de prueba de Stripe**: quien prueba paga con las tarjetas de prueba de Stripe (`4242 4242 4242 4242`) y no se cobra nada. En ese modo pre muestra también los productos aún sin publicar, para probarlos antes de que los vean los usuarios. Las compras de prueba van a una **cuenta de prueba de Stripe** del dev, que abre una vez desde el panel (Publicar › Borrador y publicada) con los datos de prueba que ofrece Stripe, y quedan marcadas como de prueba: pro no las cuenta nunca, tampoco cuando pre usa los datos de pro, y ahí no dan créditos. El dev puede apagarlo ahí o con `neblla_pre_stripe_test`, y entonces pre cobra de verdad en su cuenta, como pro. El webhook saliente `purchase` de una compra de prueba lleva `test: true`. Quién más abre pre lo decide el dev en el panel (Publicar › Borrador y publicada) o con `neblla_pre_access`: **cuentas de Neblla por correo**, que hacen login en la página de pre, y **enlaces con contraseña** para quien no tiene cuenta (la contraseña se ve una sola vez). Las dos cosas pueden caducar en una fecha. Quien entra se queda dentro 4 horas aunque haga logout, así que puede usarla como invitado; quitar a alguien impide pases nuevos, no el que ya tiene. **Se ve en el acto.** Al publicar en pro (o subir pre a pro), Neblla sirve la versión nueva desde su propio disco (en caliente) desde ese mismo momento, también la primera de una app nueva, que ya se puede abrir y sacar en la galería. Después, en la siguiente tanda de despliegues de su proyecto de Cloudflare, la copia allí (en frío) y la sirve desde Cloudflare, sin que cambie nada de lo que ven los usuarios. Las tandas llevan el ritmo que deja el tope de despliegues al mes de todo Neblla (lo que queda del tope, repartido en lo que queda de mes; el repaso es cada 15 minutos); si el mes se gasta, la publicada sigue en caliente hasta que cambie el mes. Si pasas a pro otra vez antes de la tanda, la tanda lleva la última. Nada de eso hay que esperarlo ni contarlo: el dashboard dice «Publicado: ya se ve», y por MCP `publish` (con pro elegido) y `promote` responden `{ queued: false, live: true, promotedAt, files, message }` (con `settingsOnly: true` si solo cambiaron ajustes). Díselo al dev así: ya está publicado. **Pre, después.** Justo después de pasar a pro, pre es exactamente lo publicado: mientras siga así, su enlace privado lo lee de su copia en Cloudflare en vez del disco de Neblla, y en cuanto cambias un fichero vuelve a servirse en caliente. Si pre aún no tenía copia en Cloudflare, sigue en caliente, y la tanda que se la hace lo enfría si sigue igual. El enlace es el mismo y no notas nada. `neblla_promote_read({ action: 'diff' })` solo trae `queued` para un envío de antes de que publicar fuera instantáneo que aún no se haya aplicado, y Neblla lo aplica solo en minutos. Publicar y "Pasar a pro" **no gastan créditos**: lo que cuesta es el despliegue, y Neblla lo junta por proyecto en tandas. Aunque Neblla haya gastado los despliegues del mes, funcionan igual: la publicada se sirve en caliente hasta que cambie el mes. `neblla_promote_read({ action: 'diff' })` dice qué cambiaría (ficheros añadidos, cambiados o borrados y ajustes distintos), y `action: 'reset'` tira los cambios de pre, lo deja como lo tienen los usuarios y vuelve a pro. Un cambio de salas que descarta datos guardados en pro pide `confirmRoomsChange: true`, en `publish` o en `promote`. **Volver atrás.** Neblla guarda las **10 últimas versiones** que tuvieron los usuarios, cada una con cuándo llegó, quién la mandó (`by: { email, via }`, `via` es `dashboard` o `mcp`) y qué cambió frente a la anterior (ficheros añadidos, cambiados y borrados, y los ajustes distintos). Están en el panel, en Publicar › Borrador y publicada, y en `neblla_promote_read({ appId, action: 'releases' })`; la que tienen ahora los usuarios lleva `current: true`. `neblla_promote({ appId, action: 'rollback', releaseId })` («Volver a esta» en el panel) vuelve a mandar a pro esa versión **entera**, ficheros, servidor y ajustes tal como la tuvieron los usuarios, como "Pasar a pro": llega a los usuarios en el acto y responde igual (`live`, `promotedAt`…). **Pre no se toca**: lo que estés preparando sigue ahí, así que el próximo "Pasar a pro" manda pre otra vez. La versión vuelve a la lista como una nueva, con `rollbackOf`. Lo que se guarda son referencias a los ficheros por su contenido, no copias; los bytes salen del disco de Neblla, del despliegue o de la rama de pro en git, y si un fichero ya no está en ninguno la vuelta se niega (`version_bytes_missing`) y no cambia nada. Una versión de antes de que existiera el historial sale sin `id` y no se puede volver a ella. Un cambio de salas que descarta datos guardados pide `confirmRoomsChange: true`, como al pasar a pro. En git, cada entorno es una rama: en el repo del dev si la app lo sigue (`neblla_git`), o en el de Neblla si no (`neblla_versions`). Cuando la versión sale en pro, la rama de pre se une a la de pro. Al volver a una versión anterior, la rama de pro recibe un commit nuevo con los ficheros de esa versión, sin unir la de pre. No van por entorno, y cambian al momento en los dos: el nombre, el slug, la publicación en la galería y los baneos. Los moderadores siguen a los datos, como los usuarios: pre con los datos de pro tiene los de pro, y pre con los suyos, los suyos (comunidad.txt, «Quién modera»). **Registro de moderación (opcional).** Apagado por defecto: una app que no lo enciende no paga nada. Con `neblla_update_app({ moderationLog: true })` o el interruptor del panel (Personas › Moderación), Neblla apunta cada acto de moderación: quién nombró o quitó moderadores (de la app o de una sala), cada baneo y cada mensaje del chat o del buzón, o entrada de la comunidad, que retiró alguien que no era su autor, con quién lo hizo y cuándo. Lo lees ahí o con `neblla_get_moderation_log`. Cada línea cuesta 64 unidades más sus bytes y ocupa la base de datos de la app como cualquier fila; con BYODB va a tu base de datos (tabla `nb_modlog`). Con la base de datos llena se pierde la línea, nunca el acto de moderación. El interruptor es de la app; las líneas van a los datos del entorno, como los moderadores. ## Publicar en la galería Crear una app (en developers.neblla.com o vía MCP) la deja funcionando en `play.neblla.com/`. Lo que subes y cambias cae en su **borrador** y llega a los usuarios al **publicarlo** (ver «El borrador, pre y pro» arriba). Publicarla en la galería pública (listarla en play.neblla.com) es un paso aparte y requiere estos metadatos: - **kind** — qué es: `"game"`, `"app"` o `"web"`. Obligatorio. Si lo falta, `/apps/publish` lo saca del arquetipo (un juego es `game`, una app `app`); una tienda o un negocio puede ser app o web y se pregunta (`kind_required`). La galería filtra por él. - **modes** — cómo se juega o se usa, varias a la vez; opcional (si faltan, se sugieren de lo que la app ya es: salas, tablero, `world`, `community`, `shop`, `business`). Juego: `solo` (un jugador), `online` (multijugador, cada uno en su aparato), `party` (pantalla común: una pantalla grande y los móviles de mando, como Kahoot), `coop` (todos juntos contra el juego), `world` (un mundo compartido que sigue ahí). App: `personal` (para ti), `live` (con otros a la vez), `community` (la gente publica y comparte), `business` (para un negocio y sus clientes). Web: `showcase` (escaparate), `shop` (tienda online), `booking` (reservas y citas), `members` (socios y comunidad). Una forma de otro tipo da `invalid_mode`. - **themes** — de qué va, de 1 a 3, obligatorio (`themes_required`; `too_many_themes` con más de tres). Juego: `strategy`, `puzzle`, `obby`, `action`, `adventure`, `cards`, `words`, `party`, `sports`, `building`. App: `productivity`, `creativity`, `social`, `utilities`. Web: `commerce`, `food`, `wellbeing`, `clubs`, `culture`, `professionals`. Los tres tipos: `education`, `music`, `health`. Una temática que no es de ese tipo da `invalid_theme`. Las tags son aparte y solo sirven a la búsqueda: no repitas ahí una forma ni una temática (las que empiezan por `mode-` o `theme-` están reservadas y se descartan). - **section** — retirado: da `section_retired` con los campos nuevos. `neblla_get_app` devuelve `gallery.kind`, `gallery.modes`, `gallery.themes` y `gallerySuggestion` (lo que Neblla sugeriría). - **titles** — objeto con título por idioma: `{ en: "…", es: "…", … }`. La ficha admite los 10 idiomas en que está toda la web y la galería de Neblla: en, es, fr, de, it, pt, ja, ko, zh, ar. Son solo los de la ficha: dentro de tu app pones los idiomas que quieras, y Neblla ni los limita ni se mete. **La app sale en la galería solo en los idiomas en que la escribas**: `neblla_publish_app` publica los idiomas que le pases (exige al menos una descripción, en el idioma que sea; ninguno en concreto, tampoco el inglés) y no traduce nada. Un visitante ve la app en la lista solo si su idioma es uno de los escritos: una profesora de yoga que escribe solo en español no recibe visitas ni reseñas en inglés que no puede leer. Para llegar a más gente, escribe más idiomas (o pide en el panel «Traducir vacíos», que traduce con tu clave cuando tú lo pides). Dentro de la ficha, un título que falta cae a la versión de `defaultLang` y, si faltara incluso ese, al `name` de la app. - **descriptions** — objeto con descripción por idioma (máx 500 chars). - **tags** — hasta 20 tags cortos. - **defaultLang** — el idioma original del contenido. Si pides uno que no escribiste, se usa uno de los escritos. - **bestOn** — `"mobile"`, `"desktop"`, o se omite para ambos. - **avatar** — `true` si la app enseña a cada persona el muñeco de su cuenta Neblla (`neblla.avatar.get()` o `neblla.avatar.doll()`, capítulo mundo): el sello «compatible con Mundo» y el filtro «Sale tu muñeco» de la galería. Neblla lo comprueba en lo publicado (`avatar_not_used` si no lo usa). Se omite para dejarlo como está; `false` lo quita. - **sandbox** — `true` si los poderes de lo que una persona lleva de la tienda de Neblla (correr más, saltar más, volar) cuentan en este juego (capítulo mundo, «Poderes y modo sandbox»); sin él son solo aspecto. Todo o nada; cada sala puede apagarlo con `rooms.book({ config: { sandbox: false } })`. Sale en la galería. Necesita el módulo `world` (`world_off` si no). Se omite para dejarlo como está; `false` lo quita. - **portrait** — imagen de portada opcional: URL, data URI o base64. Se escala automáticamente a un JPEG 400x400. ## Tu studio Tu studio es quién hace tus apps, tal como lo ven tus usuarios: vale para un juego, una app o una web, sin suponer una empresa. Tiene tu nombre público (el «de …» de cada ficha de la galería), un **nombre corto** que es su página, `play.neblla.com/studio/`, y, si quieres, una presentación (quién eres, desde cuándo), tu web oficial y hasta 6 enlaces. La página es la galería con solo tus apps publicadas, cada una con su valoración, sus reseñas, su suscripción y su botón para abrirla; el «de …» de las fichas lleva a ella. Es también la web que Neblla pone en tu cuenta de Stripe, y su nombre corto el prefijo que te propone para el extracto del banco (pagos.txt). Neblla te propone un nombre corto libre a partir de tu nombre la primera vez; lo cambias en Ajustes › Cuenta › Studio o con `neblla_developer_profile({ studioSlug, about, web, links: [{ label?, url }] })` (3 a 30 letras minúsculas, números y guiones; `studio_slug_taken` si ya es de otro). Al cambiarlo, la web de tu cuenta de Stripe le sigue. ## Cómo te encuentra la galería de verdad Esto es para qué sirven los campos de la ficha, y conviene saberlo antes de escribirlos. **La búsqueda** casa, sin distinguir mayúsculas: el nombre de la app, el `title` y la `description` de la galería, y **todos los idiomas** de `gallery.titles` y `gallery.descriptions` — como subcadena. Los tags van aparte: un tag casa solo cuando el término buscado es igual al tag completo. Así que merece la pena escribir bien las descripciones en cada idioma que publiques, y dejar los tags en las palabras exactas que alguien teclearía, porque un tag `puzle cooperativo` no casará nunca con una búsqueda de `puzle`. **El orden.** Un visitante puede ordenar por: - `new` — el de por defecto, y va por **última actualización**, no por fecha de publicación. Sacar un cambio te vuelve a subir en la vista por defecto. - `top-rated` — valoración media, y luego número de valoraciones. - `most-used` — número de usuarios. - `most-favorited` — número de favoritos. O sea que valoraciones, favoritos y número de usuarios sí son señales reales de colocación, y tres de los cuatro órdenes dependen de lo que hagan tus usuarios, no de lo que escribas tú. **Filtros** que puede aplicar un visitante: el tipo (juego, app o web), dentro de él las formas (`modes`) y las temáticas (`themes`; la lista enseña solo las que tienen apps, con su número), el dispositivo (`mobile` / `desktop`), «Sale tu muñeco» (las apps con el sello) y el idioma: la lista solo enseña a cada visitante las apps escritas en su idioma (las favoritas y «hechas para ti» no se filtran). Dos consecuencias útiles: poner `bestOn: "mobile"` **no** te esconde de nadie — el filtro de dispositivo por defecto enseña todo, así que solo te ayuda cuando alguien filtra — y un idioma que no escribes es un público que no te ve: publica descripciones en los idiomas a los que quieras llegar. ## Qué subir y adónde va Una app o un juego tiene dos clases de ficheros, y cada una tiene su sitio y su límite. Subas por el MCP (`neblla_upload_file`), por el panel o por Git, el resultado es el mismo: ``` Si subes… Va a… Cuenta contra… .html .js .css .json .svg .txt .md .wasm… el código de la app (Pages) ficheros: 300 gratis, 600 Pro .png .jpg .webp .gif .mp3 .ogg .mp4 .glb… assets, en el almacén de Neblla MB de la bolsa: 100 MB gratis, 2 GB Pro (cualquier fichero que no sea texto) y servidos en SU MISMA RUTA (la bolsa de media, la misma para todo) ``` Tu código sigue cargando `img/logo.png` o `models/barco.glb` por su ruta de siempre: no hay que cambiar nada ni apuntar a otra URL. Un asset no cuenta como fichero, solo como MB; 25 MB como máximo por fichero (también los de media). El borrador guarda hasta 8 MB de texto por app; lo binario no pasa por la base de datos. `neblla_get_app` te dice las dos cuentas: «código N de 300 · assets X MB de Y». **¿Asset o media?** Pregunta: «¿lo carga el código por su ruta (logo, hero, textura, sonido del juego) o lo gestiona alguien desde el panel (la foto de la galería de un restaurante, un catálogo)?». Lo primero es un **fichero de la app** (un asset); lo segundo es **media** (`neblla_media`): tiene colecciones, variantes en WebP, derechos y ventas. Si el código lo llama por ruta, es un fichero; si una persona lo añade, lo cambia o lo vende sin tocar el código, es media. **3D y vídeo.** Un `.gltf` con sus `.bin` y sus texturas funciona con rutas relativas, como en tu disco: sube la carpeta entera (`models/barco.gltf`, `models/barco.bin`, `models/textures/…`) y las rutas se mantienen. Un `.glb` es un solo fichero y es lo más cómodo. El vídeo y el audio se sirven con `Range` (saltar a mitad del `.mp4` o del `.mp3` no descarga lo anterior), y con `ETag` (el navegador no los baja otra vez si no cambiaron). **Empaquetar (lo hace el dev; Neblla no instala paquetes).** Si tu web tiene `node_modules/` o muchos módulos sueltos, no los subas: junta el código en pocos ficheros y sube solo el resultado. Con esbuild: ``` npx esbuild src/main.js --bundle --minify --outdir=dist --format=esm ``` Copia `index.html` y tus `img/` y `models/` a `dist/` (o dile a tu `package.json` que lo haga) y sube o enlaza `dist/`. **Sin npm:** si no quieres empaquetar, carga las librerías por CDN con un `importmap` en tu `index.html` y escribe `import * as THREE from 'three'` como siempre: ```html ``` **Git.** Enlaza **una carpeta pública** del repo (`subdir`, p. ej. `dist` o `web`): Neblla lista solo esa carpeta; el servidor propio, `.git`, `src/` y `node_modules/` de fuera ni se leen ni cuentan. Dentro de la carpeta, un `.nebllaignore` (con patrones como `.gitignore`: `node_modules/`, `src/`, `*.map`, `!`, `**`) deja fuera de la copia y del recuento lo que no quieras en Neblla. Antes de enlazar, `neblla_git({ action: 'preview', url, subdir })` dice qué cabría (ficheros, MB, cuántos de los 300 y de la bolsa) sin escribir nada; si no cabe, el error trae los números y las carpetas que más pesan (`file_count_exceeded`, `file_too_large`, `media_quota_exceeded`). Para que el repo guarde el resultado ya empaquetado, una GitHub Action de ejemplo lo construye en una rama aparte (`neblla-build`) y enlazas esa rama: ```yaml # .github/workflows/neblla-build.yml name: neblla-build on: { push: { branches: [main] } } permissions: { contents: write } jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 22 } - run: npm ci && npx esbuild src/main.js --bundle --minify --outdir=dist --format=esm && cp index.html dist/ && cp -r img models dist/ 2>/dev/null || true - run: | git -C dist init -q && git -C dist add -A git -C dist -c user.name=build -c user.email=build@users.noreply.github.com commit -qm "build $GITHUB_SHA" git -C dist push -f "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" HEAD:neblla-build ``` Enlaza `neblla-build` como `branch` con `subdir` vacío: la rama contiene solo lo que sube. **Subcarpetas.** Funcionan: `js/game.js`, `img/ui/boton.png`. Las rutas son relativas a la raíz de la app y distinguen mayúsculas (`Logo.png` no es `logo.png`). ## El modelo de cuotas **Cuenta → proyectos → apps.** Cada cuenta tiene el proyecto «Explorer» (se renombra, no se borra) y ahí caen las apps nuevas; quien no crea otro proyecto no ve nada nuevo. **La cuota gratis es de la cuenta:** una sola bolsa de 1.000 créditos al día, 50 MB de base de datos y 300 ficheros que comparten todas las apps de sus proyectos gratis (crear apps vacías no la multiplica; cada fichero cuenta una vez por app, aunque esté en pre y en pro), y cada app puede llevar su propio tope dentro (`neblla_projects` con `set_app_caps`). **Pro y los packs son del proyecto:** un proyecto Pro tiene su propia cuota (2.000 créditos al día, 100 MB, 600 ficheros) y sus apps dejan de gastar de la gratis de la cuenta. Ejemplo: el ajedrez gasta 1.500 créditos al día del Pro del proyecto «Ajedrez» y la cuota gratis sigue entera para el yoga y las pruebas. Pro nunca viaja con una app: se queda en su proyecto. Mover una app de proyecto (`neblla_projects` con `move`, o el panel) es un traslado de verdad: su cuota cuenta en el nuevo desde ese momento, sus datos se mudan si los dos proyectos usan bases de datos distintas (pide confirmación), su caja de servidor se redespliega en el Cloudflare nuevo, y una app que vende (productos de pago o suscripciones vivas) no puede ir a un proyecto con otro Stripe. **Pro cuesta 10 € al mes por proyecto**, o 10 $ fuera de Europa. **Impuestos incluidos.** Un **pack** suelto son 14.000 créditos por 10 € o 10 $ (una semana de Pro al precio de un mes): va a la cuota de la que tira el proyecto ahora (la gratis de la cuenta, o la de su Pro), no se reinicia cada día, vale 6 meses y sirve para los picos. `neblla_billing` recibe `projectId`, o `appId` y entonces actúa sobre el proyecto de esa app. **Recarga automática, si la quieres** (es de la cuota, no de una app): `neblla_billing({ appId, action: 'auto_recharge', enabled: true, monthlyCap: 30 })` compra otro pack con tu tarjeta cuando se acaba el último, hasta el tope del mes en euros que pones tú, y te avisa por email en cada recarga y al llegar al tope. Sin tope no se activa. Paga con la tarjeta de tu último pack (sin ella, `no_saved_card`), y `neblla_billing({ action: 'auto_recharge' })` sin más te dice `{ enabled, monthlyCap, spentThisMonth, recharges }`. `neblla_billing` con `pro_checkout` (o `quota_pack_checkout`) devuelve una URL que abre **él**, y el precio sale también ahí antes de pagar — un agente no puede completar el pago. Dos cosas más de esa misma tool que conviene saber antes de usarla: **nada se concede hasta que Stripe lo confirma**, así que la subida a Pro aterriza unos segundos después de que él termine y no en el momento de abrir el enlace — avísale o pensará que no funcionó; y `cancel_pro` **no deja que Pro se renueve**: sigue hasta el final del periodo ya pagado (`endsAt`, la fecha que también enseña el panel) y luego el proyecto vuelve a Free, sin devolver nada; solo se llama cuando el desarrollador lo ha pedido con todas las letras. Mientras ese periodo dura, `resume_pro` lo deshace («Seguir con Pro», también en el panel): Pro vuelve a renovarse el mismo día (`renewsAt`) y ahora no se cobra nada. Con la cancelación pendiente, `pro_checkout` no da enlace: contesta `pro_ending` con `endsAt`, y lo que toca es ofrecerle `resume_pro`. Y no confundas las dos direcciones del dinero: `neblla_billing` es lo que **él** te paga a Neblla; cobrarle a sus usuarios es `neblla_payments_account` más `neblla_create_product` (ver pagos.txt). Cada cuenta trae una **cuota diaria gratis y flexible**, que comparten las apps de sus proyectos gratis: un presupuesto en unidades que gastan en los servicios que usen, sin dimensionar nada por adelantado. Cuando la app crece, la amplías sin rehacer nada, y puedes combinar los dos caminos: - **Cambiar piezas por las tuyas**, de una en una y sin tocar la lógica de la app. Neblla se adapta a tu proyecto, no al revés: tú montas el puzzle como quieras. Por ejemplo, la base de datos: `neblla_byodb` apunta los guardados de usuarios, las salas y comunidad a tu propio Mongo, Turso, Postgres o MySQL, y le pagas a ese proveedor directamente. - **Pasar a Pro** y seguir con todo bajo el mismo techo. Todos los servicios consumen unidades del mismo pack: la cuota se adapta sola al uso real, sin dimensionar nada por anticipado. ## Consumos por acción **Un crédito son 35.791 unidades de payload** (≈ 35,8 kB). **El presupuesto diario gratis es de la cuenta: 1.000 créditos al día** (≈ 35,8 millones de unidades, ≈ 35,8 MB) para todas las apps de sus proyectos gratis; un proyecto Pro tiene los suyos, **2.000 créditos al día**. Las cifras de la tabla están en **unidades**, no en créditos: divide entre 35.800 si quieres pensarlo en créditos. **Base de datos lista para usar.** La cuenta trae **50 MB** en Neblla para empezar sin pensar en ello, compartidos por las apps de sus proyectos gratis y, en cada app, entre los datos de sus usuarios (`db.save()`), sus tablas, el chat de sus salas y su comunidad: tú lo repartes como quieras. Cuando haga falta más, **100 MB** propios con un proyecto Pro, o gratis con `neblla_byodb`: los datos van a tu propio Mongo, Turso, Postgres o MySQL y la base de datos de Neblla deja de contar. **Medios aparte.** Las fotos, texturas y sonidos de `neblla_media` no van en esos 50 MB ni gastan cuota del día: son una bolsa de la **cuenta**, **100 MB** entre las apps de tus proyectos gratis (originales y variantes), y **2 GB** propios en cada proyecto Pro, para sus apps. **Si se llena, no se pierde lo que se estaba haciendo.** Cuando la base de datos está llena, lo que escriben los usuarios que ya estaban dentro no falla: Neblla apunta cada escritura tal cual, en orden (`db.save()` de su documento, una fila con `{ table }`, un `db.delete()`), el usuario ve sus datos con esa cola encima y la respuesta llega con `pending: true` (`db.pending`). Mientras haya escrituras en la cola no entra ningún usuario nuevo (`storage_full`), así que la cola es pequeña. Te avisamos por correo el primer día, el quinto y la víspera del séptimo con cuántas esperan. Haz sitio (borra datos que ya no uses), pasa a Pro o conecta tu base de datos, y se hacen solas en orden; si en 7 días no hay sitio, se tiran. Las filas de tus tablas cuentan en la base de datos igual que los guardados. **Límite contra el abuso.** Guardar es una línea (`neblla.db.save()`), así que tú pones el máximo que puede guardar cada usuario. Así nadie que manipule el cliente puede usar el espacio de tu app para sus cosas. Por defecto son 10 KB; lo cambias a lo que tu app necesite con `neblla_update_app({ saves: { maxBytesPerUser: 102400 } })`, o lo quitas con `maxBytesPerUser: 0`. **Lo que cuesta.** Con BYODB deja de contar lo guardado, no la escritura: cada `db.save()` cuesta sus bytes de payload, porque cruza el servidor. Y BBDD propia tampoco hace gratis el tráfico de comunidad: publicar una entry cuesta los bytes de su payload, leerla cuesta su petición como un mensaje y lo que llega como respuesta, y una consulta de listado, facetas o cola cuesta **50 unidades** fijas más la respuesta, viva el contenido en la base de datos que sea. **Dos cosas que se presupuestan mal.** Aterrizar en el `lobby` pasa automáticamente al conectar y **no** se cobra como `rooms.join`: cargar la página cuesta la descarga del SDK más las 100 unidades del handshake, sin un `rooms.join` de más. Y **tu app no tiene que estar alojada aquí.** Neblla la aloja en `play.neblla.com/` si quieres, pero puedes servir tu propio HTML desde tu dominio y cargar el SDK desde `neblla.com/connect/app/` — añade ese origen con `neblla_update_app({ addDomain })`, que es una lista de orígenes permitidos, y entonces Neblla es puramente tu backend. Esa es la forma que quiere la mayoría de los sitios que no son juegos. **1 unidad ≈ 1 byte en la red** (bytes UTF-8). Las tarifas siguen lo que cuesta de verdad mover cada cosa por el servidor: **cada mensaje que pasa por Neblla paga 64 unidades fijas** (el marco, el cifrado y el reenvío) **más sus bytes**, una vez al entrar y otra por cada destinatario al que llega a través de Neblla. Son dos fijos aparte: **el de entrada** se queda en 64 aunque entrar cueste menos, como freno para que el servidor no haga de canal en tiempo real en lugar del P2P; **el de salida**, por destinatario y por respuesta, es hoy también 64 y se ajustará a lo que cuesta de verdad cuando se mida. Lo que viaja P2P directo no paga. ``` Acción Coste (unidades) ────────────────────────────────────────────── ───────────────────────────── Cargar la página: la parte viva del SDK 500 + 1/10 de sus bytes (la parte fija, socket.io y el código de servidor no cuestan: el navegador los guarda) Usuario nuevo (/connect/auth) 50 + los bytes de la respuesta Volver con la sesión guardada (reconexión) 50 + 1/4 de los bytes de la respuesta Conectar (handshake) 100 Login (silencioso / consentimiento) 30 / 100 Invitar a un amigo 30 Entrar / crear una sala (rooms.join) 100 Heartbeat y reloj (como mucho 1 cada 30 s) 1 + 1 Mensaje que entra a Neblla 64 + sus bytes … y por cada destinatario que lo recibe 64 + sus bytes a través de Neblla (P2P directo: 0) (message.send y message.to, desde un cliente o desde el server; state.set / state.merge, db.setPublicInfo, room.setData, inbox.send / inbox.update) Respuestas con datos (listas, historial, 64 + sus bytes el estado al reconectar, una entry leída, la fila guardada, el valor de una clave, lo que devuelve neblla.api.call) db.save() (usuario con login) la mitad de lo que mandas (ver identidad.txt) Una fila de tabla (db.save con { table }) 64 + lo que mandas room.data de una sala permanente la mitad de sus bytes cada vez que se escribe (en los momentos clave, no en cada setData) Comunidad: publicar / leer / listar sus bytes / 64 + la petición / 50 neblla.api.key / neblla.api.call 64 + la petición / 128 + lo que sale Testigo antitrampas / latencyWatch / pings 20 / 2 / 1 Webhook entrante 64 + sus bytes Automatización: abrir sala / mensaje / API 64 / 64 + sus bytes / 128 + lo que sale y lo que vuelve Webhook saliente 128 + lo que sale y lo que vuelve Registro de moderación (si lo enciendes), 64 + sus bytes por línea neblla.tools (No-code), por llamada 64 + sus bytes Ficheros que sirve Neblla (pre, o una app sus bytes (si el navegador ya en caliente; publicada, los sirve tiene la copia, 0) Cloudflare y no cuestan) Subir un fichero la mitad de su tamaño Relé TURN sus bytes Sincronizar con git 5 créditos Server en la nube: la caja despierta ≈1 por ms (unos 100 créditos por hora; dormida, 0) … y cada mensaje que procesa 5 neblla.ai.prompt(...) — por llamada 500 (los tokens del proveedor de IA se facturan aparte, contra la clave del dev o del usuario) ``` Ejemplo: una posición de 10 bytes que llega por Neblla a 7 usuarios cuesta 74 + 7 × 153 = **1.145 unidades**: cada copia lleva, además del mensaje, el sobre que dice de quién es y cuándo salió (`{ user, time }`, unos 79 bytes), y esos bytes salen de verdad del servidor. Por P2P directo, 0. Un trazo de 200 bytes, 264 + 7 × 343 = 2.665. Y solo pasa si lo enciendes: el reenvío de mensajes por el servidor viene apagado (ver más abajo). El estado (`state.set`) viaja igual: P2P directo, 0; solo paga hacia un dispositivo que no conecta P2P y lo recibe reenviado por Neblla. En una sala permanente, un mensaje que va P2P también avisa a cada usuario de que quedó guardado (unas 64 + 110 unidades por usuario). ## Lo que NO consume cuota - Los mensajes y ficheros que viajan P2P **directo** (WebRTC de peer a peer, nunca tocan a Neblla). Cuando dos dispositivos no pueden conectarse directamente —NAT simétrico, algunas redes corporativas o móviles—, WebRTC puede usar un **relay TURN de Neblla**. Ese relay ocupa nuestros servidores, así que sus bytes cuentan contra la cuota de la app, y **tú decides si tu app lo permite** (`p2p: { turn: true }`; apagado por defecto). El orden es siempre: P2P directo, después el relé TURN si lo encendiste y, como **último recurso**, el **reenvío de mensajes por el servidor de Neblla**, que también viene apagado: lo enciendes aparte con `connections: 'all'` (o en Config → Conexión entre dispositivos), y cada copia cuenta como mensaje reenviado. Con los dos apagados (`connections: 'p2p'`, con el que nace una app nueva; una app creada antes de este cambio, sin el ajuste guardado, se lee como `'all'` y sigue como siempre, con el servidor), un dispositivo que no llega a nadie de la sala se queda fuera de sus mensajes: Neblla se lo dice al usuario («Tu red no permite conectar con esta sala»), tu app recibe `neblla.room.on('leftOut')`, y el panel cuenta cuántos se han quedado fuera en los últimos 7 días, con un botón para encender el TURN y otro para el servidor. Debajo, en los mismos 7 días, cuántas conexiones P2P se abrieron y cuántas de ellas fueron por el relé TURN, con cuántos usuarios y cuántos MB (cada dispositivo cuenta su lado de la conexión). El estado de la sala (`state.set` desde Fog) y los avisos de la sala sí siguen llegando por Neblla, porque sin ellos no podría jugar; en una sala con historial el mensaje se guarda igual, aunque no se reenvíe. `connections: 'server'` lo manda todo por el servidor, nunca P2P; `'none'`, nada: ni P2P ni servidor, y los mensajes de la sala tampoco se guardan en su historial. Sin TURN ni P2P se pierden también los ficheros (`room.sendFile`). - El **almacenamiento** de `db.save()` cuando hay BBDD propia configurada: esos bytes en reposo no los guarda Neblla, así que no cuentan (ver identidad.txt). La **operación** sí se cobra — el payload cruza el servidor igual, con la RAM y la CPU de enrutarlo. Un `db.save()` son dos cosas y solo una es gratis. - `neblla.ai.prompt()` en modo `browser` (la llamada va directa del navegador a Anthropic). Estos números pueden ajustarse con el tiempo; el panel del dashboard muestra siempre el consumo real. **Sugerencias.** Lo que gasta cuota viene apagado, y Neblla mira cómo usan tu app de verdad en los últimos 7 días (hoy y los 6 días UTC anteriores) para decirte qué encender: el **Inicio** del panel (Sugerencias) (o `neblla_get_app`, que trae `suggestions: [{ id, data, change, pending, live }]`; `change` se aplica con `neblla_update_app`). Cada regla mira pro, donde están tus usuarios, y por debajo de su mínimo no sugiere nada. Hoy son cinco: el **relé TURN** (`p2p.turn`) si 3 o más usuarios distintos se quedaron fuera de una sala, la app usa WebRTC (`p2p.allow` no es `false` y `connections` no es `'server'` ni `'none'`) y el despliegue tiene relé (`turnAvailable`); el **servidor de respaldo** (`connections: 'all'`) si `connections` es `'p2p'`, el TURN está encendido y 3 o más usuarios distintos siguieron fuera con él; la **vigilancia de tiempos imposibles** (`fog.latencyWatch`) si la app tiene el módulo de salas y las Fogs no se pusieron de acuerdo 3 o más veces (sin contar los casos que anulaste); el **«¿sigues ahí?» espaciado** (`connection.presence` 120 s / 600 s) si la app tiene el módulo de salas, hubo 20 o más conexiones nuevas, nadie entró en una sala y no has puesto tú una presencia (ni en pro ni en pre); y el **registro de moderación** (`moderationLog`) si se baneó a 3 o más jugadores que siguen baneados. Cada una dice por qué, lo que cuesta, y se aplica con un clic. Las cuatro primeras son ajustes que pasan por el borrador: el cambio (`change`) entra en él como cualquier ajuste, conservando el resto de ese ajuste, y llega a tus usuarios al publicar; mientras tanto la sugerencia aparece como pendiente (`pending: true`, cuando el borrador o pre ya tienen lo que sugiere). El registro de moderación es un ajuste de la app, no de un entorno (`live: true`): se escribe en pro y en pre a la vez, está activo al momento y nunca sale como pendiente. Si una comprobación falla, el panel lo dice (y `neblla_get_app` trae `suggestionsFailed` con sus ids, o `suggestionsError`): una lista vacía entonces no quiere decir que no haya nada que sugerir. ## Dos cosas que nadie pregunta y todo el mundo necesita **Dónde se publica tu app.** En la web (su URL y un enlace para compartir), en la galería de Neblla y en la app de Neblla para Android e iOS, que trae todas las apps de la galería: las tres sin hacer nada. Y, cuando quieras, como app propia en Google Play y App Store con tu cuenta de desarrollador: `neblla_publish_store({ appId, store: 'google' | 'apple', action: 'set', packageName, serviceAccountJson, track, certs })` guarda tu cuenta, `build` empaqueta la app y la sube en segundo plano, y `status` dice cómo va. `certs` es el SHA-256 de firma que te da la consola de la tienda: con él la app abre a pantalla completa, sin barra de navegador. La primera versión de una app nueva se crea a mano en la consola de la tienda; las siguientes, aquí. `remove` olvida la cuenta y conserva la clave de subida. **Tu dominio, de dos maneras.** Si alojas tú, **sirves el HTML** desde donde ya despliegas, cargas el SDK desde `neblla.com/connect/app/` y autorizas ese origen con `neblla_update_app({ addDomain })`, para que el SDK acepte sus conexiones. Si aloja Neblla, la dirección es `play.neblla.com/`, y le pones tu dominio: `neblla_update_app({ customDomain: 'mijuego.com' })` responde `{ host, cname, status, note }`; apuntas un CNAME a `cname`, Neblla pone el certificado y `status` pasa de `pending` a `active`. El dominio queda también autorizado como origen. `customDomain: null` lo quita, y `domain_taken` avisa si otra app ya lo usa. En el panel es Publicar › Tu propio dominio: el dev escribe el dominio, ve el CNAME que hay que poner y el estado (pendiente o activa, con «Comprobar») y lo quita. Ponerlo, comprobarlo y quitarlo es solo del dueño de la app, en el panel y en el MCP, porque cambia todas las direcciones que ya se compartieron: a alguien del equipo (también un Socio) le responde `not_allowed`, y lo ve sin poder tocarlo; ahí no se confunde con la lista de webs que pueden usar la app (`addDomain`), que está en Publicar › Enlace y webs permitidas. Las dos son válidas y la primera es la que quiere casi todo lo que no es un juego, porque no subes nada y no despliegas nada aquí. Para plataformas que sirven cada juego desde su propio subdominio, vale un comodín: `addDomain: '*.plataforma.com'` autoriza **un nivel** de subdominio de ese dominio (no el dominio a secas ni niveles más hondos); `*` solo o `*.com` se rechazan. **Tu Cloudflare, por temas (opcional).** Todo funciona sin él. Quien quiera, conecta su propia cuenta de Cloudflare en el panel (Mis apps › Conexiones del proyecto o de la cuenta) y elige qué temas pasan a ella, cada uno por separado: **Tus ficheros** (las imágenes y ficheros de las apps en tu R2: sin el tope de almacenamiento de Neblla, 10 GB gratis al mes y Cloudflare te factura lo demás; pide una tarjeta en Cloudflare y activar R2 una vez, que no se puede hacer por API: el panel abre la pantalla exacta; los ficheros que ya había se mueven solos), **Tu dominio** (gratis; abajo), **El correo de tu dominio** (gratis) y **Tu servidor** (`room.js`, ver servidor.txt). Un solo token para todo, creado con el enlace de Cloudflare que el panel da ya rellenado con los permisos de los temas elegidos (los que Cloudflare no deja rellenar, el panel los nombra para añadirlos a mano). **El token se pega en el panel, nunca en el chat.** Desde el MCP: `neblla_projects` con `cloudflare_setup`, `cloudflare_topic` y `cloudflare_check`. **Un dominio que ya tienes, en tu Cloudflare.** Para quien llega de otro sitio (Jimdo, Wix, un registrador cualquiera) con su dominio y su correo: `neblla_domains({ action: 'add', domain })` crea la zona en su Cloudflare y copia todos los registros que ve el DNS público (la web, el MX, los TXT, DKIM…), y compara MX, TXT, www y el dominio a secas. Si coincide, da los dos nameservers que el dev pone en su registrador, el único paso a mano; la web y el correo de antes siguen funcionando mientras tanto, y Neblla avisa por correo cuando el dominio está activo. Si algo no coincide, dice qué antes de seguir. Después, `point_web` lleva www a una app (el dominio a secas redirige a www) y `unpoint_web` lo deshace. **El registro, si quiere:** `registration` dice quién lo tiene y cuándo caduca, y Neblla avisa para empezar el traslado a tiempo (17 días antes); `transfer_check` dice si se puede y su precio (suma un año), y el código de traslado se pega en el panel; `check_new` y `register` compran uno nuevo en su cuenta de Cloudflare, a precio de Cloudflare y nunca a nombre de Neblla. **El correo:** `mail_forward({ domain, to, from? })` reenvía todas las direcciones (o una, como info@) a un buzón del dev, que confirma un correo de Cloudflare; si el dominio tiene buzones en otro sitio, pregunta antes de sustituirlos (`replaceMailboxes: true`), los guarda y `mail_restore` los devuelve. Es reenvío: escribir desde info@ necesita un buzón de verdad, que no es de Neblla. **Una app gratis que nadie toca se archiva sola a los 30 días, y vuelve sola.** Conviene que el dev lo sepa: si durante **30 días** no entra el desarrollador **ni** hace login ningún usuario, Neblla empaqueta los datos de la app fuera de su base de datos activa —cifrados y en un bucket privado, nunca junto a los ficheros públicos de la app— y libera el sitio. Por diseño **no se borra nada**, y no hay que pedir nada para recuperarlo: la ficha sigue encontrable en la galería, y la primera vez que alguien abre la app —o el dev entra al editor— se restaura automáticamente antes de continuar. El único efecto visible es que **esa primera apertura es más lenta**. Las apps con suscripción activa están **exentas** pase lo que pase, incluso sin actividad ninguna, porque ahí el dev está pagando el alojamiento. Dos consecuencias prácticas: un prototipo que dejas parado medio año sigue ahí cuando vuelvas, y si te importa que el primer visitante no espere, ábrela tú de vez en cuando. ## Qué cambia Pro Esto suele descubrirse tope a tope y con una sorpresa cada vez, así que va junto. Todos los números salen de la configuración del servidor, no de una estimación. «Gratis» es la cuota de la cuenta, compartida por las apps de sus proyectos gratis; «Pro», la de cada proyecto Pro, compartida por sus apps: ``` Gratis Pro ───────────────────────────────── ─────────────── ───────────────── Presupuesto diario 1.000 créditos 2.000 créditos (por cuenta) (por proyecto) Bolsa de rebose acumulada hasta 1 día hasta 1 día Ficheros 300 600 Tamaño máximo por fichero 25 MB 25 MB Base de datos 50 MB 100 MB (usuarios + comunidad) Medios (bolsa de la cuenta) 100 MB 2 GB por proyecto Límite antiabuso por usuario 10 KB por defecto igual (lo ajustas o lo quitas) Payload por entry 64 KB por defecto igual (límite antiabuso: lo ajustas o lo quitas) Buzón por usuario 100 mensajes 2.000 mensajes room.data por sala 10 KB por defecto igual (límite antiabuso: lo ajustas o lo quitas) Archivado por inactividad a los 30 días exenta siempre ``` En resumen: un proyecto Pro tiene su propia cuota (el doble de créditos al día que la gratis de toda la cuenta, 100 MB y 600 ficheros solo para sus apps), multiplica el buzón por veinte y quita el archivado automático, y la cuota gratis de la cuenta queda entera para las demás apps. Cada app puede llevar además su propio tope dentro de su cuota (`neblla_projects` con `set_app_caps`), para que una no se coma la de las demás. Para guardar mucho por usuario, `neblla_byodb`. **Ficheros que no caben.** Si la app necesita ficheros de más de 25 MB (un vídeo largo, un pack de música) o muchos más de los de su plan, ponlos en tu propia CDN y cárgalos por URL desde la app: Neblla no tiene que alojar todo. El resto, súbelo aquí y se sirve desde la CDN de Neblla. **Al agotarse, aterriza suave.** Cuando la app gasta el día, la bolsa y los packs, no se corta en seco: las salas que ya están en marcha siguen, y solo se rechaza lo que abriría algo nuevo (otra sala, una reserva, un usuario que llega) con `quota_exceeded`, que la app puede enseñar. Hay un margen del 10 % del día (`quotaGracePercent`) para que lo empezado termine. Si también se cruza, las salas se cierran una vez: los datos de las salas permanentes se escriben, cada sala oye `room.on('closed')` (`{ room, reason: 'quota_exceeded', resetsAt }`) y todos salen, el lobby también. Los guardados de los usuarios (`db.save`, sus filas) tampoco se aceptan desde que se agota: esperan en su navegador y salen cuando vuelve la cuota, en la siguiente visita si hace falta (identidad.txt); el panel (Configuración → Capacidad) cuenta cuántos usuarios tienen guardados esperando. Lo que escribe el código de servidor sí pasa. A las 00:00 UTC, o en cuanto entra un pack, todos vuelven al lobby. **Un usuario no se come la app.** Ningún usuario puede gastar más del 20 % del día de su app (el tope por usuario, `playerQuota`, en % de lo que la app puede gastar ese día: el día, la bolsa y los packs). Cuenta lo que gastan sus propios eventos (una cuenta con varias pestañas suma todas); el servidor de la sala no tiene tope. Al pasarlo, sus eventos se rechazan con `player_quota_exceeded` hasta las 00:00 UTC (sus guardados esperan en su navegador), y el resto sigue con normalidad. El dev lo cambia, o lo apaga con 0, en `neblla_update_app({ playerQuota })` o en el panel, Dinero › Plan y uso. Una compra lo puede subir a quien la tenga: `limits: { vip: { playerQuota: 50 } }` en `neblla_update_app`, o en Dinero › Productos; con varios productos cuenta el más generoso. **La cuota sin gastar se guarda.** Al cerrar el día, **la mitad de lo que no gastaste** se acumula en la bolsa de esa cuota, hasta un día entero (1.000 créditos en la gratis de la cuenta, 2.000 en un proyecto Pro). O sea que una cuenta que pasa la semana tranquila llega al sábado pudiendo gastar **hasta el doble** de un día normal, que es exactamente la forma que tiene una partida con amigos: nada de lunes a viernes y una noche larga el fin de semana. El orden de consumo también está pensado: primero el día, luego la bolsa, y solo al final el pack comprado — así el pack, que no se recarga, se queda para el pico de verdad mientras la bolsa se vuelve a llenar sola. ## Los avisos de tu app Todo lo que el dev tiene que saber de su app llega a un registro de avisos de esa app: sale en el panel (Inicio › Necesita tu atención, y en la página de su tema) hasta que se resuelve o el dev lo marca como visto, y queda 90 días en «Avisos anteriores». Por el MCP, `neblla_alerts` y `alerts` en `neblla_get_app`. Cada aviso dice en palabras claras qué pasa, qué notan los usuarios y qué hacer o quién lo tiene que mirar («Tus usuarios siguen jugando, pero lo que guardan no llega a tu base de datos. Lo tiene que mirar quien lleva tu base de datos»), en los 10 idiomas del panel. Los avisos (`type`, y entre paréntesis los urgentes): - `store_full` (urgente): la base de datos está llena; las escrituras esperan aparte y la cuenta atrás de 7 días dice hasta cuándo. Se cierra cuando entran. `store_dropped` (urgente): pasaron los 7 días y se tiraron. - `byodb_down` (urgente): tu base de datos no responde y las escrituras esperan en la cola. `byodb_payments_waiting` (urgente): pagos de Stripe esperando a que responda. `migration_paused`: el traslado de tus datos se paró por la cuota y sigue solo. `broadcast_paused`: un «Escribir a todos» se paró por la cuota y sigue solo tras el reinicio. - `quota_margin`: se acabó la cuota del día y la app está en el margen del 10 % (no entra nadie nuevo). `quota_closed` (urgente): también se pasó el margen y las salas están cerradas. Los dos terminan solos a las 00:00 UTC, o al entrar un pack. - `save_loop`: usuarios que guardan en bucle (ver identidad.txt). - `legal_notice` (urgente): un aviso formal sobre una entrada de la comunidad, abierto hasta que se cierra el caso. `community_hold` (urgente): Neblla pausó la comunidad. - `erasure_request` (urgente): un usuario pide que borres sus datos (la ley da un mes); se cierra al borrarlo. - `review_unanswered`: una reseña nueva en la galería; se cierra al contestarla. - `promote_failed`: un «Pasar a pro» no se pudo aplicar entero y se reintenta solo; se cierra cuando se aplica. - `legacy_sdk` (urgente): lo que tienen los usuarios llama al SDK de antes de los módulos (`sendMessage`, `joinRoom`…), que ya no existe, así que esas partes fallan. `neblla_get_app` lo detalla en `legacy`; se cierra con el primer «Pasar a pro» que ya no las tenga. - `auto_recharge`: la recarga automática de cuota hecha; por email al dueño si no lo cambias, y se puede apagar. `auto_recharge_failed` (urgente): la recarga no se pudo hacer. - `cloud_app_limit` (urgente): la app ha gastado su parte del día en la nube de Neblla (4 h de caja despierta) y sus salas siguen sin nube, en Fog o esperando, hasta las 00:00 UTC. `cloud_day_full`: lo mismo porque se acabó el día de toda la nube. `cloud_full`: la nube no tenía sitio para la caja de la app; se cierra cuando se despliega (servidor.txt). **A quién y por dónde (Ajustes › Avisos).** Todo aviso está en el panel. Además, para cada tipo, el dev elige a quién del equipo le llega por email (`"owner"` o miembros de `neblla_team`) y si va a un canal de Slack o de Discord con un webhook entrante (la URL se guarda cifrada y no se enseña nunca). Por defecto, los urgentes y la recarga hecha van por email al dueño de la app (una vez al abrirse) y el resto solo al panel. Los que tienen su propio email (base de datos llena, aviso legal, comunidad pausada, recarga fallida) le llegan siempre al dueño, elija lo que elija (`ownerEmailAlways`); el registro añade los demás destinatarios y los webhooks. La recarga hecha sí se puede apagar. Es un ajuste de la app que va en el acto (no pasa por pre). Panel: `POST /api/apps/alerts` (`{ appId, lang?, topic?, history? }`), `/alerts/dismiss`, `/alerts/settings`, `/alerts/settings/set` y `/alerts/test`.