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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 0.6.1 — en préparation (non publiée)

- Projections du plan/PDF conditionnelles ou explicitement non concluantes ; aucun grade absent ni objectif de dégradation.
- Attentes des étapes MTA-STS/TLS conservatrices, y compris perte possible du HTTPS moderne.
- Attentes des extensions préservées après stockage ; table courante prioritaire pour les intégrés reconnus.
- Gains calculables conservés hors actions inconnues, avec exclusions explicites et cible complète toujours inconnue.
- Recette image officielle v0.5.0/v0.6.0 ↔ candidate à schéma 5, libellés FR/EN et compteur de priorités corrigés.
- Rapports de démonstration PDF FR/EN, génération reproductible sans réseau.
- Notes observées inchangées ; génération 4, schéma 5, API, événements et sécurité réseau conservés.
- Aucun tag, release, publication GHCR ou déploiement autorisé implicitement.

## 0.6.0 — 2026-10-24 (publié sur GitHub et GHCR)

**Mission Control** — comprendre les changements observés et préparer les corrections sans modifier les services analysés.
Expand Down
2 changes: 2 additions & 0 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Depuis la **v0.6.0**, une vue **Portefeuille** aide à repérer les domaines qui

Avec **« Et si… ? »**, vous pouvez sélectionner plusieurs corrections (y compris les étapes successives de DMARC) et voir ce qui *pourrait* s'améliorer. La simulation **ne modifie ni vos DNS ni vos serveurs** : seule une nouvelle analyse permettra de confirmer le résultat réel.

**Exemple fictif :** [télécharger le rapport PDF Mission Control](docs/examples/posture-cockpit-mission-control-demo.fr.pdf). Rapport généré par la candidate v0.6.1 sur `menuiserie-dupont.example`, sans analyse d’une organisation réelle. Certaines actions ont un effet non déterminable ; les projections restent à confirmer.

## Qu'est-ce que Posture Cockpit vérifie exactement ?

| Partie examinée | En termes simples |
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Since **v0.6.0**, a portfolio view helps you spot domains that need attention. E

The **“What if…?”** simulator lets you select several proposed corrections (including successive DMARC stages) and see *conditional* outcomes before doing anything. It does **not** change your DNS or servers, and a new scan is still needed to verify a real improvement.

**Fictional example:** [download a Mission Control PDF report](docs/examples/posture-cockpit-mission-control-demo.en.pdf). Generated by the v0.6.1 candidate for `menuiserie-dupont.example`, with no real organization scanned. Some actions have an unknown effect; projections still need confirmation.

## What does Posture Cockpit actually check?

| Area | In everyday language |
Expand Down
2 changes: 1 addition & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ name: posture-cockpit # fixes the volume name, posture-cockpit_posture-cockpit-

services:
posture-cockpit:
image: ${POSTURE_COCKPIT_IMAGE:-ghcr.io/vinzgithub/posture-cockpit:0.6.0}
image: ${POSTURE_COCKPIT_IMAGE:-ghcr.io/vinzgithub/posture-cockpit:0.6.1}
container_name: ${POSTURE_COCKPIT_CONTAINER:-posture-cockpit}
restart: unless-stopped
ports:
Expand Down
4 changes: 4 additions & 0 deletions docs/developer/EXTENSIONS.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ Puis, sur un vrai domaine : `pip install -e .` dans le dossier de l'extension, e
Puis, dans le `.env` de l'installation : `POSTURE_COCKPIT_IMAGE=<votre image>`, et `docker compose up -d`.

`posture-cockpit extensions` liste ce qui est chargé ; la page **Administration** aussi, avec un interrupteur par extension et l'erreur exacte d'une extension qui n'a pas pu être chargée.

## Contrat de remédiation v0.6.1

