flexway
FRENParler à un expert

Lire avec la console

GuideExploitationDepuis la version 0.1.0

Markdown

Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : guide produit ; l'appeler : guide développeur.

Objectif : exploiter sans lire les fichiers - ce qui est servi, ce qui s'applique, ce qui passe, ce qui est consommé.

# Avec vos backends : la console lit votre Prometheus, construit des liens vers vos traces,
# ne reçoit ni ne stocke rien
helm install console oci://ghcr.io/flexwiz/charts/flexway-console --version <version> \
  --set gateway.url=http://orders-flexway-admin:9090 --set metrics.url=http://victoriametrics:8428 \
  --set traces.url='https://grafana.example/explore?traceId={trace_id}'

# Sans backend : la passerelle pousse métriques et traces vers la console (OTLP, TLS, mTLS par
# défaut), qui garde l'historique sur un volume persistant par réplique (8 Gio par défaut)
helm install console oci://ghcr.io/flexwiz/charts/flexway-console --version <version> \
  --set gateway.url=http://orders-flexway-admin:9090 --set persistence.size=8Gi \
  --set otlp.tls.issuerRef.name=flexway-ca --set 'otlp.tls.clientNames={orders-flexway.observability.svc}'

Sans backend, la passerelle s'annonce elle-même en poussant (tracesToConsole, ci-dessous) : chaque réplique porte son nom de pod (service.instance.id), et une réplique qui se tait 45 s passe unknown. Avec plusieurs répliques de la console, celle qui reçoit un envoi le relaie aux autres (file bornée, réessais) : chacune garde une copie complète ; une réplique revenue demande les deux dernières heures à une autre. Les journaux /events et /errors restent lus sur chaque réplique de la passerelle par son Service sans tête (orders-flexway-admin-headless) ; son port d'administration est ouvert aux pods de la console (même namespace par défaut, admin.networkPolicy.console.podLabels). Changer de mode = désinstaller puis réinstaller : le Secret d'état et ses comptes sont gardés.

Rétention (sans backend) : 35 jours de détail (retention.detail, réglable), agrégats journaliers

  • requêtes, erreurs, tokens, euros - sur 13 mois (retention.daily) pour comparer mois sur mois et année sur année ; volume persistant obligatoire (le rendu est refusé avec persistence.enabled: false), plein à 80 % au plus, le plus ancien sortant d'abord. Hors rétention, un chiffre est --, jamais 0. Un bandeau en haut de Vue d'ensemble, Trafic, Amonts, Catalogue et Consommation dit la source et la rétention ; son panneau « Détails » montre les répliques qui envoient, le dernier envoi reçu et le volume (aux admins). Au-delà de 35 jours, prévoir 10 Gio au moins ; au-delà encore, votre Prometheus.

Premier démarrage, tel que les NOTES le disent : lire le mot de passe généré du premier admin dans le Secret d'état (kubectl get secret <release>-flexway-console-state -o jsonpath='{.data.initial-admin-password}', décodé en base64), se connecter en admin, choisir un mot de passe, enrôler une application TOTP et garder les dix codes de secours. Puis Utilisateurs -> Inviter : un lien d'invitation à usage unique (72 h par défaut, 24 h ou 7 jours au choix) à transmettre - la console n'envoie pas de courriel. Accès perdu : kubectl exec deploy/<release>-flexway-console -- flexway-console users reset admin (sts/ au lieu de deploy/ sans backend) ; un compte verrouillé : ... users unlock <login>.

Sécurité - Qui peut exec dans le pod de la console en est administrateur : réserver pods/exec sur ce Deployment à ses exploitants (chaque action de la commande est auditée). Ne pas exposer la console publiquement. Derrière une ingress, déclarer son réseau dans trustedProxies : chaque client est alors compté sous sa propre adresse - cinq échecs verrouillent l'adresse de l'attaquant, jamais celle du propriétaire, qui attend au plus une minute.

Pour brancher un fournisseur d'identité (Entra ID, Okta, Google, Auth0, Keycloak, Authentik...) :

