diff --git a/README.md b/README.md index 9b4fec9..e97567b 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ redémarrages (volume `/data`). Variables d'environnement du conteneur : | `LOKI_AUTO_UPDATE` | `0` : pas de mise à jour des binaires au démarrage | `1` | | `LOKI_FACADE` | `0` : sans façade Rust, le Go sert directement le port public (porte de sortie) | `1` | | `LOKI_GO_PORT` | port local (127.0.0.1) du serveur Go derrière la façade | `8091` | -| `LOKI_CHAT` | `rust` : la conversation est tenue par la façade Rust (expérimental, migration en cours), outils fichiers et shell compris. Le Go garde les autres outils, les pièces jointes, les fichiers et l'export. Avec `PROJ_SNAPSHOT`, `KEEP_TURN_IMAGES`, `NUDGE_IN_TOOL`, `SIDE_SLOT`, `SLOT_PERSIST`, `PREWARM` ou des motifs `retry_patterns`, le Go garde la conversation (décidé au démarrage) | Go | +| `LOKI_CHAT` | qui tient la conversation : la façade Rust par défaut (outils fichiers, shell et git compris ; le Go garde les autres outils, les pièces jointes, les fichiers et l'export). `go` : la rend au Go (porte de sortie). Avec `PROJ_SNAPSHOT`, `KEEP_TURN_IMAGES`, `NUDGE_IN_TOOL`, `SIDE_SLOT`, `SLOT_PERSIST`, `PREWARM` ou des motifs `retry_patterns`, le Go la garde de toute façon (décidé au démarrage) | Rust | **Accès par nom de domaine.** Sans clé de pilotage, l'API n'accepte que les hôtes locaux (IP, `localhost`, nom sans point, `.local`/`.lan`…) et refuse diff --git a/docker-compose.unraid.yml b/docker-compose.unraid.yml index 961718e..314d245 100644 --- a/docker-compose.unraid.yml +++ b/docker-compose.unraid.yml @@ -41,6 +41,9 @@ services: # sinon l'API répond 403 (protection contre les sites tiers / DNS # rebinding). Inutile pour un accès par IP ou nom local. - LOKI_TRUSTED_HOSTS= + # Conversation tenue par la façade Rust (défaut). « go » la rend au Go si + # quelque chose ne va pas — puis Compose Down / Up. + # - LOKI_CHAT=go volumes: # PERFORMANCE — /mnt/user/… passe par la couche FUSE des partages # Unraid (shfs) : chaque écriture de la base et chaque page d'un gros diff --git a/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md b/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md index e65d244..ac0ad24 100644 --- a/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md +++ b/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md @@ -1,6 +1,6 @@ # Migration Rust — lot 6 : le cœur chat -Date : 2026-10-14 · Statut : **brouillon de conception** (aucun code) · +Date : 2026-10-14 · Statut : **livré** (étapes 1 à 6 ; défaut Rust depuis le 2026-10-15, `LOKI_CHAT=go` pour revenir au Go) · Prérequis : lots 1-5 (façade, auth, store via API interne, `/v1`). ## Pourquoi c'est un lot à part diff --git a/rust/loki-rs/src/main.rs b/rust/loki-rs/src/main.rs index 1d735ca..a63fffb 100644 --- a/rust/loki-rs/src/main.rs +++ b/rust/loki-rs/src/main.rs @@ -114,9 +114,9 @@ fn serve() -> ExitCode { return ExitCode::FAILURE; } }; - // LOKI_CHAT=rust : la conversation est tenue par la façade. Le Go le - // sait (il remet alors à la façade le trafic de chat du tunnel) et - // connaît son adresse locale. + // Conversation tenue par la façade (défaut ; LOKI_CHAT=go pour la + // rendre au Go). Le Go le sait (il remet alors à la façade le trafic + // de chat du tunnel) et connaît son adresse locale. let chat_rust = chat_held_by_facade(); let mut env = vec![(auth::INTERNAL_ENV.to_string(), token.clone())]; if chat_rust { @@ -184,9 +184,19 @@ fn serve() -> ExitCode { }) } -/// `LOKI_CHAT=rust` : conversation tenue par la façade (Go par défaut). +/// Conversation tenue par la façade : c'est le défaut. `LOKI_CHAT=go` (ou +/// 0/off/false/no) la rend au Go — porte de sortie. Le Go la garde de toute +/// façon si un réglage que la façade ne sait pas honorer est posé +/// (`facadeChatBlocker`). fn chat_held_by_facade() -> bool { - std::env::var("LOKI_CHAT").is_ok_and(|v| v.trim().eq_ignore_ascii_case("rust")) + chat_wanted(std::env::var("LOKI_CHAT").ok().as_deref()) +} + +fn chat_wanted(v: Option<&str>) -> bool { + !matches!( + v.map(|v| v.trim().to_ascii_lowercase()).as_deref(), + Some("go" | "0" | "off" | "false" | "no") + ) } /// Le Go garde-t-il la conversation ? (quelques essais : il vient de démarrer) @@ -218,7 +228,7 @@ async fn start_chat( ); s.load().await?; let shared = ChatShared::for_sessions(&s, store, home.join("chatimg")); - eprintln!("[loki-rs] conversation tenue par la façade (LOKI_CHAT=rust)"); + eprintln!("[loki-rs] conversation tenue par la façade (LOKI_CHAT=go pour la rendre au Go)"); Ok(ChatService::new( std::sync::Arc::new(s), std::sync::Arc::new(shared), @@ -265,3 +275,18 @@ async fn shutdown_signal() { let _ = tokio::signal::ctrl_c().await; } } + +#[cfg(test)] +mod tests { + #[test] + fn conversation_tenue_par_la_facade_par_defaut() { + use super::chat_wanted; + assert!(chat_wanted(None)); + assert!(chat_wanted(Some(""))); + assert!(chat_wanted(Some("rust"))); + assert!(chat_wanted(Some(" RUST "))); + for v in ["go", "GO", "0", "off", "false", "no"] { + assert!(!chat_wanted(Some(v)), "{v}"); + } + } +}