Composer avec les pannes

Que devrait-il se passer lorsque votre code appelle check() et qu'Agreely ne peut être joint? La réponse d'Agreely est refuser, par défaut, partout. Ce guide explique pourquoi, et comment faire une exception étroite et auditée lorsque vous en avez réellement besoin.

Le refus par défaut est la valeur par défaut

Lorsqu'Agreely est injoignable (un 503, un délai d'expiration ou une erreur réseau) :

  • check() renvoie false (refus);
  • checkDetailed() lève AgreelyUnavailableError;
  • la CLI sort avec 4 (distinct du 10 d'un refus).

Un véritable refus 200 n'est jamais touché par tout cela. Une panne n'est pas un refus, mais la valeur par défaut sûre lors d'une panne est de se comporter comme si c'était refusé, de sorte qu'un problème d'infrastructure ne puisse jamais accorder l'accès aux données en silence.

Distinguez la panne du refus

Utilisez checkDetailed() (ou le code de sortie de la CLI) lorsque vous devez distinguer les deux. Un refus est une réponse faisant autorité que vous devez honorer; une panne est une défaillance transitoire que vous pouvez réessayer, signaler par une alerte, ou, pour une catégorie choisie, dégrader. check() ramène les deux à false à dessein, de sorte que l'intégration la plus simple est toujours sûre.

L'exception d'ouverture en cas de panne

Certaines catégories sont à risque assez faible pour que les refuser pendant une panne soit pire que les autoriser (par exemple, un signal d'analytique non sensible). Les SDK vous laissent fonctionner en ouverture pour de telles catégories, mais seulement à travers des barrières indépendantes, afin que cela ne puisse jamais se produire par accident et soit toujours audité.

TypeScript : trois barrières

const agreely = new Agreely({
  apiKey,
  degradeOnOutage: {
    mode: "fail-open",                 // gate 1a: the explicit word
    categories: ["Browsing/usage"],    // gate 1b: ONLY these may ever degrade
    maxOutageWindow: "5m",             // refuse to degrade past this
    onDegrade: (ctx) => audit.log(ctx) // MANDATORY, without it, the constructor throws
  },
});

// gate 2: the call must ALSO opt in. Effective only because the category is
// allow-listed. Without the config, this per-call opt-in still denies.
await agreely.check("cust_8812", "Browsing/usage", "Analytics", { onOutage: "allow" });

// gate 3 (operator): break-glass, for an active incident.
agreely.breakGlass.engage({ reason: "incident-4471", ttl: "30m", scope: ["Browsing/usage"] });

PHP : deux barrières

PHP livre la même liste d'autorisation de configuration + l'adhésion par appel. Il omet le bris de glace en v1, parce qu'un runtime à portée de requête ne peut détenir honnêtement un état « engagé » à l'échelle du parc (voir le SDK PHP).

$agreely = new Agreely([
    'apiKey' => $key,
    'degradeOnOutage' => [
        'mode'            => 'fail-open',
        'categories'      => ['Browsing/usage'],
        'maxOutageWindow' => '5m',
        'onDegrade'       => fn ($ctx) => $audit->log($ctx), // MANDATORY
    ],
]);
$agreely->check('cust_8812', 'Browsing/usage', 'Analytics', ['onOutage' => 'allow']);

Les garde-fous

Ce que l'ouverture en cas de panne ne vous laissera pas faire

  • Aucune dégradation silencieuse. onDegrade est obligatoire; sans collecteur d'audit, le constructeur lève une exception. Chaque autorisation dégradée émet un enregistrement de preuve, et chaque autorisation accordée par bris de glace émet son propre événement.
  • Aucune fenêtre non bornée. Le ttl et le maxOutageWindow sont tous deux plafonnés à 24 h; au-delà du plafond, lève AgreelyConfigError.
  • Aucune mémoire-cache d'autorisations. Il n'y a aucune mémoire-cache des décisions d'autorisation; l'ouverture en cas de panne est calculée par appel pendant une panne, jamais rejouée à partir d'une autorisation stockée.
  • Aucune adhésion accidentelle. Une onOutage: "allow" par appel sans catégorie correspondante inscrite n'a aucun effet (la vérification refuse toujours) et consigne un avertissement unique.

Régler les 429, pas les pannes

Une limite de débit (429) n'est pas une panne : c'est un signal de ralentir. Deux options du constructeur la règlent, sans jamais toucher à la politique de refus par défaut. maxRetries (défaut 0) ajoute des tentatives supplémentaires sur un 429 seulement et uniquement pour les appels idempotents (GET et check); un appel mutant ne ré-essaie jamais. respectRetryAfter (défaut true) honore l'en-tête Retry-After du serveur. Une panne, elle, n'est jamais ré-essayée en silence : elle refuse (ou lève AgreelyUnavailableError) et vous laisse décider.

Recommandation

Gardez la valeur par défaut. Le refus par défaut est correct pour tout ce qui touche à des données sensibles ou porte une obligation de conformité, soit la plupart de ce que vous protégerez. Réservez l'ouverture en cas de panne à une catégorie précise et à faible risque où la disponibilité l'emporte réellement sur l'application stricte, et câblez son collecteur onDegrade dans la même piste d'audit que vous montreriez à un régulateur.

Ensuite