Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.snakysec.com/llms.txt

Use this file to discover all available pages before exploring further.

TECH CONTEXT — SnakySec MSSP Platform

Contexte technique pour le consultant UX externe. Sources : platform/package.json, platform/src/, docs/architecture.md.

1. Stack frontend

BriqueChoixNotes
RuntimeNext.js 16 (App Router)Turbopack par défaut (dev + build)
UI libReact 19Server Components, async params (await params)
LangTypeScript strictimports @/... mappés sur platform/src/
StyleTailwind CSS + shadcn/ui (Radix primitives)Composants ui dans src/components/ui/ — 25 composants shadcn
ChartsRecharts7 charts custom dans src/components/charts/
TablesTanStack Table v8Wrappés dans <DataTable>
FormsReact Hook Form + ZodValidation coté client + server schemas
Iconslucide-reactImportés à la pièce — pas de barrel global
i18nnext-intl FR/ENFichiers platform/messages/fr.json + en.json ; provider via src/i18n/
Auth clientnext-auth v5 (useSession, signOut)+ custom <SessionGuard> qui force re-auth si refresh token Entra fail
Dateshelpers maison (timeAgo, scoreColor) dans lib/utils.tsLocale fr-FR ou en-US selon getLocale()
Toastsshadcn toaster + useToast hookPas de Sonner
Sentry@sentry/nextjsWired dans instrumentation.ts + sentry.{client,server,edge}.config.ts

2. Stack backend