# values.yaml du chart services/console/deploy/helm
gateway: { url: "http://orders-flexway-admin:9090" }      # la surface d'administration
metrics: { url: "http://victoriametrics:8428" }           # une API Prometheus
# Grafana Explore sur Tempo ; tout outil de traces adressable par URL convient :
# {operation}, {client} (l'agent), {start_us}/{end_us} (microsecondes), {start_ms}/{end_ms} (millisecondes)
traces:  { url: "https://grafana.example/explore?schemaVersion=1&orgId=1&panes=%7B%22a%22%3A%7B%22datasource%22%3A%22tempo%22%2C%22queries%22%3A%5B%7B%22refId%22%3A%22A%22%2C%22datasource%22%3A%7B%22type%22%3A%22tempo%22%2C%22uid%22%3A%22tempo%22%7D%2C%22queryType%22%3A%22traceql%22%2C%22query%22%3A%22%7Bresource.service.name%3D%5C%22flexway%5C%22%20%26%26%20span.operation_id%3D%5C%22{operation}%5C%22%7D%22%7D%5D%2C%22range%22%3A%7B%22from%22%3A%22{start_ms}%22%2C%22to%22%3A%22{end_ms}%22%7D%7D%7D" }
oidc:
  issuer: https://login.example.com/realms/staff
  clientId: flexway-console
  redirectUrl: https://console.example.com/auth/callback
  displayName: Keycloak               # le bouton « Se connecter avec Keycloak »
  accountUrl: https://login.example.com/realms/staff/account   # facultatif : lien de Mon compte
  rolesClaim: realm_access.roles      # où lire le rôle
  tenantFrom: claim                   # ou role : flexway-viewer:acme donne le tenant acme -
                                      # des rôles que l'utilisateur ne peut pas créer lui-même
  tenantClaim: tenant                 # où lire le tenant d'un lecteur
existingSecret: console-secrets       # clé client-secret, montée en fichier
auth:
  local:
    mode: breakglass                  # accounts (défaut), breakglass (admins locaux, 1 h) ou off

Vérif :

