# psmw — nouvelle branche

Récapitulatif de la structure et des décisions prises, pour référence pendant le déploiement.

## Structure de fichiers

**Avant**
```
santepilote.com/psmw/                          ← retiré
vendor/9d83b1ea47f8_project/config.php         ← retiré (entièrement redondant avec _init.php)
```

**Après**
```
santepilote.com/_init.php                      ← inchangé, + C_PATH_VENDOR ajouté en haut
santepilote.com/psmw-router.php                ← nouveau — seul point d'entrée HTTP public
vendor/psmw/psmw-core.php                      ← nouveau (classe Psmw — point d'entrée)
vendor/psmw/psmw-db.php                        ← nouveau (classe PsmwDb)
vendor/psmw/psmw-crypto.php                    ← nouveau (classe PsmwCrypto)
vendor/psmw/psmw-session.php                   ← nouveau (classe PsmwSession)
vendor/psmw/psmw-cache.php                     ← nouveau (classe PsmwCache)
vendor/psmw/psmw-mail.php                      ← déplacé, type hint mis à jour (PsmwClient → Psmw)
vendor/psmw/psmw-security.php                  ← déplacé tel quel (zéro dépendance, voir avertissement ci-dessous)
vendor/psmw/psmw-function.php                  ← déplacé tel quel depuis {client}/psmw/
vendor/psmw/.cache/                            ← généré automatiquement (cache fichier, ne pas committer)
vendor/psmw/actions/psmw-*.php                 ← nouveau — logique métier par action
```

`vendor/` est hors du webroot public (`dirname($_SERVER['DOCUMENT_ROOT']) . '/vendor'`) — rien sous `vendor/psmw/` n'est atteignable directement par URL. `psmw-router.php`, à la racine du site client, est le seul fichier public ; il route vers `vendor/psmw/actions/` selon `?action=`.

## À ajouter dans `_init.php` (en haut, avant toute autre modification d'environnement)

```php
define("C_PATH_VENDOR", dirname($_SERVER['DOCUMENT_ROOT']) . '/vendor');
```

Tout le reste de `_init.php` (PSMW_API_KEY, PSMW_VERSION, etc.) reste inchangé.

## Nomenclature

| Ancien | Nouveau | Rôle |
|---|---|---|
| `psmw-agent.js` | `psmw-loader.js` | Capture des API natives + découverte du CDN disponible + injection de `psmw.js`. Se produit **une fois** par chargement de page. |
| — | `psmw-router.php` | Point d'entrée HTTP unique du site client. Dispatch **à chaque requête**, vers des dizaines d'actions possibles. |

Les deux noms sont volontairement différents : un *loader* charge une fois, un *router* dispatche en continu — ce ne sont pas le même genre de composant, malgar leur rôle structurellement similaire ("trouver où aller").

## Bootstrap — `Psmw::boot()` / `Psmw::bootPublic()`

```php
$psmw = Psmw::boot(PSMW_API_KEY);        // cas par défaut — cipher + auth requis
$psmw = Psmw::bootPublic(PSMW_API_KEY);  // login/signup/forgot — cipher requis, pas d'auth
```

Le déchiffrement E2E est toujours automatique dans les deux cas. Il n'y a plus de constante `NO_CIPHER` — un échec de déchiffrement retourne toujours un message générique, ce qui est suffisant puisque ce chemin n'est jamais empruntable par un utilisateur légitime.

## Routage — `?action=`

```
psmw.fetchE2E('/psmw-router.php?action=auth_login', { email, password })
```

L'action est visible dans l'URL (logs serveur, devtools) volontairement — voir la discussion sur le compromis débogage/opacité. Seul le contenu du payload est chiffré.

Liste des actions sans authentification requise, à tenir à jour dans `psmw-router.php` :
```php
const PSMW_PUBLIC_ACTIONS = ['auth_login', 'auth_signup', 'auth_forgot'];
```

## `$psmw->db` — résumé des règles

- Connexion paresseuse, jamais réutilisée entre requêtes (évite la pression sur les slots MySQL en haute concurrence).
- `load($sql, $params)` / `loadOne($sql, $params)` — lecture, SQL libre écrit par le développeur.
- `insert($table, $data)` — insère une ligne vide puis applique chaque champ comme un update (toutes les colonnes sont nullables par convention PSMW).
- `update($table, $data, $where)` — lit la ligne avant d'écrire, ne logge que les champs réellement changés.
- `delete($table, $where)` — soft-delete (`IND_DEL = 1`) si la colonne existe, vrai `DELETE` sinon.
- Chaque champ modifié est loggé automatiquement dans `LOG_ALL`. Passer `false` en dernier paramètre pour désactiver le logging sur un appel précis.
- `connection()` — échappatoire PDO brut pour les transactions multi-étapes ou requêtes batch.

