diff --git a/src/features/admin/README.md b/src/features/admin/README.md
new file mode 100644
index 0000000..92cebd3
--- /dev/null
+++ b/src/features/admin/README.md
@@ -0,0 +1,293 @@
+# Admin
+
+Ce répertoire fournit l'interface d'administration complète : layout, navigation, tableau de bord, gestion des utilisateurs et des rôles. Il expose un **registre runtime** pour permettre aux projets consommateurs d'ajouter des widgets, des entrées de navigation et des pages sans modifier le core.
+
+---
+
+## Structure
+
+```
+src/features/admin/
+├── index.js protectAdmin, isAdmin, buildNavigationSections, registre
+├── protect.js gardes d'accès
+├── navigation.js buildNavigationSections, buildBottomNavItems
+├── registry.js registre runtime d'extensions
+├── AdminLayout.server.js layout RSC de l'admin
+├── AdminPage.server.js page RSC racine (protège + collecte les données widgets)
+├── AdminPage.client.js shell client
+├── components/
+│ ├── index.js re-export
+│ ├── AdminHeader.js
+│ ├── AdminShell.js
+│ ├── AdminSidebar.js
+│ ├── AdminTop.js
+│ ├── RoleEditModal.client.js
+│ ├── ThemeToggle.js
+│ └── UserEditModal.client.js
+├── devkit/
+│ ├── ComponentsPage.client.js
+│ ├── DevkitPage.client.js
+│ └── IconsPage.client.js
+├── pages/
+│ ├── ConfirmEmailChangePage.client.js
+│ ├── DashboardPage.client.js
+│ ├── ProfilePage.client.js
+│ ├── RolesPage.client.js
+│ ├── SettingsPage.client.js
+│ └── UsersPage.client.js
+└── widgets/
+ ├── index.client.js auto-registration des widgets core (côté client)
+ ├── index.server.js auto-registration des widgets core (côté serveur)
+ ├── users.client.js widget Utilisateurs (composant)
+ └── users.server.js widget Utilisateurs (fetcher)
+```
+
+---
+
+## Import
+
+```js
+import { protectAdmin, isAdmin, buildNavigationSections } from '@zen/core/features/admin';
+import {
+ registerWidget,
+ registerWidgetFetcher,
+ registerNavItem,
+ registerNavSection,
+ registerPage,
+} from '@zen/core/features/admin';
+```
+
+---
+
+## Pages intégrées
+
+| Route | Page |
+|-------|------|
+| `/admin/dashboard` | Tableau de bord avec widgets |
+| `/admin/users` | Liste et gestion des utilisateurs |
+| `/admin/roles` | Gestion des rôles et permissions |
+| `/admin/settings` | Paramètres de l'application |
+| `/admin/profile` | Profil de l'utilisateur connecté |
+| `/admin/confirm-email-change` | Confirmation de changement d'email |
+
+---
+
+## API
+
+### `protectAdmin(options?)`
+
+Garde serveur. Redirige si l'utilisateur n'est pas connecté ou n'a pas la permission `ADMIN_ACCESS`. Retourne la session courante.
+
+```js
+const session = await protectAdmin();
+// session.user est disponible
+```
+
+| Option | Type | Défaut | Description |
+|--------|------|--------|-------------|
+| `redirectTo` | `string` | `'/auth/login'` | Redirection si non authentifié |
+| `forbiddenRedirect` | `string` | `'/'` | Redirection si non autorisé |
+
+---
+
+### `isAdmin()`
+
+Vérifie si l'utilisateur courant a la permission `ADMIN_ACCESS`. Retourne `boolean`.
+
+```js
+const admin = await isAdmin();
+if (!admin) return null;
+```
+
+---
+
+### `buildNavigationSections(pathname)`
+
+Construit les sections de navigation pour la sidebar à partir du registre. Marque l'entrée active selon `pathname`.
+
+```js
+const sections = buildNavigationSections('/admin/users');
+// [{ id, title, icon, items: [{ name, href, icon, current }] }]
+```
+
+---
+
+## Registre d'extensions
+
+Le registre permet d'ajouter des widgets, des entrées de navigation et des pages sans toucher au core. Les enregistrements se font via des imports à effet de bord dans le layout racine du projet consommateur.
+
+### Ajouter un widget
+
+Un widget est composé de deux parties : un fetcher serveur qui collecte les données, et un composant client qui les affiche.
+
+```js
+// app/admin/orders/ordersWidget.server.js
+import { registerWidgetFetcher } from '@zen/core/features/admin';
+import { countOrders } from './orders.server.js';
+
+registerWidgetFetcher('orders', async () => ({
+ total: await countOrders(),
+}));
+```
+
+```js
+// app/admin/orders/ordersWidget.client.js
+'use client';
+import { registerWidget } from '@zen/core/features/admin';
+import { StatCard } from '@zen/core/shared/components';
+
+function OrdersWidget({ data, loading }) {
+ return (
+
+ );
+}
+
+registerWidget({ id: 'orders', Component: OrdersWidget, order: 20 });
+```
+
+Le composant reçoit `data` (retour du fetcher) et `loading` (booléen). Si le fetcher échoue, `data` est `null` et `loading` reste `false`.
+
+**`registerWidgetFetcher(id, fetcher)`**
+
+| Paramètre | Type | Description |
+|-----------|------|-------------|
+| `id` | `string` | Identifiant unique du widget |
+| `fetcher` | `async () => object` | Fonction serveur qui retourne les données |
+
+**`registerWidget({ id, Component, order? })`**
+
+| Paramètre | Type | Description |
+|-----------|------|-------------|
+| `id` | `string` | Identifiant unique (doit correspondre au fetcher) |
+| `Component` | `ReactComponent` | Composant client affiché dans le tableau de bord |
+| `order` | `number` | Position dans la grille (défaut : `0`) |
+
+---
+
+### Ajouter une entrée de navigation
+
+```js
+import { registerNavSection, registerNavItem } from '@zen/core/features/admin';
+
+registerNavSection({ id: 'commerce', title: 'Commerce', icon: 'ShoppingBag03Icon', order: 30 });
+
+registerNavItem({
+ id: 'orders',
+ label: 'Commandes',
+ icon: 'ShoppingBag03Icon',
+ href: '/admin/orders',
+ sectionId: 'commerce',
+ order: 10,
+});
+```
+
+**`registerNavSection({ id, title, icon, order? })`**
+
+| Paramètre | Type | Description |
+|-----------|------|-------------|
+| `id` | `string` | Identifiant unique de la section |
+| `title` | `string` | Titre affiché dans la sidebar |
+| `icon` | `string` | Nom d'icône Hugeicons |
+| `order` | `number` | Ordre d'affichage (défaut : `0`) |
+
+**`registerNavItem({ id, label, icon, href, sectionId?, order?, position? })`**
+
+| Paramètre | Type | Description |
+|-----------|------|-------------|
+| `id` | `string` | Identifiant unique de l'entrée |
+| `label` | `string` | Texte affiché |
+| `icon` | `string` | Nom d'icône Hugeicons |
+| `href` | `string` | URL de destination |
+| `sectionId` | `string` | Section parente (défaut : `'main'`) |
+| `order` | `number` | Ordre d'affichage (défaut : `0`) |
+| `position` | `string` | `'bottom'` pour épingler en bas de la sidebar |
+
+---
+
+### Ajouter une page
+
+```js
+import { registerPage } from '@zen/core/features/admin';
+import OrdersPage from './OrdersPage.js';
+
+registerPage({
+ slug: 'orders',
+ Component: OrdersPage,
+ title: 'Commandes',
+});
+```
+
+La page est rendue sous `/admin/`. `AdminPage.client.js` résout le composant à partir du slug dans les paramètres de route.
+
+**`registerPage({ slug, Component, title?, breadcrumbLabel? })`**
+
+| Paramètre | Type | Description |
+|-----------|------|-------------|
+| `slug` | `string` | Segment d'URL sous `/admin/` |
+| `Component` | `ReactComponent` | Composant client rendu pour cette route |
+| `title` | `string` | Titre de la page (optionnel) |
+| `breadcrumbLabel` | `string` | Label du fil d'Ariane (optionnel, défaut : `title`) |
+
+---
+
+## Câbler les extensions dans le projet consommateur
+
+Regrouper tous les enregistrements dans un fichier de point d'entrée unique, puis l'importer une seule fois depuis le layout racine.
+
+```js
+// app/zen.extensions.js
+import './admin/orders/ordersWidget.server.js';
+import './admin/orders/ordersWidget.client.js';
+import { registerNavSection, registerNavItem, registerPage } from '@zen/core/features/admin';
+import OrdersPage from './admin/orders/OrdersPage.js';
+
+registerNavSection({ id: 'commerce', title: 'Commerce', icon: 'ShoppingBag03Icon', order: 30 });
+registerNavItem({ id: 'orders', label: 'Commandes', icon: 'ShoppingBag03Icon', href: '/admin/orders', sectionId: 'commerce' });
+registerPage({ slug: 'orders', Component: OrdersPage, title: 'Commandes' });
+```
+
+```js
+// app/layout.js
+import './zen.extensions'; // les side effects enregistrent tout
+```
+
+---
+
+## DevKit
+
+Le DevKit est une section de l'admin réservée au développement. Il expose une galerie de composants et un catalogue d'icônes. Il s'active via la variable d'environnement `ZEN_DEVKIT_ENABLED=true` et n'est jamais rendu en production.
+
+| Route | Contenu |
+|-------|---------|
+| `/admin/devkit/components` | Galerie des composants partagés |
+| `/admin/devkit/icons` | Catalogue d'icônes Hugeicons |
+
+---
+
+## Ajouter un widget core
+
+Les widgets intégrés au core suivent le même pattern que les widgets consommateurs, avec une étape supplémentaire : déclarer les fichiers dans les index d'auto-registration.
+
+```js
+// src/features/admin/widgets/myWidget.server.js
+import { registerWidgetFetcher } from '../registry.js';
+registerWidgetFetcher('myWidget', async () => ({ ... }));
+
+// src/features/admin/widgets/index.server.js
+import './myWidget.server.js'; // ajouter cette ligne
+```
+
+```js
+// src/features/admin/widgets/myWidget.client.js
+'use client';
+import { registerWidget } from '../registry.js';
+// ...
+registerWidget({ id: 'myWidget', Component: MyWidget, order: 20 });
+
+// src/features/admin/widgets/index.client.js
+import './myWidget.client.js'; // ajouter cette ligne
+```
diff --git a/src/features/auth/README.md b/src/features/auth/README.md
new file mode 100644
index 0000000..32e144a
--- /dev/null
+++ b/src/features/auth/README.md
@@ -0,0 +1,230 @@
+# Auth
+
+Ce répertoire gère l'authentification : inscription, connexion, sessions, réinitialisation de mot de passe, vérification d'adresse courriel et gestion du profil. Il expose des server actions Next.js, des routes API REST et des composants de pages prêts à l'emploi.
+
+---
+
+## Structure
+
+```
+src/features/auth/
+├── index.js barrel serveur
+├── actions.js server actions Next.js ('use server')
+├── api.js routes API REST (users, roles)
+├── auth.js register, login, resetPassword, updateUser
+├── session.js createSession, validateSession, deleteSession
+├── email.js tokens de vérification + envoi des e-mails
+├── password.js hashPassword, verifyPassword, generateToken
+├── db.js createTables, dropTables
+├── storage-policies.js politiques d'accès au stockage
+├── AuthPage.server.js page RSC racine (route catch-all)
+├── AuthPage.client.js shell client
+├── GUIDE-custom-login.md guide pour les pages personnalisées
+├── components/
+│ └── AuthPageHeader.js
+├── pages/
+│ ├── index.js re-export
+│ ├── LoginPage.client.js
+│ ├── RegisterPage.client.js
+│ ├── ForgotPasswordPage.client.js
+│ ├── ResetPasswordPage.client.js
+│ ├── ConfirmEmailPage.client.js
+│ └── LogoutPage.client.js
+└── templates/
+ ├── VerificationEmail.js
+ ├── PasswordResetEmail.js
+ ├── PasswordChangedEmail.js
+ ├── EmailChangeConfirmEmail.js
+ └── EmailChangeNotifyEmail.js
+```
+
+---
+
+## Import
+
+```js
+import { getSession, loginAction, logoutAction } from '@zen/core/features/auth/actions';
+import { LoginPage, RegisterPage } from '@zen/core/features/auth/pages';
+import { validateSession, createSession } from '@zen/core/features/auth';
+```
+
+---
+
+## Pages intégrées
+
+La route catch-all `app/auth/[...auth]/page.js` suffit pour exposer toutes les pages sans configuration supplémentaire.
+
+```js
+// app/auth/[...auth]/page.js
+export { default } from '@zen/core/features/auth/server';
+```
+
+| Route | Page |
+|-------|------|
+| `/auth/login` | Connexion |
+| `/auth/register` | Inscription |
+| `/auth/forgot` | Mot de passe oublié |
+| `/auth/reset` | Réinitialisation du mot de passe |
+| `/auth/confirm` | Vérification de l'adresse courriel |
+| `/auth/logout` | Déconnexion |
+
+---
+
+## Server actions
+
+Toutes les actions sont dans `@zen/core/features/auth/actions`. Elles attendent un `FormData` sauf `getSession`, `setSessionCookie` et `refreshSessionCookie`.
+
+### `getSession()`
+
+Lit le cookie de session et retourne la session courante, ou `null` si l'utilisateur n'est pas connecté. Renouvelle automatiquement le cookie si la session a été rafraîchie.
+
+```js
+const session = await getSession();
+if (!session?.user) redirect('/auth/login');
+// session.user, session.session disponibles
+```
+
+---
+
+### `loginAction(formData)`
+
+Authentifie l'utilisateur et pose un cookie `HttpOnly`. Applique le rate limiting par IP et les vérifications anti-bot.
+
+```js
+const result = await loginAction(formData);
+// { success: true, user } ou { success: false, error }
+```
+
+---
+
+### `registerAction(formData)`
+
+Crée un compte et envoie l'e-mail de vérification.
+
+| Champ | Description |
+|-------|-------------|
+| `email` | Adresse courriel |
+| `password` | Mot de passe |
+| `name` | Nom d'affichage |
+
+---
+
+### `logoutAction()`
+
+Invalide la session en base et supprime le cookie.
+
+---
+
+### `forgotPasswordAction(formData)`
+
+Envoie un lien de réinitialisation si un compte existe pour l'adresse fournie. La réponse ne révèle pas si le compte existe.
+
+---
+
+### `resetPasswordAction(formData)`
+
+Vérifie le token puis met à jour le mot de passe.
+
+| Champ | Description |
+|-------|-------------|
+| `email` | Adresse courriel |
+| `token` | Token reçu par e-mail |
+| `newPassword` | Nouveau mot de passe |
+
+---
+
+### `verifyEmailAction(formData)`
+
+Vérifie le token de confirmation et marque l'adresse comme vérifiée.
+
+| Champ | Description |
+|-------|-------------|
+| `email` | Adresse courriel |
+| `token` | Token reçu par e-mail |
+
+---
+
+### `setSessionCookie(token)`
+
+Valide le token contre la base avant de l'écrire dans le cookie `HttpOnly`. À utiliser après une authentification externe ou OAuth.
+
+---
+
+### `refreshSessionCookie(token)`
+
+Revalide le token et prolonge la durée de vie du cookie (30 jours).
+
+---
+
+## Routes API REST
+
+Les routes sont enregistrées automatiquement sous le préfixe `/zen/api`. L'authentification est appliquée par le routeur avant chaque handler.
+
+### Utilisateurs
+
+| Méthode | Route | Auth | Description |
+|---------|-------|------|-------------|
+| `GET` | `/zen/api/users` | admin | Liste paginée des utilisateurs |
+| `GET` | `/zen/api/users/:id` | admin | Détail d'un utilisateur |
+| `PUT` | `/zen/api/users/:id` | admin | Modifier `name`, `role`, `email_verified` |
+| `PUT` | `/zen/api/users/:id/email` | admin | Changer l'adresse courriel |
+| `PUT` | `/zen/api/users/:id/password` | admin | Définir un mot de passe |
+| `POST` | `/zen/api/users/:id/send-password-reset` | admin | Envoyer un lien de réinitialisation |
+| `GET` | `/zen/api/users/:id/roles` | admin | Lister les rôles de l'utilisateur |
+| `POST` | `/zen/api/users/:id/roles` | admin | Assigner un rôle |
+| `DELETE` | `/zen/api/users/:id/roles/:roleId` | admin | Révoquer un rôle |
+
+### Profil (utilisateur connecté)
+
+| Méthode | Route | Description |
+|---------|-------|-------------|
+| `PUT` | `/zen/api/users/profile` | Modifier le nom |
+| `POST` | `/zen/api/users/profile/email` | Initier un changement d'adresse courriel |
+| `GET` | `/zen/api/users/email/confirm` | Confirmer le changement d'adresse |
+| `POST` | `/zen/api/users/profile/password` | Changer le mot de passe |
+| `POST` | `/zen/api/users/profile/picture` | Téléverser une photo de profil |
+| `DELETE` | `/zen/api/users/profile/picture` | Supprimer la photo de profil |
+| `GET` | `/zen/api/users/profile/sessions` | Lister les sessions actives |
+| `DELETE` | `/zen/api/users/profile/sessions` | Révoquer toutes les sessions |
+| `DELETE` | `/zen/api/users/profile/sessions/:sessionId` | Révoquer une session |
+
+### Rôles
+
+| Méthode | Route | Description |
+|---------|-------|-------------|
+| `GET` | `/zen/api/roles` | Lister les rôles |
+| `POST` | `/zen/api/roles` | Créer un rôle |
+| `GET` | `/zen/api/roles/:id` | Détail d'un rôle |
+| `PUT` | `/zen/api/roles/:id` | Modifier un rôle |
+| `DELETE` | `/zen/api/roles/:id` | Supprimer un rôle |
+
+---
+
+## Sécurité
+
+**Rate limiting par IP.** Les actions `register`, `login`, `forgot_password`, `reset_password` et `verify_email` sont limitées par adresse IP. Quand l'IP est inconnue (pas de proxy configuré), le rate limiting est suspendu et un avertissement opérateur est émis une seule fois. Activer avec `ZEN_TRUST_PROXY=true` derrière un reverse proxy vérifié.
+
+**Champs anti-bot.** Chaque formulaire embarque un champ honeypot (`_hp`) et un timestamp de chargement (`_t`). Une soumission trop rapide (moins de 1,5 s), trop ancienne (plus de 10 min) ou avec un honeypot rempli est rejetée.
+
+**Cookie HttpOnly.** Le token de session n'est jamais exposé à JavaScript. `setSessionCookie` et `refreshSessionCookie` valident le token en base avant d'écrire le cookie pour éviter qu'un token arbitraire soit accepté.
+
+**Erreurs opaques.** Les erreurs internes sont loguées côté serveur et remplacées par un message générique côté client. Seules les `UserFacingError` (token expiré, etc.) remontent verbatim.
+
+---
+
+## Base de données
+
+`db.js` expose `createTables()` et `dropTables()`, appelés par `initializeZen()`.
+
+| Table | Contenu |
+|-------|---------|
+| `zen_auth_users` | Utilisateurs (`id`, `email`, `name`, `role`, `email_verified`, `image`) |
+| `zen_auth_sessions` | Sessions actives avec IP et user-agent |
+| `zen_auth_accounts` | Comptes liés à un provider (credential, OAuth) |
+| `zen_auth_verifications` | Tokens de vérification d'e-mail et de réinitialisation |
+
+---
+
+## Pages personnalisées
+
+Pour envelopper les pages auth dans un layout existant, voir [GUIDE-custom-login.md](./GUIDE-custom-login.md). Le guide couvre le pattern serveur/client, les props de chaque composant et la protection de route.