Files
Loki/docs/superpowers/specs/2026-10-13-rust-migration-lot2-design.md
T
MichaelandClaude Opus 5.5 a99d988e70 API interne pour la façade Rust : clé de pilotage à la demande
Lot 2 de la migration : la façade va authentifier elle-même ses routes
/api/*. La clé de pilotage vit dans la base bbolt, verrouillée par ce
process ; la façade la demande donc à GET /internal/v1/auth, à chaque
requête — un changement de clé prend effet tout de suite, comme avant.

La réponse contient un secret, d'où trois verrous : route branchée
uniquement sur l'écoute TCP directe (le tunnel du relais sert le mux nu),
jeton tiré au hasard par la façade à chaque démarrage et exigé en
temps constant, 404 sans rien révéler sinon. Base illisible : 503, la
façade fermera l'accès plutôt que de l'ouvrir.

Spec du lot 2 jointe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:35:00 +02:00

3.8 KiB

Migration Rust — lot 2 : UI statique, authentification, /api/ping

Date : 2026-10-13 · Statut : validé · Prérequis : lot 1 (façade loki-rs).

But

Faire passer en Rust les premières routes réelles, et surtout poser le socle dont toutes les routes /api/* suivantes auront besoin : l'authentification.

Périmètre

  1. UI statique servie par Rust, embarquée dans le binaire à la compilation depuis internal/loki/ui/ (même source que le go:embed) :

    Route Content-Type Cache-Control Autre
    /, /index.html text/html; charset=utf-8 no-store, max-age=0
    /marked.min.js application/javascript public, max-age=86400
    /sw.js application/javascript no-store, max-age=0 Service-Worker-Allowed: /
    /manifest.webmanifest application/manifest+json public, max-age=3600
    /fonts/<nom>.woff2 font/woff2 public, max-age=31536000, immutable

    GET et HEAD seulement (les autres méthodes restent relayées au Go). Compression gzip si le client l'accepte, sauf polices (comme web_gzip.go) ; Vary: Accept-Encoding sur les types compressibles. Version compressée calculée une fois, à la première demande.

  2. API interne Go GET /internal/v1/auth → {"web_key": "…", "version": "…"}.

    • Servie uniquement sur l'écoute TCP directe de loki web (enveloppe autour du mux dans cmdWeb) : le tunnel du relais sert le mux nu et ne la voit jamais.
    • Exige l'en-tête X-Loki-Internal égal à LOKI_INTERNAL_TOKEN (≥ 32 caractères, comparaison en temps constant). Jeton absent, trop court ou faux → 404 (rien ne révèle l'existence de la route).
    • Base illisible → 503 : la façade fermera l'accès (jamais ouvert par défaut sur une panne d'E/S, même règle que requireWebAuth).
    • La façade tire un jeton aléatoire de 32 octets à chaque démarrage et le passe au Go par l'environnement.
  3. Authentification de pilotage en Rust (auth.rs), port à l'identique de requireWebAuth + crossSiteReject + localHostName + checkBearer :

    • clé relue à chaque requête (API interne) : un changement de clé prend effet immédiatement ;
    • API interne en erreur (Go absent, 503, réponse illisible) → 503 {"error":"configuration illisible — réessaie dans un instant"} ;
    • refus site tiers / hôte non local sans clé → 403 {"error":"<raison>"} (mêmes messages que le Go) ;
    • Bearer absent ou faux → 401 {"error":"non autorisé"} + WWW-Authenticate: Bearer realm="loki" ;
    • X-Loki-Via n'est jamais pris en compte côté Rust (effacé à l'entrée, le trafic du tunnel ne traverse pas la façade).
  4. /api/ping en Rust : {"ok":true,"service":"loki","version":"<version Go>"} après authentification — première route /api/* servie par Rust.

  5. Durcissement de la façade : /internal/* venant de l'extérieur → 404 ; en-tête X-Loki-Internal entrant effacé avant relais.

Hors périmètre

Toutes les autres routes /api/*, /v1, le flux chat : lots suivants.

Tests

  • Go : route interne (sans jeton → 404, mauvais jeton → 404, bon jeton → 200 avec clé et version, autres chemins relayés au mux).
  • Rust : cas de TestCrossSiteReject repris un pour un ; local_host_name (IP, IPv6 entre crochets, nom sans point, suffixes locaux, hôte de confiance) ; Bearer ; statiques (types, cache, gzip, HEAD, police inconnue → relayée) ; intégration façade + faux backend interne : ping 200 sans clé, 401 avec clé sans Bearer, 200 avec bon Bearer, 503 si l'API interne tombe, 403 rebinding, /internal/v1/auth depuis l'extérieur → 404.
  • Bout en bout local : vrai loki Go derrière la façade, loki set-web-key, ping avec et sans clé.