# Publica tu primer feed

Publica tu primer feed de Cabuya: dos archivos, una cabecera, una ejecución del validador y una entrada en el registro. Copiar y pegar toma cinco minutos.

Canonical: https://cabuya.org/es/developers/quickstart
Language: es

## Si tienes un agente de código

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.

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.

> **Diagrama.** Una tarde, paso a paso. Cada paso produce un archivo o un resultado que puedes inspeccionar.
>
> Cinco pasos: escribir el manifiesto, exportar un feed, correr el validador, arreglar lo que reporte y abrir un pull request al registro. Los primeros tres suelen tomar menos de una hora; el camino completo es una tarde para una aplicación pequeña.

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

```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"
    }
  ]
}
```

## Publica tu primer feed

**Si lo vas a hacer a mano** — Cinco pasos. El paso 3 es el punto de falla más frecuente, y conviene leerlo completo.

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.

## places.json

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

Cuatro de las veinte aplicaciones del análisis fundacional fallaron aquí, y las cuatro creían haber publicado. El manifiesto se alcanzaba, respondía 200 y contenía su página de inicio.

- **Next.js** — 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`)
- **Vite / React SPA** — 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`)
- **Astro** — 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`)
- **Laravel** — 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`)
- **PHP / Apache** — 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`)
- **Django** — Agrega una ruta estática para `/.well-known/` antes del urlpattern catch-all. El orden en `urlpatterns` es todo el mecanismo.
- **Static host** — 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.

> La especificación la llama el único MUST no obvio. Sin `Access-Control-Allow-Origin: *`, cualquier consumidor que corra en un navegador necesita un proxy en servidor para leerte — así que quienes tienen el menor costo de implementación son justo los que quedan afuera. El validador la reporta como ENV007, y es la comprobación que falla primero un publicador que hizo bien todo lo demás.

**Cloudflare Pages** — `_headers`

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: *
```

**Netlify** — `_headers`

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: *
```

**Vercel** — `vercel.json`

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

```
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Access-Control-Allow-Origin", "value": "*" }
      ]
    }
  ]
}
```

**nginx**

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 *;
}
```

**Apache** — `.htaccess`

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>
```

**Amazon S3 + CloudFront**

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**

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_person, nombre, nombres, apellido, apellidos, phone, telefono, teléfono, celular, movil, móvil, whatsapp, wa, email, correo, mail, cedula, cédula, documento, dni, nit_persona, direccion_casa, foto, photo, contacto, contact_phone, contact_email, responsable, encargado, beneficiario, beneficiary, victima, víctima, desaparecido, missing_person.

Formas de valor que marca dondequiera que aparezcan: email-address, colombian-mobile, intl-phone, whatsapp-link, national-id.

Una persona toma esta decisión una vez, cuando se escribe el mapeo. El validador la verifica en cada ejecución posterior, y reporta el campo, nunca el valor que encontró.

## Córrelo

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

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

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:

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

## Navegación del Sitio

- [Inicio](https://cabuya.org/es)
- [Especificación](https://cabuya.org/es/developers/spec)
- [Esquemas](https://cabuya.org/es/developers/schemas)
- [RFCs](https://cabuya.org/es/rfcs)
- [Cambios](https://cabuya.org/es/changelog)
- [Desarrolladores](https://cabuya.org/es/developers)
- [Registro](https://cabuya.org/es/registry)
- [Gobernanza](https://cabuya.org/es/governance)
- [Participar](https://cabuya.org/es/join)
- [GitHub](https://github.com/Cabuya)
- [Repositorio de la skill](https://github.com/Cabuya/cabuya-skill)
- [Registro fundacional](https://github.com/Cabuya/cabuya.org/tree/main/docs/context)
