Files
ObsiGate/README.fr.md
T
bruno de6bde1613
CI / lint (push) Successful in 3m1s
CI / security (push) Failing after 1m58s
CI / test (push) Successful in 4m43s
CI / build (push) Successful in 1m39s
CI / e2e (push) Successful in 20m28s
fix: partage dirigé — dossier Partage virtuel + ouverture en onglet #196
- _resolve_shared_file: home-<user>/Partage/<f> -> fichier source (/api/file, raw, download)
- clic dossier Partage: dépliage sans navigation (fix Directory not found)
- fichiers reçus ouverts en onglet applicatif (arbre, recherche, dashboard)
- POST /api/share: path vide/null rejeté (400) — un share path:null cassait /api/shares
- +2 tests (13 total)
2026-10-10 21:48:46 -04:00

985 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ObsiGate
> **Version française** — ce document est le miroir synchronisé de [README.md](README.md) (référence complète). Dernière synchronisation : septembre 2026.
**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.
[![Version](https://img.shields.io/badge/Version-2.67.1-blue.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/)
[![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/)
[![CI/CD](https://img.shields.io/badge/CI%2FCD-Gitea_Actions-green.svg)](https://git.dracodev.net/Projets/ObsiGate/actions)
![Interface ObsiGate — tableau de bord Statistiques avec vaults, tags et raccourcis clavier](docs/images/obsigate-home.png)
> Interface web d'ObsiGate : sidebar multi-vault, recherche globale, statistiques et raccourcis.
---
## 📚 Guides
Les **guides d'utilisation** pas à pas se trouvent dans [`docs/GUIDES/`](docs/GUIDES/) :
| Guide | Contenu |
|---|---|
| 🚀 [Prise en main](docs/GUIDES/PRISE_EN_MAIN.md) | Premier lancement, interface, navigation, vaults, raccourcis |
| 🔍 [Recherche, PDF, Excel & Excalidraw](docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md) | Syntaxe de requête, recherche sémantique, lecteurs PDF/Excel, diagrammes |
| 🤖 [Assistant IA & Forge](docs/GUIDES/ASSISTANT_IA_FORGE.md) | Fournisseurs, éditeur IA, BooksLM, Forge, commandes `@` / `/` |
| 📝 [Édition & collaboration](docs/GUIDES/COLLABORATION.md) | Édition simultanée, curseurs distants, persistance |
| 📱 [PWA & hors-ligne](docs/GUIDES/PWA_HORS_LIGNE.md) | Installation, cache hors-ligne, file de synchro, notifications |
| 🔌 [API REST](docs/GUIDES/API_REST.md) | Authentification, clés API, endpoints, exemples `curl`, SSE |
| 🧩 [Serveur MCP](docs/GUIDES/MCP.md) | Brancher Claude Desktop, Cursor, Cline… sur vos vaults |
| 🔒 [Authentification & sécurité](docs/GUIDES/AUTHENTIFICATION_SECURITE.md) | Utilisateurs, MFA, permissions par vault, durcissement |
| 🐳 [Déploiement Docker](docs/GUIDES/DEPLOIEMENT_DOCKER.md) | `docker-compose`, volumes, reverse proxy, mises à jour |
| 🖥️ [Desktop (Tauri)](docs/GUIDES/DESKTOP.md) | Installation, premier lancement, build depuis les sources, dépannage |
> Index complet : [`docs/GUIDES/README.md`](docs/GUIDES/README.md).
---
## 📋 Table des matières
- ✨ [Fonctionnalités](#fonctionnalites)
- 📚 [Guides](#guides)
- 🚀 [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)
- 🖼️ [Rendu d'images Obsidian](#rendu-dimages-obsidian)
- 🖥️ [Desktop (Tauri) — Application native](#desktop-tauri-application-native)
- 📖 [Utilisation](#utilisation)
- 👥 [Collaboration temps réel](#collaboration-temps-reel)
- 🔌 [API](#api)
- 🔍 [Recherche avancée](#recherche-avancee)
- 🔧 [Dépannage](#depannage)
- ⚡ [Performance](#performance)
- 🛡️ [Sécurité](#securite)
- 🏗️ [Stack technique](#stack-technique)
- 🏠 [Architecture](#architecture)
- 📝 [Développement](#developpement)
- 📄 [Licence](#licence)
- 🤝 [Support](#support)
- 📝 [Changelog](#changelog)
---
## ✨ Fonctionnalités
- **🤖 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
- **🧩 Serveur MCP & agent IA** — Serveur Model Context Protocol intégré (`/mcp`) et assistant avec function calling : lisez, cherchez et modifiez vos vaults depuis Claude Desktop, Cursor… avec confirmations two-step, permissions par vault, rate limiting et redaction des secrets ([guide](docs/GUIDES/MCP.md))
- **👥 Collaboration temps réel** — Édition simultanée d'un même document (Yjs/CRDT) : curseurs distants colorés, indicateur de présence, fusion sans conflit, reconnexion automatique et persistance serveur ([détail](docs/features/collaboration.md))
- **📖 Guide d'utilisation intégré** — Aide complète en FR/EN accessible depuis le menu Options : interface, navigation, recherche, fichiers, IA, sécurité, API & intégrations (OpenAPI, MCP), hors-ligne, collaboration, desktop, plus une section **Architecture** avec diagramme Mermaid ; téléchargeable en **Markdown** et **PDF** dans la langue courante ([détail](docs/features/guide-coverage-105.md))
- **📱 Éditeur mobile natif** — Édition optimisée pour le tactile : barre d'outils Markdown flottante (gras/italique/code/liste/lien), bouton « Coller » persistant (contournement iOS), zoom par pincement et hauteur ajustable, raccourcis swipe (liens entrants / table des matières) et mode lecture plein écran avec navigation entre fichiers ([détail](docs/features/mobile-editor.md))
- **🗺️ 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 ; chaque clic sur un répertoire de l'arbre ouvre un **onglet de navigation** — chemin, récents, sous-répertoires cliquables, facettes Vaults · Tags · Extensions, tri Pertinence/Date et enregistrement du répertoire — qui coexiste avec vos fichiers ouverts
- **🔍 Recherche avancée** : Moteur TF-IDF avec stemming français, normalisation des accents, snippets surlignés, facettes, pagination et tri — plus une **recherche sémantique** optionnelle (embeddings `all-MiniLM-L6-v2`, fusion hybride TF-IDF + RRF) activable via le toggle `~` ([détail](docs/features/semantic-search.md))
- **💡 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
- **🎬 Audio & vidéo** : Lecteurs HTML5 intégrés (`.mp3 .wav .flac .mp4 .webm`…) avec streaming HTTP Range (lecture, déplacement, plein écran) et **lecture persistante** (mini-lecteur flottant / mini-fenêtre vidéo, retour au média ou arrêt à tout moment, contrôles écran verrouillé via Media Session), repli téléchargement si le format n'est pas lisible par le navigateur
- **🎨 Diagrammes Excalidraw** : Visualiseur/éditeur natif des fichiers `.excalidraw` et `.excalidraw.md` (iframe sandboxée, auto-save, thème clair/sombre, texte des diagrammes indexé pour la recherche)
- **📊 Tableurs Excel** : les fichiers `.xlsx` et `.xlsm` s'ouvrent dans un visualiseur dédié — un tableau par feuille avec onglets, en-têtes A1 et édition directe des cellules (`PUT /api/file/{vault}/xlsx/save`, backup automatique, écriture atomique), plus le téléchargement du fichier d'origine. Le visualiseur rend polices, couleurs, cellules fusionnées et volets figés, et offre navigation et raccourcis clavier (`Ctrl+S`, `Suppr`, `F2`, `Ctrl+Origine/Fin`, `PgPréc/PgSuiv`, `Ctrl+flèches`), barre de formule avec noms de fonctions, zone Nom éditable (« Atteindre » `A1:B3`), presse-papiers de plage (copier/couper/coller un bloc, depuis ou vers Excel), un menu **Mise en forme** (gras/italique/souligné, alignements, couleurs, formats de nombre, fusions, volets figés, largeur/hauteur — `PUT /api/file/{vault}/xlsx/style`), tri/filtre/recherche sur toutes les feuilles, export CSV/Markdown/HTML et impression (sélection ou feuille), édition de la structure (feuilles, lignes, colonnes) et un tableau de bord du classeur (plages nommées, détection graphiques/TCD, stats par feuille) ; un `.csv` s'édite dans la même grille (RFC 4180) tandis que `.xls` et `.ods` s'ouvrent en lecture seule. Les classeurs contenant des éléments qu'ObsiGate ne peut pas conserver (valeurs calculées, segments, contrôles de formulaire, signature…) affichent un **avertissement** et demandent confirmation avant l'enregistrement ; une saisie commençant par `=` ou `@` est stockée comme texte sauf activation du bouton `f(x)`, et les écritures concurrentes d'un autre poste sont détectées (`If-Match` → « Réessayer »). L'assistant IA peut lister les feuilles, injecter un tableau borné dans son contexte, rechercher dans le classeur, analyser une plage, modifier des cellules et ajouter des lignes — sur `.xlsx`, `.xlsm` et `.csv`. Sur mobile (≤ 768 px), la barre de menus et le ruban sont **repliés par défaut** — un bouton ☰ les déplie — pour que la grille occupe toute la hauteur d'écran
- **🎨 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
- **🖥️ Application desktop native** : Tauri (Rust) + backend Python embarqué, sans Docker ni navigateur
- **🐳 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
---
## 🚀 Prérequis
### Système requis
- **Docker** >= 20.10
- **docker-compose** >= 2.0
- **Espace disque** : ~200MB pour l'image Docker
### Systèmes supportés
- Linux (Ubuntu, Debian, CentOS, etc.)
- macOS (Intel et Apple Silicon)
- Windows (avec Docker Desktop)
- NAS compatibles Docker (Synology, QNAP, etc.)
---
## ⚡ Installation rapide
### 1. Cloner le dépôt
```bash
git clone https://git.dracodev.net/Projets/ObsiGate.git
cd ObsiGate
```
### 2. Configurer vos vaults et vos secrets
Éditez `docker-compose.yml` pour ajouter vos vaults Obsidian :
```yaml
volumes:
- /chemin/absolu/vers/votre/vault:/vaults/NomDeVotreVault:ro
```
> **Important** : Le chemin doit être absolu et le volume en lecture seule (`:ro`)
Créez votre fichier `.env` pour l'authentification et les secrets :
```bash
cp .env.example .env
# Éditez .env pour configurer vos mots de passe et options
```
> **Ne committez jamais `.env` !** Il est dans `.gitignore`. Utilisez `.env.example` comme référence.
### 3. Lancer l'application
```bash
chmod +x build.sh # une seule fois
./build.sh # build + déploiement en une commande
```
> Options utiles : `./build.sh --help`, `./build.sh --cache` (rebuild rapide), `./build.sh --build-only` (construire sans démarrer).
### 4. Accéder à l'interface
Ouvrez votre navigateur sur : **http://localhost:2020**
---
## ⚙️ Configuration détaillée
### Étape 1 : Préparation des vaults
1. **Localisez vos vaults Obsidian** sur votre système
2. **Notez les chemins absolus** vers chaque dossier
3. **Vérifiez les permissions** : Docker doit pouvoir lire ces dossiers
### Étape 2 : Configuration docker-compose.yml
```yaml
services:
obsigate:
build:
context: .
image: obsigate:latest
container_name: obsigate
restart: unless-stopped
ports:
- "2020:8080" # Port local 2020 → Port conteneur 8080
volumes:
- /home/user/Documents/Obsidian-Recettes:/vaults/Recettes:ro
- /home/user/Documents/Obsidian-IT:/vaults/IT:ro
- ./data:/app/data # Persistance des données d'auth
environment:
- VAULT_1_NAME=Recettes
- VAULT_1_PATH=/vaults/Recettes
- VAULT_2_NAME=IT
- VAULT_2_PATH=/vaults/IT
- OBSIGATE_AUTH_ENABLED=true
- OBSIGATE_ADMIN_USER=admin
env_file:
- .env # Contient OBSIGATE_ADMIN_PASSWORD et autres secrets
```
### Étape 3 : Build & déploiement
```bash
chmod +x build.sh
./build.sh
```
**Alternative manuelle :**
```bash
docker compose build --no-cache
docker compose up -d
```
> **Compatibilité Docker** : l'image utilise une variante minimale d'`uvicorn` et `fastapi 0.110.3` afin d'éviter les dépendances optionnelles natives (`watchfiles`, `uvloop`, …) qui peuvent échouer au build sur Alpine, ARM ou i386.
---
## 🌍 Variables d'environnement
Les vaults sont configurées par paires `VAULT_N_NAME` / `VAULT_N_PATH` (N = 1, 2, 3…) :
| Variable | Description | Exemple |
|----------|-------------|---------|
| `VAULT_1_NAME` | Nom affiché de la vault | `Recettes` |
| `VAULT_1_PATH` | Chemin dans le conteneur | `/vaults/Obsidian-RECETTES` |
| `VAULT_1_ATTACHMENTS_PATH` | Dossier d'attachements (optionnel) | `06_Boite_a_Outils/6.2_Attachments` |
| `VAULT_1_SCAN_ATTACHMENTS` | Scan d'images au démarrage (défaut : true) | `true` |
| `OBSIGATE_HOME_ROOT` | Racine des dossiers personnels (un vault `home-<user>` par compte). Absente = fonctionnalité désactivée. | `/vaults/Home` |
**Règles de nommage :** lettres, chiffres et tirets uniquement ; pas d'espaces ; le nom doit correspondre au chemin dans le conteneur.
---
## 🔒 Authentification
> **Désactivée par défaut** — Compatible avec toutes les installations existantes.
Système optionnel basé sur **JWT + Argon2id** avec contrôle d'accès par vault.
### Activer l'authentification
1. `cp .env.example .env`
2. Éditez `.env` :
```bash
OBSIGATE_AUTH_ENABLED=true
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
```
3. Dans `docker-compose.yml` : `env_file: - .env`
> **Ne mettez jamais de mot de passe dans `docker-compose.yml` !** Utilisez toujours `.env`.
### Premier démarrage
Si aucun utilisateur n'existe, ObsiGate crée automatiquement un compte admin et **affiche le mot de passe une seule fois dans les logs** :
```bash
docker-compose logs obsigate | grep -A4 "PREMIER"
```
### Gestion des utilisateurs via CLI
```bash
# Créer un utilisateur
docker exec obsigate python backend/create_admin.py create alice MonMotDePasse --role user --vaults Recettes IT
# Créer un admin avec accès total
docker exec obsigate python backend/create_admin.py create bob SecretPass --role admin --vaults "*"
# Lister / supprimer
docker exec obsigate python backend/create_admin.py list
docker exec obsigate python backend/create_admin.py delete alice
```
### Interface d'administration
Un compte **admin** connecté voit une icône 🛡️ dans le header : liste, création/édition/suppression d'utilisateurs, assignation des vaults, activation/désactivation de comptes.
### Contrôle d'accès par vault
| Valeur vaults | Accès |
|---------------|-------|
| `["*"]` | Toutes les vaults (y compris futures) — défaut admin |
| `["Recettes", "IT"]` | Uniquement ces vaults |
| `[]` | Aucun accès |
### Variables d'environnement d'auth
| 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_ACCOUNT_MAX_ATTEMPTS` | Tentatives de login max par compte | `10` |
| `OBSIGATE_LOGIN_WINDOW_SECONDS` | Fenêtre de rate limiting (secondes) | `900` |
| `OBSIGATE_TRUST_PROXY` | Faire confiance à `X-Forwarded-For` pour l'IP client (reverse proxy) | `false` |
| `OBSIGATE_WEBHOOK_ALLOW_HTTP` | Autoriser les webhooks non HTTPS | `false` |
| `OBSIGATE_WEBHOOK_ALLOW_PRIVATE` | Autoriser les webhooks vers des adresses privées/boucle | `false` |
| `OBSIGATE_PDF_MAX_SIZE_MB` | Taille max des PDF extraits (text indexation) | `50` |
| `OBSIGATE_MEDIA_MAX_INLINE_MB` | Taille max pour la lecture audio/vidéo intégrée (au-delà : téléchargement) | `500` |
| `OBSIGATE_PDF_EXTRACT_TIMEOUT` | Timeout extraction PDF (secondes) | `30` |
| `OBSIGATE_TAVILY_API_KEY` / `OBSIGATE_BRAVE_API_KEY` / `OBSIGATE_SERPAPI_API_KEY` / `OBSIGATE_EXA_API_KEY` | Fournisseurs de recherche web à clé (essayés avant SearXNG) | — |
| `OBSIGATE_WEB_PROVIDERS` | Ordre des fournisseurs de recherche (ex. `brave,searxng`) | — |
| `OBSIGATE_WEB_RETRY` | Réessais réseau des outils web (backoff maison) | `1` |
| `OBSIGATE_WEB_CACHE_TTL` | Durée du cache SQLite des résultats web (secondes, `0` = off) | `900` |
| `OBSIGATE_GITEA_URL` / `OBSIGATE_GITEA_TOKEN` | Source connectée Gitea (outil `git_list_repos`…) | — |
| `OBSIGATE_GITHUB_TOKEN` | Jeton GitHub (outil `git_list_repos`…) | — |
> Ces clés peuvent aussi être saisies **depuis l'interface** (menu → Configurations →
> « Sources connectées & recherche ») : la valeur saisie est stockée dans `data/api_keys.json`
> et prime sur la variable d'environnement.
### Volume pour la persistance
Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data` :
```yaml
volumes:
- ./data:/app/data # Persistance des utilisateurs et clé JWT
```
---
## ➕ Ajouter une nouvelle vault
### Méthode 1 : Édition directe
1. `docker-compose down`
2. Ajoutez le volume : `- /nouveau/chemin/vault:/vaults/NouvelleVault:ro`
3. Ajoutez les variables : `VAULT_4_NAME=NouvelleVault` + `VAULT_4_PATH=/vaults/NouvelleVault`
4. Redémarrez : `./build.sh`
### Méthode 2 : Hot-reload (recommandé)
1. Ajoutez volume + variables comme ci-dessus
2. `./build.sh`
3. `curl http://localhost:2020/api/index/reload`
### Méthode 3 : API dynamique (sans redémarrage)
```bash
curl -X POST http://localhost:2020/api/vaults/add \
-H "Content-Type: application/json" \
-d '{"name": "NouvelleVault", "path": "/vaults/NouvelleVault"}'
curl -X DELETE http://localhost:2020/api/vaults/NouvelleVault
```
---
## 🔨 Build & déploiement avec build.sh
### Utilisation de base
```bash
chmod +x build.sh # une seule fois
./build.sh # build from scratch + démarrage
```
### Options disponibles
| 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 |
### Ce que fait le script
1. **Vérifie** Docker et Docker Compose (versions)
2. **Valide** `docker-compose.yml` (présence + syntaxe)
3. **Vérifie chaque volume** monté (avertit si la source n'existe pas)
4. **Construit** l'image Docker (multi-stage, ~180MB)
5. **Démarre** le conteneur (`docker compose up -d`)
6. **Affiche** statut + logs en temps réel
### Arrêter / redémarrer
```bash
docker compose down # Arrêter
docker compose up -d # Redémarrer sans rebuild
docker compose logs -f # Voir les logs
```
---
## 🖼️ Rendu d'images Obsidian
ObsiGate supporte **toutes les syntaxes d'images Obsidian** avec résolution intelligente multi-stratégies.
### Syntaxes supportées
1. **Markdown standard avec attributs HTML** : `[<img width="180" src="path/to/image.svg"/>](https://example.com)`
2. **Wiki-link embed chemin complet** : `![[06_Boite_a_Outils/6.2_Attachments/image.svg]]`
3. **Wiki-link embed nom de fichier** : `![[image.svg]]`
4. **Markdown standard** : `![alt text](path/to/image.png)`
### Résolution intelligente (7 stratégies, par priorité)
1. Chemin absolu
2. Dossier d'attachements configuré (`VAULT_N_ATTACHMENTS_PATH`)
3. Index de démarrage (match unique)
4. Même répertoire que le fichier markdown
5. Racine du vault
6. Index de démarrage (match le plus proche)
7. Fallback : placeholder stylisé `[image not found: filename.ext]`
### Visionneuse & arborescence
Les images sont de plein droit des fichiers du vault : elles apparaissent dans
l'arborescence, sont indexées (nom + métadonnées, **jamais les octets**) et
s'ouvrent dans une **visionneuse dédiée** — zoom molette 0,1×–8×, pan au
glisser, double-clic pour réinitialiser, navigation ←/→ entre les images du
dossier (avec pellicule de miniatures WebP), panneau de métadonnées, lightbox
plein écran, ouverture de l'original et téléchargement. Le filtre de recherche
`ext:png`/`ext:jpg` est disponible. Formats décodables : PNG, JPEG, GIF, WebP,
BMP, ICO, SVG (SVG servi avec une politique CSP `sandbox`). **HEIC/HEIF**
(iPhone) n'est pas décodable par les navigateurs et n'est pas pris en charge.
### Configuration
```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)
```
### Rescan manuel
```bash
curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
```
---
## 🖥️ Desktop (Tauri) — Application native
> 📖 Guide complet : [Desktop (Tauri)](docs/GUIDES/DESKTOP.md)
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.
> 🚧 **Version 2.0.0 — binaires en cours de stabilisation.** Pour l'instant, le build depuis les sources est recommandé.
### Fonctionnalités desktop natives
| Fonctionnalité | 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) |
### Téléchargement (binaires pré-buildés)
Les releases sont publiées sur [Gitea](https://git.dracodev.net/Projets/ObsiGate/releases) :
| Plateforme | Format |
|---|---|
| **Linux** | `.deb` + `.AppImage` |
| **Windows** | `.msi` + `.exe` (NSIS) |
```bash
# Linux — .deb (Debian/Ubuntu/Deepin)
sudo dpkg -i obsigate_2.0.0_amd64.deb
# Linux — .AppImage (toute distrib)
chmod +x ObsiGate_2.0.0_amd64.AppImage && ./ObsiGate_2.0.0_amd64.AppImage
```
```cmd
REM Windows : double-cliquer sur ObsiGate_2.0.0_x64.msi (ou le setup NSIS)
```
### Démarrage
1. **Lancez l'application** depuis le menu ou la ligne de commande
2. Le backend Python démarre automatiquement sur `127.0.0.1:17890` (splash « Démarrage… » pendant le boot)
3. La fenêtre s'ouvre et charge l'interface ObsiGate
4. **Premier lancement** : sélectionnez le dossier de vos vaults Obsidian via le sélecteur natif
5. Pour fermer : icône tray → Quitter (arrêt propre du backend)
### Build depuis les sources
Guide détaillé : [desktop/README.md](./desktop/README.md).
#### Prérequis communs
| Outil | 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` |
> **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 ».
#### 🪟 Windows — `build-windows.bat`
```cmd
REM Prérequis (via Scoop) : rustup, curl, git
scoop install rustup curl git
rustup default stable
cargo install tauri-cli
cd desktop
build-windows.bat
```
Étapes du script :
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\`
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
→ **Artefact :** `desktop\target\x86_64-pc-windows-msvc\release\bundle\nsis\ObsiGate_2.0.0_x64-setup.exe`
#### 🐧 Linux — `build-linux.sh`
```bash
cd desktop
chmod +x build-linux.sh
./build-linux.sh
```
Étapes du script :
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/`
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
→ **Artefacts :**
- `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
**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** :
| Job | Runner | Artefacts (conservés 30 jours) |
|---|---|---|
| `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.
### Architecture Desktop
```
┌────────────────────────────────────────────┐
│ Tauri (Rust) │
│ ├─ Webview (webview système) │
│ │ └─ Frontend (HTML/JS/CSS) │
│ └─ Sidecar Python │
│ └─ uvicorn backend.main:app │
│ └─ port 127.0.0.1:17890 │
└────────────────────────────────────────────┘
```
Cycle de vie : Tauri spawn le backend Python → health check → splash de démarrage → redirection vers la webview. À la fermeture : kill propre du backend.
---
## 📖 Utilisation
### Interface web
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 🌙/☀️
### Raccourcis clavier
| Action | Raccourci |
|--------|-----------|
| Recherche | `Ctrl + K` ou `/` |
| Toggle thème | `Ctrl + T` |
| Focus recherche | `Esc` |
---
## 👥 Collaboration temps réel
> 📖 Guide complet : [Édition & collaboration](docs/GUIDES/COLLABORATION.md)
Plusieurs utilisateurs peuvent éditer le même document markdown simultanément (façon Google Docs) :
- **Fusion sans conflit** grâce à Yjs (CRDT) : deux personnes peuvent taper au même endroit, aucune
modification n'est perdue.
- **Curseurs distants colorés** et sélections visibles dans CodeMirror, avec le nom de chaque
utilisateur.
- **Indicateur de présence** dans l'en-tête de l'éditeur (avatars + statut de connexion).
- **Reconnexion automatique** (backoff exponentiel) : l'état est fusionné au retour.
- **Persistance serveur** : le document est écrit sur disque 2 s après la dernière modification.
- **Transport** : WebSocket `ws(s)://<hôte>/ws/collab/{vault}/{chemin}`, authentifié par cookie
`access_token` (ou `?token=`) et soumis au contrôle d'accès par vault.
Aucune configuration n'est nécessaire : ouvrez le même fichier dans deux navigateurs (ou deux
fenêtres) pour voir la collaboration en action.
---
## 🔌 API
> 📖 Guide complet : [API REST](docs/GUIDES/API_REST.md) · [Serveur MCP](docs/GUIDES/MCP.md)
ObsiGate expose une API REST complète :
| 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 (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 (+ `semantic=true` pour l'hybride) | 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/media/{vault}/thumb?path=&size=` | Miniature WebP (cache disque) | GET | Oui |
| `/api/config` | Lire / écrire la configuration | GET/POST | Oui/Admin |
| `/api/diagnostics` | Statistiques index et mémoire | GET | Admin |
> Quand `OBSIGATE_AUTH_ENABLED=false`, tous les endpoints sont accessibles sans token.
> Tous les endpoints exposent des schémas Pydantic documentés ; doc interactive sur `/docs` (Swagger UI).
**Exemples :**
```bash
curl http://localhost:2020/api/health
curl http://localhost:2020/api/vaults
curl "http://localhost:2020/api/search/advanced?q=recette%20tag:cuisine&vault=all&limit=20&offset=0&sort=relevance"
curl "http://localhost:2020/api/suggest?q=piz&vault=all"
curl "http://localhost:2020/api/file/Recettes?path=pizza.md"
```
---
## 🔍 Recherche avancée
> 📖 Guide complet : [Recherche, PDF, Excel & Excalidraw](docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md)
### Syntaxe de requête
| Opérateur | Description | Exemple |
|-----------|-------------|---------|
| `tag:<nom>` | Filtrer par tag | `tag:recette docker` |
| `#<nom>` | Raccourci tag | `#linux serveur` |
| `vault:<nom>` | Filtrer par vault | `vault:IT kubernetes` |
| `title:<texte>` | Filtrer par titre | `title:pizza` |
| `path:<texte>` | Filtrer par chemin | `path:recettes/soupes` |
| `ext:<type>` | Filtrer par type de fichier | `ext:md kubernetes` |
| `"phrase exacte"` | Recherche de phrase | `tag:"multi mots"` |
Les opérateurs sont combinables : `tag:linux vault:IT ext:md serveur web`.
### Support PDF
Les fichiers PDF de vos vaults s'affichent en ligne dans le navigateur via le visualiseur PDF natif (iframe + `<embed>`).
Le visualiseur streame le fichier via HTTP Range (206 Partial Content) — les gros PDF se chargent progressivement.
Le texte est extrait à l'indexation (pypdf / pymupdf) — le contenu PDF est donc recherchable via la recherche full-text.
Filtrez avec `ext:pdf` pour restreindre les résultats aux PDF.
Les métadonnées (pages, titre, auteur) sont disponibles via `GET /api/file/{vault}/pdf/info` sans transférer le document.
**Limitations :** pas d'OCR (les PDF scannés ne sont pas recherchables), pas d'annotation, pas d'édition du PDF lui-même.
### Diagrammes Excalidraw
Les fichiers `.excalidraw` et `.excalidraw.md` s'ouvrent dans un éditeur visuel Excalidraw complet, intégré dans une iframe sandboxée — dessinez, modifiez et sauvegardez sans quitter ObsiGate.
Les modifications sont sauvegardées automatiquement (2 s) ou avec `Ctrl+S` ; l'éditeur suit le thème clair/sombre.
Le texte des éléments du diagramme est extrait à l'indexation : il est donc recherchable via la recherche full-text (`ext:excalidraw`).
Les fichiers créés avec le **plugin Obsidian Excalidraw** (y compris le format `.excalidraw.md` compressé) sont compatibles.
### Raccourcis clavier
| 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 |
### Fonctionnalités
- **TF-IDF** : scoring par fréquence pondérée des termes
- **Boost titre** : correspondances dans le titre ×3
- **Normalisation des accents** : `resume` trouve `résumé`
- **Snippets surlignés** (`<mark>`), **facettes** (compteurs par vault/tag), **pagination** (50/page), **tri** pertinence/date, **chips** de filtres, **historique** (50 recherches)
- **Recherche sémantique** (optionnelle) : le toggle `~` (ou `Alt+S`) fusionne le classement
TF-IDF avec un classement par embeddings (RRF). Fonctionne sans dépendance avec un provider de
hachage ; installez `backend/requirements-semantic.txt` et/ou renseignez `OBSIGATE_EMBEDDING_*`
pour de vrais embeddings `all-MiniLM-L6-v2`. Voir
[docs/features/semantic-search.md](docs/features/semantic-search.md).
---
## 🔧 Dépannage
**Port déjà utilisé :**
```bash
sudo netstat -tulpn | grep 2020
# Changer le port dans docker-compose.yml : ports: - "2021:8080"
```
**Vault non trouvée :** chemins absolus, permissions de lecture, redémarrer le conteneur après modification.
**Build échoue :**
```bash
docker system prune -f
docker compose down
./build.sh --progress=plain
# Si l'échec persiste :
./build.sh --progress=plain 2>&1 | tee build.log
```
**Logs pour debugging :**
```bash
docker compose logs -f obsigate
docker compose logs --tail=100 obsigate
```
**Desktop :** les logs du backend sont dans `%APPDATA%\ObsiGate\logs\backend.log` (Windows) / `~/.config/obsigate/logs/backend.log` (Linux).
---
## ⚡ 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) |
| **CPU** | Non-bloquant ; recherche offloadée sur thread pool dédié |
### Paramètres recommandés par taille de vault
| 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 |
Configurables via l'interface (Settings) ou l'API `/api/config`.
### Optimisations clés
- **Index inversé avec set-intersection** + prefix matching par recherche binaire
- **ThreadPoolExecutor** : recherche CPU-bound hors de l'event loop asyncio
- **InvertedIndex incrémental** : hooks `add_document`/`remove_document`, plus de rebuild O(N)
- **Compression GZip** (~70% de bande passante économisée) + **Cache-Control immutable** (1 an)
- **Race condition guard** (`currentSearchId` + AbortController), progress bar, timeout 30s
- **Rendu Markdown singleton**, debounced icon rendering, recherche sans I/O disque
---
## 🛡️ Sécurité
> 📖 Guide complet : [Authentification & sécurité](docs/GUIDES/AUTHENTIFICATION_SECURITE.md)
- **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** : écritures/suppressions/config journalisées dans `data/audit.log` (JSON lines, rotation 10 MB)
- **Backup automatique** : chaque modification/suppression sauvegardée dans `.obsigate-backup/` avec timestamp
- **Secret redaction** : masquage automatique des JWT, mots de passe, clés API (OpenAI, GitHub, Google, AWS, Slack, Stripe…), tokens dans les aperçus — cliquez sur un masque pour copier la valeur
- **Utilisateur non-root** : conteneur Docker sous `obsigate` (UID 1000)
- **Volumes read-only** : vaults montées en `:ro` par défaut
- **Secrets dans `.env`** : jamais dans `docker-compose.yml`
- **Atomic writes** : tmp+replace pour users.json, shares.json, webhooks.json
---
## 🏗️ Stack technique
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id)
- **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
- **PDF Export** : WeasyPrint 60+
- **Desktop** : Tauri v2 (Rust) + Python embarqué
- **Image Docker** : python:3.11-slim (multi-stage)
- **Stockage utilisateurs** : JSON local (`data/users.json`) — aucune base de données
- **Architecture** : SPA + API REST + SSE
---
## 🏠 Architecture
```
┌─────────────────┐ ┌─────────────────────────────────────────┐
│ Navigateur │◄───►│ FastAPI (backend/main.py) │
│ (SPA) │ REST │ ┌──────────────┐ ┌──────────────┐ │
│ │ │ │ indexer.py │ │ search.py │ │
│ app.js │ │ │ (scan+cache) │ │ (in-memory) │ │
│ style.css │ │ └───────┬────── └──────┬───────┘ │
│ index.html │ │ │ │ │
└─────────────────┘ │ ┌───┴──────────────┴───┐ │
│ │ Index en mémoire │ │
│ │ (fichiers, tags, │ │
│ │ contenu, lookup) │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────┘
```
**Flux de données :**
1. Au démarrage, `indexer.py` scanne tous les vaults en parallèle (thread pool, non-bloquant)
2. Contenu, tags (YAML + inline) et métadonnées mis en cache en mémoire
3. Table de lookup O(1) pour la résolution des wikilinks
4. `watcher.py` surveille les fichiers (watchdog natif ou polling)
5. Modifications → mise à jour incrémentale de l'index
6. Changements notifiés au frontend via SSE
7. Recherche sur l'index mémoire (zéro I/O disque)
8. Le frontend SPA communique via REST + SSE
---
## 📝 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` (~10 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
```
Sous Windows, si `bash` n'est pas exploitable (WSL indisponible, git-bash
bloqué par une politique de contrôle d'application), utiliser le lanceur
PowerShell équivalent :
```powershell
npm run test:e2e:ps
pwsh -File scripts/run-e2e-local.ps1 -PlaywrightArgs @('-g','reset panes')
```
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
```
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 (JWT + Argon2id)
│ └── create_admin.py # CLI gestion utilisateurs
├── frontend/ # Interface web (Vanilla JS, zéro framework)
│ ├── index.html # Page SPA + écran de login + splash de boot
│ ├── js/ # Modules ES (app, viewer, editor, graph, …)
│ └── style.css # Styles (CSS variables, thèmes, responsive)
├── desktop/ # Application native Tauri (Rust + Python embarqué)
│ ├── src/main.rs # Shell natif : cycle de vie backend, tray, menus
│ ├── build-windows.bat
│ └── build-linux.sh
├── data/ # Données persistantes (créé au démarrage)
├── Dockerfile # Multi-stage, healthcheck, non-root
├── docker-compose.yml # Déploiement avec healthcheck et auth env vars
├── build.sh # Build & déploiement automatisé
└── docs/ # ROADMAP, guides, audits
```
### Contribuer
Voir [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) pour les standards de code et
[docs/DELIVERY_WORKFLOW.md](./docs/DELIVERY_WORKFLOW.md) pour la méthode de livraison obligatoire.
---
## 📄 Licence
Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour les détails.
---
## 🤝 Support
- **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)
---
## 📝 Changelog
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.67.1).
---
*Projet : ObsiGate | Version : 2.67.1 | Dernière mise à jour : Septembre 2026*