Bascule des endpoints OIDC de kaya.freeboxos.fr:9080/9081 vers les URLs
publiques HTTPS. Les ports restent un chemin réseau valide mais le SSO
est désormais canonique sur le domaine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IngressRoutes websecure + certResolver Let's Encrypt pour Authentik et
Guacamole, en plus des routes internes *.lab.local (non destructif).
Cert auth.funklab.online émis et vérifié (issuer Let's Encrypt).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bbarthe (et tout compte Authentik sans email) échouait au login SSO :
Grafana, email vide, tentait l'endpoint /emails inexistant chez Authentik
→ 404 InternalError. email_attribute_path avec repli preferred_username +
login_attribute_path évitent le fallback. Plus besoin d'email par user.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
EXTENSION_PRIORITY "openid, *" : plus de formulaire local, l'arrivée sur
portail.lab.local redirige directement vers Authentik. L'admin passe par
un compte SSO avec « Administer system » (guacadmin devient inaccessible ;
retour arrière = remettre "*, openid").
Doc : pièges slug d'application (≠ guacamole → JWKS 404) et anti-bruteforce
(IP Traefik bloquée pour tous après 5 échecs) découverts pendant la mise
en route.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Le premier démarrage exécute les migrations de schéma (plusieurs minutes) ;
la livenessProbe (60 s) tuait le pod en boucle avant qu'il ne soit prêt.
La startupProbe accorde jusqu'à 10 min avant d'armer liveness/readiness.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Permet de configurer des alertes « préviens-moi par mail quand NVDA atteint 200$ »
depuis la Console IA. Un sidecar surveille les cours en continu.
- finlab/watcher.py : boucle toutes les 5 min, lit price_alerts.yaml (workspace),
évalue les seuils above/below, envoie un email au franchissement. Anti-spam
(1 mail/seuil, état .watcher-state.json, ré-armé en ré-éditant). Modes --once / --test
- Email via le relais postfix du lab (storage-01:25, SMTP_HOST) qui réécrit
l'expéditeur en Gmail — AUCUN secret (pods k8s dans mynetworks)
- price_alerts.yaml : config seedée dans le workspace (email_to + rules), éditée
par la Console IA sur demande
- deployment : conteneur sidecar `watcher` (boucle 5 min, ressources minimes)
- Dockerfile : seed price_alerts.yaml ; persona console + admin/ia/finlab.md documentés
Logique testée en local (SMTP mocké) : franchissement → envoi, anti-spam au 2e run,
seuil non atteint → rien.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Console Claude Code throttlée ~11% en pic à 1 cœur (CPU moy ~0,2, RAM ~384 Mo —
non contrainte). On double le plafond CPU pour fluidifier les bursts ; un peu de
marge RAM. Mesuré via Prometheus. (La latence du modèle reste côté API.)
requests cpu 150m→300m, mem 512Mi→768Mi ; limits cpu 1→2, mem 1536Mi→2Gi.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Ajoute une interface graphique à finlab, en plus de la console Claude Code (les
deux cohabitent sur finance.lab.local) :
- dashboard/ : backend FastAPI (sans LLM/clé) exposant les données finlab en JSON
(/api/portfolio, /api/scan, /api/ohlc, /api/plan, /api/alerts) + front statique
(lightweight-charts) : graphiques chandeliers MM50/200 + volume, portefeuille
(P&L/poids/secteurs), watchlists thématiques scannées, alertes, plan de trade R:R
- Découplage code/data : finlab lit ses configs depuis FINLAB_HOME si défini
(workspace persistant) ; repli sur le dossier du package en dév local
- Image double usage (un build, deux conteneurs) : code → /opt/app, seed minimal
(configs + persona + MCP) → workspace
- Fix seed NFS (cause racine du workspace vide) : WORKDIR n'est plus sur le volume
(le runtime le créait en root sur NFS) ; seed déplacé dans un initContainer
robuste (recrée le dossier s'il est non-inscriptible)
- Deployment : initContainer seed + 2 conteneurs (console ttyd -b /console, dashboard
uvicorn) ; Service 2 ports ; IngressRoute 2 routes (/console + dashboard), basicAuth
Validé en local (podman) : seed OK, dashboard /api/portfolio → 16k€/10 positions
via FINLAB_HOME, endpoints ohlc/scan/plan/alerts OK, ttyd/claude/finlab présents.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(finlab): console Claude Code finance in-cluster + toolkit d'analyse
Intègre finlab (ex-projet Projets/Finance) au lab comme une console Claude Code
web spécialisée finance — l'esprit OpenAlice, mais c'est le vrai Claude Code sur
l'abonnement (login persisté, pas d'API facturée), agentique, avec la boîte à
outils finlab (Yahoo Finance) branchée en MCP.
- tools/finlab/ : source finlab rapatriée + Dockerfile (Python 3.12 + Node +
claude-code + ttyd) + persona workspace/CLAUDE.md + branchement MCP + entrypoint
(seed du workspace no-clobber sur le PVC)
- .github/workflows/build-finlab.yml : build GHCR funk-finlab + bump manifest (main)
- k8s/apps/finlab/ : Deployment/Service/PVC/IngressRoute (finance.lab.local) +
Middleware basicAuth (shell web protégé) ; PVC = HOME (login) + workspace
- k8s/apps-of-apps/apps/finlab.yaml : Application ArgoCD
- .mcp.json (racine) : outils finlab dans les sessions Claude Code du lab
- admin/ia/finlab.md + READMEs + CLAUDE.md : doc + enregistrement
Analyse/aide à la décision uniquement — aucun ordre réel (paper trading Alpaca
fictif seul exécutable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(finlab): ttyd absent des dépôts bookworm → binaire statique GitHub
Le build amont échouait (`E: Package 'ttyd' has no installation candidate`) :
ttyd n'est pas packagé dans Debian bookworm. On récupère le binaire statique
(musl, pin TTYD_VERSION=1.7.7) depuis les releases GitHub. Build complet validé
en local (podman) : ttyd 1.7.7, claude-code 2.1.195, import finlab + seed OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Projet mis en pause : Kraken via « CCXT Custom » non supporté (fetchBalance
avorte) + pas d'automatisation actions. Pod éteint, mais Deployment + PVC
openalice-data (config, clés scellées, données) conservés. Relance = replicas 1.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Le Dockerfile amont v0.60.0-beta.1 copie src/workspaces/templates dans l'image
runtime mais oublie src/workspaces/cli, alors que cliBinPath()
(/app/src/workspaces/cli/bin) est requis au runtime pour poser les shims
alice/alice-uta/traderhub sur le PATH des agents. Sans eux, l'agent ne peut pas
joindre le moteur de trading ("alice-uta introuvable sur mon PATH").
- build-openalice.yml : patch du Dockerfile cloné (COPY src/workspaces/cli) + guard
- tag image suffixé via IMAGE_PATCH=p1 → nouveau tag immuable, ArgoCD redéploie
- deployment.yaml → :v0.60.0-beta.1-p1
- doc admin/ia/openalice.md (correctifs locaux)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Déploie TraderAlice/OpenAlice (AGPL-3.0) dans le namespace ai comme back-end de
connaissances financières pour Asa, consommé via le connecteur MCP Ask.
- CI build-openalice.yml : rebuild depuis le Dockerfile amont (ref épinglée v0.60.0-beta.1)
→ ghcr.io/alkatrazz24/funk-openalice, bump manifest sur main
- k8s/apps/openalice : Deployment (restricted + fsGroup, Recreate), PVC 5Gi NFS /data,
Service web:47331 + mcpask:3003, IngressRoute openalice.lab.local
- ArgoCD app + doc admin/ia/openalice.md (RAG) + MAJ CLAUDE.md
- Cerveau hybride : LiteLLM/Qwen3-8B local (gratuit) + Claude basculable ; données
TraderHub (sans clé) + FMP gratuit. Lecture/recherche seule — aucun broker, zéro trading.
Onboarding premier boot (Web UI) imposé : credentials scellés dans /data par design.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Le pod SearXNG crashait en boucle : granian recevait --port tcp://<ip>:8080
au lieu d'un entier. Cause classique k8s : le Service « searxng » fait injecter
l'env service-link SEARXNG_PORT=tcp://<clusterIP>:8080, que l'entrypoint SearXNG
lit comme port de bind.
- enableServiceLinks:false (coupe l'injection des env service-link, inutiles ici)
- SEARXNG_PORT=8080 + SEARXNG_BIND_ADDRESS=0.0.0.0 explicites (défense en profondeur)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Asa ne répondait qu'à partir d'un contexte figé (RAG doc ou blocs live
pré-câblés, à présélectionner) → les questions à état live (« gpu-01 tourne
bien ? ») tombaient sur « la doc ne le précise pas ». Bascule vers une boucle
de function-calling : le modèle décide quels outils appeler puis répond à
partir de leurs résultats.
- Nouveau contexte `asa` (STT_DEFAULT_CONTEXT=asa, défaut prod) + tools.py
(registre schémas OpenAI + exécuteurs) + brain.ask_with_tools (boucle bornée
STT_TOOL_MAX_ITERS=4, réponse forcée au-delà ; _post factorisé + retry).
- Outils Phase 1, LECTURE SEULE : search_docs (RAG funk-docs), host_health
(gpu-01|storage-01 : up/charge/RAM + llama-server), cluster_status,
prometheus_query (PromQL arbitraire). Réutilisent sources.py / knowledge.py.
- Trace des outils renvoyée dans `context` → visualiseur HUD (un bloc par appel).
- 100 % local (Qwen3-8B) : tool-calling natif llama.cpp validé, survit au
/no_think. LiteLLM transmet bien les tools. PromQL validés contre le vrai
Prometheus in-cluster. 3 tests unitaires de la boucle (hors-ligne).
Web search (SearXNG in-cluster) = Phase 2 ; actions admin pilotées par le LLM
(hermes-exec comme outil) = Phase 3.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(stt): active les actions via Hermes (STT_ACTIONS_ENABLED=true)
hermes-exec déployé sur storage-01 (:9096) + secret k8s stt-server-secrets/hermes-exec-token
en place → on active le contexte « agent ». Après déploiement ArgoCD, le contexte
🤖 Agent apparaît dans le sélecteur et les actions (avec confirmation) sont opérationnelles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: journal 2026-06-21 + état STT (portail, contextes, intents, actions Hermes)
- progress/2026-06-21.md : récap complet de la journée (portail + santé live, contextes
visualiseur + presets live, intents vocaux, jeton Ghostfolio unifié, actions via Hermes,
ASR Parakeet) + l'incident de merge des PR empilées et sa leçon.
- PROGRESS.md : ligne du jour.
- CLAUDE.md : « État actuel » daté 2026-06-21, ligne STT enrichie.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(vault): ajoute vault_hermes_exec_token (jeton actions hermes-exec, chiffré)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Asa peut AGIR sur le homelab (l'autre moitié de la vision), via le vrai agent Hermes,
profil par défaut, tous ses outils — avec confirmation et jeton.
Découverte : Hermes n'a pas d'API HTTP (appli TUI), mais un mode one-shot `hermes -z
"<prompt>" --yolo`. On s'appuie dessus.
storage-01 — exécuteur :
- tools/hermes-exec/server.py : service HTTP qui lance `hermes -z --yolo` en user hermes,
derrière un jeton Bearer (compare_digest), timeout + audit, une action à la fois.
- rôle Ansible hermes_exec : systemd (User=hermes, env hermes-agent), jeton via
EnvironmentFile 0640 (Vault vault_hermes_exec_token) ; ajouté au playbook storage-01.
STT-server (0.5.0 → 0.6.0) :
- agent.py : pont vers hermes-exec (jeton) + détection confirme/annule.
- contexts.py : contexte « agent » (court-circuite le LLM).
- app.py : flux dédié — handshake 2 temps par session (pending action) → sur « confirme »,
appelle hermes-exec ; « annule » annule. /v1/contexts masque « agent » si désactivé.
- config : STT_ACTIONS_ENABLED (opt-in, défaut false) + URL + jeton (secret k8s).
- deployment : env actions + secret stt-server-secrets/hermes-exec-token (optionnel).
Sécurité : opt-in désactivé par défaut ; jeton obligatoire (sinon contexte caché + exécuteur
refuse tout) ; --yolo atteint seulement jeton+confirmation en main ; audit storage-01.
Client : AUCUN changement (le contexte « agent » apparaît tout seul dans le sélecteur).
Validé en local : exécuteur (401 sans/mauvais jeton, exec avec bon jeton via echo),
handshake serveur (TestClient : confirme→exécute, annule→annule, agent masqué si OFF).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Asa n'est plus bloqué sur le seul contexte « doc cluster grounding-strict ». Le client
choisit un contexte par requête ; le serveur change le system prompt ET injecte les
données live du domaine, puis renvoie le contexte assemblé pour le visualiseur du HUD.
- contexts.py : profils funk / ghostfolio / grafana / alerting / cluster (system prompt
+ sources) + assemble() (prompt final + structure de visualisation).
- sources.py : fetchers live best-effort (Ghostfolio auth+details, Alertmanager alerts
hors Watchdog, Prometheus cluster/metrics), env-config, dégradation propre.
- brain.py : ask() reçoit le system prompt déjà assemblé (assemblage remonté).
- app.py : /v1/ask accepte `context`, renvoie context_id + le contexte assemblé ;
nouveau GET /v1/contexts ; RAG doc conditionné au profil.
- config.py : URLs sources + STT_GHOSTFOLIO_TOKEN + STT_DEFAULT_CONTEXT.
- deployment : env in-cluster (Prometheus/Alertmanager monitoring, Ghostfolio ai),
jeton via secret optionnel stt-server-secrets/ghostfolio-token.
- bump 0.3.1 → 0.4.0.
Validé en local : assemblage (blocs+RAG+mémoire), parsing des sources (mock),
endpoints /v1/contexts et /v1/ask (LLM mocké) — context_id, visualiseur, fallback
contexte inconnu → funk.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(stt): mémoire long-terme sémantique via Qdrant (5b)
Serveur : longterm.py — collection Qdrant stt-memory (embeddings Qwen3 gpu-01, dim auto,
Cosine), recall top-k injecté au prompt, remember des tours user. Tout dégrade proprement
si Qdrant/embeddings injoignables (la mémoire court-terme tient). Env STT_MEMORY_LONGTERM,
STT_QDRANT_URL, STT_EMBED_URL, STT_MEMORY_TOPK.
Testé en process : dégradation OK (Qdrant down → mem=0, pas de crash, court-terme tient).
Qdrant réparé le 17/06 (5c). Recherche sémantique réelle à valider sur cluster.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT
* feat(stt): endpoint /v1/memory/health + upsert Qdrant synchrone
- /v1/memory/health sonde activement embeddings + Qdrant + collection et
expose les erreurs (recall/remember dégradent en silence → indébogables).
Permet de diagnostiquer la mémoire long-terme sans kubectl exec.
- remember() : upsert avec ?wait=true → le souvenir est immédiatement
cherchable (sans wait, Qdrant met l'écriture en file → un recall
cross-session immédiat pouvait le rater).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT
* docs(stt): 5b mémoire long-terme validée en prod + backlog nomic-embed-text
- Rappel cross-session confirmé (« Felix » retrouvé dans une nouvelle session),
points_count vérifié via /v1/memory/health.
- Note du fix upsert ?wait=true et de l'endpoint de diagnostic.
- Roadmap : 5d (nomic-embed-text dim 768) en backlog qualité ; états haut/bas
du doc mis à jour (déployé + validé sur cible).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT
* feat(stt): embeddings dédiés nomic-embed-text sur gpu-01 (:1238) + migration auto
Remplace Qwen3 (chat réutilisé, dim 4096, peu discriminant) par un modèle
d'embedding spécialisé nomic-embed-text (dim 768) pour la mémoire long-terme.
Ansible (rôle llama_server) :
- nouvelle instance optionnelle `llama-embed` (llama_embed_enabled) servant un
modèle d'embedding dédié sur :1238, GPU ; télécharge le GGUF si absent.
- activée sur gpu-01 (host_vars) : nomic-embed-text-v1.5 f16.
STT-server :
- STT_EMBED_URL → :1238, STT_EMBED_MODEL → nomic-embed-text (deployment + config).
- _ensure_collection détecte le changement de dimension (4096→768) et recrée
automatiquement la collection stt-memory (anciens vecteurs incomparables) —
pas de drop manuel.
Docs : llama_server README, rag.md, stt.md (5d ✅), CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT
---------
Co-authored-by: Claude <noreply@anthropic.com>