Versões, canais e relatórios assinados
Una ficha de Fulgeon es una lista de versiones, y cada versión es una lista de archivos con sus destinos. Todo lo demás —descripción, capturas, categoría— se cambia cuando quieras. Las versiones publicadas, no.
En la ficha pública esa lista no sale desplegada entera: se enseña una versión cada vez. Por defecto, la última estable publicada, que es la misma que anuncia /api/latest (si la ficha destacara una y el manifiesto otra, el aviso de actualización llevaría a una página que enseña otra cosa). El resto se elige con el selector de versiones, que es el parámetro ?v= de la URL. Los informes firmados que se listan son los de la versión que se está viendo, no los de todas; el registro completo sigue entero en la API. Así que si tu ficha «solo enseña una versión», no le falta nada: es como funciona.
Los tres estados
Una versión nace en borrador: editable e invisible para todo el mundo menos para ti. Puedes añadirle archivos, quitárselos y borrarla entera.
Al publicarla pasa a publicada y se vuelve inmutable. Para publicarla hace falta al menos un archivo o enlace principal.
Una versión publicada se puede retirar (yank) explicando el motivo, con un mínimo de cinco caracteres. Retirada deja de ofrecerse, pero el registro y el informe firmado siguen ahí.
Publicar la versión no publica la ficha: son dos actos distintos, y la ficha exige tener al menos una versión ya publicada.
Por qué las versiones son inmutables
Porque si el archivo de la 1.2.0 pudiera cambiar, el hash de la 1.2.0 no significaría nada. Y sin eso no hay informe verificable ni forma de que nadie compruebe que lo que descargó es lo que se publicó.
Consecuencias prácticas:
- Repetir un número de versión da error, con este mensaje: «La versión 1.2.0 ya existe. El contenido nuevo va en una versión nueva.» No existe sobrescribir. Si tu CI reintenta un tag ya publicado, ese error es la señal de que ya estaba hecho: trátalo como éxito, no como fallo del build.
- Una versión publicada no se borra. Se retira. Borrar borraría también la prueba de que existió.
- Tocar una versión publicada responde
409: «Esta versión ya está publicada y es inmutable. Si algo está mal, retírala y publica una versión nueva.»
Los borradores sí se borran, con sus archivos.
Canales
Cada versión va en un canal: stable (el de por defecto), beta o alpha.
El canal no cambia lo que puedes subir ni cómo se firma: es cómo etiquetas lo que estás pidiendo que instale la gente. Si publicas desde CI con el workflow de ejemplo, el canal sale del propio tag — v1.3.0-beta.1 es beta.
El changelog es obligatorio
Mínimo 10 caracteres, y se comprueba en el servidor:
Las notas de versión son obligatorias: cuenta qué cambia (mínimo 10 caracteres).
Diez caracteres es un listón bajo a propósito, pero es un listón: una versión sin notas es una versión que nadie sabe si le conviene instalar. El CLI lo comprueba además en local, antes de subir un solo byte.
Qué no debe contar un changelog
Dos cosas, y las dos por el mismo motivo: tus notas las lee también quien todavía no se ha actualizado.
- La mecánica de los fallos corregidos. «Corregido un fallo de seguridad al pegar» está bien; la receta paso a paso de cómo se explotaba, no. Esa receta describe un ataque que sigue funcionando en cada equipo que aún tiene la versión anterior, y los primeros días eso son casi todos.
- Las interioridades de tus licencias: formato de las claves, cadencia de comprobación, cómo se validan. Nada de eso ayuda a decidir si conviene actualizar, y todo ello ayuda a quien quiere saltárselas.
Si unas notas ya publicadas cruzan esa raya, Fulgeon puede sustituirlas por una redacción segura, dejando constancia de la intervención en el registro de moderación. Es la única excepción a la inmutabilidad de una versión publicada, y alcanza solo al texto de las notas: el archivo y su hash no los toca nadie.
Cómo se enseñan las notas
En la web, la ficha renderiza un markdown mínimo por línea: **negrita**, *cursiva* y código entre acentos graves. A las aplicaciones, en cambio, las notas llegan sin marcas por el manifiesto de /api/latest: los avisos de «hay versión nueva» pintan texto plano, y un **Guarda en MP4** con los asteriscos puestos es peor que sin negrita. Escribe las notas de forma que se lean bien en los dos sitios: las marcas pueden embellecer la web, pero no pueden ser imprescindibles para entender la frase.
Artefactos y destinos
Un artefacto es un archivo subido o un enlace https:// a donde vive. Ambos cuentan igual para poder publicar, y ambos llevan informe firmado. Pero no dan la misma garantía, y tu ficha lo dirá: la entrega por enlace sale marcada como «sin verificar», porque el archivo vive en tu servidor, Fulgeon no tiene su hash y no puede garantizar que siga siendo el mismo de siempre. Su informe firmado identifica el enlace, no el contenido, y lo declara con integridad: "ninguna". Para lo que de verdad vive en otra tienda es lo honesto; para un binario tuyo que podrías subir aquí, subirlo da más garantía.
- Máximo 500 MB por archivo.
- Extensiones:
.dmg.pkg.zip.exe.msi.appimage.deb.rpm.tar.gz.tgz.jar.apk.vsix.gz.7z.blockmap - Papel del archivo:
primary(el de por defecto),signature,sourcesuother. Para publicar hace falta al menos unoprimary.
El .blockmap de la lista es el mapa de bloques que genera electron-builder junto a cada instalador. No se instala: sirve para que una actualización baje sólo lo que ha cambiado, y va con papel other. Está contado en actualización automática.
Un destino dice sobre qué corre ese artefacto:
| Campo | Obligatorio | Ejemplo |
|---|---|---|
host | sí | macos, windows, linux, web, chrome, firefox, vscode, obsidian, wordpress, minecraft-java |
arch | no (any) | arm64, x86_64, universal |
loader | sólo donde aplique | fabric, forge, neoforge, paper, spigot |
min_version / max_version | no | 13.0 / 15.9 |
version_list | no | ["1.20.1", "1.20.4"], donde la gramática sea de lista |
Si el anfitrión no aplica al tipo de tu aplicación, o el cargador no corresponde a ese anfitrión, la respuesta te dice cuál de las dos cosas falla.
El SHA-256 lo calcula el servidor mientras recibe el archivo, en streaming. No te lo pide: te lo dice. Compáralo con el tuyo y tienes comprobación de integridad de extremo a extremo gratis; si no coinciden, algo se corrompió por el camino y esa versión no se debe publicar.
El informe firmado
Al registrar un artefacto, Fulgeon crea un informe de identidad y lo firma con Ed25519. Lo que se firma es exactamente esto:
{
"v": 2,
"informe": "identidad",
"app": "clipio",
"version": "1.2.0",
"archivo": "Clipio-1.2.0-arm64.dmg",
"sha256": "9f2c...",
"bytes": 42381209,
"enlace": null,
"integridad": "sha256",
"ts": 1786000200
}Hechos comprobables: este archivo, con este hash exacto, de este tamaño, registrado en este momento para esta versión. Y una declaración, dentro del propio informe, de qué garantiza: integridad vale "sha256" cuando el archivo se subió aquí y el hash ata su identidad, y "ninguna" cuando el artefacto es un enlace. En ese segundo caso archivo, sha256 y bytes van vacíos y lo que se firma es el enlace: no hay archivo que medir, y el informe lo dice en vez de aparentar una garantía que no puede dar.
El informe no dice «esto es seguro». No hay antivirus detrás, ni análisis de comportamiento, ni juicio sobre lo que hace el programa. Dice qué archivo es, y lo dice de forma que nadie —tampoco nosotros— pueda alterarlo después sin que se note. Eso es defendible; «es seguro» no lo sería.
Cómo lo verifica cualquiera
El informe se sirve en la ficha y en la API pública de lectura, junto a su firma. La clave pública está publicada y no hace falta cuenta para pedirla:
curl -sS https://fulgeon.com/apps/api/v1/pubkey
{"algorithm": "ed25519", "format": "spki-der-base64", "key": "MCowBQYDK2Vw..."}Hay además un verificador en la web, sin cuenta ni clave: verificar informe. Pegas el payload y la firma, y te dice si cuadran.
Verificarlo por tu cuenta es reproducir dos pasos:
- Serializar el payload igual que se firmó: JSON compacto, sin espacios (separadores
,y:) y con las claves ordenadas alfabéticamente. Se firman esos bytes exactos. - Comprobar la firma Ed25519 —viene en base64— contra la clave pública.
Si la firma valida, ese payload es literalmente lo que Fulgeon firmó. Si alguien cambia un byte del hash o del tamaño, deja de validar.
El informe puede venir vacío si la instalación no tiene clave de firma configurada. El catálogo funciona igual, pero ese artefacto sale sin informe verificable, y por eso se dice en vez de callarlo.
La API pública de lectura
Sin clave y con CORS abierto, por si quieres construir algo encima:
| Endpoint | Qué devuelve |
|---|---|
GET /apps/api/v1/packages | Listado filtrable de todo lo publicado. |
GET /apps/api/v1/app/{slug} | Ficha completa: versiones, artefactos e informes firmados. |
GET /apps/api/v1/pubkey | La clave pública Ed25519 con la que se firman los informes. |
Hay una cuarta dirección pensada para que tu propia aplicación pregunte si hay versión nueva, GET /api/latest/{slug}, con la última versión estable y el SHA-256 de cada archivo. Está contada en actualización automática.