Module banking complet en lecture seule via Powens (ex-Budget Insight)
pour détecter automatiquement les paiements clients et arrêter les
relances dès qu'une facture est payée. Réservé plans Pro / Business,
kill switch global BANKING_ENABLED désactivé en prod tant que le KYC
Powens n'est pas validé (cf. .claude/deploy-memory.md).
Backend (apps/api)
- PowensClient bas niveau : init user, code temporaire 30s, build
webview URL, list/get/delete connections, accounts, transactions,
vérif HMAC SHA-256 timing-safe pour webhook.
- BankingService : ensurePowensUser (chiffrement token via Adonis
encryption / APP_KEY), createWebviewUrl avec state HMAC anti-CSRF
(TTL 10 min), handleCallback (upsert connection + accounts +
fire-and-forget mail + sync 90j + reconcile), disconnect (DELETE
Powens + soft-revoke en DB), setReconciliationMode.
- Réconciliation : match transactions ↔ factures sur montant exact
+ label normalisé (numero ou nom client, NFD strip + alphanum).
Confiance HIGH (label matche) vs LOW (montant seul). Mode auto +
HIGH → invoice.status=paid + bonus rubis + cancel relances +
enqueuePaymentThanks (client) + sendInvoiceAutoPaidNotification
(user). Mode manual ou LOW → match_status='suggested' (UI V2).
- Webhook /webhooks/powens : vérif HMAC, lookup org par
powens_user_id, dispatch CONNECTION_SYNCED / NEW_TRANSACTIONS /
USER_SYNC_ENDED → sync incrémental 7j + reconcile, CONNECTION_ERROR
/ SCA_REQUIRED → update state + last_error. Réponse 200 immédiate
puis processing fire-and-forget pour ne pas timeout côté Powens.
- 4 migrations : bank_connections, bank_accounts, bank_transactions
+ colonnes powens_user_id (chiffré APP_KEY) et reconciliation_mode
sur organizations.
- 2 templates React Email : BankConnectedEmail (post-connection,
récap comptes + lien settings) et InvoiceAutoPaidNotificationEmail
(notif user après match auto, lien direct facture + libellé
bancaire détecté). Toujours en branding Rubis (notif Rubis → user,
jamais marque blanche).
- 2 commandes ace : banking:reconcile (rejoue le reconcile sans
reconnecter la banque) et banking:simulate-payment (injecte une
bank_transaction synthétique qui matche une facture, pour test E2E
sans devoir attendre un vrai virement sandbox).
- Kill switch isBankingEnabled() : flag BANKING_ENABLED + check des
credentials Powens. Endpoint public GET /banking/status renvoie
{ enabled }, /banking/powens/init throw 503 banking_disabled si OFF.
- Fix handler exceptions : UNIQUE violation composite (org, X)
rapporte désormais la vraie colonne en faute (numero/slug/…) avec
message lisible « Le numéro de facture "F2026-0013" existe déjà »,
au lieu d'un message ambigu sur organization_id.
Frontend (apps/web)
- /parametres : nouvelle SettingsSection "Banque" gated par kill
switch + plan Pro/Business. Si Free → upsell card avec CTA vers
/parametres/abonnement. Si Pro/Business sans banque → CTA "Connecter
une banque". Si banque connectée → carte avec accounts (IBAN
masqué FR76 **** **** **** 1234), solde, last sync, bouton
Déconnecter. Toggle Manuel/Auto pour reconciliation_mode.
- /parametres/banque/success : nouvelle route dédiée post-callback
avec badge ✓ animé + halo glow rubis, récap des comptes
synchronisés, 2 CTAs ("Voir mes paramètres" / "Retour dashboard"),
note sécurité "lecture seule, aucun déplacement de fonds".
- Hooks : useBankingStatus, useBankConnections (avec opt-out via
{ enabled }), useInitBanking, useDisconnectBank, useBankingSettings,
useUpdateBankingSettings.
Infrastructure (k3s)
- ConfigMap rubis-api-config : BANKING_ENABLED='false' par défaut,
BANKING_PROVIDER='powens', POWENS_DOMAIN='rubis',
POWENS_API_BASE_URL='https://rubis.biapi.pro/2.0/',
POWENS_REDIRECT_URI='https://app.rubis.pro/api/v1/banking/powens/callback'.
- Secret rubis-app-secrets : 3 nouvelles clés POWENS_CLIENT_ID,
POWENS_CLIENT_SECRET, POWENS_WEBHOOK_SECRET (valeurs sandbox posées
via kubectl patch, à remplacer post-KYC).
Sécurité
- Token Powens chiffré au repos via Adonis encryption (AES-256-GCM,
clé APP_KEY).
- State HMAC SHA-256 signé sur APP_KEY pour le flow webview
(anti-CSRF + porte l'org_id à travers le redirect).
- Webhook HMAC SHA-256 sur header BI-Signature avec
POWENS_WEBHOOK_SECRET, comparaison timing-safe.
- IBAN masqué côté API (transformer).
- Scope par org sur tous les endpoints (anti-IDOR).
- Rate limiting via le middleware Adonis existant.
- Idempotence DB : UNIQUE (org, powens_connection_id), (connection,
powens_account_id), (account, powens_id) → rejouer un event ou un
callback ne pose pas de problème.
Documentation
- /docs/tech/banking-setup.md : procédure complète setup dev avec
Cloudflare Quick Tunnel, compte sandbox Powens, whitelist URLs.
- /.claude/deploy-memory.md : section "Banking (Powens) — activation
prod" avec procédure en 6 étapes (KYC → secrets → ConfigMap →
flip flag → smoke test), snippet kubectl patch pour rotation
ciblée de secrets.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
314 lines
14 KiB
Markdown
314 lines
14 KiB
Markdown
# Deploy memory — rubis
|
||
|
||
Ce repo contient **deux déploiements distincts** sur la même infra K3s,
|
||
namespace `rubis` :
|
||
|
||
1. **Landing** statique (`rubis.pro`) — image nginx-alpine.
|
||
2. **App SaaS** (`app.rubis.pro`) — image AdonisJS + React (V1).
|
||
|
||
Chacun a sa propre image, son propre Dockerfile, son propre workflow CI.
|
||
|
||
---
|
||
|
||
## 1. Landing (`rubis.pro`)
|
||
|
||
### Infra
|
||
- Deployment : `rubis` · Container : `rubis` · NodePort : `30109`
|
||
- Image : `git.arthurbarre.fr/ordinarthur/rubis`
|
||
- Domaine : https://rubis.pro (sous-domaine **temporaire** —
|
||
domaine définitif `rubis.pro` pas encore acheté)
|
||
- Manifests : `k3s/{namespace,deployment,service}.yml`
|
||
- Route Traefik : `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis.yml.j2`
|
||
- Workflow CI : `.gitea/workflows/deploy.yml`
|
||
- Source : `landing/` (servi par nginx)
|
||
|
||
### Mise à jour
|
||
Push git, le CI build + rollout auto (filter sur `landing/**`, `Dockerfile`,
|
||
`k3s/{namespace,deployment,service}.yml`).
|
||
|
||
---
|
||
|
||
## 2. App SaaS (`app.rubis.pro`)
|
||
|
||
**Architecture en 2 services** : nginx en frontal (web) + API Node interne.
|
||
|
||
```
|
||
Traefik :443 → app.rubis.pro:30110 → rubis-web (nginx)
|
||
├─ / → SPA static (try_files)
|
||
└─ /api/* → rubis-api (ClusterIP :3333)
|
||
```
|
||
|
||
### rubis-web — frontend (NodePort 30110, exposé)
|
||
- Image : `git.arthurbarre.fr/ordinarthur/rubis-web`
|
||
- Container : `web` (nginx-alpine + SPA Vite dist)
|
||
- Sert /assets/* (cache 1y immutable), / (SPA fallback try_files)
|
||
- Reverse-proxy /api/* → rubis-api.rubis.svc.cluster.local:3333
|
||
- Manifest : `k3s/app/web.yml`
|
||
- Workflow CI : `.gitea/workflows/deploy-web.yml`
|
||
- Source : `apps/web`, `packages/shared` + `apps/web/nginx.conf`
|
||
|
||
### rubis-api — backend (ClusterIP, interne uniquement)
|
||
- Image : `git.arthurbarre.fr/ordinarthur/rubis-api`
|
||
- Container : `api` (Node 22 + AdonisJS V7)
|
||
- Workers BullMQ dans le même process (cf. `start/queue.ts`)
|
||
- Init container `migrate` : `node ace.js migration:run --force` depuis
|
||
`/app/apps/api/build` (idempotent)
|
||
- Probes K3s sur `/api/v1/health`
|
||
- Manifest : `k3s/app/api.yml` (Deployment + Service ClusterIP + ConfigMap)
|
||
- Workflow CI : `.gitea/workflows/deploy-api.yml`
|
||
- Source : `apps/api`, `packages/shared`
|
||
|
||
### rubis-redis — backend BullMQ + cache (ClusterIP)
|
||
- Image : `redis:7.4-alpine`
|
||
- PVC 1Gi local-path, AOF on, maxmemory 256mb allkeys-lru
|
||
- Manifest : `k3s/app/redis.yml`
|
||
- Re-déployé par le workflow API (path filter inclut `redis.yml`)
|
||
|
||
### Infra commune
|
||
- Domaine : https://app.rubis.pro (DNS A 5413305619)
|
||
- Route Traefik : `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis-app.yml.j2`
|
||
|
||
### Dépendances externes (déjà déployées)
|
||
- **Postgres** : 10.10.10.3:5432, base `rubis_prod`, user `rubis`
|
||
- **MinIO** : `minio.minio.svc.cluster.local:9000`, bucket
|
||
`rubis-prod-invoices`, creds = root MinIO
|
||
- **Redis** : interne au namespace `rubis`, pas de dépendance externe
|
||
|
||
### Secrets K3s (`rubis-app-secrets`)
|
||
Posés une fois manuellement via `kubectl create secret generic` (jamais
|
||
dans les manifests). Re-pose si rotation ou ajout :
|
||
```bash
|
||
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
-n rubis create secret generic rubis-app-secrets \
|
||
--from-literal=APP_KEY=... \
|
||
--from-literal=PG_PASSWORD=... \
|
||
--from-literal=S3_ACCESS_KEY=... \
|
||
--from-literal=S3_SECRET_KEY=... \
|
||
--from-literal=RESEND_API_KEY=... \
|
||
--from-literal=MISTRAL_API_KEY=... \
|
||
--from-literal=REDIS_PASSWORD="" \
|
||
--from-literal=GOOGLE_CLIENT_ID=... \
|
||
--from-literal=GOOGLE_CLIENT_SECRET=... \
|
||
--from-literal=MICROSOFT_CLIENT_ID=... \
|
||
--from-literal=MICROSOFT_CLIENT_SECRET=... \
|
||
--from-literal=STRIPE_SECRET_KEY=... \
|
||
--from-literal=STRIPE_WEBHOOK_SECRET=... \
|
||
--from-literal=SENTRY_DSN_API=... \
|
||
--from-literal=POWENS_CLIENT_ID=... \
|
||
--from-literal=POWENS_CLIENT_SECRET=... \
|
||
--from-literal=POWENS_WEBHOOK_SECRET=... \
|
||
--dry-run=client -o yaml | kubectl apply -f -
|
||
```
|
||
|
||
> ⚠️ Si tu rotates UN secret, repose la commande complète avec **toutes**
|
||
> les vars (sinon `apply` supprime celles omises). Garde la liste à jour
|
||
> dans un coffre-fort perso (1Password, Bitwarden…).
|
||
>
|
||
> **Alternative pour ajouter UNE/quelques clés sans toucher au reste** :
|
||
> ```bash
|
||
> kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
> -n rubis patch secret rubis-app-secrets --type=strategic --patch '{
|
||
> "stringData": { "NOUVELLE_VAR": "valeur" }
|
||
> }'
|
||
> ```
|
||
> `stringData` accepte du clair — Kubernetes encode en base64 automatiquement.
|
||
|
||
### Google SSO — setup Google Cloud Console
|
||
Si la clé OAuth est perdue ou qu'on doit la régénérer :
|
||
1. https://console.cloud.google.com/apis/credentials → projet courant
|
||
2. **Create Credentials** → **OAuth client ID** → type **Web application**
|
||
3. **Authorized JavaScript origins** :
|
||
- `https://app.rubis.pro`
|
||
- `http://localhost:5173` (dev SPA)
|
||
4. **Authorized redirect URIs** :
|
||
- `https://app.rubis.pro/api/v1/auth/google/callback`
|
||
- `http://localhost:3333/api/v1/auth/google/callback` (dev API)
|
||
5. Copier `Client ID` + `Client secret` → mettre dans `apps/api/.env` (dev)
|
||
et `rubis-app-secrets` (prod, snippet ci-dessus).
|
||
|
||
L'écran de consentement OAuth doit être configuré au moins en mode "Testing"
|
||
avec l'email du user courant ajouté en testeur. Pour la prod (n'importe qui
|
||
peut se connecter), il faut passer en "In production" (vérification Google
|
||
si scopes sensibles ; les nôtres `userinfo.email` + `userinfo.profile` sont
|
||
non-sensibles, validation auto).
|
||
|
||
### Microsoft SSO — setup Azure / Entra ID
|
||
1. https://portal.azure.com → **Microsoft Entra ID** → **App registrations**
|
||
→ **New registration**
|
||
2. **Name** : `Rubis Sur l'Ongle` ; **Supported account types** :
|
||
- "Accounts in any organizational directory and personal Microsoft accounts"
|
||
(tenant=common, recommandé)
|
||
- ou "Accounts in any organizational directory" (tenant=organizations,
|
||
M365 strict)
|
||
3. **Redirect URI** type **Web** :
|
||
- `https://app.rubis.pro/api/v1/auth/microsoft/callback`
|
||
4. Après création : ajouter en plus le redirect dev via **Authentication →
|
||
Add a platform → Web** :
|
||
- `http://localhost:3333/api/v1/auth/microsoft/callback`
|
||
5. **Certificates & secrets → New client secret** : créer, copier la
|
||
*Value* (visible une seule fois) → `MICROSOFT_CLIENT_SECRET`
|
||
6. Page **Overview** → copier *Application (client) ID* → `MICROSOFT_CLIENT_ID`
|
||
7. **API permissions** : `User.Read` (déjà délégué par défaut), pas besoin
|
||
d'admin consent pour les comptes individuels
|
||
8. Mettre les valeurs dans `apps/api/.env` (dev) et `rubis-app-secrets` (prod).
|
||
|
||
Le client secret expire (Azure force 6 ou 12 mois max) — penser à le
|
||
renouveler avant échéance ; sinon les nouvelles connexions échoueront
|
||
en silence après expiration.
|
||
|
||
### Banking (Powens) — activation prod
|
||
|
||
La feature banking est **déployée mais désactivée par défaut** en prod via
|
||
le flag `BANKING_ENABLED: 'false'` dans le ConfigMap `rubis-api-config`.
|
||
La section banque dans /parametres reste invisible, et `/api/v1/banking/*`
|
||
renvoie 503 `banking_disabled`. Procédure d'activation une fois le KYC
|
||
Powens prod validé :
|
||
|
||
1. **Powens** — créer un domaine prod chez Powens (KYC requis : Kbis,
|
||
contrat AISP/DSP2). Récupérer :
|
||
- Slug du domaine (ex : `rubis` → URL `rubis.biapi.pro`).
|
||
- `client_id` + `client_secret` prod (différents du sandbox).
|
||
- Webhook secret (à générer dans la console Powens prod).
|
||
|
||
2. **Whitelist côté console Powens prod** :
|
||
- Allowed redirect URIs :
|
||
`https://app.rubis.pro/api/v1/banking/powens/callback`
|
||
- Webhook URL :
|
||
`https://app.rubis.pro/api/v1/webhooks/powens`
|
||
|
||
3. **Mettre à jour les secrets K3s** (re-pose le snippet
|
||
`kubectl create secret generic` ci-dessus en remplissant les 3 vars
|
||
`POWENS_CLIENT_ID` / `POWENS_CLIENT_SECRET` / `POWENS_WEBHOOK_SECRET`
|
||
avec les valeurs prod).
|
||
|
||
4. **Mettre à jour le ConfigMap** dans `k3s/app/api.yml` si le slug
|
||
diffère de `rubis` (changer `POWENS_DOMAIN` et `POWENS_API_BASE_URL`).
|
||
Puis `BANKING_ENABLED: 'true'`. Commit + push → CI redéploie.
|
||
|
||
5. **Smoke test prod** : se connecter avec un compte Pro/Business, aller
|
||
sur `/parametres` → la section "Connecter votre banque" doit
|
||
apparaître. Cliquer "Connecter une banque" → la webview Powens prod
|
||
doit s'ouvrir. Tester avec un vrai compte bancaire perso d'abord
|
||
pour valider toute la chaîne (sync + reconcile + emails).
|
||
|
||
6. **Backup avant flip** : la 1re connexion crée un user Powens prod et
|
||
stocke `powens_user_id` + token chiffré (clé `APP_KEY`) sur l'org.
|
||
Si on doit un jour faire rotate `APP_KEY`, prévoir une migration des
|
||
tokens (déchiffrer avec ancienne clé → re-chiffrer avec nouvelle).
|
||
|
||
> ℹ️ Le webhook Powens (`POST /api/v1/webhooks/powens`) attend la
|
||
> signature HMAC SHA-256 dans le header `BI-Signature` (ou
|
||
> `X-Webhook-Signature`). Vérification automatique dans le controller
|
||
> avec `POWENS_WEBHOOK_SECRET`. Si Powens reçoit autre chose qu'un 200
|
||
> en réponse il retry agressivement → garder un œil sur les logs au
|
||
> démarrage.
|
||
|
||
### Mise à jour
|
||
Push git → un (ou les deux) workflow(s) CI se déclenchent selon les paths
|
||
modifiés. Build+rollout indépendants.
|
||
|
||
| Path modifié | Workflow déclenché |
|
||
|---|---|
|
||
| `apps/web/**`, `Dockerfile.web`, `k3s/app/web.yml` | deploy-web.yml |
|
||
| `apps/api/**`, `Dockerfile.api`, `k3s/app/api.yml`, `k3s/app/redis.yml` | deploy-api.yml |
|
||
| `packages/shared/**`, `pnpm-lock.yaml`, `tsconfig.base.json`, … | les deux |
|
||
|
||
### Particularités du Dockerfile.api
|
||
- `node ace build` plante avec `ERR_UNKNOWN_FILE_EXTENSION` car
|
||
`@poppinss/ts-exec` ne s'enregistre pas à temps avant l'import de
|
||
`bin/console.ts`. **Solution** : `pnpm exec tsx ace.js build
|
||
--ignore-ts-errors` (tsx = esbuild, gère .ts nativement).
|
||
- `--ignore-ts-errors` car `tests/bootstrap.ts` référence un type généré
|
||
tardivement (`.adonisjs/client/registry/schema.d.ts`). Le typecheck
|
||
strict est exécuté en CI séparée (`pnpm typecheck`).
|
||
|
||
### Particularités du Dockerfile.web
|
||
- Vite build appelé directement (`pnpm exec vite build`) au lieu du
|
||
script `tsc -b && vite build` qui plante sans cache `.tsbuildinfo` à
|
||
cause de @tanstack/router-core.
|
||
- nginx.conf inclut le upstream `rubis-api.rubis.svc.cluster.local:3333`.
|
||
Si on renomme le service API, c'est ici qu'il faut mettre à jour
|
||
(en plus du manifest).
|
||
|
||
---
|
||
|
||
## Voie manuelle (debug, hors CI)
|
||
|
||
```bash
|
||
TAG=$(git rev-parse --short HEAD)
|
||
# Landing (rubis.pro)
|
||
docker build --platform linux/amd64 -t git.arthurbarre.fr/ordinarthur/rubis:$TAG .
|
||
docker push git.arthurbarre.fr/ordinarthur/rubis:$TAG
|
||
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
-n rubis set image deploy/rubis rubis=git.arthurbarre.fr/ordinarthur/rubis:$TAG
|
||
|
||
# App — web (frontend)
|
||
docker build --platform linux/amd64 -f Dockerfile.web \
|
||
-t git.arthurbarre.fr/ordinarthur/rubis-web:$TAG .
|
||
docker push git.arthurbarre.fr/ordinarthur/rubis-web:$TAG
|
||
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
-n rubis set image deploy/rubis-web web=git.arthurbarre.fr/ordinarthur/rubis-web:$TAG
|
||
|
||
# App — api (backend)
|
||
docker build --platform linux/amd64 -f Dockerfile.api \
|
||
-t git.arthurbarre.fr/ordinarthur/rubis-api:$TAG .
|
||
docker push git.arthurbarre.fr/ordinarthur/rubis-api:$TAG
|
||
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
-n rubis set image deploy/rubis-api api=git.arthurbarre.fr/ordinarthur/rubis-api:$TAG \
|
||
&& kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
|
||
-n rubis patch deploy/rubis-api --type=json \
|
||
-p="[{\"op\":\"replace\",\"path\":\"/spec/template/spec/initContainers/0/image\",\"value\":\"git.arthurbarre.fr/ordinarthur/rubis-api:$TAG\"}]"
|
||
```
|
||
|
||
⚠️ Cross-compile ARM Mac → linux/amd64 plante sur `@swc/core` au build de
|
||
l'app. Préférer pousser et laisser le CI Gitea (linux/amd64 natif).
|
||
|
||
Si changement route/domaine : `cd ~/dev/perso/proxmox/ansible && ansible-playbook playbooks/gateway.yml`. Penser à `sudo systemctl restart traefik`
|
||
sur la gateway si un nouveau domaine ne récupère pas son cert ACME tout
|
||
seul (le hot-reload de la dynamic config ne déclenche pas toujours le
|
||
challenge LE).
|
||
|
||
---
|
||
|
||
## Domaine actuel — `rubis.pro` ✅ migré (2026-05-07)
|
||
|
||
- **Landing** : https://rubis.pro
|
||
- **App SaaS** : https://app.rubis.pro
|
||
- **Compat legacy** : `rubis.arthurbarre.fr` / `app.rubis.arthurbarre.fr` → 301 vers `rubis.pro` / `app.rubis.pro` (Traefik dynamic config dans `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis*.yml.j2`)
|
||
|
||
### Email rubis.pro
|
||
|
||
| Flux | Provider | Setup | Adresses |
|
||
|---|---|---|---|
|
||
| **Sortant** (transactionnel) | Resend | DKIM + SPF + DMARC sur `send.rubis.pro` (verified dashboard Resend) | `relances@rubis.pro` (via `MAIL_FROM_ADDRESS` secret K3s) |
|
||
| **Entrant** (humain) | OVH MX Plan (gratuit) | MX `@` → OVH (auto-configuré via Espace Client) | `contact@rubis.pro`, `dev@rubis.pro` |
|
||
|
||
⚠️ Resend Inbound (toggle "Enable Receiving") doit rester **OFF** : il veut le MX `@`, qui est pris par OVH MX Plan.
|
||
|
||
### Décommissionnement progressif `arthurbarre.fr` (à faire dans 30-90 jours)
|
||
|
||
Quand confiance acquise et plus aucune référence vivante :
|
||
1. Vire les blocs `rubis-legacy-redirect` / `rubis-app-legacy-redirect` (et middlewares) dans `rubis*.yml.j2`
|
||
2. `ansible-playbook playbooks/gateway.yml`
|
||
3. Supprime A records `rubis` (id 5413044152) + `app.rubis` (id 5413305619) dans la zone DNS OVH d'`arthurbarre.fr`
|
||
|
||
---
|
||
|
||
## Déjà fait — NE PAS refaire
|
||
- Dockerfile (landing) + Dockerfile.web + Dockerfile.api (app split)
|
||
- nginx.conf (apps/web/nginx.conf) avec upstream rubis-api
|
||
- Manifests `k3s/` (landing) + `k3s/app/{api,web,redis}.yml`
|
||
- Routes Traefik `rubis.yml.j2` + `rubis-app.yml.j2`
|
||
- DNS OVH : A records `rubis` (id 5413044152) + `app.rubis` (id 5413305619)
|
||
- Repo Gitea + secrets CI (`KUBECONFIG`, `REGISTRY_PASSWORD`)
|
||
- Namespace + secret registry K3s (`gitea-registry`)
|
||
- Postgres : base `rubis_prod` + user `rubis` (10.10.10.3)
|
||
- MinIO : bucket `rubis-prod-invoices`
|
||
- Secret K3s `rubis-app-secrets` (APP_KEY, DB pwd, MinIO, Resend, Mistral,
|
||
Google/Microsoft SSO, Stripe ; Powens à poser au moment de l'activation
|
||
KYC)
|
||
- ConfigMap `rubis-api-config` (env non-sensibles incl. banking flags
|
||
désactivés par défaut)
|
||
|
||
Les prochains `/deploy` font uniquement build + rollout via push git.
|