Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions docs/conception.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ Compte système unique, hors rôles Atelier.
- arbitre correction et annulation ;
- priorise et ajoute une consigne ;
- modifie les informations descriptives d'un incident actif ;
- annule un incident non pris ou reprend le contrôle d'un incident en
attente ;
- annule un incident actif non pris ou en attente ;
- invalide une clôture avec motif ;
- suit des incidents ;
- accède seul au journal transverse (le Pilotage, comme le Dashboard et la
Expand Down Expand Up @@ -101,8 +100,10 @@ Atelier.

### Board

Le code local est comparé à un hash bcrypt. La session est limitée dans le
temps, révocable par version et ne donne accès qu'à la projection Board. Un
Le code local est comparé à un hash bcrypt. La durée de session est réglée
par l'administrateur (0 = sans expiration automatique) ; la session reste
révocable à tout moment par version et ne donne accès qu'à la projection
Board. Un
utilisateur Atelier déjà connecté peut aussi lire cette projection sans
obtenir de nouveau droit.

Expand Down Expand Up @@ -253,6 +254,11 @@ uniquement si l'incident n'est pas pris et n'a pas d'arbitrage. Cas
`PENDING` : uniquement `RESPONSABLE`, comme décision de supervision. Effet :
statut `CANCELED`, conservation intégrale dans l'historique.

**Archivage forcé d'une ligne.** Acteur : administrateur, après affichage de
l'impact. Effet : tous les incidents actifs de la ligne, y compris pris ou en
attente, passent `CANCELED` avec le motif `line_archived`, et leurs
arbitrages ouverts deviennent `SUPERSEDED`, dans la même transaction.

**`INVALIDATE_CLOSED`.** Acteur : `RESPONSABLE`. Condition : incident
`CLOSED`. Donnée requise : motif. Effet : statut `INVALIDATED`, sans
réouverture de l'incident.
Expand All @@ -273,6 +279,10 @@ la croix/Escape : aucun changement métier. Seul `ACTIVE` compte dans la
pastille rouge « À arbitrer ». Une consultation du dossier déclenchée en
dehors du bouton d'arbitrage ne marque jamais le cas lu.

**Retrait.** `WITHDRAW_CANCEL` : l'opérateur déclarant retire sa demande
tant qu'elle attend l'arbitrage. Le cas passe `WITHDRAWN`, l'incident reste
actif et le marqueur de demande est effacé.

**Décision.** `APPROVE_CANCEL` : cas `APPROVED`, incident `CANCELED`.
`REJECT_CANCEL` : cas `REJECTED`, incident reste actif, demande effacée.
Archivage/annulation globale rendant la demande obsolète : cas
Expand Down Expand Up @@ -342,7 +352,7 @@ Les états décrivent l'anomalie, indépendamment du statut de traitement.
| `CONSULTED` | consultation explicitement demandée, décision encore attendue |
| `APPROVED` | demande acceptée |
| `REJECTED` | demande refusée |
| `WITHDRAWN` | correction retirée par son demandeur |
| `WITHDRAWN` | demande (correction ou annulation) retirée par son demandeur |
| `SUPERSEDED` | demande rendue caduque par une autre opération |

Comportement de navigation : décider directement est le chemin principal ;
Expand Down
33 changes: 24 additions & 9 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,17 @@ au lieu de redéfinir localement des couleurs d'état.
### 5.2 Mouvement (P1, P2)

Le mouvement est l'attribut pré-attentif le plus puissant : il est réservé à
de rares cas et n'est jamais mis en boucle. Les transitions servent la
continuité — comprendre ce qui change — non la décoration. Le réglage
`prefers-reduced-motion` est respecté.
de rares cas et n'est jamais mis en boucle pour décorer. Les transitions
servent la continuité — comprendre ce qui change — non la décoration. Le
réglage `prefers-reduced-motion` est respecté : il ramène toute animation à
une seule itération quasi instantanée.

Exceptions assumées, parce qu'elles signalent une attente ou un état
dégradé : les indicateurs de chargement (spinner d'action ponctuelle,
squelette), l'indicateur « écrit… » de l'assistance, et le bandeau
« Hors ligne » du Board, qui pulse doucement tant que l'écran n'est plus
synchronisé — le seul cas où le Board risquerait d'afficher une situation
dépassée.

### 5.3 Densité (P2, P3)

Expand All @@ -344,11 +352,17 @@ esthétique. L'échelle de tokens (`--text-*`) en est le vocabulaire.
### 5.6 Temps (P7)

Le temps s'exprime en durée vécue (« depuis 3 h », « depuis 8 j »), la date
précise restant accessible au second plan. Le vieillissement module le
niveau d'attention (§5.1) de manière continue ; le seuil « 7 jours » est un
précise restant accessible au second plan. Le vieillissement doit moduler le
niveau d'attention (§5.1) de manière progressive ; le seuil « 7 jours » est un
repère, pas une alarme. Les durées sont alignées et lisibles d'un coup d'œil
pour permettre la comparaison sans calcul.

État actuel : la durée vécue est appliquée partout via une fonction unique
(`formatDuration`). La fonction de montée par ancienneté (`ageAttentionLevel`,
paliers à 1, 3 et 7 jours) existe mais n'est pas encore branchée sur les
cartes, dont le niveau dépend aujourd'hui du statut, de la priorité et de la
prise en charge (`incidentAttentionLevel`). C'est une évolution prévue.

---

## 6. Usage de la doctrine
Expand Down Expand Up @@ -391,8 +405,8 @@ Le chantier a été mené en cinq phases, des fondations transversales vers les
| Phase | Lot | Objet | État |
|-------|-----|-------|------|
| 1 — Fondations | F1 | Tokens de niveaux d'attention (`--attention-calm/-watch/-act/-critical`) | Fait |
| 1 — Fondations | F2 | Composant unique appliquant la grammaire d'attention | Fait |
| 1 — Fondations | F3 | Fonction unique « durée vécue + niveau d'attention dérivé de l'âge » | Fait |
| 1 — Fondations | F2 | Fonction unique `incidentAttentionLevel()` et classes CSS appliquant la grammaire d'attention (le composant `AttentionBadge`, jamais branché, a été retiré en juillet 2026) | Fait |
| 1 — Fondations | F3 | Fonction unique « durée vécue + niveau d'attention dérivé de l'âge » | Fait (niveau par l'âge non encore branché, voir §5.6) |
| 1 — Fondations | F4 | Skeleton unifié, spinner réservé aux actions ponctuelles | Fait |
| 2 — Urgence | U1 | Refonte des cartes urgentes du Dashboard selon F1 | Fait |
| 2 — Urgence | U2 | Alignement des cartes Board sur F1, lisibilité à distance vérifiée | Fait |
Expand All @@ -417,7 +431,8 @@ service backend, ramenant l'Historique à une seule question au sens de P3.
- un lot correspond à un commit testé et déployable ;
- chaque changement est justifié par un principe (référence explicite en
commit) ;
- le backend n'a pas été touché : la doctrine agit comme une couche
d'expérience au-dessus d'un domaine jugé sain ;
- le backend n'a été modifié que pour S2 (contrôle d'accès du Journal côté
service) : la doctrine agit comme une couche d'expérience au-dessus d'un
domaine jugé sain ;
- l'ordre des phases n'était pas négociable — les fondations avant les
écrans, pour ne pas refaire le travail deux fois.
50 changes: 30 additions & 20 deletions docs/production.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,11 @@ ne pas interrompre le script pendant la bascule.
```

Le script refuse de démarrer si une sauvegarde est en cours (verrou partagé
avec `backup.sh`), et refuse tout dump sans `.sha256` associé sauf ajout
explicite de `--allow-unverified` (journalise un avertissement audité). Il
avec `backup.sh` dans le même répertoire : si les dumps sont écrits avec
`--dir`, lancer la restauration avec la même valeur, par exemple
`BACKUP_DIR=/srv/backups/sentinel ./scripts/restore.sh …`), et refuse
tout dump sans `.sha256` associé sauf ajout explicite de
`--allow-unverified` (journalise un avertissement audité). Il
importe dans une base temporaire, contrôle le schéma, exige que le ledger
corresponde exactement aux migrations canoniques du checkout (noms, ordre et
checksums), puis arrête le backend et bascule les bases. En cas d'échec
Expand Down Expand Up @@ -546,7 +549,7 @@ vérificateur dans la trace d'intervention.
| `BOARD_ACCESS_CODE_HASH` | oui | hash bcrypt du code Board initial (entre quotes simples) |
| `ADMIN_USERNAME` | base vide | bootstrap Admin, non vide, max. 80 caractères, non numérique |
| `ADMIN_PASSWORD` | base vide | mot de passe temporaire du premier admin |
| `CADDY_DOMAIN` | topo A | domaine servi par Caddy (annexe uniquement) |
| `CADDY_DOMAIN` | oui | domaine servi par Caddy ; exigé à la lecture du Compose même en topologie B, où Caddy ne démarre pas |

Ne jamais mettre un secret dans une commande versionnée, un ticket public ou
une capture d'écran.
Expand Down Expand Up @@ -585,19 +588,22 @@ locale (`up -d --no-build --force-recreate backend`). La rotation de
`JWT_SECRET` invalide toutes les sessions : planifier l'opération et
prévenir les utilisateurs.

**Code Board.** Voie normale : Administration > Paramètres. Procédure de
secours :
**Code Board.** Voie normale et voie de secours : Administration >
Paramètres. Le nouveau hash est enregistré en base et la version de session
Board est incrémentée : toutes les sessions Board existantes sont révoquées
immédiatement. Dès qu'un code a été défini depuis l'interface, le hash en
base est prioritaire et `BOARD_ACCESS_CODE_HASH` (`.env`) est ignoré ; cette
variable ne sert qu'au code initial, tant qu'aucun code n'est enregistré en
base :

```bash
cd backend
BOARD_ACCESS_CODE='nouveau-code-temporaire' npm run hash:board
cd ..
```

Mettre le hash bcrypt dans `.env` entre quotes simples, recréer le backend,
vérifier une nouvelle connexion Board. Les sessions Board antérieures
peuvent rester valides jusqu'à expiration ; une rotation du `JWT_SECRET` les
invalide toutes.
Si l'accès Admin est lui-même perdu, le rétablir d'abord (paragraphe
suivant), puis définir le nouveau code Board depuis Paramètres.

**Mot de passe admin.** Voie normale : Administration > Sécurité. Si l'accès
est perdu :
Expand Down Expand Up @@ -999,10 +1005,15 @@ signataires.

## 16. État vérifié historique de l'instance publique

Dernière preuve de production consignée dans ce document : **31 juillet
2026**, sur le SHA `deecf6d57d3f0304e18fe9fd56847f5d9cd0d1a7` (tag
`v1.0.0-rc.8`). Cette section est une photographie historique RC8 : elle ne
prouve pas que le candidat RC9 en préparation est déjà déployé.
La release `v1.0.0-rc.9` (SHA `ed26a25e3c005cabb0da30a4553dfbbee03afe81`)
est publiée ; le 17 septembre 2026, `/api/health` de l'instance publique
renvoyait ce SHA. Cette observation datée, avec la CI, la publication et les
digests d'images, est consignée dans
[rc9-verification-2026-09-17.md](rc9-verification-2026-09-17.md).

La suite de cette section est la photographie historique précédente, sur RC8 :
**31 juillet 2026**, SHA `deecf6d57d3f0304e18fe9fd56847f5d9cd0d1a7` (tag
`v1.0.0-rc.8`).

- `/api/health` répondait `{"status":"ok","db":"ok","version":"deecf6d57d3f0304e18fe9fd56847f5d9cd0d1a7"}` ;
- le DNS A de `sentinel.akiksystems.fr` pointait vers l'adresse du VPS, les
Expand Down Expand Up @@ -1034,12 +1045,11 @@ sécurité RC9. La revue RC9 du 16 septembre 2026 est décrite dans
[technique.md](technique.md) §16 : politique sans exception active et quatre
audits npm à zéro vulnérabilité au moment de la revue.

Sans accès SSH nominatif au VPS, cette section ne prouve pas les fichiers
Compose actifs, les binds loopback ni les images/digests internes. Une
nouvelle preuve de production doit être créée après publication et
déploiement du candidat RC9 : SHA de `/api/health`, digests de release et
d'images, puis recette courte. Aucune affirmation d'alignement RC9 ne doit
être déduite de la preuve RC8 ci-dessus.
Une observation publique ne prouve ni les fichiers Compose actifs, ni les
binds loopback, ni les digests effectivement exécutés : ces points se
contrôlent sur le serveur (§5.2 et §9). Aucune affirmation RC9 ne doit être
déduite de la photographie RC8 ci-dessus : la vérification RC9 est le
document daté cité en tête de section.

### Historique des audits

Expand All @@ -1050,7 +1060,7 @@ rapports historiques — y compris un verdict du 17 juillet 2026 explicitement
invalidé après coup parce que le VPS n'était alors pas encore aligné sur le
candidat audité — restent conservés dans l'historique Git comme preuve d'un
processus itératif réel plutôt qu'effacés ou réécrits. Ils ne remplacent pas
la preuve à produire pour RC9.
la vérification RC9 citée en tête de section.

## 17. Publication GitHub — spécificités release

Expand Down
26 changes: 18 additions & 8 deletions docs/technique.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ jamais sur le texte du message.
### 6.5 Défenses HTTP

- CORS limité à `CLIENT_ORIGIN` ;
- garde anti-CSRF sur toute requête d'écriture : `Origin` (ou `Referer` à
défaut) doit valoir exactement `CLIENT_ORIGIN`, et un `Sec-Fetch-Site`
inter-sites est refusé (`middlewares/csrfProtection.ts`), en plus des
cookies `SameSite=Strict` ;
- headers de sécurité applicatifs ;
- corps JSON limité à 50 Ko ;
- limite globale par IP et limite renforcée sur les connexions ;
Expand Down Expand Up @@ -342,8 +346,11 @@ CLOSED -> INVALIDATED
Une prise en charge est une revendication ou un transfert explicite. Les
champs `is_taken`, `taken_by_user_id` et `taken_at` sont cohérents par
contrainte SQL. Une seule anomalie active peut exister pour un emplacement
machine donné. Voir [conception.md](conception.md) pour le détail complet des
transitions et invariants métier.
machine donné. Un incident `OPEN` pris ne passe à `CANCELED` que par
l'archivage forcé de sa ligne ; un incident `PENDING` peut aussi être annulé
par le responsable.
Voir [conception.md](conception.md) pour le détail complet des transitions et
invariants métier.

### 7.3 Arbitrage

Expand Down Expand Up @@ -766,9 +773,10 @@ job Containers inspecte les images applicatives réellement construites.
`scripts/backup.sh` produit un dump gzip atomique avec checksum et
rétention. `scripts/restore.sh` valide le dump dans une base temporaire avant
une bascule de noms, avec arrêt court du backend et tentative de rollback en
cas d'échec. Les deux scripts partagent un même verrou de fichier : sauvegarde
et restauration ne peuvent jamais s'exécuter en même temps. La restauration
refuse par défaut tout dump sans checksum SHA-256 associé, importe dans une
cas d'échec. Les deux scripts partagent un même verrou de fichier
(`$BACKUP_DIR/.sentinel-backup.lock`) : sauvegarde et restauration ne peuvent
pas s'exécuter en même temps sur un même répertoire de sauvegarde. La
restauration refuse par défaut tout dump sans checksum SHA-256 associé, importe dans une
base temporaire, valide la présence des tables du schéma et l'égalité exacte
du ledger `schema_migrations` avec les fichiers canoniques du checkout (noms,
ordre et checksums) avant d'échanger les noms de base. Un trap nettoie la
Expand Down Expand Up @@ -799,9 +807,11 @@ est dans [production.md](production.md).
et le verrou de migration tolèrent plusieurs workers, mais un déploiement
horizontal demanderait une stratégie explicite de sessions, santé et
orchestration ;
- le rate limiting (`backend/src/utils/inMemoryRateLimit.ts`) est un
compteur en mémoire de processus : il protège correctement une réplique
unique mais ne partage aucun état entre instances. Tout passage à
- le rate limiting (`backend/src/middlewares/loginRateLimit.ts`, utilisé
pour les limites globale, connexion, réinitialisation et support ;
`backend/src/utils/inMemoryRateLimit.ts` pour la réauthentification Admin)
repose sur des compteurs en mémoire de processus : il protège correctement
une réplique unique mais ne partage aucun état entre instances. Tout passage à
plusieurs répliques exige un stockage partagé (Redis ou équivalent) avant
déploiement ;
- les migrations sont forward-only ; le rollback de schéma passe par une
Expand Down
Loading