diff --git a/README.fr.md b/README.fr.md
index ee58737..b117430 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -756,6 +756,67 @@ Configurables via l'interface (Settings) ou l'API `/api/config`.
## đ DĂ©veloppement
+### đ§Ș Tests et CI â rĂšgles obligatoires (avant chaque push)
+
+> â ïž **RĂšgle absolue â contributeurs humains et agents IA** : ne jamais faire
+> de `git push` sans avoir exécuté **localement** la séquence complÚte
+> ci-dessous avec **100 % de succÚs**. Le push ne sert pas à découvrir les
+> échecs : la CI est un filet de sécurité, pas un outil de diagnostic.
+
+#### Vérifications locales avant commit
+
+| Ătape | Commande | Job CI Ă©quivalent |
+|---|---|---|
+| Lint backend | `ruff check backend/` | `lint` |
+| Validation des imports frontend | `node tests/frontend/validate-imports.mjs` | `lint` |
+| Tests unitaires frontend | `node tests/frontend/unit.test.mjs` | `lint` |
+| Tests backend | `pytest tests/ -q` | `test` |
+| **E2E Playwright** | `npm run test:e2e` (~5 min) | `e2e` |
+
+#### Tests E2E locaux (`npm run test:e2e`)
+
+Le script `scripts/run-e2e-local.sh` reproduit fidĂšlement les conditions du
+job CI `e2e` (`.gitea/workflows/ci.yml`) :
+
+- backend démarré nativement (venv `.venv-e2e`, Python 3.11 via `uv`) ;
+- authentification désactivée (`OBSIGATE_AUTH_ENABLED=false`) ;
+- fixtures `test_vault` (TestVault) et `test_dir` (TestDir) ;
+- port `2029` (baseURL de `playwright.config.ts`) ;
+- projet Playwright `chromium-desktop` uniquement (comme la CI).
+
+Prérequis : `uv`, Node.js ℠20, navigateurs Playwright
+(`npx playwright install chromium`). Le venv est créé automatiquement au
+premier lancement. Docker n'est pas nécessaire.
+
+```bash
+npm run test:e2e # suite complĂšte
+bash scripts/run-e2e-local.sh --headed # navigateur visible
+bash scripts/run-e2e-local.sh -g "reset panes" # filtre sur un test
+```
+
+La suite doit se terminer sur **tous les tests passant** (60 actuellement),
+sans échec ni dépendance aux retries. En cas d'échec : corriger et relancer
+localement jusqu'Ă 100 %, puis seulement commiter.
+
+#### RÚgles d'écriture des tests (leçon apprise)
+
+- **Vérifier chaque sélecteur dans le DOM réel** avant de l'utiliser dans un
+ test. Bug réel rencontré : un test ciblait `#cp-input` alors que l'input de
+ la palette porte uniquement la classe `.cp-input` (aucun id) â le test ne
+ pouvait passer dans aucun environnement.
+- Tout test nouveau ou modifiĂ© doit ĂȘtre **exĂ©cutĂ© localement** (au minimum
+ avec `-g "nom du test"`) avant le push.
+- Pas de contournements qui masquent une instabilité (`waitForTimeout`
+ arbitraires, fallback silencieux) : traiter la cause racine.
+
+#### Commits et push
+
+- Message de commit : sujet à l'impératif †50 caractÚres ; corps (72 col.)
+ uniquement s'il apporte une information utile.
+- Un commit = un changement logique (ne pas mĂȘler fix et refactoring).
+- Push uniquement quand les 5 étapes locales sont vertes. Pipeline CI :
+ `lint â test â security â build â e2e`.
+
### Structure du projet
```
diff --git a/README.md b/README.md
index a92adb2..fabdafe 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ObsiGate
-**Porte d'entrĂ©e web ultra-lĂ©ger pour vos vaults Obsidian** â AccĂ©dez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
+**Ultra-light web gateway for your Obsidian vaults** â Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
[]()
[](https://opensource.org/licenses/MIT)
@@ -10,12 +10,12 @@
```
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
-â [đ Recherche...] [â/đ ThĂšme] ObsiGate â
+â [đ Search...] [â/đ Theme] ObsiGate â
ââââââââââââââââŹâââââââââââââââââââââââââââââââââââââââââââ€
â SIDEBAR â CONTENT AREA â
-â ⌠Recettes â đ Titre du fichier â
-â đ Soupes â Tags: #recette #rapide â
-â đ Pizza â [Contenu Markdown rendu] â
+â ⌠Recipes â đ File Title â
+â đ Soups â Tags: #recipe #quick â
+â đ Pizza â [Rendered Markdown Content] â
â ⌠IT â â
â đ Docker â â
â Tags Cloud â â
@@ -24,128 +24,128 @@
---
-## đ Table des matiĂšres
+## đ Table of Contents
-- [Fonctionnalités](#fonctionnalites)
+- [Features](#features)
- [Architecture](#architecture)
-- [Prérequis](#prerequis)
-- [Installation rapide](#installation-rapide)
-- [Configuration détaillée](#configuration-detaillee)
-- [Variables d'environnement](#variables-denvironnement)
-- [đ Authentification](#authentification)
-- [Ajouter une nouvelle vault](#ajouter-une-nouvelle-vault)
-- [Build & déploiement avec build.sh](#build-deploiement-avec-buildsh)
-- [Desktop (Tauri) â Application native](#desktop-tauri-application-native)
-- [Utilisation](#utilisation)
+- [Prerequisites](#prerequisites)
+- [Quick Installation](#quick-installation)
+- [Detailed Configuration](#detailed-configuration)
+- [Environment Variables](#environment-variables)
+- [đ Authentication](#authentication)
+- [Adding a New Vault](#adding-a-new-vault)
+- [Build & Deployment with build.sh](#build--deployment-with-buildsh)
+- [Desktop (Tauri) â Native Application](#desktop-tauri--native-application)
+- [Usage](#usage)
- [API](#api)
- [Performance](#performance)
-- [Dépannage](#depannage)
-- [Stack technique](#stack-technique)
+- [Troubleshooting](#troubleshooting)
+- [Tech Stack](#tech-stack)
- [Changelog](#changelog)
---
-## ⚠Fonctionnalités
+## âš Features
-- **đ€ AI Editor intĂ©grĂ©** â Ăditeur CodeMirror 6 avec toolbar IA : amĂ©lioration, correction, traduction, gĂ©nĂ©ration, réécriture personnalisĂ©e, toolbox (liste, tableau, frontmatter, canvas) â multi-provider DeepSeek/OpenRouter/Gemini
-- **đșïž Vue graphe interactive** â Canvas force-directed avec Barnes-Hut O(n log n), filtres (tag, type), profondeur, mode focus, historique de navigation âââ, export PNG, aperçu au survol (Ctrl+click)
-- **đïž Multi-vault** : Visualisez plusieurs vaults Obsidian simultanĂ©ment
-- **đł Navigation arborescente** : Parcourez vos dossiers et fichiers dans la sidebar
-- **đ Recherche avancĂ©e** : Moteur TF-IDF avec stemming français, normalisation des accents, snippets surlignĂ©s, facettes, pagination et tri
-- **đĄ AutocomplĂ©tion intelligente** : Suggestions de fichiers, tags et historique avec navigation clavier
-- **đ§© Syntaxe de requĂȘte** : OpĂ©rateurs `tag:`, `#`, `vault:`, `title:`, `path:`, `ext:` avec chips visuels
-- **đ Historique de recherche** : PersistĂ© en localStorage (max 50 entrĂ©es, LIFO, dĂ©dupliquĂ©)
-- **đ·ïž Tag cloud** : Filtrage par tags extraits des frontmatters YAML
-- **đ Wikilinks** : Les `[[liens internes]]` Obsidian sont cliquables
-- **đŒïž Images Obsidian** : Support complet des syntaxes d'images Obsidian avec rĂ©solution intelligente
-- **đš Syntax highlight** : Coloration syntaxique des blocs de code
-- **đ ThĂšme clair/sombre** : Toggle persistĂ© en localStorage
-- **đĄ Synchronisation temps rĂ©el** : Surveillance automatique des fichiers via watchdog avec mise Ă jour incrĂ©mentale de l'index
-- **đĄ Server-Sent Events** : Notifications SSE pour les changements d'index avec reconnexion automatique
-- **â Gestion dynamique des vaults** : Ajout/suppression de vaults via API sans redĂ©marrage
-- **đł Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
-- **đ Authentification** : JWT + Argon2id, sessions persistantes, contrĂŽle d'accĂšs par vault
-- **đĄïž SĂ©curitĂ©** : Rate limiting, audit log, backup automatique, redaction de secrets, headers CSP, protection path traversal, utilisateur non-root
-- **⥠Performance** : Compression GZip, Cache-Control immutable, index inversé incrémental, search sans I/O disque
-- **â€ïž Healthcheck** : Endpoint `/api/health` intĂ©grĂ© pour Docker et monitoring
+- **đ€ Integrated AI Editor** â CodeMirror 6 editor with AI toolbar: improve, correct, translate, generate, custom rewrite, toolbox (list, table, frontmatter, canvas) â multi-provider DeepSeek/OpenRouter/Gemini
+- **đșïž Interactive Graph View** â Canvas force-directed with Barnes-Hut O(n log n), filters (tag, type), depth, focus mode, navigation history âââ, export PNG, preview on hover (Ctrl+click)
+- **đïž Multi-vault** : View multiple Obsidian vaults simultaneously
+- **đł Tree Navigation** : Browse your folders and files in the sidebar
+- **đ Advanced Search** : TF-IDF search engine with French stemming, accent normalization, highlighted snippets, facets, pagination, and sorting
+- **đĄ Smart Autocomplete** : Suggestions for files, tags, and history with keyboard navigation
+- **đ§© Query Syntax** : Operators `tag:`, `#`, `vault:`, `title:`, `path:`, `ext:` with visual chips
+- **đ Search History** : Persisted in localStorage (max 50 entries, LIFO, deduplicated)
+- **đ·ïž Tag Cloud** : Filtering by tags extracted from YAML frontmatters
+- **đ Wikilinks** : `[[internal links]]` from Obsidian are clickable
+- **đŒïž Obsidian Images** : Full support for all Obsidian image syntaxes with intelligent resolution
+- **đš Syntax Highlight** : Syntax highlighting for code blocks
+- **đ Light/Dark Theme** : Toggle persisted in localStorage
+- **đĄ Real-time Sync** : Automatic file monitoring via watchdog with incremental index updates
+- **đĄ Server-Sent Events** : SSE notifications for index changes with automatic reconnection
+- **â Dynamic Vault Management** : Add/remove vaults via API without restart
+- **đł Multi-platform Docker** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
+- **đ Authentication** : JWT + Argon2id, persistent sessions, per-vault access control
+- **đĄïž Security** : Rate limiting, audit log, automatic backup, secret redaction, CSP headers, path traversal protection, non-root user
+- **⥠Performance** : GZip compression, Cache-Control immutable, incremental inverted index, search with no disk I/O
+- **â€ïž Healthcheck** : `/api/health` endpoint integrated for Docker and monitoring
---
-## đ PrĂ©requis
+## đ Prerequisites
-### SystĂšme requis
+### System Requirements
- **Docker** >= 20.10
- **docker-compose** >= 2.0
-- **Espace disque** : ~200MB pour l'image Docker
+- **Disk space** : ~200MB for the Docker image
-### SystÚmes supportés
+### Supported Systems
- Linux (Ubuntu, Debian, CentOS, etc.)
-- macOS (Intel et Apple Silicon)
-- Windows (avec Docker Desktop)
-- NAS compatibles Docker (Synology, QNAP, etc.)
+- macOS (Intel and Apple Silicon)
+- Windows (with Docker Desktop)
+- Docker-compatible NAS (Synology, QNAP, etc.)
---
-## ⥠Installation rapide
+## ⥠Quick Installation
-### 1. Cloner le dépÎt
+### 1. Clone the Repository
```bash
git clone https://git.dracodev.net/Projets/ObsiGate.git
cd ObsiGate
```
-### 2. Configurer vos vaults et vos secrets
+### 2. Configure Your Vaults and Secrets
-Ăditez le fichier `docker-compose.yml` pour ajouter vos vaults Obsidian :
+Edit the `docker-compose.yml` file to add your Obsidian vaults:
```yaml
volumes:
- - /chemin/absolu/vers/votre/vault:/vaults/NomDeVotreVault:ro
+ - /absolute/path/to/your/vault:/vaults/YourVaultName:ro
```
-> **Important** : Le chemin doit ĂȘtre absolu et le volume en lecture seule (`:ro`)
+> **Important** : The path must be absolute and the volume must be read-only (`:ro`)
-Créez votre fichier `.env` pour l'authentification et les secrets :
+Create your `.env` file for authentication and secrets:
```bash
cp .env.example .env
-# Ăditez .env pour configurer vos mots de passe et options
+# Edit .env to configure your passwords and options
```
-> **Ne committez jamais `.env` !** Il est dans `.gitignore`. Utilisez `.env.example` comme référence.
+> **Never commit `.env`!** It is in `.gitignore`. Use `.env.example` as a reference.
-### 3. Lancer l'application
+### 3. Launch the Application
```bash
-# Rendre le script exécutable (une seule fois)
+# Make the script executable (once)
chmod +x build.sh
-# Build + déploiement en une commande
+# Build + deploy in one command
./build.sh
```
-> **C'est tout !** `build.sh` vérifie Docker, contrÎle vos volumes, construit l'image et démarre le conteneur automatiquement.
+> **That's it!** `build.sh` checks Docker, validates your volumes, builds the image, and starts the container automatically.
>
-> Options utiles : `./build.sh --help` pour l'aide, `./build.sh --cache` pour un rebuild plus rapide, `./build.sh --build-only` pour construire sans démarrer.
+> Useful options: `./build.sh --help` for help, `./build.sh --cache` for a faster rebuild, `./build.sh --build-only` to build without starting.
-### 4. Accéder à l'interface
+### 4. Access the Interface
-Ouvrez votre navigateur sur : **http://localhost:2020**
+Open your browser at: **http://localhost:2020**
---
-## âïž Configuration dĂ©taillĂ©e
+## âïž Detailed Configuration
-### Ătape 1 : PrĂ©paration des vaults
+### Step 1: Prepare Your Vaults
-1. **Localisez vos vaults Obsidian** sur votre systĂšme
-2. **Notez les chemins absolus** vers chaque dossier `.obsidian`
-3. **Vérifiez les permissions** : Docker doit pouvoir lire ces dossiers
+1. **Locate your Obsidian vaults** on your system
+2. **Note the absolute paths** to each `.obsidian` folder
+3. **Check permissions** : Docker must be able to read these folders
-### Ătape 2 : Configuration docker-compose.yml
+### Step 2: Configure docker-compose.yml
-Voici un exemple complet :
+Here's a complete example:
```yaml
services:
@@ -156,369 +156,370 @@ services:
container_name: obsigate
restart: unless-stopped
ports:
- - "2020:8080" # Port local 2020 â Port conteneur 8080
+ - "2020:8080" # Local port 2020 â Container port 8080
volumes:
- # Exemples de vaults (adaptez Ă vos chemins)
- - /home/user/Documents/Obsidian-Recettes:/vaults/Recettes:ro
+ # Vault examples (adapt to your paths)
+ - /home/user/Documents/Obsidian-Recipes:/vaults/Recipes:ro
- /home/user/Documents/Obsidian-IT:/vaults/IT:ro
- - /home/user/Documents/Obsidian-Perso:/vaults/Perso:ro
- # Persistance des données d'auth
+ - /home/user/Documents/Obsidian-Personal:/vaults/Personal:ro
+ # Auth data persistence
- ./data:/app/data
environment:
- # Configuration des vaults
- - VAULT_1_NAME=Recettes
- - VAULT_1_PATH=/vaults/Recettes
+ # Vault configuration
+ - VAULT_1_NAME=Recipes
+ - VAULT_1_PATH=/vaults/Recipes
- VAULT_2_NAME=IT
- VAULT_2_PATH=/vaults/IT
- - VAULT_3_NAME=Perso
- - VAULT_3_PATH=/vaults/Perso
- # Auth (les secrets sont dans .env)
+ - VAULT_3_NAME=Personal
+ - VAULT_3_PATH=/vaults/Personal
+ # Auth (secrets are in .env)
- OBSIGATE_AUTH_ENABLED=true
- OBSIGATE_ADMIN_USER=admin
env_file:
- - .env # Contient OBSIGATE_ADMIN_PASSWORD et autres secrets
+ - .env # Contains OBSIGATE_ADMIN_PASSWORD and other secrets
```
-### Ătape 3 : Build & dĂ©ploiement
+### Step 3: Build & Deploy
```bash
- # Utilisez le script de build automatisé (recommandé)
+ # Use the automated build script (recommended)
chmod +x build.sh
./build.sh
```
- Le script `build.sh` gÚre tout : vérification des prérequis, validation des volumes, construction de l'image et démarrage.
+ The `build.sh` script handles everything: prerequisite checks, volume validation, image building, and startup.
- **Alternative manuelle :**
+ **Manual alternative:**
```bash
docker compose build --no-cache
docker compose up -d
```
-> **Compatibilité Docker** : l'image utilise la variante minimale de `uvicorn` et une version de `fastapi` compatible (`0.110.3`) afin d'éviter certaines dépendances optionnelles natives (`watchfiles`, `uvloop`, `httptools`, `fastapi-cli`, etc.) qui peuvent échouer au build sur certaines plateformes comme Alpine, ARM ou i386.
+> **Docker Compatibility** : The image uses the minimal `uvicorn` variant and a compatible `fastapi` version (`0.110.3`) to avoid certain optional native dependencies (`watchfiles`, `uvloop`, `httptools`, `fastapi-cli`, etc.) that may fail to build on some platforms like Alpine, ARM, or i386.
---
-## đ Variables d'environnement
+## đ Environment Variables
-Les vaults sont configurĂ©es par paires de variables `VAULT_N_NAME` / `VAULT_N_PATH` (N = 1, 2, 3âŠ) :
+Vaults are configured using pairs of `VAULT_N_NAME` / `VAULT_N_PATH` variables (N = 1, 2, 3âŠ):
-| Variable | Description | Exemple |
+| Variable | Description | Example |
|----------|-------------|---------|
-| `VAULT_1_NAME` | Nom affiché de la vault | `Recettes` |
-| `VAULT_1_PATH` | Chemin dans le conteneur | `/vaults/Obsidian-RECETTES` |
-| `VAULT_1_ATTACHMENTS_PATH` | Chemin relatif vers le dossier d'attachements (optionnel) | `06_Boite_a_Outils/6.2_Attachments` |
-| `VAULT_1_SCAN_ATTACHMENTS` | Activer le scan d'images au démarrage (optionnel, défaut: true) | `true` |
-| `VAULT_2_NAME` | Nom affiché de la vault | `IT` |
-| `VAULT_2_PATH` | Chemin dans le conteneur | `/vaults/Obsidian_IT` |
+| `VAULT_1_NAME` | Display name of the vault | `Recipes` |
+| `VAULT_1_PATH` | Path inside the container | `/vaults/Obsidian-RECIPES` |
+| `VAULT_1_ATTACHMENTS_PATH` | Relative path to the attachments folder (optional) | `06_Toolbox/6.2_Attachments` |
+| `VAULT_1_SCAN_ATTACHMENTS` | Enable image scanning on startup (optional, default: true) | `true` |
+| `VAULT_2_NAME` | Display name of the vault | `IT` |
+| `VAULT_2_PATH` | Path inside the container | `/vaults/Obsidian_IT` |
-**RĂšgles de nommage :**
-- Utilisez uniquement des lettres, chiffres et tirets
-- Pas d'espaces ou caractÚres spéciaux
-- Le nom doit correspondre au chemin dans le conteneur
+**Naming rules:**
+- Use only letters, numbers, and hyphens
+- No spaces or special characters
+- The name must match the path inside the container
---
-## đ Authentification
+## đ Authentication
-> **DĂ©sactivĂ©e par dĂ©faut** â Compatible avec toutes les installations existantes.
+> **Disabled by default** â Compatible with all existing installations.
-ObsiGate supporte un systÚme d'authentification optionnel basé sur **JWT + Argon2id** avec contrÎle d'accÚs par vault.
+ObsiGate supports an optional authentication system based on **JWT + Argon2id** with per-vault access control.
-### Activer l'authentification
+### Enable Authentication
-1. **Copiez le template `.env`** :
+1. **Copy the `.env` template** :
```bash
cp .env.example .env
```
-2. **Ăditez `.env`** et dĂ©commentez/configurez les variables :
+2. **Edit `.env`** and uncomment/configure the variables :
```bash
- OBSIGATE_AUTH_ENABLED=*** OBSIGATE_ADMIN_USER=admin
- OBSIGATE_ADMIN_PASSWORD=votre_mot_de_passe # Laissez vide = auto-généré (voir logs)
- # OBSIGATE_SECURE_COOKIES=false # true si derriĂšre HTTPS
+ OBSIGATE_AUTH_ENABLED=true
+ OBSIGATE_ADMIN_USER=admin
+ OBSIGATE_ADMIN_PASSWORD=your_password # Leave empty = auto-generated (see logs)
+ # OBSIGATE_SECURE_COOKIES=false # true if behind HTTPS
```
-3. **Dans `docker-compose.yml`**, assurez-vous d'avoir :
+3. **In `docker-compose.yml`**, make sure you have :
```yaml
env_file:
- .env
```
-> **Ne mettez jamais de mot de passe dans `docker-compose.yml` !** Utilisez toujours `.env`.
+> **Never put passwords in `docker-compose.yml`!** Always use `.env`.
-### Premier démarrage
+### First Startup
-Si aucun utilisateur n'existe, ObsiGate crée automatiquement un compte admin et **affiche le mot de passe dans les logs** :
+If no user exists, ObsiGate automatically creates an admin account and **displays the password in the logs** :
```bash
-docker-compose logs obsigate | grep -A4 "PREMIER"
+docker-compose logs obsigate | grep -A4 "FIRST"
```
```
============================================================
-PREMIER DĂMARRAGE â Compte admin créé automatiquement
- Utilisateur : admin
- Mot de passe : xK9mQ3pLr7wN2jT5
-CHANGEZ CE MOT DE PASSE dĂšs la premiĂšre connexion !
+FIRST STARTUP â Admin account created automatically
+ Username : admin
+ Password : xK9mQ3pLr7wN2jT5
+CHANGE THIS PASSWORD on first login!
============================================================
```
-### Gestion des utilisateurs via CLI
+### User Management via CLI
```bash
-# Créer un utilisateur
-docker exec obsigate python backend/create_admin.py create alice MonMotDePasse --role user --vaults Recettes IT
+# Create a user
+docker exec obsigate python backend/create_admin.py create alice MyPassword --role user --vaults Recipes IT
-# Créer un admin avec accÚs total
+# Create an admin with full access
docker exec obsigate python backend/create_admin.py create bob SecretPass --role admin --vaults "*"
-# Lister les utilisateurs
+# List users
docker exec obsigate python backend/create_admin.py list
-# Supprimer un utilisateur
+# Delete a user
docker exec obsigate python backend/create_admin.py delete alice
```
-### Interface d'administration
+### Admin Interface
-Lorsqu'un compte **admin** est connectĂ©, une icĂŽne đĄïž apparaĂźt dans le header. Cliquer dessus ouvre le panneau d'administration permettant de :
+When an **admin** account is logged in, a đĄïž icon appears in the header. Clicking it opens the admin panel allowing you to:
-- Lister tous les utilisateurs
-- Créer / modifier / supprimer des utilisateurs
-- Assigner les vaults accessibles par utilisateur
-- Activer/désactiver des comptes
+- List all users
+- Create / edit / delete users
+- Assign accessible vaults per user
+- Enable/disable accounts
-### ContrĂŽle d'accĂšs par vault
+### Per-Vault Access Control
-| Valeur vaults | AccĂšs |
-|---------------|-------|
-| `["*"]` | Toutes les vaults (y compris futures) â dĂ©faut admin |
-| `["Recettes", "IT"]` | Uniquement ces vaults |
-| `[]` | Aucun accĂšs |
+| vaults value | Access |
+|--------------|--------|
+| `["*"]` | All vaults (including future ones) â admin default |
+| `["Recipes", "IT"]` | Only these vaults |
+| `[]` | No access |
-### Variables d'environnement d'auth
+### Auth Environment Variables
-| Variable | Description | Défaut |
-|----------|-------------|--------|
-| `OBSIGATE_AUTH_ENABLED` | Activer l'authentification | `false` |
-| `OBSIGATE_ADMIN_USER` | Nom de l'admin auto-créé | `admin` |
-| `OBSIGATE_ADMIN_PASSWORD` | Mot de passe admin (vide = auto-généré) | *(auto)* |
-| `OBSIGATE_SECURE_COOKIES` | Cookie `Secure` (HTTPS uniquement) | `false` |
-| `OBSIGATE_ACCESS_TOKEN_TTL` | Durée de vie token JWT (secondes) | `3600` |
-| `OBSIGATE_REFRESH_TOKEN_TTL` | Durée de vie refresh token (secondes) | `2592000` |
-| `OBSIGATE_LOGIN_MAX_ATTEMPTS` | Tentatives de login max par IP | `10` |
-| `OBSIGATE_LOGIN_WINDOW_SECONDS` | FenĂȘtre de rate limiting (secondes) | `900` |
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `OBSIGATE_AUTH_ENABLED` | Enable authentication | `false` |
+| `OBSIGATE_ADMIN_USER` | Auto-created admin username | `admin` |
+| `OBSIGATE_ADMIN_PASSWORD` | Admin password (empty = auto-generated) | *(auto)* |
+| `OBSIGATE_SECURE_COOKIES` | `Secure` cookie (HTTPS only) | `false` |
+| `OBSIGATE_ACCESS_TOKEN_TTL` | JWT token lifetime (seconds) | `3600` |
+| `OBSIGATE_REFRESH_TOKEN_TTL` | Refresh token lifetime (seconds) | `2592000` |
+| `OBSIGATE_LOGIN_MAX_ATTEMPTS` | Max login attempts per IP | `10` |
+| `OBSIGATE_LOGIN_WINDOW_SECONDS` | Rate limiting window (seconds) | `900` |
->Toutes ces variables sont documentées dans `.env.example`.
+>All these variables are documented in `.env.example`.
-### Volume pour la persistance
+### Volume for Persistence
-Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data`. Ajoutez un volume pour les persister :
+Auth data (`users.json`, `secret.key`) is stored in `/app/data`. Add a volume to persist it:
```yaml
volumes:
# vaults...
- - ./data:/app/data # Persistance des utilisateurs et clé JWT
+ - ./data:/app/data # Persist users and JWT key
```
---
-## â Ajouter une nouvelle vault
+## â Adding a New Vault
-### MĂ©thode 1 : Ădition directe
+### Method 1: Direct Editing
-1. **ArrĂȘtez le conteneur** :
+1. **Stop the container** :
```bash
docker-compose down
```
-2. **Ajoutez un volume** dans `docker-compose.yml` :
+2. **Add a volume** in `docker-compose.yml` :
```yaml
volumes:
- - /nouveau/chemin/vault:/vaults/NouvelleVault:ro
+ - /new/vault/path:/vaults/NewVault:ro
```
-3. **Ajoutez les variables d'environnement** :
+3. **Add the environment variables** :
```yaml
environment:
- - VAULT_4_NAME=NouvelleVault
- - VAULT_4_PATH=/vaults/NouvelleVault
+ - VAULT_4_NAME=NewVault
+ - VAULT_4_PATH=/vaults/NewVault
```
-4. **Redémarrez** :
+4. **Restart** :
```bash
./build.sh
```
-### Méthode 2 : Hot-reload (recommandé)
+### Method 2: Hot-reload (recommended)
-1. **Ajoutez le volume et les variables** comme ci-dessus
-2. **Appliquez les changements** :
+1. **Add the volume and variables** as above
+2. **Apply the changes** :
```bash
./build.sh
```
-3. **Rechargez l'index** via l'interface ou l'API :
+3. **Reload the index** via the interface or the API :
```bash
curl http://localhost:2020/api/index/reload
```
-### Méthode 3 : API dynamique (sans redémarrage)
+### Method 3: Dynamic API (without restart)
-Ajoutez une vault Ă chaud via l'API (le volume doit dĂ©jĂ ĂȘtre montĂ©) :
+Add a vault on the fly via the API (the volume must already be mounted):
```bash
curl -X POST http://localhost:2020/api/vaults/add \
-H "Content-Type: application/json" \
- -d '{"name": "NouvelleVault", "path": "/vaults/NouvelleVault"}'
+ -d '{"name": "NewVault", "path": "/vaults/NewVault"}'
```
-Supprimez une vault :
+Remove a vault:
```bash
-curl -X DELETE http://localhost:2020/api/vaults/NouvelleVault
+curl -X DELETE http://localhost:2020/api/vaults/NewVault
```
---
-## đš Build & dĂ©ploiement avec build.sh
+## đš Build & Deployment with build.sh
-Le script `build.sh` est la méthode recommandée pour construire et déployer ObsiGate.
+The `build.sh` script is the recommended way to build and deploy ObsiGate.
-### Utilisation de base
+### Basic Usage
```bash
-chmod +x build.sh # une seule fois
-./build.sh # build from scratch + démarrage
+chmod +x build.sh # once
+./build.sh # build from scratch + start
```
-### Options disponibles
+### Available Options
| Option | Description |
|--------|-------------|
-| `--help`, `-h` | Affiche l'aide complĂšte |
-| `--build-only` | Construit l'image sans démarrer le conteneur |
-| `--no-cache` | Rebuild complet sans cache Docker **(défaut)** |
-| `--cache` | Utilise le cache Docker (plus rapide si peu de changements) |
-| `--progress=plain` | Sortie verbeuse (recommandé pour le debug) |
-| `--progress=tty` | Sortie interactive avec barres de progression |
+| `--help`, `-h` | Display full help |
+| `--build-only` | Build the image without starting the container |
+| `--no-cache` | Full rebuild without Docker cache **(default)** |
+| `--cache` | Use Docker cache (faster if few changes) |
+| `--progress=plain` | Verbose output (recommended for debugging) |
+| `--progress=tty` | Interactive output with progress bars |
-### Ce que fait le script
+### What the Script Does
-1. **Vérifie** que Docker et Docker Compose sont installés (avec numéros de version)
-2. **Valide** la présence et la syntaxe du fichier `docker-compose.yml`
-3. **Vérifie chaque volume** monté : avertit si un répertoire source n'existe pas
-4. **Construit** l'image Docker via `docker compose build` (multi-stage, ~180MB)
-5. **Démarre** le conteneur via `docker compose up -d`
-6. **Affiche** le statut du conteneur puis les logs en temps réel
+1. **Checks** that Docker and Docker Compose are installed (with version numbers)
+2. **Validates** the presence and syntax of the `docker-compose.yml` file
+3. **Checks each mounted volume** : warns if a source directory does not exist
+4. **Builds** the Docker image via `docker compose build` (multi-stage, ~180MB)
+5. **Starts** the container via `docker compose up -d`
+6. **Displays** the container status then real-time logs
-### Exemples
+### Examples
```bash
-# Build propre + démarrage (recommandé aprÚs changement de code)
+# Clean build + start (recommended after code changes)
./build.sh
-# Rebuild rapide avec cache (changements mineurs frontend uniquement)
+# Fast rebuild with cache (minor frontend changes only)
./build.sh --cache
-# Vérifier le build sans démarrer (pratique pour tester avant mise en prod)
+# Test the build without starting (useful for pre-prod testing)
./build.sh --build-only
-# Sortie verbeuse pour débugger un échec de build
+# Verbose output to debug a build failure
./build.sh --progress=plain
```
-### ArrĂȘter / redĂ©marrer
+### Stopping / Restarting
```bash
-docker compose down # ArrĂȘter le conteneur
-docker compose up -d # Redémarrer sans rebuild
-docker compose logs -f # Voir les logs
+docker compose down # Stop the container
+docker compose up -d # Restart without rebuild
+docker compose logs -f # View logs
```
---
-## ïżœïž Rendu d'images Obsidian
+## đŒïž Obsidian Image Rendering
-ObsiGate supporte **toutes les syntaxes d'images Obsidian** avec un systÚme de résolution intelligent multi-stratégies.
+ObsiGate supports **all Obsidian image syntaxes** with an intelligent multi-strategy resolution system.
-### Syntaxes supportées
+### Supported Syntaxes
-1. **Standard Markdown avec attributs HTML** (compatible Obsidian) :
+1. **Standard Markdown with HTML attributes** (Obsidian-compatible) :
```markdown
[
](https://example.com)
```
-2. **Wiki-link embed avec chemin complet** :
+2. **Wiki-link embed with full path** :
```markdown
- ![[06_Boite_a_Outils/6.2_Attachments/image.svg]]
+ ![[06_Toolbox/6.2_Attachments/image.svg]]
```
-3. **Wiki-link embed avec nom de fichier uniquement** :
+3. **Wiki-link embed with filename only** :
```markdown
![[image.svg]]
```
-4. **Markdown standard** :
+4. **Standard Markdown** :
```markdown

```
-### Résolution intelligente des chemins
+### Intelligent Path Resolution
-ObsiGate utilise 7 stratégies de résolution par ordre de priorité :
+ObsiGate uses 7 resolution strategies in order of priority:
-1. **Chemin absolu** : Si le chemin est absolu et existe
-2. **Dossier d'attachements configuré** : Via `VAULT_N_ATTACHMENTS_PATH`
-3. **Index de démarrage (match unique)** : Recherche par nom de fichier dans l'index
-4. **MĂȘme rĂ©pertoire** : Relatif au fichier markdown courant
-5. **Racine du vault** : Relatif Ă la racine du vault
-6. **Index de dĂ©marrage (match le plus proche)** : Si plusieurs fichiers portent le mĂȘme nom
-7. **Fallback** : Affiche un placeholder stylisé `[image not found: filename.ext]`
+1. **Absolute path** : If the path is absolute and exists
+2. **Configured attachments folder** : Via `VAULT_N_ATTACHMENTS_PATH`
+3. **Startup index (unique match)** : Search by filename in the index
+4. **Same directory** : Relative to the current markdown file
+5. **Vault root** : Relative to the vault root
+6. **Startup index (closest match)** : If multiple files have the same name
+7. **Fallback** : Display a styled placeholder `[image not found: filename.ext]`
### Configuration
-Pour optimiser la résolution, configurez le dossier d'attachements de chaque vault :
+To optimize resolution, configure the attachments folder for each vault:
```yaml
environment:
- - VAULT_1_NAME=MonVault
- - VAULT_1_PATH=/vaults/MonVault
- - VAULT_1_ATTACHMENTS_PATH=Assets/Images # Chemin relatif
- - VAULT_1_SCAN_ATTACHMENTS=true # Activer le scan (défaut)
+ - VAULT_1_NAME=MyVault
+ - VAULT_1_PATH=/vaults/MyVault
+ - VAULT_1_ATTACHMENTS_PATH=Assets/Images # Relative path
+ - VAULT_1_SCAN_ATTACHMENTS=true # Enable scanning (default)
```
-### Rescan manuel
+### Manual Rescan
-Pour rescanner les images d'un vault aprĂšs ajout/suppression :
+To rescan images in a vault after adding/removing:
```bash
-curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
+curl -X POST http://localhost:2020/api/attachments/rescan/MyVault
```
---
-## đ„ïž Desktop (Tauri) â Application native
+## đ„ïž Desktop (Tauri) â Native Application
-ObsiGate Desktop est une application native construite avec [Tauri](https://tauri.app/) (Rust + webview systĂšme). Elle embarque le backend Python et le frontend dans un exĂ©cutable standalone â zĂ©ro Docker, zĂ©ro ligne de commande.
+ObsiGate Desktop is a native application built with [Tauri](https://tauri.app/) (Rust + system webview). It embeds the Python backend and frontend in a standalone executable â zero Docker, zero command line.
-> đ§ **Version 2.0.0 â binaires en cours de stabilisation.** Pour l'instant, le build depuis les sources est recommandĂ©.
+> đ§ **Version 2.0.0 â binaries are being stabilized.** For now, building from source is recommended.
-### Fonctionnalités desktop natives
+### Native Desktop Features
-| Fonctionnalité | Web | Desktop |
+| Feature | Web | Desktop |
|---|---|---|
-| AccÚs fichiers local | Via upload | Natif (sélecteur dossier) |
-| ThĂšme systĂšme | Manuel | Auto (suit OS dark/light) |
-| Notifications | Service Worker | Natif OS |
-| Associations `.md` | â | â
« Ouvrir avec ObsiGate » |
-| Tray icon | â | â
Barre des tĂąches |
-| Auto-update | â | â
Vérifie les releases Gitea |
-| Mode hors-ligne | Limité | Complet (backend local) |
+| Local file access | Via upload | Native (folder picker) |
+| System theme | Manual | Auto (follows OS dark/light) |
+| Notifications | Service Worker | Native OS |
+| `.md` associations | â | â
"Open with ObsiGate" |
+| Tray icon | â | â
Taskbar |
+| Auto-update | â | â
Checks Gitea releases |
+| Offline mode | Limited | Full (local backend) |
-### Téléchargement (binaires pré-buildés)
+### Download (Pre-built Binaries)
-Les releases sont publiées sur [Gitea](https://git.dracodev.net/Projets/ObsiGate/releases) :
+Releases are published on [Gitea](https://git.dracodev.net/Projets/ObsiGate/releases) :
-| Plateforme | Format |
+| Platform | Format |
|---|---|
| **Linux** | `.deb` + `.AppImage` |
| **Windows** | `.msi` + `.exe` (NSIS) |
@@ -528,9 +529,9 @@ Les releases sont publiées sur [Gitea](https://git.dracodev.net/Projets/ObsiGat
```bash
# .deb (Debian/Ubuntu/Deepin)
sudo dpkg -i obsigate_2.0.0_amd64.deb
-# Lancer : ObsiGate depuis le menu applications ou `obsigate-desktop`
+# Launch : ObsiGate from the applications menu or `obsigate-desktop`
-# .AppImage (toute distrib)
+# .AppImage (any distro)
chmod +x ObsiGate_2.0.0_amd64.AppImage
./ObsiGate_2.0.0_amd64.AppImage
```
@@ -538,39 +539,39 @@ chmod +x ObsiGate_2.0.0_amd64.AppImage
#### Windows
```cmd
-:: Double-cliquer sur ObsiGate_2.0.0_x64.msi
-:: Ou lancer ObsiGate depuis le menu Démarrer
+:: Double-click ObsiGate_2.0.0_x64.msi
+:: Or launch ObsiGate from the Start menu
```
-### Démarrage
+### Getting Started
-1. **Lance l'application** depuis le menu ou la ligne de commande
-2. Le backend Python démarre automatiquement sur `127.0.0.1:17890`
-3. La fenĂȘtre s'ouvre et charge l'interface ObsiGate
-4. **Premier lancement** : sélectionne le dossier de tes vaults Obsidian via le sélecteur natif
-5. Pour fermer : icĂŽne tray â Quitter (arrĂȘt propre du backend)
+1. **Launch the application** from the menu or command line
+2. The Python backend starts automatically on `127.0.0.1:17890`
+3. The window opens and loads the ObsiGate interface
+4. **First launch** : select your Obsidian vaults folder via the native picker
+5. To close : tray icon â Quit (clean backend shutdown)
-### Build depuis les sources
+### Build from Source
-Guide détaillé : [desktop/README.md](./desktop/README.md).
+Detailed guide: [desktop/README.md](./desktop/README.md).
-#### Prérequis communs
+#### Common Prerequisites
-| Outil | Version | Installation |
+| Tool | Version | Installation |
|---|---|---|
| Rust (cargo) | â„ 1.75 | `rustup` |
| Tauri CLI | â„ 2.0 | `cargo install tauri-cli` |
| Git | â | â |
-| DĂ©pendances systĂšme Linux | â | `sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev` |
+| Linux system dependencies | â | `sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev` |
-> **Important â staging :** `tauri.conf.json` embarque `backend/**` et `frontend/**` **depuis le dossier `desktop/`**.
-> Les scripts de build copient automatiquement `../backend` et `../frontend` dans `desktop/` avant `cargo tauri build`.
-> Sans ce staging, le build échoue avec « glob pattern backend/**/* path not found ».
+> **Important â staging :** `tauri.conf.json` embeds `backend/**` and `frontend/**` **from the `desktop/` folder**.
+> The build scripts automatically copy `../backend` and `../frontend` into `desktop/` before `cargo tauri build`.
+> Without this staging, the build fails with "glob pattern backend/**/* path not found".
#### đȘ Windows â `build-windows.bat`
```cmd
-REM Prérequis (via Scoop) : rustup, curl, git
+REM Prerequisites (via Scoop) : rustup, curl, git
scoop install rustup curl git
rustup default stable
cargo install tauri-cli
@@ -579,17 +580,17 @@ cd desktop
build-windows.bat
```
-Ătapes du script :
+Script steps:
-1. Tue les processus Python résiduels (`taskkill /F /IM python.exe`)
-2. TĂ©lĂ©charge **Python 3.11 embed** (python.org) â `desktop\python-embed\` + activation de pip (`python311._pth`)
-3. `pip install -r ..\backend\requirements.txt` dans l'embed
-4. **Staging** : copie `..\backend` et `..\frontend` dans `desktop\`
+1. Kills residual Python processes (`taskkill /F /IM python.exe`)
+2. Downloads **Python 3.11 embed** (python.org) â `desktop\python-embed\` + enables pip (`python311._pth`)
+3. `pip install -r ..\backend\requirements.txt` in the embed
+4. **Staging** : copies `..\backend` and `..\frontend` to `desktop\`
5. `cargo tauri build --target x86_64-pc-windows-msvc --bundles nsis`
-6. Copie `python-embed` à cÎté de l'exécutable (`target\x86_64-pc-windows-msvc\release\`) pour le mode dev local
-7. Nettoie les dossiers stagés
+6. Copies `python-embed` next to the executable (`target\x86_64-pc-windows-msvc\release\`) for local dev mode
+7. Cleans the staged folders
-â **Artefact :** `desktop\target\x86_64-pc-windows-msvc\release\bundle\nsis\ObsiGate_2.0.0_x64-setup.exe`
+â **Artifact :** `desktop\target\x86_64-pc-windows-msvc\release\bundle\nsis\ObsiGate_2.0.0_x64-setup.exe`
#### đ§ Linux â `build-linux.sh`
@@ -599,38 +600,38 @@ chmod +x build-linux.sh
./build-linux.sh
```
-Ătapes du script :
+Script steps:
-1. Vérifie Rust + Tauri CLI, installe les dépendances systÚme (apt)
-2. Crée un venv Python `desktop/python-embed/venv` + `pip install -r ../backend/requirements.txt`
-3. **Staging** : copie `../backend` et `../frontend` dans `desktop/`
+1. Checks Rust + Tauri CLI, installs system dependencies (apt)
+2. Creates a Python venv `desktop/python-embed/venv` + `pip install -r ../backend/requirements.txt`
+3. **Staging** : copies `../backend` and `../frontend` to `desktop/`
4. `cargo tauri build --target x86_64-unknown-linux-gnu --bundles deb,appimage`
-5. Copie le runtime (`python-embed/`, `backend/`, `frontend/`) à cÎté de l'exécutable
+5. Copies the runtime (`python-embed/`, `backend/`, `frontend/`) next to the executable
-â **Artefacts :**
+â **Artifacts :**
- `desktop/target/x86_64-unknown-linux-gnu/release/bundle/deb/obsigate_2.0.0_amd64.deb`
- `desktop/target/x86_64-unknown-linux-gnu/release/bundle/appimage/ObsiGate_2.0.0_amd64.AppImage`
-### đ€ Builds CI/CD â artefacts automatiques
+### đ€ CI/CD Builds â Automatic Artifacts
-**Oui** â le workflow [`.gitea/workflows/desktop-build.yml`](./.gitea/workflows/desktop-build.yml) construit les binaires desktop Ă chaque push sur `main` touchant `desktop/**`, `frontend/**` ou `backend/**` (et manuellement via `workflow_dispatch`), sur des **runners self-hosted** :
+**Yes** â the workflow [`.gitea/workflows/desktop-build.yml`](./.gitea/workflows/desktop-build.yml) builds desktop binaries on every push to `main` touching `desktop/**`, `frontend/**`, or `backend/**` (and manually via `workflow_dispatch`), on **self-hosted runners** :
-| Job | Runner | Artefacts (conservés 30 jours) |
+| Job | Runner | Artifacts (retained 30 days) |
|---|---|---|
| `build-windows` | `[self-hosted, windows, desktop]` | `desktop/target/release/bundle/msi/*.msi` |
| `build-linux` | `[self-hosted, linux, desktop]` | `*.AppImage` + `*.deb` |
-- Les artefacts sont téléchargeables depuis la page **Actions** du run Gitea.
-- La publication en **Gitea Release** est prévue sur les tags `v*` (étape `Publish to Gitea Release`).
-- Le workflow web [`.gitea/workflows/ci.yml`](./.gitea/workflows/ci.yml) gĂšre de son cĂŽtĂ© lint â tests â sĂ©curitĂ© â build Docker â e2e Playwright.
+- Artifacts are downloadable from the **Actions** page of the Gitea run.
+- Publishing to **Gitea Release** is planned for tags `v*` (`Publish to Gitea Release` step).
+- The web workflow [`.gitea/workflows/ci.yml`](./.gitea/workflows/ci.yml) separately handles lint â tests â security â Docker build â e2e Playwright.
-### Architecture Desktop
+### Desktop Architecture
```
ââââââââââââââââââââââââââââââââââââââââââââââ
â Tauri (Rust) â
-â ââ Webview (webview systĂšme) â
+â ââ Webview (system webview) â
â â ââ Frontend (HTML/JS/CSS) â
â ââ Sidecar Python â
â ââ uvicorn backend.main:app â
@@ -638,246 +639,249 @@ chmod +x build-linux.sh
ââââââââââââââââââââââââââââââââââââââââââââââ
```
-Cycle de vie : Tauri spawn le backend Python â health check â ouvre la webview. Ă la fermeture : SIGTERM â arrĂȘt propre â nettoyage.
+Lifecycle: Tauri spawns the Python backend â health check â opens the webview. On close: SIGTERM â clean shutdown â cleanup.
---
-## ïżœđ Utilisation
+## đ Usage
-### Interface web
+### Web Interface
-1. **Navigation** : Cliquez sur les vaults dans la sidebar pour les développer
-2. **Recherche** : Utilisez la barre de recherche pour chercher dans toutes les vaults
-3. **Tags** : Cliquez sur les tags pour filtrer les contenus
-4. **Wikilinks** : Les liens `[[page]]` sont cliquables et navigables
-5. **Images** : Toutes les syntaxes d'images Obsidian sont rendues automatiquement
-6. **ThĂšme** : Basculez entre thĂšme clair/sombre avec l'icĂŽne đ/âïž
+1. **Navigation** : Click on vaults in the sidebar to expand them
+2. **Search** : Use the search bar to search across all vaults
+3. **Tags** : Click on tags to filter content
+4. **Wikilinks** : `[[page]]` links are clickable and navigable
+5. **Images** : All Obsidian image syntaxes are rendered automatically
+6. **Theme** : Toggle between light/dark theme with the đ/âïž icon
-### Raccourcis clavier
+### Keyboard Shortcuts
-| Action | Raccourci |
-|--------|-----------|
-| Recherche | `Ctrl + K` ou `/` |
-| Toggle thĂšme | `Ctrl + T` |
-| Focus recherche | `Esc` |
+| Action | Shortcut |
+|--------|----------|
+| Search | `Ctrl + K` or `/` |
+| Toggle theme | `Ctrl + T` |
+| Focus search | `Esc` |
---
## đ API
-ObsiGate expose une API REST complĂšte :
+ObsiGate exposes a complete REST API :
-| Endpoint | Description | Méthode | Auth |
-|----------|-------------|---------|------|
-| `/api/health` | Health check (status, version, stats) | GET | Non |
-| `/api/auth/status` | Statut auth (activé, utilisateurs présents) | GET | Non |
-| `/api/auth/login` | Connexion (retourne access token + cookie refresh) | POST | Non |
-| `/api/auth/refresh` | Renouveler l'access token via cookie refresh | POST | Cookie |
-| `/api/auth/logout` | Déconnexion + révocation refresh token | POST | Oui |
-| `/api/auth/me` | Infos utilisateur courant | GET | Oui |
-| `/api/auth/change-password` | Changer son mot de passe | POST | Oui |
-| `/api/auth/admin/users` | Lister / créer des utilisateurs | GET/POST | Admin |
-| `/api/auth/admin/users/{u}` | Modifier / supprimer un utilisateur | PATCH/DELETE | Admin |
-| `/api/vaults` | Liste des vaults (filtrée par permissions) | GET | Oui |
-| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET | Oui |
-| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET | Oui |
-| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET | Oui |
-| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET | Oui |
-| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT | Oui |
-| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE | Oui |
-| `/api/search/advanced` | Recherche avancée TF-IDF | GET | Oui |
-| `/api/suggest` / `/api/tags/suggest` | Autocomplétion | GET | Oui |
-| `/api/tags?vault=` | Tags uniques avec compteurs | GET | Oui |
-| `/api/index/reload` | Force un re-scan des vaults | GET | Admin |
-| `/api/events` | Flux SSE temps réel | GET | Oui |
-| `/api/vaults/add` / `/api/vaults/{name}` | Gestion dynamique des vaults | POST/DELETE | Admin |
-| `/api/image/{vault}?path=` | Servir une image | GET | Oui |
-| `/api/config` | Lire / écrire la configuration | GET/POST | Oui/Admin |
-| `/api/diagnostics` | Statistiques index et mémoire | GET | Admin |
+| Endpoint | Description | Method | Auth |
+|----------|-------------|--------|------|
+| `/api/health` | Health check (status, version, stats) | GET | No |
+| `/api/auth/status` | Auth status (enabled, users present) | GET | No |
+| `/api/auth/login` | Login (returns access token + refresh cookie) | POST | No |
+| `/api/auth/refresh` | Refresh access token via refresh cookie | POST | Cookie |
+| `/api/auth/logout` | Logout + revoke refresh token | POST | Yes |
+| `/api/auth/me` | Current user info | GET | Yes |
+| `/api/auth/change-password` | Change password | POST | Yes |
+| `/api/auth/admin/users` | List / create users | GET/POST | Admin |
+| `/api/auth/admin/users/{u}` | Modify / delete a user | PATCH/DELETE | Admin |
+| `/api/vaults` | List vaults (filtered by permissions) | GET | Yes |
+| `/api/browse/{vault}?path=` | Browse folders | GET | Yes |
+| `/api/file/{vault}?path=` | Rendered content of a file | GET | Yes |
+| `/api/file/{vault}/raw?path=` | Raw content of a file | GET | Yes |
+| `/api/file/{vault}/download?path=` | Download a file | GET | Yes |
+| `/api/file/{vault}/save?path=` | Save a file | PUT | Yes |
+| `/api/file/{vault}?path=` | Delete a file | DELETE | Yes |
+| `/api/search/advanced` | Advanced TF-IDF search | GET | Yes |
+| `/api/suggest` / `/api/tags/suggest` | Autocomplete | GET | Yes |
+| `/api/tags?vault=` | Unique tags with counters | GET | Yes |
+| `/api/index/reload` | Force a rescan of vaults | GET | Admin |
+| `/api/events` | Real-time SSE stream | GET | Yes |
+| `/api/vaults/add` / `/api/vaults/{name}` | Dynamic vault management | POST/DELETE | Admin |
+| `/api/image/{vault}?path=` | Serve an image | GET | Yes |
+| `/api/config` | Read / write configuration | GET/POST | Yes/Admin |
+| `/api/diagnostics` | Index and memory statistics | GET | Admin |
-> Quand `OBSIGATE_AUTH_ENABLED=false`, tous les endpoints sont accessibles sans token.
+> When `OBSIGATE_AUTH_ENABLED=false`, all endpoints are accessible without a token.
-> Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur `/docs` (Swagger UI).
+> All endpoints expose documented Pydantic schemas. Interactive docs are available at `/docs` (Swagger UI).
-**Exemple d'utilisation :**
+**Usage Example :**
```bash
# Health check
curl http://localhost:2020/api/health
-# Lister les vaults
+# List vaults
curl http://localhost:2020/api/vaults
-# Recherche simple (legacy)
-curl "http://localhost:2020/api/search?q=recette&vault=all"
+# Simple search (legacy)
+curl "http://localhost:2020/api/search?q=recipe&vault=all"
-# Recherche avancée avec TF-IDF, facettes et pagination
-curl "http://localhost:2020/api/search/advanced?q=recette%20tag:cuisine&vault=all&limit=20&offset=0&sort=relevance"
+# Advanced search with TF-IDF, facets, and pagination
+curl "http://localhost:2020/api/search/advanced?q=recipe%20tag:cuisine&vault=all&limit=20&offset=0&sort=relevance"
-# Autocomplétion de titres
+# Title autocomplete
curl "http://localhost:2020/api/suggest?q=piz&vault=all"
-# Autocomplétion de tags
+# Tag autocomplete
curl "http://localhost:2020/api/tags/suggest?q=rec&vault=all"
-# Obtenir un fichier
-curl "http://localhost:2020/api/file/Recettes?path=pizza.md"
-## đ Recherche avancĂ©e
+# Get a file
+curl "http://localhost:2020/api/file/Recipes?path=pizza.md"
+```
-### Syntaxe de requĂȘte
+## đ Advanced Search
-| Opérateur | Description | Exemple |
-|-----------|-------------|---------|
-| `tag:` | Filtrer par tag | `tag:recette docker` |
-| `#` | Raccourci tag | `#linux serveur` |
-| `vault:` | Filtrer par vault | `vault:IT kubernetes` |
-| `title:` | Filtrer par titre | `title:pizza` |
-| `path:` | Filtrer par chemin | `path:recettes/soupes` |
-| `ext:` | Filtrer par type de fichier | `ext:md kubernetes` |
-| `"phrase exacte"` | Recherche de phrase | `tag:"multi mots"` |
+### Query Syntax
-Exemples de filtre par extension : `ext:sh` pour les scripts bash, `ext:py` pour les scripts Python, `ext:md` pour les fichiers Markdown.
+| Operator | Description | Example |
+|----------|-------------|---------|
+| `tag:` | Filter by tag | `tag:recipe docker` |
+| `#` | Tag shortcut | `#linux server` |
+| `vault:` | Filter by vault | `vault:IT kubernetes` |
+| `title:` | Filter by title | `title:pizza` |
+| `path:` | Filter by path | `path:recipes/soups` |
+| `ext:` | Filter by file type | `ext:md kubernetes` |
+| `"exact phrase"` | Phrase search | `tag:"multiple words"` |
-Les opérateurs sont combinables : `tag:linux vault:IT ext:md serveur web` recherche "serveur web" dans les fichiers Markdown du vault IT avec le tag linux.
+Extension filter examples: `ext:sh` for bash scripts, `ext:py` for Python scripts, `ext:md` for Markdown files.
-### Raccourcis clavier
+Operators are combinable: `tag:linux vault:IT ext:md server web` searches for "server web" in Markdown files of the IT vault with the linux tag.
-| Raccourci | Action |
-|-----------|--------|
-| `Ctrl+K` / `Cmd+K` | Focaliser la barre de recherche |
-| `/` | Focaliser la recherche (hors champ texte) |
-| `â` / `â` | Naviguer dans les suggestions |
-| `Enter` | Sélectionner la suggestion active ou lancer la recherche |
-| `Escape` | Fermer les suggestions / quitter la recherche |
+### Keyboard Shortcuts
-### Fonctionnalités
+| Shortcut | Action |
+|----------|--------|
+| `Ctrl+K` / `Cmd+K` | Focus search bar |
+| `/` | Focus search (outside text fields) |
+| `â` / `â` | Navigate suggestions |
+| `Enter` | Select active suggestion or launch search |
+| `Escape` | Close suggestions / exit search |
-- **TF-IDF** : Scoring basé sur la fréquence des termes pondérée par l'inverse de la fréquence documentaire
-- **Boost titre** : Les correspondances dans le titre reçoivent un score 3à supérieur
-- **Normalisation des accents** : `resume` trouve `résumé`, `elephant` trouve `éléphant`
-- **Snippets surlignés** : Les termes trouvés sont encadrés par `` dans les extraits
-- **Facettes** : Compteurs par vault et par tag dans les résultats
-- **Pagination** : Navigation par pages de 50 résultats
-- **Tri** : Par pertinence (TF-IDF) ou par date de modification
-- **Chips visuels** : Les filtres actifs sont affichés comme des chips colorés supprimables
-- **Historique** : Les 50 derniÚres recherches sont mémorisées en localStorage
+### Features
+
+- **TF-IDF** : Scoring based on term frequency weighted by inverse document frequency
+- **Title boost** : Title matches receive 3Ă higher score
+- **Accent normalization** : `resume` finds `résumé`, `elephant` finds `éléphant`
+- **Highlighted snippets** : Found terms are wrapped in `` in excerpts
+- **Facets** : Counters by vault and by tag in results
+- **Pagination** : Page navigation with 50 results per page
+- **Sorting** : By relevance (TF-IDF) or modification date
+- **Visual chips** : Active filters are shown as removable colored chips
+- **History** : Last 50 searches are stored in localStorage
---
-## đ§ DĂ©pannage
+## đ§ Troubleshooting
-### ProblĂšmes courants
+### Common Issues
-**Port déjà utilisé :**
+**Port already in use:**
```bash
-# Vérifier qui utilise le port
+# Check who is using the port
sudo netstat -tulpn | grep 2020
-# Changer le port dans docker-compose.yml
+# Change the port in docker-compose.yml
ports:
- "2021:8080"
```
-**Vault non trouvée :**
-- Vérifiez que les chemins sont absolus
-- Assurez-vous que les permissions permettent la lecture
-- Redémarrez le conteneur aprÚs modification
+**Vault not found:**
+- Verify paths are absolute
+- Ensure read permissions
+- Restart the container after modifications
-**Build échoue :**
+**Build fails:**
```bash
-# Nettoyer le cache Docker et rebuild
+# Clean Docker cache and rebuild
docker system prune -f
docker compose down
./build.sh --progress=plain
-# Si l'échec persiste, vérifier les logs de build
+# If failure persists, check build logs
./build.sh --progress=plain 2>&1 | tee build.log
```
-**Logs pour debugging :**
+**Logs for debugging:**
```bash
-# Logs en temps réel
+# Real-time logs
docker compose logs -f obsigate
-# Logs détaillés
+# Detailed logs
docker compose logs --tail=100 obsigate
```
---
+
## ⥠Performance
-| Métrique | Estimation |
-|----------|------------|
-| **Indexation** | ~1â2s pour 1 000 fichiers markdown |
-| **Recherche avancĂ©e** | < 10ms pour la plupart des requĂȘtes (index inversĂ© + TF-IDF) |
-| **Résolution wikilinks** | O(1) via table de lookup |
-| **MĂ©moire** | ~80â150MB par 1 000 fichiers (contenu capĂ© Ă 100 KB/fichier) |
-| **Image Docker** | ~180MB (multi-stage, sans outils de build) |
-| **CPU** | Non-bloquant ; recherche offloadée sur thread pool dédié |
+| Metric | Estimate |
+|--------|----------|
+| **Indexing** | ~1â2s for 1,000 markdown files |
+| **Advanced Search** | < 10ms for most queries (inverted index + TF-IDF) |
+| **Wikilink Resolution** | O(1) via lookup table |
+| **Memory** | ~80â150MB per 1,000 files (content capped at 100 KB/file) |
+| **Docker Image** | ~180MB (multi-stage, no build tools) |
+| **CPU** | Non-blocking; search offloaded to dedicated thread pool |
-### ParamÚtres recommandés par taille de vault
+### Recommended Settings by Vault Size
-| Taille | Fichiers | `search_workers` | `prefix_max_expansions` | `max_content_size` |
-|--------|----------|-------------------|--------------------------|---------------------|
-| Petit | < 500 | 1 | 50 | 100 000 |
-| Moyen | 500â5 000 | 2 | 50 | 100 000 |
-| Grand | 5 000+ | 4 | 30 | 50 000 |
+| Size | Files | `search_workers` | `prefix_max_expansions` | `max_content_size` |
+|------|-------|------------------|--------------------------|---------------------|
+| Small | < 500 | 1 | 50 | 100,000 |
+| Medium | 500â5,000 | 2 | 50 | 100,000 |
+| Large | 5,000+ | 4 | 30 | 50,000 |
-Ces paramĂštres sont configurables via l'interface (Settings) ou l'API `/api/config`.
+These parameters are configurable via the interface (Settings) or the `/api/config` API.
-### Optimisations clés (v1.2.0)
+### Key Optimizations (v1.2.0)
-- **Index inversé avec set-intersection** : la recherche utilise les posting lists pour un retrieval O(k à postings) au lieu de O(N) scan complet
-- **Prefix matching par recherche binaire** : O(log V + k) au lieu de O(V) scan linéaire du vocabulaire
-- **ThreadPoolExecutor** : les fonctions de recherche CPU-bound sont offloadées du event loop asyncio
-- **Race condition guard** : `currentSearchId` + `AbortController` empĂȘchent le rendu de rĂ©sultats obsolĂštes
-- **Progress bar** : barre de progression animée pendant la recherche
-- **Search timeout** : abandon automatique aprĂšs 30s (configurable)
-- **Query time display** : temps serveur affiché dans les résultats (`query_time_ms`)
-- **Staleness detection fix** : utilisation d'un compteur de génération au lieu de `id(index)` pour détecter les changements d'index
+- **Inverted index with set-intersection** : Search uses posting lists for O(k Ă postings) retrieval instead of O(N) full scan
+- **Prefix matching by binary search** : O(log V + k) instead of O(V) linear vocabulary scan
+- **ThreadPoolExecutor** : CPU-bound search functions are offloaded from the asyncio event loop
+- **Race condition guard** : `currentSearchId` + `AbortController` prevent stale result rendering
+- **Progress bar** : Animated progress bar during search
+- **Search timeout** : Automatic abort after 30s (configurable)
+- **Query time display** : Server time shown in results (`query_time_ms`)
+- **Staleness detection fix** : Generation counter instead of `id(index)` to detect index changes
-### Optimisations v1.5.0 (Quick Wins 2026-05-27)
+### Optimizations v1.5.0 (Quick Wins 2026-05-27)
-- **Compression GZip** : middleware FastAPI compresse toutes les réponses >1KB (~70% de bande passante économisée)
-- **Cache-Control immutable** : les assets statiques (`/static/*`) sont cachés 1 an par le navigateur
-- **`.dockerignore`** : contexte de build Docker minimal (pas de `.git`, `docs/`, `*.md`, `__pycache__`)
-- **InvertedIndex incrémental** : hooks `add_document`/`remove_document`, plus de rebuild O(N) à chaque mutation
+- **GZip Compression** : FastAPI middleware compresses all responses >1KB (~70% bandwidth saved)
+- **Cache-Control immutable** : Static assets (`/static/*`) are cached by the browser for 1 year
+- **`.dockerignore`** : Minimal Docker build context (no `.git`, `docs/`, `*.md`, `__pycache__`)
+- **Incremental InvertedIndex** : `add_document`/`remove_document` hooks, no more O(N) rebuild on every mutation
-### Optimisations v1.1.0
+### Optimizations v1.1.0
-- **Recherche sans I/O** : le contenu des fichiers est mis en cache dans l'index mémoire
-- **Scoring multi-facteurs** : titre exact (+20), titre partiel (+10), chemin (+5), tag (+3), fréquence contenu (x1 par occurrence, capé à 10)
-- **Rendu Markdown singleton** : le renderer mistune est instancié une seule fois
-- **AbortController** : les requĂȘtes de recherche obsolĂštes sont annulĂ©es cĂŽtĂ© client
-- **Debounced icon rendering** : `lucide.createIcons()` est batché via `requestAnimationFrame`
+- **Search without I/O** : File content is cached in memory index
+- **Multi-factor scoring** : exact title (+20), partial title (+10), path (+5), tag (+3), content frequency (x1 per occurrence, capped at 10)
+- **Markdown renderer singleton** : The mistune renderer is instantiated once
+- **AbortController** : Stale search requests are aborted client-side
+- **Debounced icon rendering** : `lucide.createIcons()` is batched via `requestAnimationFrame`
---
-## đĄïž SĂ©curitĂ©
+## đĄïž Security
-- **Path traversal** : tous les endpoints fichier valident que le chemin résolu reste dans la vault
-- **Rate limiting** : 10 tentatives de login max par IP sur 15 minutes + lockout par compte (5 tentatives)
-- **Audit log** : toutes les écritures, suppressions et changements de config sont journalisés dans `data/audit.log` (JSON lines, rotation 10 MB)
-- **Backup automatique** : chaque modification ou suppression de fichier est sauvegardée dans `.obsigate-backup/` avec timestamp
-- **Secret redaction** : masquage automatique des JWT, clés API, tokens et connection strings dans les aperçus
-- **Utilisateur non-root** : le conteneur Docker tourne sous l'utilisateur `obsigate` (UID 1000)
-- **Volumes read-only** : les vaults sont montées en `:ro` par défaut dans docker-compose
-- **Secrets dans `.env`** : les mots de passe et tokens ne sont jamais dans `docker-compose.yml`
-- **Atomic writes** : les fichiers de données (users.json, shares.json, webhooks.json) utilisent tmp+replace
+- **Path traversal** : All file endpoints validate that the resolved path stays within the vault
+- **Rate limiting** : 10 login attempts max per IP over 15 minutes + per-account lockout (5 attempts)
+- **Audit log** : All writes, deletions, and config changes are logged in `data/audit.log` (JSON lines, 10 MB rotation)
+- **Automatic backup** : Every file modification or deletion is saved in `.obsigate-backup/` with timestamp
+- **Secret redaction** : Automatic masking of JWTs, API keys, tokens, and connection strings in previews
+- **Non-root user** : The Docker container runs under user `obsigate` (UID 1000)
+- **Read-only volumes** : Vaults are mounted as `:ro` by default in docker-compose
+- **Secrets in `.env`** : Passwords and tokens are never in `docker-compose.yml`
+- **Atomic writes** : Data files (users.json, shares.json, webhooks.json) use tmp+replace
---
-## đïž Stack technique
+## đïž Tech Stack
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id)
-- **Security** : rate limiting (IP + account lockout), audit logging, secret redaction, atomic writes
-- **File Watcher** : watchdog 4.x (inotify natif + fallback polling)
-- **Frontend** : Vanilla JS + HTML + CSS (zéro framework, zéro build)
-- **Rendu Markdown** : mistune 3.x
+- **Security** : Rate limiting (IP + account lockout), audit logging, secret redaction, atomic writes
+- **File Watcher** : watchdog 4.x (native inotify + fallback polling)
+- **Frontend** : Vanilla JS + HTML + CSS (zero framework, zero build)
+- **Markdown Rendering** : mistune 3.x
- **PDF Export** : WeasyPrint 60+
-- **Compression** : GZip middleware (FastAPI), Cache-Control immutable pour les assets
-- **Image Docker** : python:3.11-slim (multi-stage), `.dockerignore` pour contexte minimal
-- **Stockage utilisateurs** : JSON local (`data/users.json`) â aucune base de donnĂ©es
-- **Architecture** : SPA + API REST + SSE
+- **Compression** : GZip middleware (FastAPI), Cache-Control immutable for assets
+- **Docker Image** : python:3.11-slim (multi-stage), `.dockerignore` for minimal context
+- **User Storage** : Local JSON (`data/users.json`) â no database
+- **Architecture** : SPA + REST API + SSE
---
@@ -885,7 +889,7 @@ Ces paramĂštres sont configurables via l'interface (Settings) ou l'API `/api/con
```
âââââââââââââââââââ âââââââââââââââââââââââââââââââââââââââââââ
-â Navigateur ââââââșâ FastAPI (backend/main.py) â
+â Browser ââââââșâ FastAPI (backend/main.py) â
â (SPA) â REST â â
â â â ââââââââââââââââ ââââââââââââââââ â
â app.js â â â indexer.py â â search.py â â
@@ -895,70 +899,131 @@ Ces paramĂštres sont configurables via l'interface (Settings) ou l'API `/api/con
â ââââââââŹââââââââ â
â â â
â ââââââââââŽââââââââââ â
- â â Index en mĂ©moire â â
- â â (fichiers, tags, â â
- â â contenu, lookup)â â
+ â â In-Memory Index â â
+ â â (files, tags, â â
+ â â content, lookup)â â
â ââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââââââââââââââââââââââ
- â Filesystem (vaults montĂ©es) â
- â /vaults/Recettes (ro) â
+ â Filesystem (mounted vaults) â
+ â /vaults/Recipes (ro) â
â /vaults/IT (ro) â
âââââââââââââââââââââââââââââââââ
```
-**Flux de données :**
-1. Au démarrage, `indexer.py` scanne tous les vaults en parallÚle (thread pool)
-2. Le contenu, les tags (YAML + inline) et les métadonnées sont mis en cache en mémoire
-3. Une table de lookup O(1) est construite pour la résolution des wikilinks
-4. `watcher.py` démarre la surveillance des fichiers (watchdog natif ou polling)
-5. Les modifications détectées déclenchent une mise à jour incrémentale de l'index
-6. Les changements sont notifiés au frontend via Server-Sent Events (SSE)
-7. Les requĂȘtes de recherche utilisent l'index en mĂ©moire (zĂ©ro I/O disque)
-8. Le frontend SPA communique via REST + SSE et gÚre l'état cÎté client
+**Data Flow:**
+1. On startup, `indexer.py` scans all vaults in parallel (thread pool)
+2. Content, tags (YAML + inline), and metadata are cached in memory
+3. An O(1) lookup table is built for wikilink resolution
+4. `watcher.py` starts file monitoring (native watchdog or polling)
+5. Detected changes trigger incremental index updates
+6. Changes are notified to the frontend via Server-Sent Events (SSE)
+7. Search queries use the in-memory index (zero disk I/O)
+8. The SPA frontend communicates via REST + SSE and manages state client-side
---
-## đ DĂ©veloppement
+## đ Development
-### Structure du projet
+### đ§Ș Tests and CI â Mandatory Rules (Before Each Push)
+
+> â ïž **Absolute rule â human contributors and AI agents**: never `git push`
+> without having **locally** executed the entire sequence below with **100 %
+> success**. Push is not meant to discover failures: CI is a safety net, not a
+> diagnostic tool.
+
+#### Local Checks Before Commit
+
+| Step | Command | Equivalent CI Job |
+|---|---|---|
+| Backend lint | `ruff check backend/` | `lint` |
+| Frontend import validation | `node tests/frontend/validate-imports.mjs` | `lint` |
+| Frontend unit tests | `node tests/frontend/unit.test.mjs` | `lint` |
+| Backend tests | `pytest tests/ -q` | `test` |
+| **E2E Playwright** | `npm run test:e2e` (~5 min) | `e2e` |
+
+#### Local E2E Tests (`npm run test:e2e`)
+
+The `scripts/run-e2e-local.sh` script faithfully reproduces the `e2e` CI job
+conditions (`.gitea/workflows/ci.yml`):
+
+- backend started natively (venv `.venv-e2e`, Python 3.11 via `uv`) ;
+- authentication disabled (`OBSIGATE_AUTH_ENABLED=false`) ;
+- fixtures `test_vault` (TestVault) and `test_dir` (TestDir) ;
+- port `2029` (baseURL from `playwright.config.ts`) ;
+- Playwright project `chromium-desktop` only (same as CI).
+
+Prerequisites: `uv`, Node.js â„ 20, Playwright browsers
+(`npx playwright install chromium`). The venv is created automatically on
+first run. Docker is not required.
+
+```bash
+npm run test:e2e # full suite
+bash scripts/run-e2e-local.sh --headed # visible browser
+bash scripts/run-e2e-local.sh -g "reset panes" # filter on a test
+```
+
+The suite must end with **all tests passing** (60 currently), with no failure
+or reliance on retries. In case of failure: fix and re-run locally until 100 %,
+then only commit.
+
+#### Test Writing Rules (Lesson Learned)
+
+- **Check every selector in the actual DOM** before using it in a test. Real
+ bug encountered: a test was targeting `#cp-input` while the command palette
+ input only has the `.cp-input` class (no id) â the test could not pass in any
+ environment.
+- Any new or modified test must be **run locally** (at least with
+ `-g "test name"`) before push.
+- No workarounds that mask flakiness (arbitrary `waitForTimeout`,
+ silent fallbacks): fix the root cause.
+
+#### Commits and Push
+
+- Commit message: imperative subject †50 chars; body (72 col.) only if it
+ adds useful information.
+- One commit = one logical change (do not mix fix and refactor).
+- Push only when all 5 local steps are green. CI pipeline:
+ `lint â test â security â build â e2e`.
+
+### Project Structure
```
ObsiGate/
-âââ backend/ # API FastAPI
-â âââ main.py # Endpoints, Pydantic models, rendu markdown
-â âââ indexer.py # Scan des vaults, index en mĂ©moire, lookup table
-â âââ search.py # Moteur de recherche fulltext avec scoring
-â âââ watcher.py # Surveillance fichiers (watchdog + debounce)
-â âââ auth/ # Module d'authentification
+âââ backend/ # FastAPI API
+â âââ main.py # Endpoints, Pydantic models, markdown rendering
+â âââ indexer.py # Vault scanning, in-memory index, lookup table
+â âââ search.py # Full-text search engine with scoring
+â âââ watcher.py # File monitoring (watchdog + debounce)
+â âââ auth/ # Authentication module
â â âââ password.py # Argon2id hashing
-â â âââ jwt_handler.py # Tokens JWT + rĂ©vocation
-â â âââ user_store.py # CRUD users.json (atomique)
-â â âââ middleware.py # DĂ©pendances FastAPI auth
-â â âââ router.py # Endpoints /api/auth/*
-â âââ create_admin.py # CLI gestion utilisateurs
+â â âââ jwt_handler.py # JWT tokens + revocation
+â â âââ user_store.py # users.json CRUD (atomic)
+â â âââ middleware.py # FastAPI auth dependencies
+â â âââ router.py # /api/auth/* endpoints
+â âââ create_admin.py # User management CLI
â âââ requirements.txt
-âââ frontend/ # Interface web (Vanilla JS, zĂ©ro framework)
-â âââ index.html # Page SPA + Ă©cran de login
-â âââ app.js # Logique SPA, AuthManager, AdminPanel
-â âââ style.css # Styles (CSS variables, thĂšmes, responsive)
-âââ data/ # DonnĂ©es persistantes (créé au dĂ©marrage)
-â âââ users.json # Utilisateurs (hashĂ©s Argon2id)
-â âââ secret.key # ClĂ© secrĂšte JWT (512 bits)
+âââ frontend/ # Web interface (Vanilla JS, zero framework)
+â âââ index.html # SPA page + login screen
+â âââ app.js # SPA logic, AuthManager, AdminPanel
+â âââ style.css # Styles (CSS variables, themes, responsive)
+âââ data/ # Persistent data (created on startup)
+â âââ users.json # Users (Argon2id hashed)
+â âââ secret.key # JWT secret key (512 bits)
âââ Dockerfile # Multi-stage, healthcheck, non-root
-âââ docker-compose.yml # DĂ©ploiement avec healthcheck et auth env vars
-âââ build.sh # Build & dĂ©ploiement automatisĂ© (docker compose build + up)
-âââ CONTRIBUTING.md # Guide de contribution
+âââ docker-compose.yml # Deployment with healthcheck and auth env vars
+âââ build.sh # Automated build & deployment (docker compose build + up)
+âââ CONTRIBUTING.md # Contribution guide
```
-### Contribuer
+### Contributing
-Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour les détails.
+See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
---
-## đ Licence
+## đ License
-Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les détails.
+This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
---
@@ -966,14 +1031,14 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les
- **Issues** : [git.dracodev.net/Projets/ObsiGate/issues](https://git.dracodev.net/Projets/ObsiGate/issues)
- **Documentation** : [git.dracodev.net/Projets/ObsiGate/wiki](https://git.dracodev.net/Projets/ObsiGate/wiki)
-- **Auteur** : Bruno Beloeil
+- **Author** : Bruno Beloeil
---
## đ Changelog
-Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 â v1.7.0).
+See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 â v1.7.0).
---
-*Projet : ObsiGate | Version : 1.7.0 | DerniĂšre mise Ă jour : Mai 2026*
+*Project: ObsiGate | Version: 1.7.0 | Last updated: May 2026*