diff --git a/.gitignore b/.gitignore index 05f25c6..af21de8 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,8 @@ codeql-db/ codeql-results.sarif docs/reviews/ grob-codex-proxy.toml + +# Generated demo recordings +docs/demos/showcase-rssi/demo.cast +docs/demos/showcase-rssi/demo.gif +docs/demos/showcase-rssi/demo.webm diff --git a/deploy/demo/Makefile b/deploy/demo/Makefile index aea12d4..38019d1 100644 --- a/deploy/demo/Makefile +++ b/deploy/demo/Makefile @@ -7,6 +7,8 @@ # make demo-logs Suit les logs du seed (trafic) et du watcher (coupure) # make demo-reset Remet la démo à l'état s0 (idempotent) # make demo-stop Arrête le pod sans supprimer les images +# make demo-cast Enregistre la doublure asciinema de la démo +# make demo-gif Convertit la doublure asciinema en GIF partageable # make gen Régénère demo.kube.yaml depuis les fichiers source # make grob-img (Re)construit l'image grob patchée (caviardages C1/C2) # make tools (Re)construit l'image localhost/grob-demo-tools @@ -25,8 +27,9 @@ POD := grob-demo # Racine du dépôt (contexte de build pour l'image grob : le Containerfile fait # COPY . . sur tout l'arbre source). Le Makefile vit dans deploy/demo. REPO_ROOT := $(abspath $(CURDIR)/../..) +DEMO_DOCS := $(REPO_ROOT)/docs/demos/showcase-rssi -.PHONY: demo grob-img tools gen demo-logs demo-stop demo-reset urls +.PHONY: demo grob-img tools gen demo-logs demo-stop demo-reset demo-cast demo-gif urls # Image grob PATCHÉE, buildée depuis l'arbre source local : elle embarque le # correctif feat(audit) qui classe les caviardages en C1/C2 (DLP_WARN), encore @@ -53,6 +56,15 @@ demo: grob-img tools gen ## Construit, régénère, puis déploie la démo compl demo-logs: ## Suit le trafic (seed) et la coupure en direct (watcher). $(PODMAN) logs -f $(POD)-seed $(POD)-watcher +demo-cast: ## Enregistre la doublure asciinema (stack déjà lancée). + @command -v asciinema >/dev/null 2>&1 || { echo "asciinema est requis. Installez asciinema puis relancez make demo-cast."; exit 1; } + cd $(REPO_ROOT) && asciinema rec -y -c "sh docs/demos/showcase-rssi/replay.sh" docs/demos/showcase-rssi/demo.cast + +demo-gif: ## Convertit demo.cast en GIF partageable (nécessite agg). + @test -f $(DEMO_DOCS)/demo.cast || { echo "demo.cast introuvable. Lancez d'abord : make demo-cast"; exit 1; } + @command -v agg >/dev/null 2>&1 || { echo "agg est requis pour générer le GIF. Installez https://github.com/asciinema/agg puis relancez make demo-gif."; exit 1; } + agg $(DEMO_DOCS)/demo.cast $(DEMO_DOCS)/demo.gif + demo-stop: ## Arrête le pod (conserve images et manifeste). $(PODMAN) kube down $(KUBE) diff --git a/deploy/demo/demo.kube.yaml b/deploy/demo/demo.kube.yaml index a54cc73..2c298fa 100644 --- a/deploy/demo/demo.kube.yaml +++ b/deploy/demo/demo.kube.yaml @@ -1002,19 +1002,19 @@ kind: ConfigMap metadata: name: cm-web-gov data: - governance.html: "\n\n\n \n\n\n \n \n Grob \u2014 Console de gouvernance des identit\xE9s IA\n\ - \ \n \n \n \n Grob \u2014 AI identity governance console\n \n \n \n \n \n\n\n
\n\ - \
\n \n

Une\ - \ identit\xE9, une cl\xE9, des preuves

\n
\n

Humains et agents\ - \ de service ont chacun leur cl\xE9. Conso, incidents\ - \ et classe de donn\xE9es capt\xE9e sont attribu\xE9s individuellement,\ - \ avec preuve sign\xE9e. Un agent qui d\xE9rive voit sa cl\xE9\ - \ coup\xE9e automatiquement.

\n
Connexion\u2026
\n\ - \
\n\n
\n Actif / trafic propre\n \ - \ \ - \ Donn\xE9e caviard\xE9e\n Menace bloqu\xE9e \u2192 seuil \u2192 coupure\n\ - \ Seuil de coupure : \u2014 violations\n
\n\n
\n
\n
\n \n \n \ - \ \n \n \n \n\ - \ \n \n \n \n \n Statut\n \n\ - \ \n \n \n \ - \ \n \n\ - \
Identit\xE9Requ\xEAtesConso \u20ACViolationsDonn\xE9\ - es capt\xE9esType d'incidentD\xE9\ - cisionPreuveLogs
En attente des donn\xE9es de gouvernance\u2026
\n
\n
\n