**Aucune validation RBAC dans `PsmwDb`** — c'est intentionnel. Cette classe n'est accessible que depuis du code serveur écrit par le développeur du client (jamais depuis le navigateur), donc le SQL n'est jamais sous contrôle d'un attaquant. Si un jour une version navigateur de `psmw.db` voit le jour, elle devra passer par des requêtes nommées et le RBAC existant (`SECU_EFFECTIVE_RIGHTS_V`) — voir la discussion archivée à ce sujet avant de t'y attaquer.

## Autonomie réseau

`PsmwCache` a deux niveaux : mémoire (5 min, par worker) et fichier local chiffré sous `vendor/psmw/.cache/` (24h, survit aux redémarrages de worker et aux pannes réseau vers `api.genesia.ca`). En cas de panne, la dernière configuration connue est réutilisée même expirée plutôt que de faire tomber le site.

À noter pour une itération future : ce cache est actuellement clé par `session_id`, comme dans l'ancienne version — donc chaque session déclenche sa propre résolution même si les credentials DB/Stripe/Mail sont identiques pour tout le site. Les séparer (clé par domaine) réduirait encore les appels réseau.

## Méthodes restaurées sur `Psmw` (depuis l'ancien `PsmwClient`)

Après un premier passage trop rapide, un audit méthode par méthode de l'ancien `PsmwClient` a été fait avant de continuer. Restaurées parce qu'utilisées ailleurs dans le code déjà vu ou liées à une fonctionnalité confirmée : `setUser()`, `incrementCsrfAttempts()`, `setCaptcha()`, `rotate()`, `getStripe()`, `getMail()`, `getPsmwUrl()`, `invalidateCache()`, `getSessionId()`.

Non restaurées, faute de preuve d'usage dans les fichiers vus jusqu'ici — à confirmer si elles servent ailleurs : `resolve()` (résolution `ENC_*` legacy, probablement du code mort depuis la migration Vault), `fetchE2E()`/`_httpPost()` (appels S2S sortants depuis le PHP du client), `getSession()`, `getApiUrl()`, `getCdnUrl()`.

`encryptGz()` (compression avant chiffrement, utilisée par l'ancien `psmw-db_load.php` pour les jeux de données volumineux) n'a pas encore été réintégrée dans `PsmwCrypto` — décision en attente.

## ⚠️ Migration — `auto_prepend_file`

`psmw-security.php` est chargé via `.htaccess` (ou config vhost) avec un **chemin absolu codé en dur** :
```
php_value auto_prepend_file "/home/user/public_html/psmw/psmw-security.php"
```
Comme ce fichier déménage vers `vendor/psmw/psmw-security.php` (hors webroot), cette ligne doit être mise à jour **en même temps que** le déplacement des fichiers — sinon `auto_prepend_file` pointe vers un fichier inexistant, et comme il s'exécute avant absolument tout, ça peut faire planter chaque page du site, pas seulement les endpoints PSMW. À vérifier site par site au moment du déploiement.

`psmw-security.php` lui-même n'a pas été modifié — il ne dépend d'aucune des nouvelles classes (zéro dépendance par conception), donc rien à porter, seulement son chemin de référencement à corriger.

## Décision de nommage

`PsmwDb` devient le fichier `psmw-db.php`, dans le même répertoire que les anciens `psmw-db_load.php`/`psmw-db_add.php`/etc. (endpoints Webix, non touchés). Risque de confusion accepté puisque les deux vivent dans des répertoires différents (`vendor/psmw/` vs `{client}/psmw/`).

## Ce qui n'est pas encore fait

- Les vraies actions (`auth_login`, `auth_signup`, `profil`, `abonnement_*`, etc.) doivent être portées une par une depuis l'ancien code — `auth_login.php` fourni ici est un squelette illustrant le pattern `bootPublic()`, pas un port complet (la requête RBAC réelle doit être copiée depuis l'ancien fichier, pas reconstruite).
- `psmw-loader.js` (ex-`psmw-agent.js`) n'a pas été modifié — son contenu réel n'a pas été fourni dans cette conversation, seul son rôle a été décrit. Le renommage du fichier reste à faire.
- Le mécanisme de déploiement/synchronisation automatique de `vendor/psmw/` vers les sites clients reste manuel pour l'instant, comme convenu.
- Aucun linter PHP n'a pu être exécuté dans cet environnement — une relecture humaine avant déploiement est nécessaire.