BriqueChoixNotes
APINext.js Route Handlers (/api/v1/*, /api/internal/*, /api/admin/*, /api/webhooks/*)Pas de tRPC
ORMPrisma 7 + PostgreSQLprisma.config.ts au root — datasource séparée du schema.prisma (Prisma 7 breaking)
Auth serverNextAuth.js v5 + Entra ID (cert X.509)Cert PEM + private key dans Vault. Pas de client_secret en prod
QueuesBullMQ + Redis 78 workers : audit-import, audit-scheduler, audit-chain, monthly-digest, email-sender, deadline, permission-expiry, regression, retention
SecretsHashiCorp Vault (KV v2)Shamir seal prod (5/3) + AppRole (mssp-app, mssp-worker). Cert X.509 Entra jamais sur disque
Crypto applicativeAES-256-GCM (lib/crypto.ts)Encryption key dans Vault
Audit chainEd25519 + SHA-256 hash chain (RFC 8785 canonicalization)LogAnchor journalier signé, AuditLogAcknowledgement si tampering détecté
MailMicrosoft Graph SendMail (Application + Mail.Send + Application Access Policy mailbox noreply@snakysec.com)Entra app reg dédiée, indépendante du SSO
ObservabilitéSentry SaaS (EU)DSN via Vault, scrubbing PII obligatoire, Session Replay désactivé. Migration self-hosted Q3-Q4 2026

3. Structure des dossiers

platform/
├── messages/                 # i18n FR/EN (next-intl)
│   ├── fr.json
│   └── en.json
├── prisma/
│   ├── schema.prisma         # 30+ models (audit, RBAC, GRC, audit log chain)
│   ├── seed.ts               # Upsert SnakySec client + baselines
│   └── migrations/
├── prisma.config.ts          # Prisma 7 — datasource URL (séparée du schema)
├── instrumentation.ts        # Vault → process.env injection (BEFORE Node start)
├── sentry.{client,server,edge}.config.ts
├── compose/                  # Docker compose overlays (postgres, redis, vault, app, traefik, openproject)
├── docker/
│   └── next-app/entrypoint.sh    # Vault AppRole login → exports env → start node
├── scripts/
│   └── generate-roadmap-xlsx.mjs # Plus quelques outils ops/seed
└── src/
    ├── app/                  # Next.js App Router
    │   ├── (auth)/login/         # Public — SSO callback
    │   ├── api/                  # Route handlers
    │   │   ├── auth/[...nextauth]/   # NextAuth
    │   │   ├── webhooks/gitlab/      # HMAC webhook (bypass proxy)
    │   │   ├── health/               # Liveness probe
    │   │   ├── internal/             # Coverage / digest trigger
    │   │   ├── admin/queues/         # BullMQ admin UI
    │   │   ├── portal/impersonate/   # Cookie set/clear
    │   │   └── v1/                   # REST API publique
    │   │       ├── audits/           # CRUD audits, trigger, retry, reports, stream SSE
    │   │       ├── clients/          # CRUD clients, secrets, documents (8 GRC), milestones, deadlines
    │   │       ├── findings/         # CRUD findings + actions remediation
    │   │       ├── milestones/       # CRUD milestones + status
    │   │       ├── remediation-actions/
    │   │       ├── reports/          # Generate / verify / download
    │   │       ├── alerts/           # Read + mark read (PATCH)
    │   │       ├── api-keys/
    │   │       ├── audit-log/        # Hash chain integrity, anchors, verify, ack tampering
    │   │       ├── deadlines/
    │   │       ├── email/unsubscribe/    # 1-click unsubscribe (no auth)
    │   │       ├── ciso/                 # CISO Assistant connection-test, setup
    │   │       └── admin/                # Onboarding admin endpoints, roles, users, grants
    │   ├── dashboard/            # MSSP UI
    │   │   ├── page.tsx          # Hub dashboard
    │   │   ├── layout.tsx        # Sidebar + Header + TamperingBanner + SessionGuard
    │   │   ├── alerts/
    │   │   ├── audits/
    │   │   ├── clients/
    │   │   │   └── [id]/
    │   │   │       ├── controls/, findings/, remediation/, remediation-plan/
    │   │   │       ├── trajectory/, documents/, secrets/, edit/
    │   │   │       └── onboarding/[step]/
    │   │   ├── documents/        # Hub GRC + 7 sous-catégories
    │   │   ├── help/dr/[...slug]/    # DR runbook (gated INTERNAL_DOCS_VIEW)
    │   │   ├── integrations/
    │   │   └── settings/         # Hub settings + 6 pages
    │   ├── portal/               # Client UI (CLIENT_USER + impersonation MSSP)
    │   │   ├── layout.tsx        # PortalSidebar + Header + ImpersonationBanner
    │   │   ├── page.tsx          # Overview KPIs
    │   │   ├── audits/, findings/, remediation/, documents/
    │   ├── global-error.tsx
    │   ├── globals.css
    │   ├── layout.tsx            # Root layout (next-intl provider, Toaster)
    │   └── page.tsx              # → redirect /dashboard
    ├── components/               # Voir UI_MAP.md pour inventaire
    │   ├── ui/                   # 25 shadcn primitives
    │   ├── audit/                # 7 composants audit
    │   ├── charts/               # 7 charts Recharts
    │   ├── onboarding/           # Wizard + 8 steps
    │   ├── remediation/          # 7 composants
    │   ├── trajectory/           # 1 composant dashboard trajectoire
    │   ├── brand/                # Wordmark + flags
    │   └── *.tsx                 # Composants top-level (sidebar, header, alert-feed, etc.)
    ├── lib/                      # Logique métier non-UI
    │   ├── auth.ts               # NextAuth config (charge cert from env injecté par Vault)
    │   ├── auth.edge.ts          # Edge-compatible auth pour proxy
    │   ├── auth-helpers.ts       # requireAuth, requireRole, getCurrentUser, getPortalScope
    │   ├── auth-cert.ts          # X.509 cert helpers
    │   ├── permissions/          # 60-perm catalog + hasPermission/requirePermission
    │   │   ├── catalog.ts        # enum + ROLE_BASELINES (data only, importable seed)
    │   │   ├── index.ts          # Next.js helper avec audit log side-effect
    │   │   ├── groups.ts         # 17 UI groups pour matrice
    │   │   └── client-update-fields.ts
    │   ├── prisma.ts             # Prisma singleton
    │   ├── redis.ts              # Redis singleton
    │   ├── crypto.ts             # AES-256-GCM
    │   ├── log-chain.ts          # Hash chain SHA-256 + canonicalization RFC 8785
    │   ├── log-redact.ts         # Denylist + truncation pour logs
    │   ├── audit-log.ts          # PlatformAuditLog writer (seal chain)
    │   ├── audit-chain-verify.ts # Verifier (anchor by anchor)
    │   ├── audit-progress.ts     # Stage tracking pipeline
    │   ├── audit-score.ts        # Score % computing
    │   ├── audit-import-queue.ts
    │   ├── audits/               # Helpers DB-side
    │   ├── client-resolver.ts    # hostname → Client.domain → Vault → SSO config
    │   ├── gitlab.ts             # GitLab REST (trigger, cancel, artifact)
    │   ├── grc/                  # 8 GRC document generators (DOCX + PDF)
    │   │   ├── components/       # 13 *-document.tsx + cover-page + grc-page + shared-styles
    │   │   ├── generate-docx.ts
    │   │   ├── generate-grc-pdf.ts
    │   │   ├── generate-monthly-digest-pdf.ts
    │   │   ├── generate-remediation-guide-pdf.ts
    │   │   ├── generate-milestones-plan-pdf.ts
    │   │   ├── assemble-grc-data.ts
    │   │   ├── assemble-monthly-digest.ts
    │   │   ├── assemble-remediation-guide.ts
    │   │   ├── assemble-remediation-plan.ts
    │   │   ├── split-action.ts
    │   │   └── types.ts
    │   ├── import/               # Schema v3 JSON → Prisma ControlResult
    │   ├── report/               # Audit reports (PDF/Excel/HTML/JSON) — TRACKING + CERTIFICATION
    │   ├── ciso-assistant/       # CISO Assistant API client + sync
    │   ├── dr-docs/              # MD parser pour DR runbook in-app
    │   ├── milestones/           # Milestone helpers (sortOrder fractional indexing)
    │   ├── onboarding/           # State machine wizard (status × step)
    │   ├── email/                # dispatch, templates, preferences
    │   ├── email.ts              # Graph mail client
    │   ├── scoring/              # CMMI computing
    │   ├── diff/                 # Audit run diff
    │   ├── frameworks-mappings.ts # CIS / SCuBA / ISO / NIST mappings
    │   ├── pagination.ts         # Cursor pagination (ADR)
    │   ├── rate-limit.ts
    │   ├── dashboard-kpi.ts      # Cached KPIs per (clientId, period)
    │   ├── review-evidence.ts    # Filesystem evidence files
    │   └── validations/          # Zod schemas
    ├── jobs/                     # BullMQ workers (boot via instrumentation)
    │   ├── audit-import-worker.ts
    │   ├── audit-scheduler-worker.ts
    │   ├── audit-chain-worker.ts
    │   ├── deadline-worker.ts
    │   ├── email-sender-worker.ts
    │   ├── monthly-digest-worker.ts
    │   ├── permission-expiry-worker.ts
    │   ├── regression-worker.ts
    │   └── retention-worker.ts
    ├── data/                     # Static data (registry, baselines refs)
    ├── i18n/                     # next-intl config (locales, request)
    ├── hooks/                    # use-toast, etc.
    ├── env.ts                    # Env validation (Zod)
    ├── proxy.ts                  # Edge proxy (RBAC routes, CSRF, role gates)
    ├── scripts/
    └── test/                     # (vide ou minimal — 0 test V1)

4. Patterns utilisés

4.1 Server vs Client Components

  • Pages = Server Components par défaut : presque toutes les pages dans /dashboard/* et /portal/* sont async et utilisent await prisma.* directement. Voir /dashboard/page.tsx ou /dashboard/clients/[id]/page.tsx.
  • export const dynamic = "force-dynamic" sur la plupart des pages → pas de cache statique, requête DB à chaque render.
  • Client Components quand interactif : tous les composants <Sidebar>, <Header>, <AuditActions>, <AlertFeed>, charts, formulaires onboarding → "use client"; en haut. Importent useTranslations, useSession, useRouter, useState.
  • Pattern Promise.all pour concurrency : les pages fetch DB + i18n en parallèle (Promise.all([prisma..., getTranslations(...)])).
  • searchParams: Promise<...> + await params partout (Next 16 / React 19).

4.2 Actions / mutations

  • API routes REST : pas de Server Actions (Next form actions) côté client. Toutes les mutations passent par fetch("/api/v1/...", { method: "POST" | "PATCH" | "DELETE" }).
  • Pattern récurrent dans les composants client :
    const res = await fetch("/api/v1/alerts", { method: "PATCH", ... });
    if (res.ok) router.refresh();
    
  • Toast feedback via useToast() (shadcn) — success/error pattern dans audit-actions.tsx.
  • Optimistic updates locales dans certains composants (ex: AlertFeed met à jour setAlerts(prev => ...) après succès PATCH).

4.3 Navigation

  • Liens : <Link href={...}> Next.js partout — pas de programmatic router.push() en grande quantité.
  • Sidebar active state : compute via usePathname() + pathname.startsWith(href) (avec exact flag pour /dashboard lui-même).
  • Mobile : <Sheet side="left"> dans le Header avec <Sidebar /> ou <PortalSidebar /> selon pathname.startsWith("/portal").

4.4 Auth / RBAC

  • platform/src/proxy.ts : Edge proxy Next 16 (remplace middleware.ts). 6 règles : webhooks bypass, NextAuth bypass, health bypass, login bypass, CSRF origin check sur state-changing API, RBAC route patterns (adminOnly, mssp, portal).
  • Pages-level guards : chaque page Server Component fait getCurrentUser() + requirePermission(...) ou requireRole(...) puis lit Prisma. Pas de couche service intermédiaire — Prisma direct.
  • requirePermission(perm, scope?) écrit un permission.denied row au audit log à chaque refus → préférer hasPermission() (no side-effect) pour les checks OR.

4.5 i18n

  • next-intl : provider dans le root layout, getTranslations("namespace") dans Server Components, useTranslations("namespace") dans Client.
  • Namespaces récurrents : nav, common, dashboard, clients, clientDetail, auditDetail, audits, portal, onboarding, header, sidebar, auditActions, settings, integrations, alerts.
  • Locale détection : getLocale()"fr" ou "en" ; date locale derived (fr-FR / en-US).
  • Hardcoded strings résiduels : <PortalSidebar> items ("Overview", etc.) et footer "Logout" — pas i18n. Header title "SnakySec" + "Client Portal" aussi hardcoded EN.

4.6 Multi-tenant SSO

  • Login page lit hostname via headers → resolve Client.domain en DB → si match → load Vault mssp/data/clients/<slug> → SSO config pour ce tenant.
  • Si pas de match → fallback platform Entra credentials (sentinel slug __platform__).
  • Cookie mssp_client pour le slug, lu par resolveClientFromCookie().

5. Points d’attention / fragilités

5.1 Bugs récents et zones fragiles

  • alert-feed.tsx hydration mismatch : timeAgo(new Date(alert.createdAt)) calculé au render → divergence SSR/CSR si timezone diffère. Bug récent identifié.
  • portal-sidebar.tsx pas i18n : labels hardcoded en anglais alors que le reste de l’app est bilingue. Risque visuel pour user FR (mix FR + EN dans la nav).
  • Pas de breadcrumb sur le portail client — uniquement sur /dashboard/*. Asymétrie navigation.

5.2 Composants modifiés plusieurs fois (signaux à surveiller)

  • alert-feed.tsx : composant client + appelé en SSR dans dashboard (initialAlerts) + page alerts dédiée (initialAlerts). 3 sites d’utilisation.
  • audit-actions.tsx : 2 variantes (dropdown | buttons) maintenues, ~330 lignes — risque de divergence quand on ajoute une action.
  • sidebar.tsx : section Documents collapsible avec state local (docsOpen) + auto-open via useEffect quand active route → couplage state UI + URL.
  • 8 *-step.tsx onboarding : 8 fichiers indépendants, pas de wrapper commun. Layout internes (positions boutons next/prev) divergent au gré des évolutions.

5.3 Zones où la dette technique est visible

  • Schema legacy + new fields cohabitent :
    • PlatformAuditLog : colonnes userId, resource, details, ipAddress legacy retained for backward compat — la version moderne est actorType / actorId / resourceType / resourceId / actorIp. 2 namespaces dans la même table.
    • ControlResult.evidence (legacy JSON string) backfillé dans evidencePrimary (Json typé). Lisible par 2 chemins.
  • User.roleId → Role FK pas wirée : la table Role est un cache du ROLE_BASELINES TypeScript ; user.role enum reste source de vérité. Wiring V2.
  • User.role enum (3 valeurs) cohabite avec Permission enum (60 valeurs) : matrice de transition ROLE_BASELINES (data) → 3 baselines précomputés. Drift possible si on ajoute une permission au TS sans seed.
  • Reports : 5 formats × 2 types = 10 entrées potentielles dans AuditReport ; toutes ne sont pas systématiquement générées. UI doit gérer les états manquants.
  • CISO Assistant 4 statuts de connexion : le composant <IntegrationsPage> doit gérer tous les cas + état “loading” + “testing”. Page entièrement client-rendered → pas de Server Component pré-render.
  • Onboarding state machine : 4 status × 7 steps × minStepForStatus() + maxReachableStep() → 28+ combinaisons théoriques. Logique testable mais non testée (Vitest 0%).
  • prisma.config.ts Prisma 7 : breaking vs Prisma 6 — toute personne qui clone le repo et lance npx prisma db push doit avoir le fichier au root. Documenté mais facile à manquer.
  • instrumentation.ts runs ONCE au boot : Vault secrets injectés là, AVANT que auth.ts soit chargé. Toute lecture process.env.ENTRA_* au module-load casserait l’auth (commentaire explicite dans CLAUDE.md).
  • AUTH_SECRET rotation invalide toutes les sessions : pas de double-key transitoire, rotation = downtime users (clear cookies + re-login).

5.4 Performance / scaling

  • export const dynamic = "force-dynamic" sur la quasi-totalité des pages → 1 query DB par render, pas de cache Next.js. Acceptable V1 (faible volume de users), mais à surveiller.
  • Dashboard KPIs : getDashboardKpis(clientId, period) est cached séparément (pattern de ses propres caches), mais Promise.all([kpis, alerts, runs, ...]) lance 5 requêtes parallèles à chaque render. Pas de surcharge à 1 user, mais à scaler si analyste consulte en permanence.
  • AlertFeed limit 10 + page Alerts limit 50 : pas de pagination cursor — si les alerts dépassent 50 jamais, l’historique est tronqué.
  • AuditRun.findMany take: 20 sur la page audits, page-based pagination simple. Pas de cursor.

5.5 Sentry / observabilité

  • DSN dans Vault mssp/data/platform/sentry_dsn → injecté au boot via instrumentation.ts.
  • Scrubbing PII obligatoire — denylist des secrets (AUTH_SECRET, ENCRYPTION_KEY, *_PASSWORD, VAULT_*, GITLAB_TOKEN), email/IP truncated, body > 10KB truncated.
  • Session Replay désactivé (décision produit, RGPD-friendly).
  • Project Sentry : snakysec/mssp-platform (EU). MCP tool dispo pour interroger Sentry depuis Claude (list_issues, get_sentry_resource, analyze_issue_with_seer).

6. Conventions code

  • Imports @/... mappés sur platform/src/.
  • Conventional commits : feat:, fix:, chore:, refactor:, docs:. Pas de Co-Authored-By trailer.
  • Branches : main (protected), fix/*, feat/*. Squash merge local préféré.
  • Lang : code EN, commentaires FR/EN OK, output user FR/EN bilingue.
  • GitLab CI : pipeline parametré FRAMEWORK × PRODUCT_AREA.
  • Docker production : make app-rebuild après changement TypeScript ; ephemeral container prisma db push après changement schema ; make app-restart après changement .env.

7. Liens documentation