> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Credenciales de máquina — API keys

> Cuando esto se entregue, el punto de venta de un tenant podrá llamar a nuestra API con una key que hace exactamente lo que necesita y nada más, y rotarla no dejará la caja fuera de servicio.

> Traducción. Autoritativo: [`fs-idn-0006-api-keys.md`](/modules/identity/features/fs-idn-0006-api-keys).

## Contexto

Una API key es lo primero que toca un integrador, y su validación está en el camino caliente de
Runtime: cada llamada a `track` la paga. Tiene que ser rápida y tiene que ser segura, y eso tira en
direcciones opuestas.

DEC-D5 elimina la complicación habitual: una key es **un sujeto con asignaciones de rol**, no una
cosa con su propio lenguaje de scopes. La misma resolución y la misma verificación sirven a una
persona y a una máquina, así que no hay un segundo camino de autorización que pueda separarse.

DEC-D6 elimina otra: **a los terceros nunca se les exige entregar rangos de IP**. Key más HMAC más
rate limits es el modelo de seguridad; la allowlist de IP existe como endurecimiento opcional,
apagada por defecto, porque exigir IPs a un integrador que corre en la nube de otro es pedir algo
que no puede dar.

## Alcance *(normativo)*

* `identity.api_keys` con secreto hasheado y prefijo en claro para identificación.
* Asignación de roles a una key, por el mismo mecanismo que a un usuario.
* Rotación con ventana de solapamiento para que ningún request falle durante el cambio.
* `last_used_at` y última IP usada, para detección.
* Expiración opcional.
* Write keys públicas para ingesta desde el navegador (F1b, con el widget).

## Fuera de alcance *(normativo)*

* Clientes OAuth2 — FS-IDN-0007. Una key la emite el tenant para sí mismo; un cliente OAuth se lo
  otorga el tenant a otro.
* El modelo de permisos — FS-IDN-0002.
* Allowlist de IP por tenant, una feature posterior de política de seguridad.

## Comportamiento *(normativo)*

1. Una key se muestra **exactamente una vez, al crearla**. Se guarda hasheada y no se puede recuperar
   — perderla significa rotarla, y ese es el canje correcto.
2. Toda key lleva un **prefijo en claro** (`sk_live_a1b2…`). Una key encontrada en un repositorio
   público se identifica y revoca desde el prefijo, sin que nadie tenga el secreto.
3. Una key es un sujeto con asignaciones de rol. No hay lenguaje de scopes específico de keys, y la
   verificación es el mismo camino de código que para un usuario.
4. La rotación emite un secreto nuevo y mantiene el viejo válido durante una **ventana de
   solapamiento configurable, 24 horas por defecto**. Una rotación que rompe producción es una
   rotación que nadie ejecuta, y una key que nadie rota es el riesgo real.
5. La validación se cachea y hace **cero consultas** en el camino caliente con la caché tibia. La
   revocación invalida la caché de inmediato.
6. `last_used_at` y la última IP se registran de forma asíncrona, nunca en el camino del request.
7. Las keys pueden tener expiración opcional, con aviso anticipado a quien la creó.
8. Crear o rotar una key exige reautenticación (FS-IDN-0004) y escribe una fila de auditoría.
9. Una key pertenece a **una organización** y jamás puede actuar sobre otra, lleve los roles que lleve.
10. Las **write keys públicas** (F1b) son un tipo distinto: solo ingesta de track, seguras de embeber
    en un bundle de navegador, con rate limit más duro y acotadas a orígenes configurados. Nunca
    reciben otro permiso.
11. PROHIBIDO: una key en un log, un payload de evento, un mensaje de error o un query param.

## Datos *(normativo)*

| Tabla               | Invariantes clave                                                                                                                                                                                                                    |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `identity.api_keys` | `organization_id`; `prefix` en claro y único; `secret_hash`; `label`; `kind` secret\|public\_write; `expires_at` nullable; `rotated_from` nullable con `overlap_until`; `last_used_at`, `last_used_ip`; `revoked_at`; nunca se borra |

## API *(normativo)*

| Endpoint                                 | Clase      | Permiso                            | Presupuesto |
| ---------------------------------------- | ---------- | ---------------------------------- | ----------- |
| `GET /v1/identity/api-keys`              | Management | `identity.api_keys.read`           | p95 \<1 s   |
| `POST /v1/identity/api-keys`             | Management | `identity.api_keys.create`, reauth | p95 \<1 s   |
| `POST /v1/identity/api-keys/{id}/rotate` | Management | `identity.api_keys.rotate`, reauth | p95 \<1 s   |
| `DELETE /v1/identity/api-keys/{id}`      | Management | `identity.api_keys.revoke`         | p95 \<1 s   |

El listado nunca devuelve un secreto, solo prefijos y metadata.

## Eventos *(normativo)*

`identity.api_key.created`, `.rotated` y `.revoked`, con el prefijo y el actor — **nunca el secreto**.

## Criterios de aceptación *(normativo)*

1. Una key se devuelve exactamente una vez; toda lectura posterior devuelve solo su prefijo.
2. Durante el solapamiento autentican el secreto viejo y el nuevo; después, solo el nuevo.
3. La validación hace cero consultas con la caché tibia, verificado con un contador de consultas.
4. La revocación es efectiva en el siguiente request.
5. Una key de la organización A no puede actuar sobre la B bajo ningún rol.
6. Una write key pública solo alcanza la ingesta de track; todo otro endpoint la rechaza.
7. `last_used_at` se actualiza sin agregar latencia medible al request.
8. Crear una key sin reautenticación se rechaza.
9. **Negativo:** ningún secreto aparece en logs, eventos, errores ni URLs.

## Ejecución

Un solo slice, comando síncrono. La validación vive en el middleware de auth de la API con caché en
Upstash; la resolución de roles se comparte con FS-IDN-0002.

## Preguntas abiertas

| # | Pregunta                                                          | Decide | Para             |
| - | ----------------------------------------------------------------- | ------ | ---------------- |
| 1 | ¿Las keys expiran por defecto, y en cuánto tiempo?                | Daniel | antes de aprobar |
| 2 | Ventana de solapamiento — ¿24 horas, o configurable hasta 7 días? | Daniel | antes de aprobar |

## Changelog

| Versión | Fecha      | Cambio           | Por qué | Autor                  |
| ------- | ---------- | ---------------- | ------- | ---------------------- |
| 0.1.0   | 2026-08-17 | Borrador inicial | —       | daniel + claude-opus-5 |

## Registro de entrega

*Aún no implementado.*