\n \ - \ Conso indicative (backend simul\xE9, aucun co\xFBt r\xE9el). Une violation\ - \ = requ\xEAte bloqu\xE9e par la protection (injection de prompt, exfiltration)\ - \ ; les secrets et donn\xE9es personnelles sont caviard\xE9s (la requ\xEA\ - te aboutit, expurg\xE9e). Le type d'incident reprend la classification\ - \ d'audit la plus grave vue pour l'identit\xE9 : \n\n\n

\n
\n \ + \ \n

One identity, one\ + \ key, signed proof

\n
\n

Humans and service agents\ + \ each get their own key. Spend, incidents, data classes and signed audit proof\ + \ stay tied to the exact identity. A drifting agent is cut off automatically.

\n\ + \
Connecting...
\n
\n\n
\n Active\ + \ / clean traffic\n Redacted data\n Blocked threat ->\ + \ threshold -> cutoff\n Cutoff threshold: - violations\n
\n\ + \n
\n
\n
\n \ + \ \n \n \n \n\ + \ \n \n \n \ + \ \n \n \ + \ \n \n \ + \ \n \n \n \n \n \n \n
IdentityRequestsSpend \u20ACViolationsData classesIncident typeDecisionProofStatusLogs
Waiting for governance data...
\n\ + \
\n
\n

\n Simulated backend:\ + \ indicative spend only. Violations are blocked injection or\ + \ exfiltration attempts; secrets and PII are redacted before the request continues.\ + \ Highest signed audit class per identity:\n Propre (Nc, aucune\ - \ action DLP) \xB7 Caviard\xE9\ - (C1) \xB7 Restreint\ - \ (C2, PII ou exfiltration bloqu\xE9e) \xB7 Attaque (C3, injection\ - \ bloqu\xE9e). La classe de donn\xE9e capt\xE9e et la preuve\ - \ sign\xE9e sont d\xE9riv\xE9es du journal d'audit sign\xE9. La coupure\ - \ est appliqu\xE9e en direct : la cl\xE9 r\xE9voqu\xE9e est refus\xE9e d\xE8s\ - \ la requ\xEAte suivante (401). \xAB Logs \xBB ouvre le tableau\ - \ de bord d'investigation par agent (Grafana), pr\xE9-filtr\xE9 sur l'identit\xE9\ - \ \u2014 chaque incident au niveau requ\xEAte, avec son type, sa preuve sign\xE9\ - e, v\xE9rifiable.\n