`Remedy.expect` indique le statut attendu après l’étape proposée, pas un résultat observé. Toute nouvelle écriture sérialise cette clé, y compris `"ok"` (défaut), `"warn"`, `"info"` ou `null` (effet inconnu). Pour les types intégrés reconnus par l’adaptateur, la table courante prévaut sur la valeur stockée ; la valeur stockée ne fait foi que pour les types inconnus, notamment les extensions. Une extension historique sans clé reste inconnue ; son ancienne intention OK ne peut pas être reconstruite. Aucun ancien JSON n’est réécrit. La v0.5.0 ignore cette clé et peut l’omettre lors de ses propres nouvelles écritures ; la remontée conserve alors l’interprétation historique prudente.
4 changes: 4 additions & 0 deletions docs/developer/EXTENSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ Then, on a real domain: `pip install -e .` in the extension folder, and `posture
Then, in the `.env` of the installation: `POSTURE_COCKPIT_IMAGE=<your image>`, and `docker compose up -d`.

`posture-cockpit extensions` lists what is loaded; so does the **Administration** page, with a switch per extension and the exact error of an extension that could not be loaded.

## v0.6.1 remediation contract

`Remedy.expect` describes the expected first-step status, never an observation. Every new write includes this key: `"ok"` (default), `"warn"`, `"info"`, or `null` (unknown effect). The current adapter table prevails for recognized built-in actions; stored expectations prevail only for unknown types, including extensions. A historical extension without the key remains unknown: its old OK intent cannot be recovered. Old JSON is never rewritten. v0.5.0 ignores the key and may omit it on its own new writes; upgrading again conservatively treats those payloads as historical.
67 changes: 67 additions & 0 deletions docs/developer/RELEASE_V061.fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Posture Cockpit v0.6.1 — dossier de candidate

**État : candidate développée et recette validée, non publiée — 24 octobre 2026.** Aucun tag, release, publication GHCR ni déploiement réalisé. Le mainteneur a autorisé le développement après validation technique du cahier des charges révision 3 par Claude. Une autorisation de publication reste distincte.

## Périmètre livré

| Lot | PR | Contenu |
|---|---|---|
| 1 | #33 | Cibles du plan et du PDF prudentes, FR/EN, T24 inchangé |
| 2 | #34 | Étapes MTA-STS/TLS : avertissements résiduels et perte du HTTPS moderne |
| 3 | #35 | `expect` explicite ; table courante prioritaire pour types intégrés reconnus, F05 bis |
| 4 | #36 | Gains hors effets inconnus, exclusions visibles ; cible complète et simulateur stricts |
| 5 | #37 | Images officielles v0.5.0/v0.6.0, recette 5→5 existante enrichie ; mode exécutable préservé |
| 6 | #38 | Gain exact, événements vides, compteur de priorités |
| 7 | #39 | Rapports fictifs FR/EN, guides, preuves et marqueurs de version 0.6.1 |

Les PR sont empilées : chacune cible la branche du lot précédent. Elles doivent être intégrées dans cet ordre sans réécrire l'historique. Les commits distants sont attribués au compte `vinzgithub`, sans co-auteur. Aucun merge implicite n'a été effectué pendant la préparation de ce dossier.

## Invariants et décisions

- T01 identique à v0.5.0 ; `GENERATION=4` ; SQLite schéma 5 ; aucun nouveau contrôle ni fonctionnalité.
- Aucun diff des contrôles, du schéma, de l'API, de l'ordonnanceur, des règles d'événements, des droits ou des protections réseau.
- La table courante interprète en mémoire les actions intégrées reconnues, même si l'ancienne attente stockée vaut WARN/null. Les extensions inconnues gardent leur valeur explicite ; l'absence historique reste inconnue. Aucun JSON ancien n'est réécrit.
- A2 : les actions inconnues ne contaminent plus le cumul calculable ; elles restent dans la cible complète et la sélection explicite du simulateur. Elles sont nommées et les estimations partielles qualifiées dans CLI/web/PDF.
- SQL de comptage et injection du registre d'extensions dans le PDF reportés comme prévu. Le registre PDF intégré peut produire le repli FAIL pour des dépendants tiers inconnus ; aucune parité globale tiers n'est revendiquée.
- F12 : test additionnel du battement récent puis expiré à horloge explicite, sans écriture pendant les lectures et sans changement de l'ordonnanceur. Aucun contrôle d'exploitation de production n'est revendiqué.

## Preuves locales obtenues

- Suite complète finale locale : **1 781 réussis, 2 ignorés** en 119,99 s. Les deux cas ignorés sont explicites : machine disposant d’IPv6 (cas destiné à une machine sans IPv6) et processus root (test de refus d’écriture non applicable à root).
- Chromium avec transport de bouclage local : **29 tests réussis**, sans service ni domaine réel.
- Ruff et mypy : réussis ; tests de mise en page PDF et T01 réussis.
- Cinq tests F04 supplémentaires comparent les attentes SPF/DNSSEC/MX/MTA-STS/CSP avec les vrais contrôles sur réponses synthétiques, avant/après stockage historique et nouveau : réussis.
- Différentiel indépendant de celui de Claude : 20 000 cas, graine `6101`, cas tirés du corpus `tests/data/observation-v050-mc3.json`, statuts aléatoires (statut original conservé lorsque des signaux imposent un verdict). `assess` et `by_category` complets normalisés en JSON : empreintes identiques v0.5.0/candidate, `8e3c2d973bc0e0fd66d74c0a7d305b6b93e326e343b0eaf2e0d230bfeee70013`. Ce générateur ne prétend pas reproduire celui de Claude.
- Compatibilité au niveau du code : trois scans synthétiques avec extension `expect` OK/WARN/null, lecture et vraies écritures de revérification avec le tag v0.5.0 (`88623bccf9054b3fbbb1c360151efd27f467460f`), remontée candidate ; notes conservées et `integrity_check=ok`.
- Deux rapports PDF fictifs normaux, FR/EN, 57/C, quatre pages chacun ; texte extrait contrôlé et huit pages rendues/inspectées. Recette : [exemples](../examples/README.md).

## Preuves CI finales obtenues

**SHA applicatif et tests validés : `d7d21cd4bc5a957d336815a47dc462a68792f9f0`.** Arbre Git identique à l’environnement local : `4f9d7d63c650e5605380980dbf1f4bc3ba7b55b4`. La mise à jour de preuves de ce dossier est postérieure et documentaire uniquement ; elle ne change aucun code applicatif, test, image ou dépendance.

- [CI complète finale — run 40119185870](https://github.com/vinzgithub/posture-cockpit/actions/runs/40119185870) : **11 jobs réussis**, aucune étape requise en échec. Python 3.11/3.12/3.13, Chromium, Docker, cinq chemins upgrade et retour arrière.
- [Dry run multiarchitecture final — run 40119186044](https://github.com/vinzgithub/posture-cockpit/actions/runs/40119186044) : **réussi**, builds AMD64/ARM64, recette AMD64 et démarrage ARM64 émulé. Le job de tests réutilisé est intentionnellement ignoré dans ce workflow sur PR ; la preuve complète est le run séparé ci-dessus. Le registre de test est local à Actions : aucune publication GHCR.
- Jobs F08 finaux : `upgrade (v0.5.0)` **120946797200**, `upgrade (v0.6.0)` **120946797102**. Logs contrôlés : l’ancienne image redémarre sur les données de la 0.6.1, la candidate revient en 0.6.1, trois scans sont relus, `integrity_check=ok`, aucune restauration.
- Suite exécutée à l’intérieur de l’image : job **120946797085**, réussi.
- Les cas supplémentaires de cibles inconnues et conditionnelles sont rendus en vrais PDF FR/EN par les tests ; première page contrôlée. Quatre rapports de recette additionnels ont été rendus pour inspection visuelle, en plus des deux exemples publics.

Le code cumulé de #33–#38 et les compléments de #39 sont validés. Les premiers essais Actions ont détecté la perte du mode exécutable du script lors du transfert Git ; ce défaut de transfert a été corrigé par commits additionnels sans réécriture d'historique. Les exécutions antérieures en échec ne valent pas preuve de recette.

- Python 3.11/3.12/3.13, Ruff/mypy et tests applicatifs.
- Chromium, Docker installation et suite dans l'image.
- Upgrade historiques v0.3.0/v0.4.0/v0.4.1 et rollback avec restauration conservés.
- **F08 complète bloquante sur AMD64** : officielle v0.5.0 et v0.6.0 → candidate → ancienne image → candidate, même volume, sauvegarde vérifiée hors volume mais jamais restaurée pour cette preuve ; analyse sous candidate avec `expect`, analyse sous ancienne image, trois scans conservés, clé et intégrité contrôlées.
- ARM64 : build et démarrage émulé « quick » existants, dans le dry run du workflow release. **Limite : pas de F08 complète ARM64**, conformément à la spécification.
- Aucun test manuel avec lecteur d'écran réel n'est revendiqué. Docker n'est pas disponible dans l'environnement local : seule Actions peut fournir ici la preuve au niveau image.

## Procédure de retour prévue

Sur copie isolée : sauvegarde vérifiée hors volume, ordonnanceur off, arrêt propre, image officielle v0.5.0 épinglée par digest sur le volume courant (schéma 5), contrôle de `info`, connexion/lecture/écriture et intégrité, arrêt puis remontée candidate. Les projections anciennes DMARC de v0.5.0 peuvent réapparaître, pas une divergence des notes observées. Ne pas confondre ce retour avec le retour schéma 5→4 qui exige une restauration et perd les données post-sauvegarde.

Digest officiel v0.5.0 résolu directement auprès de GHCR : `sha256:c8dacc1e82646562a3a386ba850a8dca785df5ceee98bf9910ba74eef67a5651`. Image v0.6.0 : digest conservé de la publication vérifiée `sha256:70da24128abef742dc4649235d662a37596ed786a322d840c85aa02afc2fe633`. La CI vérifie aussi la version embarquée.

## Publication séparée

Les marqueurs sources (`pyproject.toml`, `__init__.py`, valeur par défaut Compose) sont préparés en 0.6.1 pour une candidate cohérente. Les commandes publiques des README et guides restent épinglées au tag publié v0.6.0. Pour tester la candidate, fournir une image locale/CI via `POSTURE_COCKPIT_IMAGE` ; ne pas prétendre que `ghcr.io/vinzgithub/posture-cockpit:0.6.1` existe déjà.

La recette est acceptée techniquement au SHA ci-dessus ; les sept PR restent ouvertes pour la décision d’intégration du mainteneur. Présenter le SHA, les PR et les preuves avant fusion et publication. Tag, GitHub Release, publication GHCR et déploiement nécessitent son autorisation explicite. La CI sur PR n'utilise qu'un registre de test local pour son dry run de publication.
16 changes: 16 additions & 0 deletions docs/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Mission Control — fictional report examples

- [Français — PDF](posture-cockpit-mission-control-demo.fr.pdf)
- [English — PDF](posture-cockpit-mission-control-demo.en.pdf)

These reports use only the bundled `menuiserie-dupont.example` fixture. No real domain is scanned. Generated by the **v0.6.1 candidate**, not a published release. Observed result: **57/C**; corrections and projections remain hypothetical until a new observation confirms them.

Reproduce after installing the project and its PDF libraries:

```bash
python3 scripts/demo-report.py
```

The script freezes the observation date at `2026-10-24T12:00:00Z`, uses `FixtureNetwork`, saves to a temporary synthetic SQLite database and calls the normal `build_pdf` renderer. The bundled fixture is `src/posture_cockpit/demo/menuiserie-dupont.example.json`. No scenario is forced to a marketing target. Font/PDF library versions can change the binary metadata or pagination: inspect rendered pages after regeneration.

Checked: 4 pages per language, visible fictional marker, no `None`, no certain target claim, summary and perimeter on page 1; all eight pages rendered and visually inspected. No real organizational data, credentials or production backup are included.
Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions docs/user/INSTALLATION.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,7 @@ docker compose exec posture-cockpit posture-cockpit reset-password <identifiant>
docker compose exec posture-cockpit posture-cockpit disable-totp <identifiant> # téléphone et codes de secours perdus
docker compose exec posture-cockpit posture-cockpit make-admin <identifiant>
```

