Auditor Workbench + MSP Cockpit
Auditor Workbench (/auditor)
POST /api/v1/auditor/bundle avec body {period_start, period_end}.
Owner / admin / auditor uniquement. TOTP requis en production (purpose
auditor_bundle) ; pas de TOTP en dev.
Contenu du bundle (ZIP) :
manifest.json métadonnées du bundle + liste des packs + liste des clésverify.py vérificateur Ed25519 hors-ligne (basé sur pynacl)signing_keys.json clés publiques + raisons de rotation sur la périodecompliance_summary.md rapport d'audit en markdownevidence_packs/<id>.tar.gz chaque pack de la période (uniquement depuis des chemins de stockage en allowlist)Garde anti path-traversal : evidence_packs.storage_uri DOIT commencer par
l’un des chemins suivants :
/var/lib/monsys/ai-evidence-packs//var/lib/monsys/copilot-evidence-packs//var/lib/monsys/openai-evidence-packs/
Sinon le pack est exclu du bundle (une ligne archive_path:"" reste dans
manifest.json pour la traçabilité).
L’URL de téléchargement est one-shot, expiration 24 h. Le claim atomique est dans l’UPDATE :
UPDATE auditor_bundle_tokens SET downloaded_at = NOW(), downloaded_by = $3 WHERE token = $1 AND tenant_id = $2 AND downloaded_at IS NULL AND expires_at > NOW()RETURNING file_pathLe MVCC Postgres + le row-lock garantissent que deux requêtes concurrentes ne reçoivent pas toutes les deux le RETURNING.
Vérification hors-ligne
Chaque destinataire exécute python3 verify.py (pynacl) à l’intérieur du
bundle décompressé. Aucun réseau, aucun credential monsys nécessaire —
vérifie la signature de chaque pack contre le signing_keys.json embarqué.
Output :
pack 1234: OKpack 1235: BAD SIGNATURE=== 12 OK, 1 BAD ===Exit code 0 = tout OK, 1 = au moins un pack échoue.
MSP Cockpit (/msp/cockpit)
GET /api/v1/msp/overview. Strictement isolé : role = msp_operator OU
is_superadmin = true. Un user owner/admin classique reçoit 403 —
cet endpoint parcourt chaque tenant de la plateforme.
Par tenant :
- nom + plan
- Trust Score actuel + Δ7j
- nombre d’alertes critical+warning ouvertes
- nombre de lignes d’evidence compliance en échec
- dernière activité de login
Tri par défaut = urgency-composite :
urgency = open_alerts*5 + failing_controls + (-Δ7d if Δ7d < 0) + 0.5 * (60 - score) if score < 60Le tri par colonne est disponible depuis le dashboard (urgency / nom / score).
Nouveaux rôles
La migration 064 étend le CHECK sur users.role avec :
| Rôle | Accès |
|---|---|
auditor | read-only + génération/téléchargement de bundle (TOTP requis en production) |
msp_operator | cross-tenant via /api/v1/msp/overview |
Les rôles existants (analyst, admin, owner) restent inchangés.