\n
\n\n \n\n\n" + \ dot.classList.add(\"live\");\n statusText.textContent = \"Connected\ + \ - monitoring active\";\n render(data);\n })\n .catch(function\ + \ () {\n dot.classList.remove(\"live\");\n statusText.textContent\ + \ = \"Waiting for data...\";\n });\n }\n poll();\n setInterval(poll,\ + \ 2000);\n \n\n\n" --- apiVersion: v1 kind: ConfigMap diff --git a/deploy/demo/web/governance.html b/deploy/demo/web/governance.html index 3c87e97..3980756 100644 --- a/deploy/demo/web/governance.html +++ b/deploy/demo/web/governance.html @@ -1,10 +1,10 @@ - + - Grob — Console de gouvernance des identités IA - + Grob — AI identity governance console + @@ -16,10 +16,10 @@ --line: #334155; --text: #F8FAFC; --muted: #94A3B8; - --ok: #22C55E; /* actif / propre */ - --warn: #F59E0B; /* alerte / caviardé */ - --block: #EF4444; /* révoqué / violations */ - --info: #38BDF8; /* liens / drill-down */ + --ok: #22C55E; /* active / clean */ + --warn: #F59E0B; /* warning / redacted */ + --block: #EF4444; /* revoked / violations */ + --info: #38BDF8; /* links / drill-down */ } * { box-sizing: border-box; } html, body { height: 100%; } @@ -60,13 +60,13 @@ main { padding: 22px 24px 16px; max-width: 1360px; margin: 0 auto; } - /* ---- Tableau par identité ---- */ + /* ---- Identity table ---- */ .card { background: linear-gradient(180deg, var(--surface), var(--surface-2)); border: 1px solid var(--line); border-radius: 18px; overflow: hidden; } - /* overflow-x: filet de sécurité sous ~1100px ; au-dessus la table tient - sans barre horizontale grâce au padding resserré et aux en-têtes courts. */ + /* overflow-x is the safety net below ~1100px; above that, compact padding + and short headers keep the table visible without horizontal scrolling. */ .table-wrap { overflow-x: auto; } table { width: 100%; border-collapse: collapse; min-width: 940px; table-layout: auto; } thead th { @@ -105,9 +105,8 @@ .num.viol-hot { color: var(--block); } .spend { font-size: 18px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; } - /* ---- Classes de données + chips ---- */ - /* max-width borne la colonne : les chips se replient (≈2 par ligne) au lieu - d'élargir la table et de provoquer un défilement horizontal. */ + /* ---- Data classes + chips ---- */ + /* max-width bounds the column: chips wrap instead of widening the table. */ .chips { display: flex; flex-wrap: wrap; gap: 6px; max-width: 184px; } .chip { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 999px; @@ -124,7 +123,7 @@ .decision.caviarde { color: var(--warn); } .decision.none { color: var(--muted); } - /* ---- Type d'incident (classification d'audit Nc/C1/C2/C3) ---- */ + /* ---- Incident type (NC/C1/C2/C3 audit classification) ---- */ .itype { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 700; white-space: nowrap; @@ -140,7 +139,7 @@ .proof svg { width: 15px; height: 15px; } .proof.no { color: var(--muted); } - /* ---- Pastille de statut ---- */ + /* ---- Status pill ---- */ .pill { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 14px; padding: 8px 14px 8px 11px; border-radius: 999px; @@ -151,7 +150,7 @@ .pill.revoque { color: var(--block); background: rgba(239,68,68,.14); border-color: rgba(239,68,68,.5); } .remediation { display: block; margin-top: 6px; font-size: 11px; color: var(--muted); max-width: 180px; } - /* ---- Lien drill-down Loki ---- */ + /* ---- Loki drill-down link ---- */ .logs-link { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; font-size: 13px; font-weight: 600; color: var(--info); text-decoration: none; @@ -162,7 +161,7 @@ .logs-link:hover { background: rgba(56,189,248,.2); border-color: rgba(56,189,248,.55); transform: translateY(-1px); } .logs-link svg { width: 15px; height: 15px; } - /* ---- Accessibilité : anneau de focus visible au clavier ---- */ + /* ---- Accessibility: visible keyboard focus ring ---- */ a:focus-visible, .logs-link:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; border-radius: 9px; } @@ -174,20 +173,20 @@ tr.just-revoked { background: rgba(239,68,68,.10); animation: flash 1.2s ease 1; } @keyframes flash { 0%,100% { background: rgba(239,68,68,.10); } 40% { background: rgba(239,68,68,.28); } } - /* ---- Légende ---- */ + /* ---- Legend ---- */ .legend { display: flex; flex-wrap: wrap; gap: 10px 16px; justify-content: center; padding: 18px 40px 8px; color: var(--muted); font-size: 13px; } .legend .item { display: inline-flex; align-items: center; gap: 8px; } .legend .sw { width: 12px; height: 12px; border-radius: 3px; } .footnote { text-align: center; color: var(--muted); font-size: 12.5px; padding: 12px 40px 44px; max-width: 1000px; margin: 0 auto; line-height: 1.55; } .footnote a { color: var(--info); } - /* ---- Reflow gracieux sur écrans étroits (375 / 768) ---- */ + /* ---- Graceful reflow on narrow screens (375 / 768) ---- */ @media (max-width: 768px) { header { padding: 26px 20px 4px; } main { padding: 16px 14px 12px; } .legend { padding: 14px 20px 6px; } .footnote { padding: 12px 20px 40px; } - /* La table reste lisible : défilement horizontal via .table-wrap (overflow-x). */ + /* The table stays readable: horizontal overflow is contained by .table-wrap. */ } @@ -195,17 +194,17 @@
-

Une identité, une clé, des preuves

+

One identity, one key, signed proof

-

Humains et agents de service ont chacun leur clé. Conso, incidents et classe de données captée sont attribués individuellement, avec preuve signée. Un agent qui dérive voit sa clé coupée automatiquement.

-
Connexion…
+

Humans and service agents each get their own key. Spend, incidents, data classes and signed audit proof stay tied to the exact identity. A drifting agent is cut off automatically.

+
Connecting...
- Actif / trafic propre - Donnée caviardée - Menace bloquée → seuil → coupure - Seuil de coupure : violations + Active / clean traffic + Redacted data + Blocked threat -> threshold -> cutoff + Cutoff threshold: - violations
@@ -214,43 +213,44 @@

Une identité, une clé, des preuves

- - - + + + - - - - - + + + + + - +
IdentitéRequêtesConso €IdentityRequestsSpend € ViolationsDonnées captéesType d'incidentDécisionPreuveStatutData classesIncident typeDecisionProofStatus Logs
En attente des données de gouvernance…
Waiting for governance data...

- Conso indicative (backend simulé, aucun coût réel). Une violation = requête bloquée par la protection (injection de prompt, exfiltration) ; les secrets et données personnelles sont caviardés (la requête aboutit, expurgée). Le type d'incident reprend la classification d'audit la plus grave vue pour l'identité : Propre (Nc, aucune action DLP) · Caviardé (C1) · Restreint (C2, PII ou exfiltration bloquée) · Attaque (C3, injection bloquée). La classe de donnée captée et la preuve signée sont dérivées du journal d'audit signé. La coupure est appliquée en direct : la clé révoquée est refusée dès la requête suivante (401). « Logs » ouvre le tableau de bord d'investigation par agent (Grafana), pré-filtré sur l'identité — chaque incident au niveau requête, avec son type, sa preuve signée, vérifiable. + Simulated backend: indicative spend only. Violations are blocked injection or exfiltration attempts; secrets and PII are redacted before the request continues. Highest signed audit class per identity: + Clean NC · Redacted C1 · Restricted C2 · Attack C3. Revoked keys fail on the next request (401). Logs opens the pre-filtered Grafana investigation view.