### Retour entre versions au schéma 5 (v0.5.0, v0.6.0 et candidate v0.6.1)

Avant tout changement d’image : sauvegarde vérifiée hors du volume, ordonnanceur désactivé, arrêt propre de l’instance. Le schéma 5 reste identique : le retour à une image officielle épinglée peut réutiliser le volume courant sans restaurer une ancienne sauvegarde. Les données créées après la mise à niveau doivent être conservées. La v0.5.0 retrouve ses anciennes projections, notamment DMARC, sans changer les notes observées. Tester ce retour sur copie isolée ; ne jamais démarrer simultanément deux instances sur le volume. Le retour au schéma 4 reste une autre procédure exigeant une restauration. Aucun tag/image v0.6.1 n’est annoncé comme publié par ce guide.
4 changes: 4 additions & 0 deletions docs/user/INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,7 @@ docker compose exec posture-cockpit posture-cockpit reset-password <username>
docker compose exec posture-cockpit posture-cockpit disable-totp <username> # lost phone and recovery codes
docker compose exec posture-cockpit posture-cockpit make-admin <username>
```

### Returning between schema-5 versions (v0.5.0, v0.6.0 and v0.6.1 candidate)

Before changing images: verify a backup outside the volume, disable scheduling and stop the instance cleanly. Schema 5 is unchanged: a pinned official older image can reuse the current volume without restoring an earlier backup. Data created after the upgrade must remain available. v0.5.0 shows its older projections, including DMARC, while observed scores remain unchanged. Test on an isolated copy; never run two instances concurrently on that volume. Returning to schema 4 still requires a separate restore procedure. This guide does not claim a v0.6.1 tag/image has been published.
4 changes: 4 additions & 0 deletions docs/user/SCORING.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,7 @@ un an au maximum. `max-age=0` efface le signal ; l’absence ou un en-tête inva
l’efface pas. Une politique parente ne s’étend qu’avec includeSubDomains. `preload` ne
prouve pas une présence dans la liste des navigateurs. L’historique et l’attribution
n’ont aucun effet sur les statuts ni sur la note.

## Précision v0.6.1 : gains et cible du plan

Les gains des actions calculables sont estimés hors actions à effet non déterminable, maintenues dans leur état observé et nommées à côté des gains. La cible du plan complet reste inconnue si une priorité a un effet inconnu. Le simulateur ne retire jamais silencieusement une action sélectionnée. Une fourchette HTTPS suppose des mesures futures concluantes ; elle ne garantit pas la prochaine note. La notation des observations reste inchangée (génération 4).
4 changes: 4 additions & 0 deletions docs/user/SCORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,7 @@ with a verified certificate over modern TLS is retained. Its lifetime is capped
year. `max-age=0` removes it; an absent or invalid header does not. An ancestor policy
applies only with includeSubDomains. `preload` is an indication, not evidence of actual
browser list membership. Neither history nor attribution changes any verdict or score.

## v0.6.1 clarification: plan gains and target

Calculable gains exclude named actions with an unknown effect, which remain in their observed state. The complete plan target remains unknown if any priority has an unknown effect. The simulator never silently removes a selected action. HTTPS bounds require conclusive future measurements and do not guarantee the next score. Observed scoring remains unchanged (generation 4).
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "posture-cockpit"
version = "0.6.0"
version = "0.6.1"
description = "Self-hosted passive security check of a domain (mail, TLS, web, DNS) with a guided plan of fixes and a PDF report."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
Loading
Loading