Écran Ce qu'il montre
Vue d'ensemble L'écran d'accueil : santé de la passerelle (statut, répliques et version, en vol face au plafond, délestage, dernier rechargement), sept indicateurs avec courbe et écart à la période précédente, carte clients -> passerelle -> amonts (un clic ouvre le résumé d'un amont ; les répliques de la passerelle en nœuds sous elle, chacune avec son cœur de santé et son libellé), opérations les plus lentes, les plus en erreur, et les plus fortes variations (débit, 5xx, p95, hausses et baisses), derniers évènements. Un lecteur y lit son tenant, sans la carte ni les mesures inter-tenants
Trafic Six indicateurs face à la période précédente (requêtes, 5xx, 4xx, p95, refus 429, flux ouverts) ; l'évolution en onglets Débit (les 5 opérations les plus actives), Erreurs, Latence ; plus fortes variations ; heure par jour de la semaine (4 dernières semaines, UTC) ; répartition par opération - jamais par URL -, par amont et par tenant (admins), filtre, tri et facettes ; un clic ouvre le résumé, puis l'écran Opération ; un lien de trace à côté de chaque opération en erreur ; sur 1 h à 7 j
Opération Une opération en détail, depuis le Catalogue, Trafic ou la palette, sur 1 h à 90 j : débit, 4xx, 5xx face à la période précédente ; p50, p95, p99 de tous les tenants et leur répartition ; erreurs par code de la passerelle (upstream_status pour une réponse de l'amont relayée), un code jamais vu les 7 jours précédents marqué « nouveau » ; part par tenant (admins) ; flux : ouverts, durée, évènements relayés ; chaîne en résumé ; erreurs et traces récentes. Un lecteur y lit son tenant ; les erreurs par code et les flux, sans dimension tenant, ne lui sont pas montrés
Amonts Une carte par amont - statut, coupe-circuit à trois états et prochain essai, disponibilité sur 24 h en tranches de 30 min, débit, 4xx, 5xx, p50, p95, p99, en vol face au cloisonnement - ou un tableau ; « Comparer » pose une fenêtre B en tirets sur chaque courbe p95 ; recherche (/), filtres, tri ; un clic ouvre le détail (historique du coupe-circuit, latences, erreurs par code, opérations, politiques qui l'appellent, configuration) - admins seulement
Catalogue Les opérations des contrats : synthèse qui filtre ; recherche (/) et filtres à facettes avec comptes (méthode, protocole, sécurité, attributs, amont, étiquette, hôte) ; une ligne par opération - propriétaire, date de retrait et jours restants, sortes d'accès, classe de débit, budget, chaîne miniature par catégorie, courbe, débit, 5xx, p95 ; un clic l'ouvre dans le panneau latéral (paramètres et réponses en bref, extensions en YAML, chaîne effective vers Politiques, trafic) ; les outils MCP fédérés à part ; export CSV et JSON de la liste filtrée ; la vue entière dans l'URL. Onglet Conformité : six règles (sans propriétaire, dépréciée sans date, sans résumé, publique, écriture sans classe déclarée, dépréciée encore appelée), les opérations en écart et quoi écrire au contrat ; deux règles garanties au chargement. Un lecteur voit tout le catalogue, le trafic de son tenant et le p95 de tous les tenants, dit à l'écran
Politiques La chaîne effective d'une opération en pipeline par phase (requête, amont, réponse, erreur), chaque étape à sa place dans le cycle de vie : sa configuration (un secret s'affiche ***), son niveau d'attache, sa condition, ce qui a été retiré ou remplacé ; l'écart avec la chaîne globale ; la chaîne copiée en YAML ; pour un admin, les compteurs de chaque étape (appliquée, ignorée par sa condition, refus 429), relus toutes les 30 s. Recherche au clavier : /, flèches, Entrée, Échap
Agents Surface MCP (chemin, audience, clients admis, bornes de délégation, outils servis), indicateurs face à la fenêtre précédente ; un agent par client déclaré (surveillé ou non, appels, refus par cause, coût, portées utilisées, dernier appel) ; panneau d'un agent ; chaîne de délégation (structure, derniers refus avec profondeur et âge face aux bornes) ; outils du contrat et fédérés, serveurs fédérés pour les admins. Un lecteur y lit son tenant
Consommation Admin sans tenant choisi : tous les tenants classés face à leur budget (statut, part du plafond, tendance, date d'atteinte), et la période jour par jour ; un clic ouvre un tenant. Un tenant : ses jauges (quotas de requêtes, tokens du jour et du mois, euros du mois) sur les fenêtres UTC de la passerelle, avec tendance, bande, date d'atteinte et statut ; le cumul face au plafond ; par backend (prix, entrée et sortie, appels), backends déclarés, par capacité, mesuré en amont, bascules, agent et opérations. Période : ce mois, mois dernier, 7 jours ou personnalisée ; export CSV de la période ; sur ce mois, le cumul du mois précédent au même jour. Onglet Historique : 13 mois, mois sur mois et année sur année à période égale, un tableau par tenant, export CSV
Utilisateurs Les comptes locaux et les identités OIDC vues (lecture seule) : synthèse par état qui filtre, hygiène des accès (admins sans second facteur, invitations expirées, comptes dormants), recherche (/) et facettes ; un clic ouvre le panneau d'un compte - détails et créateur, actions sous confirmation, historique de connexion, activité - ou d'une identité (« Oublier ») ; l'activité des comptes et la politique en vigueur - admins seulement
Mon compte Profil, mot de passe, second facteur et changement d'application, codes de secours, session et « Fermer toutes les autres sessions », connexions récentes, langue et thème ; une session OIDC y trouve son profil, ses préférences et le lien vers son compte chez le fournisseur
Diagnostic -> Erreurs Les erreurs récentes de chaque réplique de la passerelle, sans lire les journaux : par sorte (amont, refus, interne), opération, amont, étape, cause (admins), durée, réplique, et la trace (gardée par la console, ou ouverte dans votre outil) ; liste partielle dite quand une réplique ne répond pas ; au-delà de 20 erreurs par seconde et par sorte, la part non détaillée est comptée (admins) ; pour un admin, un code jamais vu sur l'opération les 7 jours précédents est marqué « nouveau ». Un lecteur lit son tenant, sans cause
Diagnostic -> Traces Sans backend de traces : les requêtes lentes ou en erreur gardées par la console (500, 24 h, 64 Mio par réplique), filtres, une trace en cascade avec les attributs gardés de chaque span, téléchargement en OTLP JSON (rejouable dans Jaeger ou Tempo). Avec un backend : l'écran dit où sont vos traces
Diagnostic -> Alertes Admins seulement, dans les deux modes : chaque règle déclarée ou intégrée, son état (ok, en attente, déclenchée, résolue, inconnu), sa valeur face au seuil, sa courbe sur 6 h et ses dernières transitions ; « Silence » (durée, motif) et sa levée ; les livraisons des webhooks (résultat, statut, cause) ; les résumés périodiques ; la réplique qui notifie ; un fichier refusé, avec sa cause. La Vue d'ensemble porte une pastille des alertes en cours

Un lecteur voit son tenant, appliqué côté serveur : un autre tenant donne un refus explicite, jamais une page vide - qu'il vienne du fournisseur ou d'un compte local. Un compte du fournisseur sans rôle reconnu n'a aucun accès et voit les rôles que portait son jeton. La console ne garde aucun jeton dans le navigateur et n'écrit que ses propres comptes : sa panne n'arrête aucun trafic. Les chiffres viennent du backend, ou des envois de la passerelle à la console (15 s) ; environ 30 s de retard sur VictoriaMetrics ; la version de configuration et l'heure de lecture sont en haut de chaque écran.

Santé de chaque réplique (admins) - chaque réplique de la passerelle a son statut : la règle de statut sur ses seuls signaux (en vol face à son plafond, délestage, part délestée, dernier rechargement refusé - le compte sur 24 h reste affiché, à titre d'information), après la fraîcheur de son dernier rapport. Une réplique envoie toutes les 15 s : sans rapport depuis 45 s, « Sans rapport » ; depuis 2 min sans arrêt annoncé, « Dégradée » (et la règle replica-silent notifie) ; oubliée après 1 h. Un arrêt propre (drainage) se dit par flexway_server_draining : la réplique est « Retirée », jamais dégradée - après un déploiement, les anciens pods restent ainsi une heure. Une réplique qui ne va pas met la passerelle « À surveiller », jamais « Dégradée » : les autres servent. Un clic sur une réplique de la carte ouvre son résumé, avec un lien vers ses erreurs (/errors?replica=). Avec votre propre backend, le collecteur doit garder service.instance.id dans l'étiquette instance (Alloy et l'écriture OTLP de Prometheus le font par défaut) : sans elle, une seule « réplique » est vue.

Plan asynchrone - un courtier (kafka, amqp en 0-9-1 ou 1.0, mqtt, nats) et une cible de webhook sont des amonts : même règle de statut, sur leurs signaux - publications en échec ou livraisons en échec, 503 overloaded et rebut comptés en refus ; leur latence est l'accusé du courtier et leur jauge le tampon borné (max_in_flight). La carte de la Vue d'ensemble les dessine avec leur protocole, publiés, livrés et abonnés ; le Catalogue filtre les portes par « Publication » et « Abonnement » ; Trafic a un panneau « Plan asynchrone » par courtier et canal ; l'écran Opération d'une porte montre ses issues, l'accusé, les abonnés ouverts et ses webhooks ; Erreurs nomme buffer_full et rejected. Un lecteur lit publiés, livrés et 503 de son tenant, l'accusé de tous les tenants (dit à l'écran), jamais les abonnés ouverts (sans dimension tenant) ; l'écran Amonts reste aux admins. Trois règles intégrées : broker-unavailable (1 min), broker-backpressure (5 min), webhook-dead-letter (aussitôt).

Historique, repères et liens - l'onglet Historique de Consommation montre 13 mois UTC, le mois en cours partiel : il se compare au mois précédent et au même mois de l'an passé à période égale (du 1er à aujourd'hui), jamais à un mois entier ; un admin sans tenant y lit une ligne par tenant ; le bouton d'export rend month,tenant,measure,value,partial en CSV. Sur toutes les courbes, un trait marque chaque rechargement de configuration, appliqué ou refusé, de toutes les répliques : heure, version et résultat au survol ou au focus. « Copier le lien » garde l'écran, la fenêtre, le tenant et les filtres ; le lien ne donne aucun droit - un destinataire hors de ce périmètre reçoit un refus.

Comparer, variations, heure par jour - « Comparer » (Trafic, Opération, Amonts) pose une fenêtre B de même durée, choisie par son début, en tirets sur les mêmes courbes ; elle reste dans le lien. Les plus fortes variations classent les opérations par l'écart à la période précédente - débit en pourcentage, 5xx en points, p95 en millisecondes -, à partir de 50 requêtes. La carte heure par jour moyenne chaque heure UTC de chaque jour sur 4 semaines. Hors rétention, rien n'est dessiné : --, jamais 0.

Traces sans Tempo - en mode intégré, la console est aussi la destination des traces de la passerelle et garde elle-même les requêtes en erreur (5xx, refus écrits après l'admission sauf 401 et 404) et les lentes (plus de 1 s, ou plus de la moitié du budget de l'opération) ; le reste est jeté. Avec traces.url (et metrics.url), son récepteur reste coupé.

# Valeurs de la console : TLS obligatoire, mTLS par défaut (cert-manager, un Issuer de type CA)
otlp:
  tls:
    issuerRef: { name: flexway-ca }                     # un Certificate est rendu pour la console
    clientNames: [orders-flexway.observability.svc]     # le nom du certificat de la passerelle
  # meshEncrypted: true                  # seulement si un maillage chiffre déjà (Linkerd, Istio strict)
# Valeurs de la passerelle :
tracesToConsole:
  enabled: true
  endpoint: https://console-flexway-console-otlp.observability:4317
  tls: { issuerRef: { name: flexway-ca } }   # rend son certificat client orders-flexway.observability.svc

Le port d'administration de la passerelle est fermé par défaut (admin.networkPolicy) : la console y entre ; un outil qui lit encore /metrics doit être ajouté à admin.networkPolicy.from.

Ce qui est gardé : les attributs d'une liste blanche (operation_id, upstream, tenant, politiques, statut, méthode et route du contrat), retirés de tout le reste à la réception - une adresse, un en-tête ou un corps n'y entrent pas. Ce qui ne l'est pas : une requête que l'échantillonnage en tête de la passerelle (observability.sampling.max_per_second) n'a pas retenue n'arrive jamais ; les erreurs restent toutes dans le journal des erreurs. La passerelle trace la requête, ses étapes - routage, identité, politiques -, ses external-call et chaque tentative vers l'amont, avec retry_count et circuit_state ; les étapes ne coûtent rien aux requêtes non échantillonnées.

Alertes - la console évalue ses règles toutes les 15 s, dans les deux modes, et notifie par webhook signé. Rien n'est saisi dans la console : règles, webhooks et résumés sont déclarés dans les valeurs du chart, relus sans redémarrage ; une version invalide est refusée et dite à l'écran Alertes, la précédente s'applique.

# Valeurs de la console
alerts:
  rules:     # sortes fermées : error_rate, p95, breaker_open, slo_burn, new_error_code
    - { name: reports-5xx, kind: error_rate, upstream: reports, threshold: 5%, for: 1m, notify: [ops] }
    - { name: orders-p95, kind: p95, operation: createOrder, threshold: 300ms, for: 2m }
  webhooks:  # https exigé (allowHttp pour le lever) ; secretFile : whsec_<base64>, une ligne par secret
    - { name: ops, url: "https://hooks.example/flexway", secretFile: /etc/flexway-console-alerts/ops }
    - { name: chat, urlFile: /etc/flexway-console-alerts/chat-url, secretFile: /etc/flexway-console-alerts/chat, allowPrivate: true }
  summaries:
    - { name: weekly, every: weekly, at: "07:00", weekday: monday, webhook: chat }   # heure UTC
  existingSecret: console-alerts    # secrets et URL à jeton, montés sur /etc/flexway-console-alerts
slos:
  - { operation: createOrder, availability: 99.9%, latency: { p95: 300ms, target: 99% }, window: 30d }
  - { name: orders-api, upstream: orders-svc, availability: 99.5% }   # un groupe : voir Objectifs SLO par groupe d'opérations
Sujet Ce qu'il faut savoir
Règles intégrées Toujours actives : breaker-open (coupe-circuit ouvert), upstream-degraded et gateway-degraded (1 min), new-error-code (un code jamais vu sur l'opération en 7 jours - il lit flexway_errors_total), slo-burn (dès qu'un objectif est déclaré, d'opération ou de groupe), replica-silent (une réplique de la passerelle sans rapport depuis 2 min sans arrêt annoncé), broker-unavailable (coupe-circuit d'un courtier ouvert ou plus de la moitié de ses publications en échec, 1 min), broker-backpressure (plus de 1 % de 503 overloaded, 5 min), webhook-dead-letter (une livraison en rebut sur 15 min)
Jugement error_rate = part de 5xx sur 1 min, à partir de 20 réponses ; p95 sur 5 min. Métriques muettes : aucun état ne change, rien n'est envoyé ni retiré, l'écran dit inconnu. Une cible absente de la configuration rend la règle invalide, jamais notifiée
Qui envoie Chaque réplique évalue ; une seule notifie, celle qui tient le Lease du chart. Le webhook-id est le même d'une réplique à l'autre : dédoublonnez sur lui (au moins une fois)
Webhook https, adresse vérifiée à la connexion : boucle locale, lien local (169.254.169.254) et multicast toujours refusés, réseau privé seulement avec allowPrivate: true (une cible interne) ; aucune redirection suivie ; délai 5 s ; réessais 5 s, 30 s, 2 min, 10 min, 30 min, puis abandon visible à l'écran ; un 4xx (hors 408, 429) est un refus définitif
Silence Écran Alertes, 24 h au plus, motif obligatoire : il suspend les notifications d'une règle, jamais son évaluation ; à son échéance, ce qui déclenche encore est notifié
Résumés daily ou weekly à l'heure UTC : requêtes face à la période précédente, parts de 5xx et 4xx, nouveaux codes, budget de chaque SLO, cinq plus fortes variations, alertes - jamais un nom de tenant
SLO Disponibilité (réponses non 5xx) et latence (part des minutes dont le p95 tient la cible) sur la fenêtre ; budget restant, burn rate sur 1 h, 6 h, 3 j ; slo-burn sonne au-delà de 14,4 sur 1 h et 5 min, ou de 6 sur 6 h et 30 min. Sur une opération ou, depuis 1.6.0, un groupe d'opérations - un amont, une étiquette, une liste, toutes (Objectifs SLO par groupe d'opérations). Onglet Objectifs (SLO) de Trafic, panneau d'Opération, objectif global sur la Vue d'ensemble ; « Créer une alerte » des graphiques et des objectifs écrit le YAML à copier dans ces valeurs
Réglages des règles intégrées alerts.builtins : name d'une règle intégrée, for (0 à 1 h) sur toutes ; threshold (un ratio) et minRequests seulement sur broker-unavailable (moitié, 20) et broker-backpressure (1 %, 20) ; excludeCauses seulement sur une règle qui compte des causes fermées (les règles du proxy Kafka natif, depuis 1.6.0). Un nom inconnu, une règle réglée deux fois ou un réglage que la règle ne porte pas refusent le fichier, comme le reste ; sans la clé, les défauts. L'écran Alertes affiche le seuil réglé, GET /api/alerts les valeurs en vigueur et leur origine (tuning)

Régler une règle intégrée plutôt que la taire - quelques cas :

alerts:
  builtins:
    # Resserrer : un petit trafic de publications sonne trop tôt ; 5 % refusés, à partir de 100 publications.
    - { name: broker-backpressure, threshold: 5%, minRequests: 100 }
    # Retarder : un courtier qui bascule en moins de 3 min n'est pas un incident.
    - { name: broker-unavailable, for: 3m }
    # Plus tôt : un coupe-circuit ouvert sonne déjà sans délai ; une passerelle dégradée, au bout de 30 s.
    - { name: gateway-degraded, for: 30s }

Avec le proxy Kafka natif (depuis 1.6.0), la règle kafka-proxy-refusals compte des causes fermées et accepte excludeCauses (Clients Kafka natifs : clusters virtuels par tenant) :

alerts:
  builtins:
    - { name: kafka-proxy-refusals, threshold: 10%, excludeCauses: [quota] }   # ne pas sonner sur un quota atteint

Le corps : {"type": "flexway.alert.firing", "timestamp": ..., "data": {"rule", "kind", "state", "since", "target", "value", "unit", "threshold", "subjects"}} (flexway.alert.resolved, flexway.summary.daily, flexway.summary.weekly) - jamais un tenant. Vérifier la signature côté destinataire (Standard Webhooks) avant tout traitement :

import base64, hashlib, hmac, time

def verify(secret: str, headers, body: bytes) -> bool:
    key = base64.b64decode(secret.removeprefix("whsec_"))
    msg_id, ts = headers["webhook-id"], headers["webhook-timestamp"]
    if abs(time.time() - int(ts)) > 300:          # rejeu : 5 minutes de tolérance
        return False
    mac = hmac.new(key, f"{msg_id}.{ts}.".encode() + body, hashlib.sha256).digest()
    want = base64.b64encode(mac).decode()
    # deux signatures pendant une rotation, séparées par une espace : une seule doit correspondre
    return any(hmac.compare_digest(sig.removeprefix("v1,"), want)
               for sig in headers["webhook-signature"].split())

Rotation : mettre le nouveau secret en seconde ligne du fichier côté console, déployer le nouveau côté destinataire, puis retirer l'ancien - la console signe avec les deux entre-temps.

Essayer une requête (1.2.0) - sous le pipeline de l'écran Politiques : méthode, chemin, hôte, identité décrite (sujet, tenant, portées, rôles) et en-têtes passent par le /simulate de la passerelle, à blanc. Le verdict s'affiche avec la requête que l'amont aurait reçue (secrets ***), et chaque étape porte son résultat : appliquée, ignorée, non simulée (un external-call n'est jamais appelé), franchie. Un lecteur simule dans son tenant : en nommer un autre est refusé.

Aller à (Ctrl+K, Cmd+K sur Mac, ou le champ de la barre du haut) - depuis tout écran : un écran, une opération (ouvre sa chaîne), un amont (ouvre son détail, admins), un agent (ouvre son panneau ; ceux de son tenant pour un lecteur), un outil ou un tenant (filtre la console ; le sien pour un lecteur). Flèches, Entrée, Échap.

Comptes - ce que l'écran Utilisateurs permet, et ce qu'il garde :

Besoin Où Ce qui se passe
Un lien plus court ou plus long Inviter, ou « Réinitialiser le mot de passe » sur un compte 24 h, 72 h (défaut) ou 7 jours ; usage unique, affiché une fois, remplacé par toute réinitialisation
Exiger le second facteur d'un lecteur Panneau du compte -> « Exiger le second facteur » Ses sessions sans second facteur se ferment, il enrôle une application à sa prochaine connexion
Changer de téléphone Mon compte -> « Changer d'application » Mot de passe et code de l'application actuelle (ou un code de secours) ; l'ancienne vaut jusqu'à la confirmation, qui remet dix codes neufs et ferme les autres sessions
Une connexion inconnue Mon compte -> « Fermer toutes les autres sessions » Toutes les autres sessions se ferment en moins de 5 s ; la courante continue
Un compte verrouillé Panneau du compte -> « Déverrouiller maintenant » Tous les verrous du compte se lèvent : chaque adresse, le délai progressif, le second facteur
Une identité du fournisseur à retirer de la liste Panneau de l'identité -> « Oublier » Elle revient à sa prochaine connexion ; ses droits restent ceux de son jeton. Retirer l'accès se fait chez le fournisseur

Ce que la console garde - une vue bornée de l'activité des comptes (500 évènements, 64 Kio, 90 jours : réseau masqué /24 ou /48, famille du navigateur, jamais une adresse ni le User-Agent) et les identités OIDC vues (empreinte de l'émetteur et du sujet, jamais le sujet ; identifiant de connexion et nom ; oubliées 90 jours après la dernière connexion), dans des clés à part du Secret d'état, lues par les seuls admins. Le journal de la console reste la piste d'audit complète. La jauge flexway_console_store_bytes{key} montre le poids de chaque clé : leur somme reste sous 900 Kio, l'activité puis le registre cèdent avant un compte. Langue et thème restent dans le navigateur.

Statuts et données - un statut suit une règle : dégradé si coupe-circuit ouvert ou plus de 5 % de 5xx, à surveiller si demi-ouvert, plus de 1 %, en vol à 80 % du plafond ou refus ; la disponibilité sur 24 h est la part des réponses non 5xx, refus de la passerelle compris. Source de métriques injoignable (backend en panne, ou source intégrée pas encore ouverte), les écrans restent lisibles : chiffres --, statut « Inconnu » ; passerelle injoignable, la carte et la liste des amonts ne s'affichent pas plutôt que périmées.

Consommation - deux temps. Les jauges (requêtes par politique quota, tokens du jour et du mois, euros du mois) suivent les fenêtres de la passerelle, jour et mois calendaires UTC : celles qui décident d'un 429. Les répartitions (par jour, par backend, par agent, opérations, export) suivent la période choisie - ce mois, mois dernier, 7 jours, ou from-to (93 jours au plus) - en jours du fuseau de la console (FLEXWAY_CONSOLE_TIMEZONE). Chaque jauge porte sa tendance (rythme des dernières 24 h), une bande (jours le plus lent et le plus rapide des 7 derniers), la date d'atteinte du plafond et un statut : dégradé si le plafond est atteint ou le sera avant la fin de la fenêtre, à surveiller si le plafond souple est franchi ou la tendance atteint 90 %, sain sinon ; aucun statut sans plafond déclaré. Backend de métriques en panne : les plafonds restent lisibles, le consommé est -- ; passerelle muette : le consommé reste lisible, les plafonds --, aucun statut. Un admin lit tous les tenants (GET /api/usage) ; l'export GET /api/usage/export rend la période en CSV (date, tenant, backend, direction, tokens, euros, requests), son tenant seul pour un lecteur.

Sur un téléphone ou une tablette - mêmes écrans, mêmes données et mêmes droits qu'au poste, de 360 à 1 024 px, en portrait et en paysage ; au poste, à la souris, rien ne change.

Pour Faire Ce qui se passe
Changer d'écran sur un téléphone La barre basse : Vue d'ensemble, Alertes (et le nombre de règles en alerte), Erreurs, Rechercher, Plus « Plus » ouvre le tiroir : tous les écrans du rôle, la session et la fraîcheur ; le retour du navigateur le ferme. Un lecteur a Traces à la place d'Alertes
Changer d'écran sur une tablette Le rail d'icônes libellées à gauche (au doigt, de 761 à 1 180 px) Les écrans d'un incident, « Plus » pour les autres ; à la souris, la barre latérale du poste reste
Lire une courbe Toucher ou glisser sur la courbe ; au clavier, les flèches La valeur s'écrit dans la ligne de lecture sous la courbe et y reste ; un repère de rechargement touché y écrit sa version, son heure et son résultat. Même chose à la souris, au poste compris
Lire une carte de chaleur, la bande de 24 h d'un amont Toucher la cellule ou la tranche Son jour, son heure et sa valeur dans la ligne de lecture
Lire une colonne masquée d'un tableau Le bouton « Détail » de la ligne Toutes les colonnes masquées, nommées, avec leur valeur
Poser un silence Alertes -> la règle (feuille plein écran) -> une durée en boutons, le motif -> « Mettre en silence » au pied de la feuille Même requête qu'au poste ; le retour du navigateur ferme la feuille
Filtrer les erreurs Erreurs -> « Filtres » Les quatre listes dans une feuille ; chaque filtre actif est une pastille qui le retire

Cibles de 44 px au moins sous un doigt, champs écrits en 16 px (Safari n'agrandit pas la page), aucune valeur au survol seul.

Qui mesure - totaux, jours, répartitions, totaux du classement, export et historique ne comptent que ce que cette passerelle mesure (metered_by!="upstream"), avec un backend de métriques comme avec la source intégrée, dont les agrégats journaliers gardent metered_by. Les jauges (et leur tendance, bande, date d'atteinte, statut) comptent ce que compte le plafond local, qui s'applique aussi aux appels relayés : toutes les séries - tokens de l'usage rendu, euros au prix déclaré seulement -, et la jauge le dit (« inclut les appels relayés, que le plafond local compte aussi »). Un backend metering: upstream relaie vers une autre Flexway qui compte l'appel elle-même : ses tokens et ses appels sont montrés à part, sous « Mesuré en amont », jamais ajoutés aux totaux - la passerelle amont fait foi pour le coût. La répartition par capacité (chat, vision, embeddings, model) compte un modèle spécialisé gRPC en appels, pas en tokens. Le panneau Backends déclarés dit, d'après /config, le genre (openai, relay, grpc), les capacités, qui mesure et l'emplacement de chaque backend ; un admin y lit aussi le schéma d'authentification (none, bearer, header, token_file) - jamais une clé ni sa référence.

Disponible depuis 0.1.0 ; comptes locaux et fournisseur facultatif en 1.0.0 ; écran Politiques en pipeline et simulation, Vue d'ensemble, Amonts refondu, palette de commandes, Utilisateurs et Mon compte refondus, activité des comptes, identités OIDC vues et propositions de configuration (Proposer un changement depuis la console) en 1.2.0. Observabilité autonome en 1.3.0 : sans backend, la console est le collecteur OTLP de la passerelle - source de métriques intégrée, historique, repères de rechargement, liens partageables ; Erreurs et Traces, traces gardées ; écran Opération, Trafic refondu, flux mesurés, erreurs par code, plus fortes variations, deux fenêtres, heure par jour, code nouveau, étapes des traces ; Alertes - règles déclarées et intégrées, webhook signé notifié par le détenteur d'un Lease, silences, SLO, résumés périodiques. Téléphone et tablette en 1.4.0 : barre basse, rail au doigt et tiroir, ligne de lecture des courbes, colonnes prioritaires, feuilles plein écran - le poste inchangé à la souris. En 1.5.0, consommation mesurée chez celui qui paie, par capacité, backends déclarés, et plan asynchrone : courtiers et cibles de webhook en amonts, portes au Catalogue, panneaux de Trafic et d'Opération, trois règles intégrées. En 1.6.0, objectifs SLO par groupe d'opérations (objectifs par niveau et panneau de l'objectif dans Trafic, objectif global sur la Vue d'ensemble, « Membre de » sur Opération), clients et clusters Kafka, réglage des règles intégrées.

Étapes suivantes