Saltar al contenido

Publica tu primer feed

Actualizado 17 de agosto de 2026

Ver el Markdown

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.

shellscript
npx skills add Cabuya/cabuya-skill
# or, without the installer:
git clone https://github.com/Cabuya/cabuya-skill .agents/skills/cabuya

Si 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.

public/.well-known/cabuya.json
{
  "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. 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. 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. 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. 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. 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.

places.json
{
  "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.

El paso 3, según tu stack

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.json

Pon 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.json

La 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.json

Registra la ruta antes del fallback del SPA, o deja el archivo en public/.well-known/ — Laravel sirve ese directorio directamente.

public/.well-known/cabuya.json

Agrega 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.json

Agrega 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.json

Una 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.

Una cabecera, que un archivo no puede llevar

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.

_headers
/*
  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.

_headers
/*
  Access-Control-Allow-Origin: *

Combina el arreglo headers con un vercel.json existente en vez de reemplazar el archivo.

vercel.json
{
  "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.

nginx
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.

.htaccess
<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á.

Amazon S3 + CloudFront
{
  "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-address
  • colombian-mobile
  • intl-phone
  • whatsapp-link
  • national-id

Córrelo

Apunta el validador a la URL de tu manifiesto. Sigue los feeds que declara y reporta lo que encontró.

Abrir el validador

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:

shellscript
npx @cabuya/validator validate https://example.org/.well-known/cabuya.json

Cuá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.

Desarrolladores