
# Aggiorna la policy delle chiavi di firma

`PATCH /v1/signing-keys/policy` · permesso `signing-keys:write`

Aggiorna la policy delle chiavi di firma con un JSON merge patch (`application/merge-patch+json`, RFC 7396). `If-Match` è obbligatorio: se manca `428`, se la versione non è aggiornata `412`. Se la policy non è mai stata scritta, la versione è `"0"`. Un patch che non cambia nulla non scrive nulla e mantiene la versione.

Cambiare `algorithm` genera subito una nuova chiave `next` con il nuovo algoritmo; può diventare attiva al più presto 7 giorni dopo, così i verificatori che memorizzano il JWKS in cache la vedono prima (una rotazione `immediate` anticipata fallisce con `409 next_key_too_young`). Fino ad allora la chiave attiva continua a firmare. Cambiare `rotation_interval_days` sposta `next_rotation_at`. Una modifica riuscita scrive il record di audit di gestione `signing_key.policy_updated`.

## Richiesta

| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
| `If-Match` | header | sì | L'`ETag` della policy letto per ultimo (`"0"` se mai scritta). Mancante: `428`; non aggiornato: `412`. |
| `algorithm` | string | no | `RS256`, `ES256`, `PS256` o `EdDSA`. Non può essere `null`. |
| `rotation_interval_days` | integer | no | 30-365. Non può essere `null`. |
| `storage` | string | no | `wrapped` (la chiave privata è archiviata cifrata con la chiave dell'ambiente e usata solo all'interno del servizio di firma). `transit` (chiave custodita nel servizio di gestione delle chiavi) non è ancora disponibile e viene rifiutato con `422`. Non può essere `null`. |

## Risposta

`200 OK` con la policy aggiornata e il nuovo `ETag`.

```json
{
  "algorithm": "ES256",
  "rotation_interval_days": 90,
  "storage": "wrapped",
  "next_rotation_at": "2026-12-04T09:30:00.123456Z",
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## Errori

| HTTP | code | Quando |
|---|---|---|
| 400 | `unknown_field` | Il patch contiene un campo sconosciuto o di sola lettura, oppure non è un oggetto JSON (`malformed_request`). |
| 412 | `precondition_failed` | `If-Match` non corrisponde alla versione attuale. |
| 415 | `unsupported_media_type` | Il content type non è `application/merge-patch+json`. |
| 422 | `validation_failed` | Un valore non è valido (`invalid_value`), è fuori intervallo (`out_of_range`), è impostato a `null` (`invalid`), oppure `storage` è `transit`. |
| 428 | `precondition_required` | L'header `If-Match` è mancante. |
| 401 | `unauthenticated` | La credenziale è mancante, non valida o revocata. |
| 403 | `insufficient_permissions` | La credenziale non ha il permesso richiesto. |
| 429 | `rate_limited` | Il limite di velocità è stato superato; riprova dopo il periodo `Retry-After`. |
| 501 | `not_implemented` | L'operazione non è ancora disponibile in questo deployment; riprovare non cambia il risultato. |
| 503 | `service_unavailable` | Una dipendenza è temporaneamente non disponibile; riprova dopo il periodo `Retry-After`. |

## Esempio

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/signing-keys/policy \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "0"' \
  -d '{"algorithm":"ES256","rotation_interval_days":90}'
```
