frontend/js/ structure: state.js (55 lines) — Shared mutable state, constants utils.js (510 lines) — EXT_ICONS, getFileIcon, escapeHtml, safeCreateIcons auth.js (547 lines) — api(), AuthManager, initLoginForm, AdminPanel search.js (1106 lines)— SearchHistory, QueryParser, Autocomplete, performSearch sidebar.js (1091 lines)— Vault tree, sidebar filter, TagFilterService, loadTags viewer.js (1554 lines)— openFile, Outline, ScrollSpy, Frontmatter, Editor ui.js (2250 lines)— Theme, Toast, Sidebar, Dropdowns, Tabs, ContextMenu dashboard.js (461 lines) — Dashboard widgets (Recent, Stats, Bookmarks) config.js (999 lines) — Config panel, Hidden files, About, Sidebar tabs sync.js (436 lines) — SSE/IndexUpdateManager, PWA registration graph.js (401 lines) — GraphViewManager (force-directed canvas graph) legacy.js (550 lines) — Remaining bridge functions (goHome, showWelcome, initSearch) app.js (80 lines) — Thin orchestrator: imports all modules, calls init() index.html: switched from <script src="app.js"> to <script type="module" src="js/app.js"> Original app.js preserved for backward compatibility. All 14 modules pass node --check syntax validation.
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.
┌─────────────────────────────────────────────────────────┐
│ [🔍 Recherche...] [☀/🌙 Thème] ObsiGate │
├──────────────┬──────────────────────────────────────────┤
│ SIDEBAR │ CONTENT AREA │
│ ▼ Recettes │ 📄 Titre du fichier │
│ 📁 Soupes │ Tags: #recette #rapide │
│ 📄 Pizza │ [Contenu Markdown rendu] │
│ ▼ IT │ │
│ 📁 Docker │ │
│ Tags Cloud │ │
└──────────────┴──────────────────────────────────────────┘
📋 Table des matières
- Fonctionnalités
- Architecture
- Prérequis
- Installation rapide
- Configuration détaillée
- Variables d'environnement
- 🔒 Authentification
- Ajouter une nouvelle vault
- Build & déploiement avec build.sh
- Utilisation
- API
- Performance
- Dépannage
- Stack technique
- Changelog
✨ Fonctionnalités
- 🗂️ 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 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/healthinté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
git clone https://git.dracodev.net/Projets/ObsiGate.git
cd ObsiGate
2. Configurer vos vaults et vos secrets
Éditez le fichier docker-compose.yml pour ajouter vos vaults Obsidian :
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 :
cp .env.example .env
# Éditez .env pour configurer vos mots de passe et options
Ne committez jamais
.env! Il est dans.gitignore. Utilisez.env.examplecomme référence.
3. Lancer l'application
# Rendre le script exécutable (une seule fois)
chmod +x build.sh
# Build + déploiement en une commande
./build.sh
C'est tout !
build.shvérifie Docker, contrôle vos volumes, construit l'image et démarre le conteneur automatiquement.Options utiles :
./build.sh --helppour l'aide,./build.sh --cachepour un rebuild plus rapide,./build.sh --build-onlypour 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
- Localisez vos vaults Obsidian sur votre système
- Notez les chemins absolus vers chaque dossier
.obsidian - Vérifiez les permissions : Docker doit pouvoir lire ces dossiers
Étape 2 : Configuration docker-compose.yml
Voici un exemple complet :
services:
obsigate:
build:
context: .
image: obsigate:latest
container_name: obsigate
restart: unless-stopped
ports:
- "2020:8080" # Port local 2020 → Port conteneur 8080
volumes:
# Exemples de vaults (adaptez à vos chemins)
- /home/user/Documents/Obsidian-Recettes:/vaults/Recettes:ro
- /home/user/Documents/Obsidian-IT:/vaults/IT:ro
- /home/user/Documents/Obsidian-Perso:/vaults/Perso:ro
# Persistance des données d'auth
- ./data:/app/data
environment:
# Configuration des vaults
- VAULT_1_NAME=Recettes
- VAULT_1_PATH=/vaults/Recettes
- VAULT_2_NAME=IT
- VAULT_2_PATH=/vaults/IT
- VAULT_3_NAME=Perso
- VAULT_3_PATH=/vaults/Perso
# Auth (les secrets sont dans .env)
- OBSIGATE_AUTH_ENABLED=true
- OBSIGATE_ADMIN_USER=admin
env_file:
- .env # Contient OBSIGATE_ADMIN_PASSWORD et autres secrets
Étape 3 : Build & déploiement
# Utilisez le script de build automatisé (recommandé)
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.
Alternative manuelle :
docker compose build --no-cache
docker compose up -d
Compatibilité Docker : l'image utilise la variante minimale de
uvicornet une version defastapicompatible (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.
🌍 Variables d'environnement
Les vaults sont configurées par paires de variables 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 |
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 |
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
🔒 Authentification
Désactivée par défaut — Compatible avec toutes les installations existantes.
ObsiGate supporte un système d'authentification optionnel basé sur JWT + Argon2id avec contrôle d'accès par vault.
Activer l'authentification
-
Copiez le template
.env:cp .env.example .env -
Éditez
.envet décommentez/configurez les variables :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 -
Dans
docker-compose.yml, assurez-vous d'avoir :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 dans les logs :
docker-compose logs obsigate | grep -A4 "PREMIER"
============================================================
PREMIER DÉMARRAGE — Compte admin créé automatiquement
Utilisateur : admin
Mot de passe : xK9mQ3pLr7wN2jT5
CHANGEZ CE MOT DE PASSE dès la première connexion !
============================================================
Gestion des utilisateurs via CLI
# 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 les utilisateurs
docker exec obsigate python backend/create_admin.py list
# Supprimer un utilisateur
docker exec obsigate python backend/create_admin.py delete alice
Interface d'administration
Lorsqu'un compte admin est connecté, une icône 🛡️ apparaît dans le header. Cliquer dessus ouvre le panneau d'administration permettant de :
- Lister tous les utilisateurs
- Créer / modifier / supprimer des utilisateurs
- Assigner les vaults accessibles par utilisateur
- Activer/désactiver des 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_LOGIN_WINDOW_SECONDS |
Fenêtre de rate limiting (secondes) | 900 |
Toutes ces variables sont documentées dans
.env.example.
Volume pour la persistance
Les données d'auth (users.json, secret.key) sont stockées dans /app/data. Ajoutez un volume pour les persister :
volumes:
# vaults...
- ./data:/app/data # Persistance des utilisateurs et clé JWT
➕ Ajouter une nouvelle vault
Méthode 1 : Édition directe
-
Arrêtez le conteneur :
docker-compose down -
Ajoutez un volume dans
docker-compose.yml:volumes: - /nouveau/chemin/vault:/vaults/NouvelleVault:ro -
Ajoutez les variables d'environnement :
environment: - VAULT_4_NAME=NouvelleVault - VAULT_4_PATH=/vaults/NouvelleVault -
Redémarrez :
./build.sh
Méthode 2 : Hot-reload (recommandé)
- Ajoutez le volume et les variables comme ci-dessus
- Appliquez les changements :
./build.sh - Rechargez l'index via l'interface ou l'API :
curl http://localhost:2020/api/index/reload
Méthode 3 : API dynamique (sans redémarrage)
Ajoutez une vault à chaud via l'API (le volume doit déjà être monté) :
curl -X POST http://localhost:2020/api/vaults/add \
-H "Content-Type: application/json" \
-d '{"name": "NouvelleVault", "path": "/vaults/NouvelleVault"}'
Supprimez une vault :
curl -X DELETE http://localhost:2020/api/vaults/NouvelleVault
🔨 Build & déploiement avec build.sh
Le script build.sh est la méthode recommandée pour construire et déployer ObsiGate.
Utilisation de base
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
- Vérifie que Docker et Docker Compose sont installés (avec numéros de version)
- Valide la présence et la syntaxe du fichier
docker-compose.yml - Vérifie chaque volume monté : avertit si un répertoire source n'existe pas
- Construit l'image Docker via
docker compose build(multi-stage, ~180MB) - Démarre le conteneur via
docker compose up -d - Affiche le statut du conteneur puis les logs en temps réel
Exemples
# Build propre + démarrage (recommandé après changement de code)
./build.sh
# Rebuild rapide avec cache (changements mineurs frontend uniquement)
./build.sh --cache
# Vérifier le build sans démarrer (pratique pour tester avant mise en prod)
./build.sh --build-only
# Sortie verbeuse pour débugger un échec de build
./build.sh --progress=plain
Arrêter / redémarrer
docker compose down # Arrêter le conteneur
docker compose up -d # Redémarrer sans rebuild
docker compose logs -f # Voir les logs
<EFBFBD>️ Rendu d'images Obsidian
ObsiGate supporte toutes les syntaxes d'images Obsidian avec un système de résolution intelligent multi-stratégies.
Syntaxes supportées
-
Standard Markdown avec attributs HTML (compatible Obsidian) :
[<img width="180" height="60" src="path/to/image.svg"/>](https://example.com) -
Wiki-link embed avec chemin complet :
![[06_Boite_a_Outils/6.2_Attachments/image.svg]] -
Wiki-link embed avec nom de fichier uniquement :
![[image.svg]] -
Markdown standard :

Résolution intelligente des chemins
ObsiGate utilise 7 stratégies de résolution par ordre de priorité :
- Chemin absolu : Si le chemin est absolu et existe
- Dossier d'attachements configuré : Via
VAULT_N_ATTACHMENTS_PATH - Index de démarrage (match unique) : Recherche par nom de fichier dans l'index
- Même répertoire : Relatif au fichier markdown courant
- Racine du vault : Relatif à la racine du vault
- Index de démarrage (match le plus proche) : Si plusieurs fichiers portent le même nom
- Fallback : Affiche un placeholder stylisé
[image not found: filename.ext]
Configuration
Pour optimiser la résolution, configurez le dossier d'attachements de chaque vault :
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
Pour rescanner les images d'un vault après ajout/suppression :
curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
<EFBFBD>📖 Utilisation
Interface web
- Navigation : Cliquez sur les vaults dans la sidebar pour les développer
- Recherche : Utilisez la barre de recherche pour chercher dans toutes les vaults
- Tags : Cliquez sur les tags pour filtrer les contenus
- Wikilinks : Les liens
[[page]]sont cliquables et navigables - Images : Toutes les syntaxes d'images Obsidian sont rendues automatiquement
- 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 |
🔌 API
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 (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 |
Quand
OBSIGATE_AUTH_ENABLED=false, tous les endpoints sont accessibles sans token.
Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur
/docs(Swagger UI).
Exemple d'utilisation :
# Health check
curl http://localhost:2020/api/health
# Lister les vaults
curl http://localhost:2020/api/vaults
# Recherche simple (legacy)
curl "http://localhost:2020/api/search?q=recette&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"
# Autocomplétion de titres
curl "http://localhost:2020/api/suggest?q=piz&vault=all"
# Autocomplétion de tags
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
### 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"` |
Exemples de filtre par extension : `ext:sh` pour les scripts bash, `ext:py` pour les scripts Python, `ext:md` pour les fichiers Markdown.
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.
### 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 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 `<mark>` 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
---
## 🔧 Dépannage
### Problèmes courants
**Port déjà utilisé :**
```bash
# Vérifier qui utilise le port
sudo netstat -tulpn | grep 2020
# Changer le port dans 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
Build échoue :
# Nettoyer le cache Docker et rebuild
docker system prune -f
docker compose down
./build.sh --progress=plain
# Si l'échec persiste, vérifier les logs de build
./build.sh --progress=plain 2>&1 | tee build.log
Logs pour debugging :
# Logs en temps réel
docker compose logs -f obsigate
# Logs détaillés
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é |
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 |
Ces paramètres sont configurables via l'interface (Settings) ou l'API /api/config.
Optimisations clés (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+AbortControllerempê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
Optimisations 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
Optimisations 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é viarequestAnimationFrame
🛡️ Sécurité
- 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
:ropar défaut dans docker-compose - Secrets dans
.env: les mots de passe et tokens ne sont jamais dansdocker-compose.yml - Atomic writes : les fichiers de données (users.json, shares.json, webhooks.json) utilisent tmp+replace
🏗️ Stack technique
- 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
- PDF Export : WeasyPrint 60+
- Compression : GZip middleware (FastAPI), Cache-Control immutable pour les assets
- Image Docker : python:3.11-slim (multi-stage),
.dockerignorepour contexte minimal - 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 │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ │
│ app.js │ │ │ indexer.py │ │ search.py │ │
│ style.css │ │ │ (scan+cache)│ │ (in-memory) │ │
│ index.html │ │ └───────┬──────┘ └──────┬───────┘ │
└─────────────────┘ │ │ │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────┴─────────┐ │
│ │ Index en mémoire │ │
│ │ (fichiers, tags, │ │
│ │ contenu, lookup)│ │
│ └──────────────────┘ │
└─────────────────────────────────────────┘
┌───────────────────────────────┐
│ Filesystem (vaults montées) │
│ /vaults/Recettes (ro) │
│ /vaults/IT (ro) │
└───────────────────────────────┘
Flux de données :
- Au démarrage,
indexer.pyscanne tous les vaults en parallèle (thread pool) - Le contenu, les tags (YAML + inline) et les métadonnées sont mis en cache en mémoire
- Une table de lookup O(1) est construite pour la résolution des wikilinks
watcher.pydémarre la surveillance des fichiers (watchdog natif ou polling)- Les modifications détectées déclenchent une mise à jour incrémentale de l'index
- Les changements sont notifiés au frontend via Server-Sent Events (SSE)
- Les requêtes de recherche utilisent l'index en mémoire (zéro I/O disque)
- Le frontend SPA communique via REST + SSE et gère l'état côté client
📝 Développement
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
│ │ ├── 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
│ └── 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)
├── 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
Contribuer
Voir CONTRIBUTING.md pour les détails.
📄 Licence
Ce projet est sous licence MIT - voir le fichier LICENSE pour les détails.
🤝 Support
- Issues : git.dracodev.net/Projets/ObsiGate/issues
- Documentation : git.dracodev.net/Projets/ObsiGate/wiki
- Auteur : Bruno Beloeil
📝 Changelog
v1.5.0 (2026-05-28)
CI/CD Pipeline (Gitea Actions)
- Pipeline 4 jobs : lint (ruff + mypy 0 erreur), test (175 tests), security (bandit + pip-audit), build (Docker)
- Runner Gitea auto-hébergé via
gitea/act_runner:0.2.11sur Proxmox/Alpine - Coverage artifact uploadé via
actions/upload-artifact@v3(natif Gitea, zéro dépendance GitHub) - Badge CI/CD dans le README
Qualité de code
- 28 erreurs mypy corrigées → 0 erreur sur 24 fichiers
- 36 erreurs ruff corrigées → 0 erreur
- Imports inutilisés,
Responseredéfini, typesOptionalnormalisés - 4 warnings Bandit résolus (faux positifs OAuth2
bearer, try/except shutdown)
Documentation
- ROADMAP.md créé
- README mis à jour (v1.5.0)
v1.4.0 (2026)
Authentification & Contrôle d'accès
- Système d'authentification JWT (HS256) optionnel — désactivé par défaut, activation via
OBSIGATE_AUTH_ENABLED=true - Hachage des mots de passe Argon2id (paramètres OWASP 2024 : time_cost=2, mem=64MB)
- Sessions avec access token (1h) + refresh token (7j, HttpOnly cookie) et révocation
- Contrôle d'accès par vault : chaque utilisateur accède uniquement à ses vaults assignées
- Bootstrap automatique du compte admin au premier démarrage (mot de passe généré et affiché dans les logs)
- CLI de gestion des utilisateurs :
python backend/create_admin.py create|list|delete - Interface d'administration dans la SPA : création/modification/suppression d'utilisateurs avec sélection de vaults
- Protection brute-force : verrouillage 15 min après 5 tentatives échouées
- Réponses en temps constant sur le login pour éviter l'énumération d'utilisateurs
- Token de rafraîchissement révocable (persisté dans
data/revoked_tokens.json) - Clé secrète JWT auto-générée (512 bits) et persistée dans
data/secret.key
Sécurité (headers)
Content-Security-Policysur toutes les réponsesX-Frame-Options: SAMEORIGINX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-origin
Frontend
- Écran de connexion avec glassmorphism (avant chargement de l'app si auth activée)
- Menu utilisateur dans le header (nom affiché, bouton admin, déconnexion)
- Rafraîchissement silencieux de l'access token (via cookie refresh)
- Compatibilité totale :
OBSIGATE_AUTH_ENABLED=false→ comportement identique à v1.3.x
v1.3.0 (2025)
Synchronisation temps réel
- Surveillance automatique des fichiers via
watchdogavec fallback polling automatique - Mise à jour incrémentale de l'index : ajout, modification, suppression et déplacement de fichiers sans rebuild complet
- Server-Sent Events (SSE) : endpoint
/api/eventspour notifications temps réel vers le frontend - Badge de synchronisation dans le header avec indicateur visuel (connecté/déconnecté/syncing)
- Panel d'événements récents accessible en cliquant sur le badge
- Reconnexion SSE automatique avec backoff exponentiel
- Toast informatif à chaque mise à jour détectée
- Rafraîchissement automatique de la sidebar et du fichier courant si affecté
Gestion dynamique des vaults
POST /api/vaults/add: ajouter une vault à chaud sans redémarrageDELETE /api/vaults/{name}: supprimer une vault de l'indexGET /api/vaults/status: statut détaillé (fichiers, tags, état de surveillance)- Les vaults ajoutées sont automatiquement surveillées par le watcher
Configuration watcher
- Nouveaux paramètres dans
config.json:watcher_enabled,watcher_use_polling,watcher_polling_interval,watcher_debounce - Section « Synchronisation automatique » dans la modal de configuration frontend
- Toggles pour activer/désactiver la surveillance et le mode polling
v1.2.0 (2025)
Performance (critique)
- Réécriture du moteur
advanced_search(): retrieval par set-intersection sur l'index inversé (O(k × postings) au lieu de O(N)) - Prefix matching par recherche binaire sur liste triée de tokens (O(log V + k) au lieu de O(V))
- Offload des fonctions de recherche CPU-bound via
ThreadPoolExecutor(2 workers par défaut) - Pré-calcul des expansions de préfixe pour éviter les recherches binaires répétées
- Fix du bug de staleness :
is_stale()utilise un compteur de génération au lieu deid(index)
Frontend
- Guard contre les race conditions :
currentSearchIdvérifié après chaquefetchavant rendu - Barre de progression animée pendant la recherche
- Timeout de recherche configurable (30s par défaut)
- Longueur minimale de requête configurable (2 caractères par défaut)
- Affichage du temps de requête serveur (
query_time_ms) dans les résultats - Pagination ajoutée sur l'endpoint legacy
/api/search(paramslimit/offset)
Configuration & Diagnostics
- Nouveaux endpoints
GET/POST /api/configpour la configuration persistante (config.json) - Nouveau endpoint
GET /api/diagnostics(stats index, mémoire, moteur de recherche) - Page de configuration étendue : paramètres frontend (debounce, résultats/page, timeout) et backend (workers, boosts, expansions)
- Panel de diagnostics intégré dans la modal de configuration
- Boutons « Forcer réindexation » et « Réinitialiser » dans les paramètres
v1.1.0 (2025)
Sécurité
- Protection path traversal sur tous les endpoints fichier
- Utilisateur non-root dans le conteneur Docker
- Dockerfile multi-stage (élimination des outils de build)
Performance
- Recherche fulltext en mémoire (zéro I/O disque par requête)
- Table de lookup O(1) pour la résolution des wikilinks
- Renderer mistune mis en cache (singleton)
- Scoring multi-facteurs (titre, chemin, tags, fréquence)
lucide.createIcons()batché viarequestAnimationFrameAbortControllersur les requêtes de recherche
Robustesse
- Swap atomique de l'index (thread-safe) pendant le reload
- Extraction des tags inline (#tag) depuis le contenu markdown
- Modèles Pydantic sur tous les endpoints API
- Gestion d'erreurs avec toasts utilisateur (frontend)
- États de chargement pour la sidebar et le contenu
- Remplacement de
on_eventdéprécié parlifespan
Infrastructure
- Endpoint
/api/healthpour monitoring - Healthcheck Docker (Dockerfile + docker-compose)
build.shamélioré (variable version, checks, couleurs)
Documentation
- Docstrings complètes sur toutes les fonctions Python
- Schémas Pydantic documentés (Swagger UI auto-générée)
- README : sections Architecture, Performance, Sécurité, Changelog
- CONTRIBUTING.md ajouté
v1.0.0 (2025)
- Version initiale
Projet : ObsiGate | Version : 1.5.0 | Dernière mise à jour : Mai 2026