# Editores de confianza

**Publica en el Hub desde CI sin almacenar un secreto `HF_TOKEN`.**

Tu trabajo de CI demuestra su identidad ante Hugging Face mediante un token de OpenID Connect (OIDC) de corta duración emitido por tu proveedor de CI, y a cambio recibe un token de Hugging Face de corta duración. No es necesario almacenar ni rotar un token de HF como secreto.

| | Token de acceso personal | Editor de confianza |
| --- | --- | --- |
| Duración | Hasta que se revoca | 1 hora |
| Almacenamiento | Secreto de CI | Nada que almacenar |
| Rotación | Manual | Automático, en cada ejecución |
| Si se filtra | Válido hasta que lo revocas | Como máximo, ~1 hora, y limitado a un solo repositorio |

Los Trusted Publishers son la misma idea que los [Trusted Publishers](https://docs.pypi.org/trusted-publishers/) de PyPI y la [publicación confiable de npm](https://docs.npmjs.com/trusted-publishers).

## Ejemplo rápido: publicar un modelo desde GitHub Actions

Mantienes `acme/awesome-model` en el HF Hub y quieres que la CI en un repositorio alojado externamente, `acme/awesome-model-training` (en GitHub, GitLab o cualquier proveedor compatible con OIDC), suba nuevos checkpoints, sin un secreto `HF_TOKEN`.

### 1. Configure el editor de confianza en el Hub

En `https://huggingface.co/acme/awesome-model/settings`, abre **Trusted Publishers** y añade:

- **Proveedor**: GitHub Actions
- **Afirmaciones** (todas deben coincidir para que el intercambio tenga éxito):
  - `repository` = `acme/awesome-model-training`
  - `branch` = `main`
  - `workflow` = `publish.yml`

> [!TIP]
> `repository` por sí solo limita el alcance del editor a un repositorio de GitHub. Añada `branch` y/o `workflow` para fijarlo también a una rama o archivo de flujo de trabajo, lo cual se recomienda.

### 2. Añade el flujo de trabajo a tu repositorio de GitHub

`.github/workflows/publish.yml`:

```yaml
name: Publish to Hugging Face

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest

    permissions:
      id-token: write  # required so the job can request an OIDC token
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Install the hf CLI
        run: |
          curl -LsSf https://hf.co/cli/install.sh | bash
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"

      - name: Upload checkpoint
        env:
          # The HF repo to publish to. For non-model repos, prefix accordingly:
          #   datasets/acme/awesome-dataset, spaces/acme/awesome-space, kernels/acme/awesome-kernel
          HF_OIDC_RESOURCE: acme/awesome-model
        run: hf upload acme/awesome-model ./checkpoint . --commit-message "Publish from ${GITHUB_SHA::7}"
```

Eso es todo. En GitHub Actions, la CLI `hf` (`huggingface_hub>=1.19.0`) detecta el proveedor, realiza el intercambio y usa el token resultante automáticamente. Solo tienes que establecer `HF_OIDC_RESOURCE`.

> [!TIP]
> ¿Publicar en varios repositorios en una sola ejecución (p. ej., un modelo **y** un conjunto de datos)? Establece `HF_OIDC_RESOURCE` por paso para que cada token esté limitado al repositorio al que ese paso sube.

#### Otros proveedores de CI

El CLI `hf` genera el token de ID de forma nativa en GitHub Actions por ahora. En GitLab, CircleCI, Bitbucket o cualquier otro proveedor, genera el token de ID usted mismo (consulte [Proveedores de CI compatibles](#supported-ci-providers)) y páselo mediante `HF_OIDC_ID_TOKEN`: el CLI lo intercambia directamente:

```yaml
# GitLab CI (.gitlab-ci.yml)
publish:
  id_tokens:
    HF_ID_TOKEN:
      aud: https://huggingface.co
  script:
    - curl -LsSf https://hf.co/cli/install.sh | bash
    - export PATH="$HOME/.local/bin:$PATH"
    - HF_OIDC_ID_TOKEN="$HF_ID_TOKEN" HF_OIDC_RESOURCE="acme/awesome-model" hf upload acme/awesome-model ./checkpoint .
```

Ejemplos completos y funcionales:

- GitHub Actions — [`github.com/coyotte508/publish-to-hf`](https://github.com/coyotte508/publish-to-hf)
- GitLab CI — [`gitlab.com/coyotte508/publish-to-hf`](https://gitlab.com/coyotte508/publish-to-hf)

## Dos variantes: editores de repositorios e identidades de CI/CD

| Sabor | Configurado en | Lo que obtienes | Úsalo para… |
| --- | --- | --- | --- |
| **Editor de confianza del repositorio** | La sección **Ajustes → Publicadores de confianza** de un repositorio | Un token con **acceso de escritura a ese único repositorio** (o [acceso solo de lectura](#read-only-repo-publishers)) | Publica un modelo, un conjunto de datos, un Space, un kernel o un bucket desde CI, o descarga uno privado |
| **Identidad de CI/CD de la cuenta** | La [**Configuración de autenticación → Acceso CI/CD**](https://huggingface.co/settings/authentication#ci-cd-access) de tu cuenta | Un token de solo lectura con el ámbito `gated-repos`, más `inference-api` si lo activas | Lee **los repositorios con acceso restringido a los que tienes acceso**, usa tus límites de tasa y, opcionalmente, llama a los proveedores de inferencia desde CI |

Ambos tokens caducan después de 60 minutos. Necesitas el rol **Write** en un repositorio de Hub para gestionar sus publicadores de confianza.

### Acceso a repositorios con control de acceso desde CI

Si solo necesitas *leer* repositorios con acceso restringido (por ejemplo, descargar un modelo desde un trabajo), añade una identidad de CI/CD en tu cuenta en [**Authentication settings → CI/CD Access**](https://huggingface.co/settings/authentication#ci-cd-access) en lugar de en un repositorio específico, y luego usa tu **username** como recurso.

En **GitHub Actions**, la CLI `hf` realiza el intercambio por ti, solo tienes que establecer `HF_OIDC_RESOURCE`:

```yaml
      - name: Download a gated model
        env:
          HF_OIDC_RESOURCE: your-hf-username
        run: hf download acme/gated-model
```

En **otros proveedores**, genera el token de ID tú mismo (consulta [Otros proveedores de CI](#other-ci-providers)) y pásalo mediante `HF_OIDC_ID_TOKEN`:

```bash
# $ID_TOKEN is the OIDC token your provider minted (e.g. $HF_ID_TOKEN on GitLab)
HF_OIDC_ID_TOKEN="$ID_TOKEN" HF_OIDC_RESOURCE="your-hf-username" hf download acme/gated-model
```

El token resultante puede leer repositorios con acceso restringido a los que tienes acceso y utiliza los límites de tasa de tu cuenta. No **puede** escribir nada y **no puede** leer tus repositorios privados. Para obtener un repositorio privado desde CI, usa en su lugar un [publicador de repositorio de solo lectura](#read-only-repo-publishers).

> [!TIP]
> ¿Necesita el propio token (para `curl`, `git clone` o una herramienta que lea `HF_TOKEN`)? `hf auth token` realiza el intercambio e imprime el token de vida corta en stdout:
>
> ```yaml
>       - name: Get a short-lived HF token
>         env:
>           HF_OIDC_RESOURCE: your-hf-username  # or a repo, e.g. acme/awesome-model
>         run: echo "HF_TOKEN=$(hf auth token)" >> "$GITHUB_ENV"
> ```
>
> Esto funciona tanto con identidades de CI/CD como con editores de repositorios: el token está limitado a lo que `HF_OIDC_RESOURCE` apunte.

### Publicadores de repositorios de solo lectura

Por defecto, un publisher de confianza de un repositorio emite tokens que pueden realizar push en el repositorio. Si una carga de trabajo solo necesita *pull* de un repositorio (por ejemplo, para descargar un modelo o conjunto de datos privado desde un trabajo de CI o una plataforma externa que emite sus propios tokens OIDC), marca **Solo lectura** al agregar el publisher. Los tokens intercambiados podrán leer el contenido del repositorio, pero no escribir en él, y la entrada mostrará una insignia de **Solo lectura** en la lista. Esto funciona para modelos, conjuntos de datos, Spaces, kernels y buckets.

El intercambio en sí no ha cambiado. Apunta `HF_OIDC_RESOURCE` (o `resource`) al repositorio.

```yaml
    permissions:
      id-token: write  # required so the job can request an OIDC token

    steps:
      - name: Download a private model
        env:
          HF_OIDC_RESOURCE: acme/private-model
        run: hf download acme/private-model
```

Como ocurre con otras configuraciones de editor, el permiso se fija en el momento de la creación. Para cambiarlo, elimina el editor y vuelve a añadirlo.

### Llamada a proveedores de inferencia desde CI

Los tokens de una identidad de CI/CD son de solo lectura de forma predeterminada. Para permitir que también llamen a Inference Providers, marca **Allow Inference Providers calls** al agregar la identidad: los tokens intercambiados llevarán entonces el ámbito `inference-api` además de `gated-repos`, y la entrada mostrará una insignia de **Inference** en la lista.

El uso de inferencia se **factura a tu cuenta**, exactamente como si hubieras realizado las solicitudes tú mismo, por lo que restringe los permisos de forma estricta (`repository` *y* `branch` y/o `workflow`) antes de habilitarlo.

```yaml
      - name: Run inference from CI
        env:
          HF_OIDC_RESOURCE: your-hf-username
        run: |
          export HF_TOKEN="$(hf auth token)"
          python inference_script.py
```

Los publicadores de repositorios no son elegibles: sus tokens tienen alcance de repositorio y no pueden llamar a los proveedores de inferencia. La capacidad se fija en la creación; para cambiarla, elimina la identidad y vuelve a añadirla.

## Proveedores de CI admitidos

La interfaz de configuración incluye presets para los proveedores que se indican a continuación, pero cualquier proveedor compatible con OIDC funciona (AWS, GCP, Buildkite, su propio IdP, …).

| Proveedor | Emisor | Cómo obtener el token de ID |
| --- | --- | --- |
| **GitHub Actions** | `https://token.actions.githubusercontent.com` | Establece `permissions: id-token: write` y luego llama al punto de acceso de metadatos con `audience=https://huggingface.co`. [Documentación](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect). |
| **GitLab CI** | `https://gitlab.com` (o su URL autoalojada) | Declara `id_tokens: { HF_ID_TOKEN: { aud: https://huggingface.co } }` en el trabajo; lee `$HF_ID_TOKEN`. [Documentación](https://docs.gitlab.com/ci/yaml/#id_tokens). |
| **CircleCI** | `https://oidc.circleci.com/org/<org-uuid>` | Usa `$CIRCLE_OIDC_TOKEN_V2` (v2 te permite establecer la audiencia en la configuración del proyecto). [Documentación](https://circleci.com/docs/openid-connect-tokens/). |
| **Bitbucket Pipelines** | `https://api.bitbucket.org/2.0/workspaces/<workspace>/pipelines-config/identity/oidc` | Establece `oidc: true` en el paso; lee `$BITBUCKET_STEP_OIDC_TOKEN`. [Documentación](https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/). |

Una vez que tengas el token de ID, la llamada de intercambio es idéntica en todos los proveedores; solo difieren las **claims** que configures en el lado del Hub.

## Cómo funciona

1. Tu proveedor de CI emite un **token de ID de OIDC** de vida corta que describe el trabajo (qué repositorio, qué rama, qué flujo de trabajo, …).
2. Su flujo de trabajo `POST` ese token a `https://huggingface.co/oauth/token`, junto con un `resource` (el repositorio o nombre de usuario al que desea acceder).
3. El Hub verifica la firma y las afirmaciones del token contra los editores que configuraste para ese recurso y devuelve un token de Hugging Face.

```
┌──────────┐  1. mint ID token   ┌──────────┐  2. exchange   ┌────────────┐
│ CI job   │ ─────────────────▶  │ CI OIDC  │ ──────────────▶│ huggingface│
│          │                     │ issuer   │                │  /oauth/   │
│          │ ◀──────────────────────────────────────────────│  token     │
└──────────┘        3. short-lived HF token (valid 1 h)      └────────────┘
```

## Referencia de la API

Endpoint, solicitud y respuesta

**Punto de conexión:** `POST https://huggingface.co/oauth/token` con `Content-Type: application/json`.

No se requiere autenticación del cliente: el token de identificación OIDC autentica la solicitud. El intercambio sigue [RFC 8693 — OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html).

**Cuerpo de la solicitud:**

| Campo | Obligatorio | Valor |
| --- | --- | --- |
| `grant_type` | sí | `urn:ietf:params:oauth:grant-type:token-exchange` |
| `subject_token_type` | sí | `urn:ietf:params:oauth:token-type:id_token` |
| `subject_token` | sí | El token de identificación OIDC (JWT) sin procesar de su proveedor de CI. Su claim `aud` **debe** ser `https://huggingface.co`. |
| `resource` | sí | Un repositorio de Hub (`namespace/name`, `datasets/namespace/name`, `spaces/namespace/name`, `kernels/namespace/name`, `buckets/namespace/name`) o un **username** de Hub (sin barra) para un token con ámbito de usuario. |

**Respuesta de éxito:**

```json
{
  "access_token":      "hf_jwt_…",   // "hf_oauth_…" for user resources
  "token_type":        "bearer",
  "expires_in":        3600,
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
```

**Errores** — `400 Bad Request` con un cuerpo de estilo OAuth:

| `error` | Por qué |
| --- | --- |
| `invalid_request` | Parámetro ausente o malformado, o formato de `resource` incorrecto. |
| `invalid_grant` | No se encontró el repositorio o el usuario; no hay ningún editor que coincida con este emisor; las afirmaciones configuradas no coinciden; falló la verificación de la firma o la audiencia; cuenta bloqueada. |

Cuando la CLI `hf` realiza el intercambio, un fallo muestra el código `error` junto con un `(Request ID: …)` — incluye ese Request ID al reportar un problema para que podamos rastrear el intercambio en nuestros registros.

## Modelo de seguridad

- **Los tokens tienen una vida útil corta.** 60 minutos desde el momento del intercambio: el reloj solo comienza cuando se llama al punto de acceso, no cuando se inicia el flujo de trabajo. No hay token de actualización; las tareas largas deben volver a intercambiar.
- **Los tokens de repositorio tienen alcance limitado al repositorio.** Un token para `acme/awesome-model` no puede acceder a `acme/anything-else`. Los pushes se atribuyen a un usuario de sistema sintético `[OIDC]`, con una referencia al emisor y al sujeto de origen. Los tokens de un publisher de solo lectura solo pueden leer ese repositorio.
- **Los tokens de usuario son de solo lectura.** El alcance `gated-repos` es el predeterminado: no permite escritura, ni repositorios privados, ni gestión de cuentas. El alcance opcional `inference-api` añade llamadas a Inference Providers (facturadas a tu cuenta), pero sigue sin permitir escritura ni acceso a repositorios privados.
- **Las afirmaciones se coinciden de forma exacta o por prefijo cuando un preset de proveedor lo requiere** (el `workflow_ref` de GitHub). Sin expresiones regulares.
- **Registros de auditoría.** Se registra la adición o eliminación de un editor, y los intercambios exitosos actualizan la última hora de uso.

## Véase también

- [Tokens de acceso de usuario](./security-tokens) — la opción adecuada para personas y scripts de uso único
- [OAuth / Iniciar sesión con HF](./oauth) — el mismo punto de acceso `/oauth/token`, utilizado para flujos interactivos
- [Gestión de Spaces con GitHub Actions](./spaces-github-actions)
- [Integración de GitHub Actions con el Hub](./repositories-github-actions)

