Qué problema resuelve
Quien vende por WhatsApp no tiene un catálogo: tiene una galería y una conversación. Cada vez que alguien pregunta qué hay, manda las mismas fotos otra vez, una por una, con el precio en un mensaje aparte. Cuando entra mercancía nueva, rehace el collage o el PDF entero. Y cuando algo se vende, avisa persona por persona: no hay una sola pantalla que todos estén mirando.
Mostrable convierte esas fotos en un catálogo con un link. Cada producto lleva un estado —disponible, apartado, agotado— que se cambia en un toque, y quien abra el link ve el de ese momento.
Cómo está construido y por qué así
El catálogo público se renderiza en el servidor, y eso no es una preferencia de rendimiento. Si la vista previa no se arma sin ejecutar la página, el link pegado en un chat sale sin foto, y el canal por el que el producto se distribuye se muere sin dar señal: el riesgo escrito antes de construir nada era exactamente ese, «el loop viral muere y no te enteras en meses». Que era real lo demostró un fallo posterior, y de otro tipo.
Esa decisión tuvo una segunda parte que solo aparece midiendo. La vista previa se emitía en PNG y una foto real pesaba 837 KB: WhatsApp la descartaba, así que las cuentas reales compartían links sin imagen mientras la de pruebas se salvaba por casualidad. No era el tamaño, era el formato: bajar la resolución apenas movió la aguja —837 a 787 KB— y pasar el PNG a JPEG la dejó en 68 KB, medidos en producción el 21 de julio de 2026.
Las fotos se comprimen en el navegador antes de tocar la red, a 1600 px de lado mayor y unos 220 KB por foto, porque un lote de cuarenta fotos de cámara ronda los 140 MB y con datos móviles eso es una subida de quince minutos. La concurrencia dejó de ser fija en tres carriles, y el interés está en cómo se decidió sin poder reproducir el fallo. La hipótesis era de memoria —una foto de 12 MP se decodifica a unos 48 MB en RGBA aunque el JPEG pese cuatro, así que tres a la vez son unos 150 MB de mapa de bits vivo—, pero no se reprodujo en ningún dispositivo, y después se demostró que la misma firma de error sale de un archivo de 3 KB en un solo carril: no hace falta memoria para producirla. Se cambió igual, y por el costo asimétrico: equivocarse cuesta que un lote pesado tarde más; no hacer nada cuesta que siga sin subir nada. Ahora los carriles se calculan según el peso medio del lote —no el máximo, para que una foto pesada entre veinte ligeras no serialice el resto.
A las fotos se les quitan los metadatos, y el motivo es concreto. El bucket de imágenes es público por diseño, y para este público el punto GPS de la foto de un producto es su casa. Antes, borrar el EXIF era un efecto secundario no declarado de recomprimir en un canvas: en cuanto una foto dejaba de comprimirse, sus metadatos viajaban enteros. Hoy hay un limpiador que recorre marcadores en lugar de decodificar la imagen: retira IPTC y comentarios y conserva el perfil ICC, porque quitarlo cambiaría los colores. Con el EXIF hace algo menos obvio y más interesante: no lo toca. En ese mismo segmento viaja la etiqueta de orientación, y quitarlo sin girar los píxeles sube tumbada 90° cualquier foto vertical —comprobado contra un archivo real—. Así que ante una foto con EXIF el limpiador se declara no concluyente y la manda al camino de comprimir, que lo borra por re-encode y de paso hornea el giro. El invariante no depende de que el atajo acierte. Y ante algo que no reconoce devuelve los bytes intactos: un limpiador que rompe una foto es peor que uno que no limpia.
El tope del plan gratuito se movió de bandera remota a Postgres, justo lo contrario de lo que decía el documento fundacional. La idea original era tenerlo como feature flag para moverlo con datos y sin desplegar. Hoy la autoridad vive en una tabla del esquema private, revocada de los roles anónimo y autenticado, y en TypeScript queda solo una constante que impide a una pantalla prometer otro número. El comentario de la migración lo resume: un experimento puede mover el copy, no los permisos de la base. El coste se asumió a la vista —mover el tope exige desplegar— y una prueba falla si el nombre de la bandera vieja reaparece en esas pantallas. El límite recorta visibilidad y nunca borra: lo que excede el cupo queda guardado y sin mostrar.
La IA propone nombre; el precio lo escribe siempre quien vende. El modelo ni siquiera devuelve un campo de precio: no conoce su margen ni a su proveedor, y un precio mal sugerido quema la confianza en el primer producto. Y se llama tarde a propósito, en tandas pequeñas conforme la vendedora avanza: quien sube cuarenta fotos y abandona en el quinto producto no paga cuarenta llamadas de visión. La otra palanca de costo es que la foto que ve el modelo va a 512 px, no a resolución completa.
Nada de lo que se cobra se activa desde la pantalla de retorno. Lo único que mueve la vigencia de un plan es el webhook: firma HMAC, ventana de frescura, cuerpo acotado y reconsulta del pago con el token del servidor antes de decidir nada; después, moneda, cuenta cobradora, referencia única por orden e importe exacto, con funciones idempotentes para otorgar y revocar. Que la paranoia hacía falta lo demostró el historial: un umbral de precio viejo se quedó en el webhook cuando el precio ya había cambiado, y un pago al precio antiguo regalaba treinta días. Por eso los importes viven en un único archivo y no se repiten en ninguna documentación. Alrededor manda Postgres y no la interfaz: RLS en todas las tablas y columnas de dinero sin permisos de cliente.
Qué hace y qué no hace hoy
Hace: subir fotos desde el móvil, confirmarlas una a una con nombre y precio, publicar un link, cambiar estados con fecha de apartado, añadir estados propios, un botón «Lo quiero» que abre WhatsApp con el producto y registra el clic antes de salir, una tarjeta con vistas y clics recientes, e instalarse como PWA. También se puede probar sin cuenta: las fotos y los borradores se quedan en el dispositivo y solo hay que entrar con Google al publicar.
No hace, y varias son decisiones y no pendientes: no hay carrito ni cobro a la compradora, porque la vendedora ya cobra en efectivo y por transferencia; no hay envíos, ni inventario numérico, ni variantes de talla o color, donde hoy una talla es un producto; no hay PDF; el link es una ruta dentro del dominio, no un subdominio; y no hay renovación automática: cada plan es un pago único y al vencer la cuenta vuelve sola al gratuito sin que se borre nada.
Tres límites más, escritos en el propio repositorio: la política de seguridad de contenido va en modo de solo reporte, a la espera de violaciones reales que observar; las métricas anónimas siguen sin límite de peticiones; y el recorrido de unas pocas fotos en un Android de gama media con datos móviles —la medida interna que ordena el alcance— no consta verificado.
Todo lo anterior describe el repositorio el 28 de agosto de 2026.
Dónde verlo
Mostrable está en producción: mostrable.com
Otros dos proyectos con ficha propia en este sitio: ControlTabs y Retomo (antes WhatsFollow).