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
| Brique | Choix | Notes |
|---|---|---|
| Runtime | Next.js 16 (App Router) | Turbopack par défaut (dev + build) |
| UI lib | React 19 | Server Components, async params (await params) |
| Lang | TypeScript strict | imports @/... mappés sur platform/src/ |
| Style | Tailwind CSS + shadcn/ui (Radix primitives) | Composants ui dans src/components/ui/ — 25 composants shadcn |
| Charts | Recharts | 7 charts custom dans src/components/charts/ |
| Tables | TanStack Table v8 | Wrappés dans <DataTable> |
| Forms | React Hook Form + Zod | Validation coté client + server schemas |
| Icons | lucide-react | Importés à la pièce — pas de barrel global |
| i18n | next-intl FR/EN | Fichiers platform/messages/fr.json + en.json ; provider via src/i18n/ |
| Auth client | next-auth v5 (useSession, signOut) | + custom <SessionGuard> qui force re-auth si refresh token Entra fail |
| Dates | helpers maison (timeAgo, scoreColor) dans lib/utils.ts | Locale fr-FR ou en-US selon getLocale() |
| Toasts | shadcn toaster + useToast hook | Pas de Sonner |
| Sentry | @sentry/nextjs | Wired dans instrumentation.ts + sentry.{client,server,edge}.config.ts |
2. Stack backend
| Brique | Choix | Notes |
|---|---|---|
| API | Next.js Route Handlers (/api/v1/*, /api/internal/*, /api/admin/*, /api/webhooks/*) | Pas de tRPC |
| ORM | Prisma 7 + PostgreSQL | prisma.config.ts au root — datasource séparée du schema.prisma (Prisma 7 breaking) |
| Auth server | NextAuth.js v5 + Entra ID (cert X.509) | Cert PEM + private key dans Vault. Pas de client_secret en prod |
| Queues | BullMQ + Redis 7 | 8 workers : audit-import, audit-scheduler, audit-chain, monthly-digest, email-sender, deadline, permission-expiry, regression, retention |
| Secrets | HashiCorp Vault (KV v2) | Shamir seal prod (5/3) + AppRole (mssp-app, mssp-worker). Cert X.509 Entra jamais sur disque |
| Crypto applicative | AES-256-GCM (lib/crypto.ts) | Encryption key dans Vault |
| Audit chain | Ed25519 + SHA-256 hash chain (RFC 8785 canonicalization) | LogAnchor journalier signé, AuditLogAcknowledgement si tampering détecté |
Microsoft 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
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 utilisentawait prisma.*directement. Voir/dashboard/page.tsxou/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. ImportentuseTranslations,useSession,useRouter,useState. - Pattern
Promise.allpour concurrency : les pages fetch DB + i18n en parallèle (Promise.all([prisma..., getTranslations(...)])). searchParams: Promise<...>+await paramspartout (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 :
- Toast feedback via
useToast()(shadcn) — success/error pattern dansaudit-actions.tsx. - Optimistic updates locales dans certains composants (ex:
AlertFeedmet à joursetAlerts(prev => ...)après succès PATCH).
4.3 Navigation
- Liens :
<Link href={...}>Next.js partout — pas de programmaticrouter.push()en grande quantité. - Sidebar active state : compute via
usePathname()+pathname.startsWith(href)(avecexactflag pour/dashboardlui-même). - Mobile :
<Sheet side="left">dans le Header avec<Sidebar />ou<PortalSidebar />selonpathname.startsWith("/portal").
4.4 Auth / RBAC
platform/src/proxy.ts: Edge proxy Next 16 (remplacemiddleware.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(...)ourequireRole(...)puis lit Prisma. Pas de couche service intermédiaire — Prisma direct. requirePermission(perm, scope?)écrit unpermission.deniedrow au audit log à chaque refus → préférerhasPermission()(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.domainen DB → si match → load Vaultmssp/data/clients/<slug>→ SSO config pour ce tenant. - Si pas de match → fallback platform Entra credentials (sentinel slug
__platform__). - Cookie
mssp_clientpour le slug, lu parresolveClientFromCookie().
5. Points d’attention / fragilités
5.1 Bugs récents et zones fragiles
alert-feed.tsxhydration mismatch :timeAgo(new Date(alert.createdAt))calculé au render → divergence SSR/CSR si timezone diffère. Bug récent identifié.- Fichier :
platform/src/components/alert-feed.tsxligne 84.
- Fichier :
portal-sidebar.tsxpas 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).- Fichier :
platform/src/components/portal-sidebar.tsx.
- Fichier :
- 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: sectionDocumentscollapsible avec state local (docsOpen) + auto-open viauseEffectquand active route → couplage state UI + URL.- 8
*-step.tsxonboarding : 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: colonnesuserId,resource,details,ipAddresslegacy retained for backward compat — la version moderne estactorType / actorId / resourceType / resourceId / actorIp. 2 namespaces dans la même table.ControlResult.evidence(legacy JSON string) backfillé dansevidencePrimary(Json typé). Lisible par 2 chemins.
User.roleId → RoleFK pas wirée : la tableRoleest un cache duROLE_BASELINESTypeScript ; user.role enum reste source de vérité. Wiring V2.User.roleenum (3 valeurs) cohabite avecPermissionenum (60 valeurs) : matrice de transitionROLE_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.tsPrisma 7 : breaking vs Prisma 6 — toute personne qui clone le repo et lancenpx prisma db pushdoit avoir le fichier au root. Documenté mais facile à manquer.instrumentation.tsruns ONCE au boot : Vault secrets injectés là, AVANT queauth.tssoit chargé. Toute lectureprocess.env.ENTRA_*au module-load casserait l’auth (commentaire explicite dans CLAUDE.md).AUTH_SECRETrotation 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), maisPromise.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 viainstrumentation.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 surplatform/src/. - Conventional commits :
feat:,fix:,chore:,refactor:,docs:. Pas deCo-Authored-Bytrailer. - 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-rebuildaprès changement TypeScript ; ephemeral containerprisma db pushaprès changement schema ;make app-restartaprès changement.env.
7. Liens documentation
- docs/architecture.md — flux audit complet
- docs/SECURITY.md — threat model + DICT
- docs/LOGGING.md — chaîne audit log + Ed25519
- docs/OBSERVABILITY.md — Sentry wiring
- docs/runbook.md — opérateur runbook
- docs/runbooks/ — runbooks spécifiques (notifications app, etc.)
- docs/dr/ — Disaster Recovery (key-holders, restore procedures)
- docs/adr/ — Architecture Decision Records (BullMQ, Vault AppRole, cert X.509, cursor pagination)