feat(finlab): console Claude Code finance in-cluster + toolkit d'analyse (#64)

* 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>
This commit is contained in:
ALI YESILKAYA 2026-06-29 23:26:09 +02:00 committed by GitHub
parent 35ad1deb64
commit 54f6f7c634
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 2115 additions and 2 deletions

68
k8s/apps/finlab/README.md Normal file
View file

@ -0,0 +1,68 @@
# FinLab — console Claude Code finance (in-cluster)
Une **vraie session Claude Code** (sur l'**abonnement** d'Alkatrazz, login persisté) servie
dans un **terminal web** (`ttyd`), spécialisée analyse de marché via la persona `CLAUDE.md` et
la boîte à outils **finlab** (Yahoo Finance, exposée en **MCP**). Esprit OpenAlice, mais c'est
Claude Code + le toolkit maison — **aucune facturation API**, **aucun broker**, analyse seule.
> ⚠️ La console expose un **shell web** → protégée par **basicAuth Traefik**. Analyse et aide à
> la décision uniquement : **aucun ordre réel** (seul le paper trading Alpaca fictif existe).
## Image
Pas d'image publique : la CI (`.github/workflows/build-finlab.yml`) build depuis
`tools/finlab/` (notre code) → `ghcr.io/alkatrazz24/funk-finlab`, tag `sha-<court>`, bump du
manifest sur `main`.
## Architecture du déploiement
| Composant | Détail |
|---|---|
| Deployment | 1 réplica, `Recreate` (volume RWO), PodSecurity *restricted* (uid 1000, `fsGroup` 1000) |
| PVC `finlab-data` | 5Gi NFS → `/home/finlab` : `~/.claude` (login abonnement) + workspace (code finlab seedé, `portfolio.yaml`/`watchlists.yaml`/`alerts.yaml` éditables, `.cache`) |
| Service `finlab` | `web:7681` (ttyd) |
| Middleware `finlab-auth` | basicAuth Traefik (secret `finlab-auth`, htpasswd) |
| IngressRoute | `finance.lab.local` → 7681 (via `finlab-auth`) |
Le workspace est **seedé depuis l'image au 1er boot** (`cp -rn`, no-clobber) puis vit sur le
PVC : tes edits (portefeuille, etc.) et ton login persistent ; une nouvelle image n'apporte que
les fichiers manquants.
## Pré-requis manuels (une fois)
### 1. Secret de pull GHCR (si absent du ns `ai`)
```bash
kubectl -n ai create secret docker-registry ghcr-pull \
--docker-server=ghcr.io \
--docker-username=Alkatrazz24 \
--docker-password=<GHCR_PAT_read_packages>
```
### 2. Secret basicAuth de la console (`finlab-auth`)
```bash
# htpasswd (paquet apache2-utils). Remplace <user>/<pass>.
htpasswd -nbB <user> '<pass>' > /tmp/finlab.htpasswd
kubectl -n ai create secret generic finlab-auth \
--from-file=users=/tmp/finlab.htpasswd
rm -f /tmp/finlab.htpasswd
```
### 3. Premier boot — login Claude Code (abonnement)
1. Ouvrir **http://finance.lab.local** (basicAuth → identifiants de l'étape 2).
2. Dans le terminal, lancer `claude`, puis `/login` → suivre l'OAuth (ouvrir l'URL sur ta
machine, autoriser, coller le code). Persisté dans `~/.claude` sur le PVC.
3. À l'invite, FinLab a déjà sa persona (`CLAUDE.md`) et les outils finlab (MCP `finlab`,
auto-activé). Essaie : *« résume mon portefeuille »*, *« plan de trade pour Micron »*.
> Le workspace `/home/finlab/workspace` contient `portfolio.yaml` etc. — éditables directement
> (par toi ou par Claude à ta demande). Pour une vue rapide hors console :
> `kubectl -n ai exec deploy/finlab -- python -m finlab.cli digest`.
## Accès depuis le terminal (sans navigateur)
```bash
kubectl -n ai exec -it deploy/finlab -- bash -lc 'cd "$WORKSPACE" && claude'
```

View file

@ -0,0 +1,72 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: finlab
namespace: ai
spec:
replicas: 1
# HOME + workspace sur un volume RWO (NFS) → pas de rolling à 2 pods sur le même volume.
strategy:
type: Recreate
selector:
matchLabels:
app: finlab
template:
metadata:
labels:
app: finlab
spec:
# Image privée sur GHCR → secret de pull créé manuellement dans le ns ai
# (cf. README : kubectl create secret docker-registry ghcr-pull ...).
imagePullSecrets:
- name: ghcr-pull
# Conformité PodSecurity « restricted » (namespace ai). fsGroup 1000 → le PVC NFS monté
# en /home/finlab (HOME : ~/.claude du login abonnement + workspace) est inscriptible.
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: finlab
# Tag bumpé par la CI (build-finlab.yml) sur main.
image: ghcr.io/alkatrazz24/funk-finlab:latest
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
ports:
- name: web
containerPort: 7681
env:
- name: TZ
value: "Europe/Paris"
# ttyd écoute en TCP : sonde TCP (pas de endpoint HTTP de santé dédié). Démarrage
# rapide, mais on laisse de la marge (seed du workspace au 1er boot).
readinessProbe:
tcpSocket:
port: 7681
initialDelaySeconds: 10
periodSeconds: 15
livenessProbe:
tcpSocket:
port: 7681
initialDelaySeconds: 30
periodSeconds: 30
# Node + Claude Code peuvent consommer ; nœuds 8 Go (OOM actif) → limite raisonnable.
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1536Mi
volumeMounts:
- name: home
mountPath: /home/finlab
volumes:
- name: home
persistentVolumeClaim:
claimName: finlab-data

View file

@ -0,0 +1,17 @@
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: finlab
namespace: ai
spec:
entryPoints:
- web
routes:
- match: Host(`finance.lab.local`)
kind: Rule
middlewares:
# basicAuth : un shell web ne doit pas être ouvert sans auth (cf. middleware.yaml).
- name: finlab-auth
services:
- name: finlab
port: 7681

View file

@ -0,0 +1,11 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: finlab-auth
namespace: ai
spec:
# La console expose un SHELL web (ttyd) → on la protège par basicAuth Traefik, même en LAN.
# Le secret `finlab-auth` (htpasswd) est créé MANUELLEMENT (hors git) — voir README.
basicAuth:
secret: finlab-auth
realm: FinLab

15
k8s/apps/finlab/pvc.yaml Normal file
View file

@ -0,0 +1,15 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: finlab-data
namespace: ai
spec:
# /home/finlab : login abonnement Claude Code (~/.claude), workspace finlab (code seedé +
# configs éditables portfolio/watchlists/alerts), cache disque (.cache), sessions/historique.
# Persiste l'onboarding (login) et les edits à travers redémarrages et rebuilds d'image.
accessModes:
- ReadWriteOnce
storageClassName: nfs
resources:
requests:
storage: 5Gi

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: finlab
namespace: ai
spec:
selector:
app: finlab
ports:
- name: web
port: 7681
targetPort: 7681