Publica tu primer feed
Actualizado 17 de agosto de 2026
Dos archivos, una cabecera de respuesta y una ejecución del validador. Todo lo de abajo está listo para copiar y pegar, y cumple con la especificación que enseña: los bloques de esta página los revisa la suite de pruebas contra el validador real.
Si tienes un agente de código
Instala la skill y pídele que publique un feed de Cabuya. La skill incluye la especificación, así que funciona sin conexión y sin adivinar nombres de campo.
npx skills add Cabuya/cabuya-skill
# or, without the installer:
git clone https://github.com/Cabuya/cabuya-skill .agents/skills/cabuyaSi lo vas a hacer a mano
Cinco pasos. El paso 3 es el punto de falla más frecuente, y conviene leerlo completo.
Guarda esto primero
Este es un manifiesto completo y conforme. Reemplaza las dos URL y el identificador de publicador por los tuyos, y el paso 1 queda hecho.
{
"protocol": {
"name": "cabuya",
"spec_version": "0.1.0"
},
"publisher": {
"publisher_id": "example-app",
"canonical_url": "https://example.org"
},
"conformance_target": "L2",
"license": "CC-BY-4.0",
"permitted_use": [
"display",
"aggregate"
],
"feeds": [
{
"name": "places",
"url": "https://example.org/feeds/places.json",
"entity": "place",
"profile": "core"
}
]
}
- 1
Escribe el manifiesto
Dice quién eres, qué publicas y bajo qué licencia. Doce líneas son un manifiesto de verdad, no un esqueleto.
- 2
Ponlo en /.well-known/cabuya.json
La ruta es fija. Los consumidores miran ahí y en ningún otro lado, así que no hay descubrimiento que implementar.
- 3
Excluye esa ruta de tu catch-all
No es opcional. Si tu framework sirve index.html en rutas desconocidas, tu manifiesto responde 200 con HTML, y todos los consumidores lo tratan como ausente.
- 4
Serializa tus lugares en el sobre
El sobre lleva cinco campos y un arreglo. Mapea lo que ya tienes; publica null para todo lo que nadie haya confirmado de verdad.
- 5
Ejecuta el validador hasta que no reporte hallazgos y abre una entrada en el registro
Cada hallazgo nombra el campo y dice cómo corregirlo. Cuando la ejecución sale limpia, un pull request te agrega al registro.
{
"last_updated": "2026-01-01T00:00:00Z",
"ttl": 300,
"version": "0.1.0",
"publisher_id": "example-app",
"license": "CC-BY-4.0",
"permitted_use": [
"display",
"aggregate"
],
"attribution": "Your App",
"data": {
"places": [
{
"id": "1",
"publisher_id": "example-app",
"name": "Coliseo Municipal",
"place_kind": "shelter",
"municipality_code": "66001",
"address_text": "Avenida Ejemplo 12-34",
"lat": 4.8133,
"lon": -75.6961,
"lifecycle_status": "active",
"service_status": "open",
"last_confirmed_at": null,
"source": {
"source_id": "example-app"
},
"public_url": "https://example.org/places/1"
}
]
}
}El paso 3, según tu stack
Busca el tuyo, aplica la línea correspondiente y luego solicita la URL y confirma que la respuesta sea JSON.
Los archivos bajo public/ se sirven antes de la ruta catch-all, así que la ubicación es todo el arreglo. Verifica de todas formas: una regla rewrites propia todavía puede capturarlo.
public/.well-known/cabuya.jsonPon el archivo en public/ y excluye /.well-known/* de la regla de reescritura del SPA en la configuración de tu host — esa regla es la que sirve index.html en rutas desconocidas.
public/.well-known/cabuya.jsonLa salida estática no tiene catch-all, así que el archivo se sirve tal cual. Con un adaptador SSR, confirma que el middleware no reescriba rutas sin coincidencia.
public/.well-known/cabuya.jsonRegistra la ruta antes del fallback del SPA, o deja el archivo en public/.well-known/ — Laravel sirve ese directorio directamente.
public/.well-known/cabuya.jsonAgrega RewriteCond %{REQUEST_URI} !^/\.well-known/ encima de la regla del front controller en .htaccess. Sin eso responde el router, con un 200 y HTML.
public/.well-known/cabuya.jsonAgrega una ruta estática para /.well-known/ antes del urlpattern catch-all. El orden en urlpatterns es todo el mecanismo.
Sube el archivo y después pídelo. Varios hosts ocultan los directorios que empiezan con punto por defecto, y el despliegue no te avisa.
/.well-known/cabuya.jsonUna cabecera, que un archivo no puede llevar
Dos archivos estáticos son válidos contra el esquema y todavía no son L2. El feed además tiene que poder leerse desde un navegador, y eso es una cabecera de respuesta que define tu hosting — no algo que puedas poner dentro del JSON.
Un archivo _headers en la raíz de la salida publicada. Cloudflare lo aplica en el borde, así que nada en tu compilación necesita saber de él.
/*
Access-Control-Allow-Origin: *El mismo formato _headers, en el directorio de publicación. Netlify ignora el archivo si queda fuera, que es la razón habitual de que un archivo correcto no surta efecto.
/*
Access-Control-Allow-Origin: *Combina el arreglo headers con un vercel.json existente en vez de reemplazar el archivo.
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
]
}Acótalo al manifiesto y a los feeds, no al servidor entero. add_header dentro de un bloque location reemplaza las cabeceras heredadas, así que decláralo donde aplica.
location /.well-known/cabuya.json {
add_header Access-Control-Allow-Origin *;
}
location /feeds/ {
add_header Access-Control-Allow-Origin *;
}Requiere mod_headers habilitado. En hosting compartido suele estarlo; si la cabecera no aparece, ese módulo es lo primero que hay que revisar.
<FilesMatch "cabuya\.json$|\.json$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>La configuración CORS del bucket. También hay que decirle a CloudFront que reenvíe la cabecera Origin, o guardará una sola respuesta para todos los orígenes y la cabecera nunca variará.
{
"CORSRules": [
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"]
}
]
}GitHub Pages no permite definir cabeceras de respuesta, y ya envía Access-Control-Allow-Origin: * en todas. No hay nada que hacer — ni nada que puedas hacer, si eso llegara a cambiar.
Antes de publicar: la única decisión que es tuya
Cabuya lleva lugares, no personas. Revisa los campos que vas a mapear y confirma que ninguno guarda el nombre de una persona, un teléfono o correo personal, un caso individual, o una decisión de moderación sobre una persona.
Nombres de campo que el validador rechaza de entrada
name_personnombrenombresapellidoapellidosphonetelefonoteléfonocelularmovilmóvilwhatsappwaemailcorreomailcedulacéduladocumentodninit_personadireccion_casafotophotocontactocontact_phonecontact_emailresponsableencargadobeneficiariobeneficiaryvictimavíctimadesaparecidomissing_person
Formas de valor que marca dondequiera que aparezcan
email-addresscolombian-mobileintl-phonewhatsapp-linknational-id
Córrelo
Apunta el validador a la URL de tu manifiesto. Sigue los feeds que declara y reporta lo que encontró.
El modo de pegado ejecuta el mismo motor en tu navegador, sin subir nada. La revisión por URL —que además mide el comportamiento de transporte— necesita un servidor, y esa parte está en construcción. La línea de comandos hace las dos cosas hoy:
npx @cabuya/validator validate https://example.org/.well-known/cabuya.jsonCuánto toma esto de verdad
Cinco minutos es el número honesto para el camino de arriba: dos archivos estáticos y una cabecera, copiados, editados y subidos. Es un nivel de conformidad real —L2— y les sirve de verdad a los consumidores.
Mapear una base de datos viva al esquema es una tarde, a veces dos: hay que reconciliar tus estados con el vocabulario compartido, y alguien tiene que decidir qué significan tus datos en realidad. Ese trabajo no se puede evitar, y es preferible decirlo aquí a dejar que aparezca en el paso 4.