claude code

This commit is contained in:
2026-01-28 19:22:30 +01:00
parent f9b1d43c81
commit bdbfa4e25a
104 changed files with 9591 additions and 261 deletions

View File

@@ -0,0 +1,192 @@
# ADR-0001 — Choix de la stack technique globale
- Statut : accepted
- Date : 2026-01-27
---
## Contexte
HomeStock est une application web self-hosted de gestion d'inventaire domestique destinée à un usage mono-utilisateur. Les contraintes principales sont :
- **Simplicité de déploiement** : L'application doit être facile à installer et maintenir sur un réseau local
- **Usage mono-utilisateur** : Pas besoin de scalabilité horizontale ni de gestion complexe de concurrence
- **Self-hosted** : Déploiement local, pas de dépendance cloud
- **Développement rapide** : Besoin d'un MVP fonctionnel rapidement avec des technologies modernes et productives
- **Maintenance à long terme** : Stack stable avec un bon écosystème et une documentation solide
Le projet nécessite :
- Une API REST robuste pour le backend
- Une interface utilisateur moderne et réactive
- Une base de données adaptée au mono-utilisateur
- Un système de stockage de fichiers (photos, notices, factures)
---
## Décision
Nous avons choisi la stack technique suivante :
### Backend
- **Langage** : Python 3.11+
- **Framework API** : FastAPI
- **ORM** : SQLAlchemy 2.0+ avec Alembic pour migrations
- **Validation** : Pydantic (intégré à FastAPI)
### Frontend
- **Langage** : TypeScript
- **Framework UI** : React 18+
- **Bundler** : Vite 5+
- **Gestion état** : TanStack Query (React Query) pour l'état serveur, Context API pour l'état local
- **Styling** : TailwindCSS avec palette Gruvbox dark
### Base de données
- **SGBD** : SQLite avec extension FTS5 pour recherche full-text
- **Fichier** : `homestock.db` en local
### Stockage fichiers
- **Système** : Système de fichiers local
- **Organisation** : Dossier `uploads/` avec sous-dossiers par type (photos/, notices/, factures/)
- **Référencement** : Chemins relatifs stockés en base de données
### Déploiement
- **Conteneurisation** : Docker Compose
- **Réseau** : Réseau local 10.0.0.0/22
---
## Alternatives considérées
### Backend
1. **Django + Django REST Framework**
- ✅ Framework très complet avec admin intégré
- ✅ ORM mature et puissant
- ❌ Plus lourd et verbeux que FastAPI
- ❌ Performance inférieure en async
- **Rejeté** : Trop de fonctionnalités inutiles pour notre cas d'usage mono-utilisateur
2. **Go avec Gin/Echo**
- ✅ Très performant, binaire standalone
- ✅ Faible consommation mémoire
- ❌ Écosystème moins riche pour gestion fichiers/images
- ❌ Développement plus verbeux, moins rapide
- **Rejeté** : Performance non critique pour mono-utilisateur, productivité Python préférée
3. **Node.js avec Express/Fastify**
- ✅ Écosystème npm très riche
- ✅ Même langage que le frontend (JavaScript/TypeScript)
- ❌ Gestion async plus complexe qu'avec FastAPI
- ❌ Préférence personnelle pour Python
- **Rejeté** : Moins d'expérience, Python préféré pour backend
### Frontend
1. **Vue.js 3**
- ✅ Simple à apprendre, Composition API moderne
- ✅ Excellente documentation
- ❌ Écosystème légèrement moins riche que React
- **Rejeté** : React préféré pour son écosystème et l'expérience existante
2. **Svelte/SvelteKit**
- ✅ Très léger, compilation au build
- ✅ Syntaxe simple et élégante
- ❌ Écosystème moins mature
- ❌ Moins de ressources et bibliothèques tierces
- **Rejeté** : Écosystème trop jeune pour un projet à long terme
### Base de données
1. **PostgreSQL**
- ✅ Très robuste, fonctionnalités avancées
- ✅ Meilleure recherche full-text (GIN indexes)
- ❌ Serveur séparé à gérer, plus complexe
- ❌ Overhead pour mono-utilisateur avec faible volume
- **Rejeté** : Trop complexe pour un usage mono-utilisateur avec <10k items attendus
2. **MySQL/MariaDB**
- ✅ Populaire, bien documenté
- ✅ Bonnes performances
- ❌ Recherche full-text moins performante
- ❌ Serveur séparé à gérer
- **Rejeté** : Même raison que PostgreSQL, SQLite suffit largement
### Stockage fichiers
1. **MinIO (S3-compatible)**
- ✅ Scalable, versioning, métadonnées riches
- ✅ Compatible S3, facilite migration future
- ❌ Service supplémentaire à déployer et maintenir
- ❌ Overhead pour mono-utilisateur
- **Rejeté** : Trop complexe pour le besoin, système fichiers local suffit
2. **Stockage en base (BLOB)**
- ✅ Pas de fichiers externes à gérer
- ✅ Transactions ACID sur fichiers
- ❌ Performance dégradée avec fichiers volumineux
- ❌ Backup et restauration plus complexes
- ❌ Taille de la base de données augmente rapidement
- **Rejeté** : Mauvaise pratique, problèmes de performance prévisibles
---
## Conséquences
### Positives
1. **Simplicité de déploiement** : Un seul `docker-compose up` lance l'ensemble de la stack
2. **Performance suffisante** : FastAPI async + SQLite suffisent largement pour mono-utilisateur
3. **Développement rapide** : FastAPI + React offrent une excellente productivité
4. **Pas de serveur BDD externe** : SQLite embarqué, un seul fichier à sauvegarder
5. **Auto-documentation API** : FastAPI génère automatiquement OpenAPI/Swagger
6. **Typage fort** : TypeScript (frontend) + Pydantic (backend) réduisent les erreurs
7. **Écosystèmes matures** : Python et React ont d'excellentes bibliothèques tierces
8. **Recherche performante** : FTS5 SQLite offre une recherche full-text native et rapide
### Négatives
1. **Limitations SQLite** : Pas de scalabilité horizontale (non critique pour mono-utilisateur)
2. **Recherche limitée** : FTS5 moins puissant qu'Elasticsearch (acceptable pour <10k items)
3. **Stockage fichiers manuel** : Pas de métadonnées avancées ni versioning
4. **Backup manuel** : Nécessite scripts de backup pour fichiers + BDD
5. **Migration future** : Si passage multi-utilisateurs, migration vers PostgreSQL nécessaire
### Neutres
1. **Deux langages** : Python (backend) + TypeScript (frontend) nécessitent deux environnements
2. **Complexité initiale React** : Courbe d'apprentissage pour développement React moderne
---
## Impacts techniques
### Organisation du code
- **Monorepo** : Backend et frontend dans le même dépôt
- **Structure backend** : `backend/app/` avec routers/, models/, schemas/, services/, repositories/
- **Structure frontend** : `frontend/src/` avec components/, pages/, hooks/, api/
### Dépendances principales
- **Backend** : fastapi, uvicorn, sqlalchemy, alembic, pydantic, python-multipart, loguru
- **Frontend** : react, react-router-dom, @tanstack/react-query, tailwindcss, axios
### Développement
- **Tests** : pytest (backend), vitest (frontend)
- **Lint/Format** : ruff + mypy (backend), eslint + prettier (frontend)
- **CI/CD** : Gitea Actions avec lint → test → build → deploy
### Déploiement
- **Docker** : 3 services (backend, frontend, reverse proxy optionnel)
- **Volumes** : Persistance pour `homestock.db` et `uploads/`
- **Ports** : Backend :8000, Frontend :5173 (dev) ou :80/:443 (prod avec reverse proxy)
### Évolutivité future
- **Migration PostgreSQL** : Possible via Alembic migrations si besoin
- **Stockage S3** : Abstraction stockage permet ajout MinIO sans refonte
- **Multi-utilisateurs** : Architecture modulaire facilite ajout authentification/RBAC
---
## Notes
Cette stack a été choisie en janvier 2026 pour un MVP mono-utilisateur. Les choix privilégient la simplicité et la rapidité de développement sur la scalabilité et les fonctionnalités avancées qui ne sont pas nécessaires pour le cas d'usage initial.
La stack permet une évolution progressive vers multi-utilisateurs et stockage cloud si besoin, mais ces aspects sont volontairement exclus du MVP pour réduire la complexité initiale.
Les alternatives (PostgreSQL, MinIO) restent envisageables pour des évolutions futures et sont documentées dans la section "Améliorations prévues" de [docs/ARCHITECTURE.md](../ARCHITECTURE.md).
---
**Contributeurs** : Gilles (décideur) + Claude Code (architecte)

View File

@@ -1,24 +0,0 @@
# ADR-0001 — Exemple (a supprimer)
- Statut : accepted
- Date : 2026-01-27
## Contexte
- Besoin dun framework API simple et maintenable.
## Décision
- Utiliser un monolithe modulaire.
## Alternatives considérées
- Microservices.
- Serverless (exécution sans serveur dédié).
## Conséquences
- Déploiement plus simple.
- Risque de croissance du monolithe.
## Impacts techniques
- Découper par domaines.
## Notes
- Revoir après 6 mois.

View File

@@ -0,0 +1,235 @@
# ADR-0002 — Architecture monolithe modulaire
- Statut : accepted
- Date : 2026-01-27
---
## Contexte
HomeStock nécessite une architecture logicielle adaptée à un projet mono-utilisateur self-hosted avec les contraintes suivantes :
- **Simplicité de déploiement** : L'application doit pouvoir être déployée facilement avec une seule commande
- **Maintenance réduite** : Pas d'équipe DevOps, maintenance manuelle par l'utilisateur
- **Développement solo** : Un seul développeur, besoin de cohérence et de simplicité
- **Évolutivité future** : Possibilité d'évoluer vers multi-utilisateurs sans refonte complète
- **Modules logiques distincts** : items, locations, categories, documents, search
Le projet démarre avec un périmètre limité (MVP) mais doit pouvoir évoluer progressivement. La question architecturale centrale est : comment organiser le code pour maximiser la maintenabilité tout en gardant une complexité opérationnelle minimale ?
---
## Décision
Nous adoptons une **architecture monolithe modulaire** avec les caractéristiques suivantes :
### Définition
- **Un seul dépôt Git** (monorepo) contenant backend et frontend
- **Un seul processus backend** (serveur FastAPI unique)
- **Modules logiques séparés** avec responsabilités clairement définies
- **Communication intra-application** via appels de fonctions directs
- **Base de données unique** partagée entre tous les modules
### Organisation backend (`backend/app/`)
```
backend/app/
├── main.py # Point d'entrée FastAPI
├── core/ # Configuration, logging, database
│ ├── config.py
│ ├── database.py
│ └── logging.py
├── models/ # Modèles SQLAlchemy (un fichier par entité)
│ ├── item.py
│ ├── location.py
│ ├── category.py
│ └── document.py
├── schemas/ # Schémas Pydantic (validation API)
│ ├── item.py
│ ├── location.py
│ └── ...
├── routers/ # Endpoints API (un fichier par ressource)
│ ├── items.py
│ ├── locations.py
│ ├── categories.py
│ ├── documents.py
│ └── search.py
├── services/ # Logique métier (orchestration)
│ ├── item_service.py
│ ├── location_service.py
│ └── ...
├── repositories/ # Accès données (abstraction BDD)
│ ├── item_repository.py
│ ├── location_repository.py
│ └── ...
└── utils/ # Utilitaires transverses
├── files.py
└── search.py
```
### Organisation frontend (`frontend/src/`)
```
frontend/src/
├── main.tsx # Point d'entrée React
├── App.tsx # Composant racine + routing
├── components/ # Composants réutilisables
│ ├── common/ # Boutons, inputs, modales...
│ ├── items/ # Composants spécifiques items
│ └── locations/ # Composants spécifiques locations
├── pages/ # Pages complètes (routes)
│ ├── Dashboard.tsx
│ ├── ItemList.tsx
│ ├── ItemDetail.tsx
│ └── ...
├── hooks/ # Custom hooks React
│ ├── useItems.ts
│ ├── useLocations.ts
│ └── ...
├── api/ # Client API (fetch/axios)
│ ├── items.ts
│ ├── locations.ts
│ └── ...
└── utils/ # Utilitaires helpers
└── formatting.ts
```
### Principes d'organisation
1. **Séparation en couches backend** : routers → services → repositories → models
2. **Dépendances unidirectionnelles** : Les couches supérieures dépendent des inférieures uniquement
3. **Modules auto-contenus** : Chaque module (items, locations, etc.) a ses propres fichiers dans chaque couche
4. **Pas de dépendances circulaires** entre modules
5. **Code partagé** dans `core/` et `utils/`
---
## Alternatives considérées
### 1. Microservices
**Description** : Séparer l'application en plusieurs services indépendants (service items, service locations, service search, etc.)
**Avantages** :
- ✅ Scalabilité horizontale indépendante par service
- ✅ Isolation des pannes (un service down ≠ tout down)
- ✅ Possibilité d'utiliser des technologies différentes par service
- ✅ Déploiement indépendant de chaque service
**Inconvénients** :
- ❌ Complexité opérationnelle énorme (orchestration, networking, monitoring)
- ❌ Nécessite API Gateway, service discovery, circuit breakers
- ❌ Communication réseau entre services (latence, sérialisations multiples)
- ❌ Transactions distribuées complexes (saga pattern)
- ❌ Debugging et traçabilité difficiles
- ❌ Overhead infrastructure (plusieurs conteneurs, load balancers, etc.)
**Verdict** : ❌ **Rejeté** - Complexité totalement disproportionnée pour un projet mono-utilisateur avec ~1 req/seconde max. Les bénéfices de scalabilité ne s'appliquent pas au cas d'usage.
### 2. Monolithe non structuré (big ball of mud)
**Description** : Tout le code dans quelques gros fichiers sans organisation claire
**Avantages** :
- ✅ Démarrage très rapide
- ✅ Pas de contraintes architecturales
**Inconvénients** :
- ❌ Maintenance cauchemardesque après quelques mois
- ❌ Code fortement couplé, difficile à tester
- ❌ Évolution impossible sans refonte complète
- ❌ Onboarding difficile pour contributeurs futurs
**Verdict** : ❌ **Rejeté** - Dette technique garantie, inacceptable même pour un projet solo
### 3. Architecture hexagonale (ports & adapters)
**Description** : Isolation stricte du domaine métier avec ports (interfaces) et adapters (implémentations)
**Avantages** :
- ✅ Isolation forte du domaine métier
- ✅ Testabilité maximale (mocks faciles)
- ✅ Changement de base de données transparent
**Inconvénients** :
- ❌ Boilerplate important (interfaces, adapters, DTOs multiples)
- ❌ Courbe d'apprentissage élevée
- ❌ Overhead pour un projet simple comme HomeStock
**Verdict** : ⚠️ **Rejeté mais inspirant** - Trop complexe pour notre besoin, mais on garde l'idée de séparation en couches (repository pattern)
### 4. Monolithe avec modules faiblement couplés (notre choix)
**Description** : Un seul déploiement avec modules logiques bien séparés et communication via interfaces claires
**Avantages** :
- ✅ Simplicité opérationnelle (un seul processus, un seul conteneur)
- ✅ Transactions ACID simples (même base de données)
- ✅ Pas de latence réseau entre modules
- ✅ Refactoring et debugging faciles
- ✅ Évolution progressive possible (extraction en microservices si vraiment nécessaire)
- ✅ Maintenance réduite
**Inconvénients** :
- ⚠️ Scalabilité horizontale limitée (non critique pour mono-utilisateur)
- ⚠️ Restart complet nécessaire pour tout changement (acceptable)
**Verdict** : ✅ **Choisi** - Meilleur équilibre complexité/bénéfices pour notre contexte
---
## Conséquences
### Positives
1. **Déploiement simple** : `docker-compose up` lance tout le système
2. **Transactions ACID** : Toutes les opérations en base profitent des transactions SQLite
3. **Performance** : Pas de latence réseau entre modules, appels de fonctions directs
4. **Debugging facile** : Stack traces complètes, un seul processus à inspecter
5. **Refactoring sans risque** : IDE et linters détectent les erreurs lors de renommages
6. **Tests simples** : Tests d'intégration faciles (toute l'app dans le même process)
7. **Maintenance réduite** : Un seul service à monitorer, logs centralisés naturellement
### Négatives
1. **Scalabilité limitée** : Impossible de scaler un module indépendamment (non pertinent pour mono-utilisateur)
2. **Couplage temporel** : Si un module plante, tout plante (acceptable avec bonne gestion erreurs)
3. **Restart complet** : Modification = restart de toute l'app (dev avec hot-reload, prod peu de déploiements)
### Mitigations
- **Pour éviter le couplage fort** : Respecter strictement les couches (routers → services → repositories)
- **Pour faciliter évolution future** : Interfaces claires entre modules, pas de dépendances circulaires
- **Pour améliorer résilience** : Gestion d'erreurs robuste, retry logic, circuit breaker patterns si nécessaire
---
## Impacts techniques
### Développement
- **Pattern 3-layer** : Chaque feature touche 3 fichiers minimum (router, service, repository)
- **Imports Python** : Imports absolus depuis `app.` pour éviter imports relatifs fragiles
- **Tests** : Tests unitaires par couche + tests d'intégration API end-to-end
### Déploiement
- **Docker** : Un seul conteneur backend (+ conteneur frontend séparé pour SPA)
- **Scaling** : Scale vertical uniquement (augmenter CPU/RAM du conteneur)
- **Rollback** : Simple (redéploiement image Docker précédente)
### Évolution future
Si le projet nécessite vraiment des microservices (multi-utilisateurs massif, équipes séparées) :
1. **Extraction progressive** : Modules déjà bien séparés, extraction facilitée
2. **API interne → API externe** : Appels de fonctions deviennent appels HTTP
3. **Base partagée → bases séparées** : Migrations Alembic facilitent séparation BDD
Cette architecture n'empêche pas l'évolution, elle la retarde jusqu'à ce qu'elle soit vraiment nécessaire (principe YAGNI = You Ain't Gonna Need It).
---
## Notes
Cette décision s'inscrit dans la philosophie "Start with a monolith" popularisée par Martin Fowler et confirmée par de nombreux retours d'expérience (Shopify, GitHub, Stack Overflow ont démarré en monolithe).
Pour HomeStock, projet mono-utilisateur avec ~1 req/sec max en pic, un monolithe modulaire est largement suffisant et restera probablement optimal même à long terme.
La complexité des microservices n'apporterait aucun bénéfice tangible et augmenterait drastiquement les coûts de développement et maintenance.
**Principe appliqué** : "Choose boring technology" - privilégier les architectures éprouvées et simples sur les architectures à la mode mais complexes.
---
**Contributeurs** : Gilles (décideur) + Claude Code (architecte)
**Références** :
- Martin Fowler - "Monolith First" : https://martinfowler.com/bliki/MonolithFirst.html
- Sam Newman - "Building Microservices" (recommande de démarrer en monolithe)

View File

@@ -0,0 +1,259 @@
# ADR-0003 — Recherche full-text avec SQLite FTS5
- Statut : accepted
- Date : 2026-01-27
---
## Contexte
HomeStock nécessite une fonctionnalité de recherche rapide et efficace pour retrouver des objets dans l'inventaire. Les utilisateurs doivent pouvoir rechercher par :
- Nom de l'objet (ex: "perceuse")
- Description (ex: "outil électrique sans fil")
- Catégorie (ex: "bricolage")
- Localisation (ex: "garage", "étagère A")
**Contraintes identifiées** :
- **Volume de données** : ~1000-5000 items attendus initialement, maximum 10000 items à long terme
- **Performance** : Résultats de recherche en <200ms souhaités
- **Simplicité** : Pas de serveur externe supplémentaire à gérer
- **Pertinence** : Recherche "fuzzy" pas nécessaire, recherche exacte par mots-clés suffit
- **Langue** : Recherche en français uniquement
Le choix de la solution de recherche impacte directement l'expérience utilisateur (fonctionnalité critique) et l'architecture technique (composant additionnel ou intégré).
---
## Décision
Nous utilisons **SQLite FTS5 (Full-Text Search 5)** pour la recherche full-text avec les caractéristiques suivantes :
### Configuration
- **Table virtuelle FTS5** : `fts_items` contenant les champs indexés
- **Champs indexés** : `Item.name`, `Item.description`, `Category.name`, `Location.path`
- **Tokenizer** : `unicode61` (support Unicode, casse insensible)
- **Synchronisation** : Triggers SQLite pour maintenir FTS5 à jour lors des INSERT/UPDATE/DELETE
### Exemple de structure
```sql
CREATE VIRTUAL TABLE fts_items USING fts5(
item_id UNINDEXED,
name,
description,
category_name,
location_path,
tokenize = 'unicode61'
);
-- Triggers pour synchronisation automatique
CREATE TRIGGER items_after_insert AFTER INSERT ON items BEGIN
INSERT INTO fts_items(item_id, name, description, category_name, location_path)
VALUES (
NEW.id,
NEW.name,
NEW.description,
(SELECT name FROM categories WHERE id = NEW.category_id),
(SELECT path FROM locations WHERE id = NEW.location_id)
);
END;
-- Triggers similaires pour UPDATE et DELETE
```
### Requêtes de recherche
```sql
-- Recherche simple
SELECT items.* FROM items
JOIN fts_items ON items.id = fts_items.item_id
WHERE fts_items MATCH 'perceuse';
-- Recherche avec boost (priorité sur le nom)
SELECT items.* FROM items
JOIN fts_items ON items.id = fts_items.item_id
WHERE fts_items MATCH '{name}: perceuse OR {description}: perceuse'
ORDER BY rank;
```
---
## Alternatives considérées
### 1. Elasticsearch
**Description** : Moteur de recherche distribué dédié, standard de l'industrie
**Avantages** :
- ✅ Recherche extrêmement performante et flexible
- ✅ Recherche fuzzy, phonétique, synonymes
- ✅ Agrégations et analytics avancées
- ✅ Scalable horizontalement
**Inconvénients** :
- ❌ Serveur Java lourd (1-2GB RAM minimum)
- ❌ Complexité opérationnelle importante
- ❌ Nécessite synchronisation BDD → Elasticsearch
- ❌ Overhead massif pour 1000-10000 items
- ❌ Configuration complexe pour français
**Verdict** : ❌ **Rejeté** - Tuer une mouche avec un bazooka. Elasticsearch est conçu pour des millions de documents et des cas d'usage complexes (facettes, agrégations, etc.) dont HomeStock n'a pas besoin.
### 2. MeiliSearch
**Description** : Moteur de recherche moderne, léger, orienté UX
**Avantages** :
- ✅ Très rapide et pertinent out-of-the-box
- ✅ API REST simple
- ✅ Typo-tolerance native
- ✅ Plus léger qu'Elasticsearch (~50MB RAM)
- ✅ Configuration minimale
**Inconvénients** :
- ❌ Service externe supplémentaire à déployer
- ❌ Synchronisation BDD → MeiliSearch nécessaire
- ❌ Overhead pour petit volume de données
- ❌ Dépendance additionnelle à maintenir
**Verdict** : ⚠️ **Rejeté mais intéressant** - Excellente solution technique mais ajoute de la complexité pour un bénéfice limité sur <10k items. À considérer si passage à >50k items.
### 3. PostgreSQL Full-Text Search (pg_trgm + GIN)
**Description** : Recherche full-text native de PostgreSQL
**Avantages** :
- ✅ Très performant avec index GIN
- ✅ Recherche trigram (similitude)
- ✅ Support langues naturelles (stemming français)
- ✅ Pas de service externe
**Inconvénients** :
- ❌ Nécessite PostgreSQL (vs SQLite choisi dans ADR-0001)
- ❌ Serveur BDD à gérer
- ❌ Complexité setup (dictionnaires français, configuration)
**Verdict** : ❌ **Rejeté** - Excellente solution technique mais nécessite PostgreSQL. Si migration vers PostgreSQL (cas multi-utilisateurs), reconsidérer.
### 4. LIKE '%keyword%' en SQL
**Description** : Recherche basique avec opérateur LIKE
**Avantages** :
- ✅ Extrêmement simple
- ✅ Aucune dépendance
**Inconvénients** :
- ❌ Performance catastrophique (full table scan)
- ❌ Pas de pertinence/ranking
- ❌ Impossible de rechercher sur plusieurs champs efficacement
- ❌ Pas d'opérateurs (AND, OR, NOT)
**Verdict** : ❌ **Rejeté** - Inacceptable même pour 1000 items, expérience utilisateur dégradée
### 5. SQLite FTS5 (notre choix)
**Description** : Module de recherche full-text intégré à SQLite
**Avantages** :
- ✅ Intégré à SQLite, pas de service externe
- ✅ Performance excellente pour <100k documents
- ✅ Support opérateurs (AND, OR, NOT, NEAR, phrases)
- ✅ Ranking par pertinence (BM25)
- ✅ Triggers pour synchronisation automatique
- ✅ Tokenizer Unicode (support français)
- ✅ Footprint mémoire minimal
**Inconvénients** :
- ⚠️ Pas de recherche fuzzy native (typos)
- ⚠️ Pas de stemming français parfait
- ⚠️ Limitations si >100k documents (acceptable pour notre cas)
**Verdict** : ✅ **Choisi** - Solution optimale pour notre contexte : performance suffisante, simplicité maximale, pas de dépendance externe
---
## Conséquences
### Positives
1. **Simplicité architecture** : Pas de service externe, tout dans SQLite
2. **Performance suffisante** : FTS5 recherche <50ms sur 10k items
3. **Synchronisation automatique** : Triggers maintiennent l'index à jour
4. **Zéro configuration** : FTS5 inclus dans SQLite depuis version 3.9.0 (2015)
5. **Backup simplifié** : Index FTS5 dans le même fichier .db que les données
6. **Opérateurs avancés** : Support AND, OR, NOT, recherche par phrase
7. **Ranking pertinent** : Algorithme BM25 pour trier par pertinence
### Négatives
1. **Pas de typo-tolerance** : "perceuze" ne trouvera pas "perceuse"
2. **Stemming limité** : "perçant" et "percer" non reliés automatiquement
3. **Performance dégradée >100k items** : Limite théorique (très au-delà de notre besoin)
4. **Pas d'agrégations** : Impossible de faire des facettes de recherche complexes
5. **Recherche française limitée** : Pas de dictionnaire français sophistiqué
### Mitigations
- **Typo-tolerance** : Implémenter suggestions de correction côté application si besoin (Levenshtein distance)
- **Stemming** : Ajouter mots-clés/tags aux items pour améliorer découvrabilité
- **Performance** : Si >100k items, migrer vers PostgreSQL pg_trgm ou MeiliSearch
- **Agrégations** : Implémenter filtres côté application (par catégorie, localisation, etc.)
---
## Impacts techniques
### Développement
- **Migration Alembic** : Créer table virtuelle FTS5 + triggers lors du setup initial
- **Service de recherche** : `search_service.py` encapsule la logique FTS5
- **API endpoint** : `GET /api/v1/search?q=keyword` retourne items matchant
### Performance attendue
- **<1000 items** : <10ms
- **1000-5000 items** : <50ms
- **5000-10000 items** : <150ms
- **>10000 items** : Dégrédation possible, monitoring nécessaire
### Structure de code
```python
# services/search_service.py
class SearchService:
async def search_items(self, query: str, limit: int = 50):
"""Recherche full-text avec FTS5"""
# Sanitize query (échapper caractères spéciaux)
safe_query = self._escape_fts5_query(query)
# Query FTS5 avec ranking
sql = """
SELECT items.*,
rank AS relevance
FROM items
JOIN fts_items ON items.id = fts_items.item_id
WHERE fts_items MATCH :query
ORDER BY rank
LIMIT :limit
"""
return await self.db.execute(sql, {"query": safe_query, "limit": limit})
```
### Évolution future
Si les limitations deviennent bloquantes :
1. **Migration PostgreSQL** : Activer pg_trgm pour recherche similitude
2. **Ajout MeiliSearch** : Service séparé pour recherche avancée (garder SQLite pour données)
3. **Hybrid search** : Combiner FTS5 (rapide) + recherche sémantique externe (typos, synonymes)
---
## Notes
SQLite FTS5 est utilisé par de nombreux projets à succès pour des cas d'usage similaires :
- **Firefox** : Recherche dans l'historique et favoris
- **Apple Mail** : Indexation locale des emails
- **VS Code** : Recherche dans l'historique de commandes
Pour HomeStock avec <10k items attendus, FTS5 est largement suffisant et offre le meilleur ratio performance/simplicité.
La décision de ne pas utiliser Elasticsearch ou MeiliSearch n'est pas dogmatique : si le projet évolue vers >50k items ou nécessite recherche fuzzy sophistiquée, migration possible sans refonte majeure (API de recherche reste identique, seule l'implémentation change).
**Principe appliqué** : "Use the simplest tool that works" - Ne pas sur-architecturer pour des besoins hypothétiques futurs.
---
**Contributeurs** : Gilles (décideur) + Claude Code (architecte)
**Références** :
- SQLite FTS5 documentation : https://www.sqlite.org/fts5.html
- BM25 ranking algorithm : https://en.wikipedia.org/wiki/Okapi_BM25

View File

@@ -0,0 +1,226 @@
# ADR-0004 — Pas d'authentification (réseau local uniquement)
- Statut : accepted
- Date : 2026-01-27
---
## Contexte
HomeStock est une application web self-hosted destinée à un usage mono-utilisateur sur un réseau local domestique. La question de la sécurité d'accès se pose :
**Contraintes du projet** :
- **Déploiement** : Réseau local 10.0.0.0/22, pas d'exposition Internet prévue
- **Utilisateurs** : Mono-utilisateur (propriétaire du domicile)
- **Données** : Inventaire personnel, factures (non sensibles au sens RGPD)
- **Accès physique** : Réseau domestique, appareils de confiance uniquement
**Questions à résoudre** :
1. Faut-il implémenter un système d'authentification (login/password) ?
2. Si oui, quel niveau de sécurité est nécessaire ?
3. Quels sont les risques réels d'un accès non authentifié sur réseau local ?
L'authentification ajoute de la complexité (gestion sessions, hash passwords, UI login) et peut dégrader l'expérience utilisateur (saisie répétée de credentials) pour un bénéfice de sécurité potentiellement faible dans ce contexte.
---
## Décision
**Nous ne déployons PAS de système d'authentification pour le MVP.** L'application est accessible librement sur le réseau local sans login ni password.
### Justification
1. **Périmètre réseau contrôlé** : Réseau domestique 10.0.0.0/22, tous les appareils sont de confiance
2. **Pas d'exposition Internet** : Application non accessible depuis l'extérieur, pas de port forwarding
3. **Mono-utilisateur** : Pas de besoin de gestion de sessions ou de rôles
4. **Simplicité UX** : Accès immédiat à l'application, pas de friction login
5. **Données non critiques** : Inventaire domestique et factures, pas de données bancaires ou médicales
### Périmètre de sécurité
-**Réseau local uniquement** : Application bind sur IP privée, pas 0.0.0.0
-**HTTPS optionnel** : TLS pour chiffrement en transit si certificat local disponible
-**Firewall** : Ports fermés en dehors du réseau local (configuration routeur)
-**Pas de login/password** : Accès direct à l'application
-**Pas de sessions** : Pas de gestion de tokens ou cookies d'authentification
-**Pas de RBAC** : Pas de rôles ou permissions (mono-utilisateur)
### Configuration réseau recommandée
```yaml
# docker-compose.yml
services:
backend:
ports:
- "10.0.0.X:8000:8000" # Bind IP privée uniquement, pas 0.0.0.0:8000
environment:
- ALLOWED_HOSTS=10.0.0.0/22 # Whitelist réseau local
```
---
## Alternatives considérées
### 1. Authentification basique (login/password)
**Description** : Page de connexion avec username/password, session cookie
**Avantages** :
- ✅ Protection contre accès non autorisé sur réseau local
- ✅ Traçabilité des accès (logs)
- ✅ Possibilité multi-utilisateurs future facilitée
**Inconvénients** :
- ❌ Complexité additionnelle (hash passwords, sessions, CSRF, refresh tokens)
- ❌ UX dégradée (saisie credentials à chaque accès)
- ❌ Gestion mot de passe oublié nécessaire
- ❌ Sécurité illusoire si appareil compromis (cookies volés)
**Verdict** : ❌ **Rejeté pour MVP** - Bénéfice limité sur réseau local de confiance, complexité injustifiée
### 2. Authentification par IP (whitelist)
**Description** : Autoriser uniquement certaines IPs du réseau local
**Avantages** :
- ✅ Simple à implémenter (middleware FastAPI)
- ✅ Pas d'UI login
**Inconvénients** :
- ❌ Gestion fastidieuse des IPs autorisées
- ❌ DHCP complique le suivi (IPs dynamiques)
- ❌ Pas de granularité utilisateur
**Verdict** : ❌ **Rejeté** - Complexité sans bénéfice majeur, réseau déjà isolé
### 3. Authentification par certificat client (mTLS)
**Description** : Certificats X.509 pour authentifier les appareils
**Avantages** :
- ✅ Sécurité forte
- ✅ Pas de password à mémoriser
**Inconvénients** :
- ❌ Complexité extrême (PKI, distribution certificats)
- ❌ Configuration browser complexe
- ❌ Totalement surdimensionné pour le cas d'usage
**Verdict** : ❌ **Rejeté** - Overkill absolu
### 4. Pas d'authentification (notre choix)
**Description** : Accès libre sur réseau local
**Avantages** :
- ✅ Simplicité maximale (pas de code auth)
- ✅ UX optimale (accès instantané)
- ✅ Maintenance réduite
- ✅ Adapté au contexte mono-utilisateur réseau local
**Inconvénients** :
- ⚠️ Accès libre pour tout appareil sur réseau local
- ⚠️ Pas de traçabilité utilisateur
- ⚠️ Si exposition Internet accidentelle = vulnérabilité
**Verdict** : ✅ **Choisi** - Solution adaptée au contexte avec mitigations appropriées
---
## Conséquences
### Positives
1. **Simplicité code** : Pas de code d'authentification, sessions, CSRF protection
2. **UX fluide** : Accès immédiat, pas de friction login
3. **Pas de gestion passwords** : Pas de hash, reset password, rotation
4. **Maintenance réduite** : Pas de vulnérabilités auth à monitorer (OWASP Top 10)
5. **Performance** : Pas de vérification session sur chaque requête
### Négatives et risques
1. **Exposition accidentelle Internet** : Si port forwarding activé par erreur → accès public
2. **Appareil compromis sur réseau** : Malware sur laptop/smartphone pourrait accéder à l'app
3. **Visiteurs réseau** : Invités connectés au WiFi domestique peuvent accéder à l'app
4. **Pas de traçabilité** : Impossible de savoir qui a modifié/supprimé des données
### Mitigations implémentées
1. **Bind IP privée uniquement** : Backend écoute sur 10.0.0.X:8000, pas 0.0.0.0
2. **Firewall routeur** : Ports 8000/5173 fermés en WAN, ouverts LAN uniquement
3. **HTTPS optionnel** : Certificat auto-signé pour chiffrement en transit
4. **Logging applicatif** : Logs des actions (créer/modifier/supprimer items) avec IP source
5. **Backup régulier** : Scripts de backup pour récupération en cas d'actions malveillantes
6. **Documentation claire** : Avertissement dans README sur importance isolation réseau
### Mitigations à envisager (post-MVP)
1. **Mode "invité" simple** : Code PIN basique si besoin de partager l'accès temporairement
2. **Alerte exposition** : Check au startup si l'app est accessible depuis Internet (API externe)
3. **Read-only mode** : Mode lecture seule pour appareils moins fiables
---
## Impacts techniques
### Code simplifié
Pas de code d'authentification signifie :
- Pas de modèle `User` en base
- Pas de hash passwords (bcrypt, argon2)
- Pas de gestion sessions/tokens
- Pas de middleware CSRF protection
- Pas d'endpoints `/login`, `/logout`, `/register`
- Pas d'UI login/signup
### Configuration Docker
```yaml
# docker-compose.yml
services:
backend:
ports:
- "10.0.0.50:8000:8000" # IP privée fixe uniquement
environment:
- ALLOWED_ORIGINS=http://10.0.0.50:5173,http://localhost:5173
- CORS_ALLOW_CREDENTIALS=false # Pas de cookies
```
### Logging renforcé
Même sans auth, logger les actions pour traçabilité :
```python
# Exemple log
logger.info(
"Item created",
extra={
"item_id": item.id,
"item_name": item.name,
"source_ip": request.client.host,
"user_agent": request.headers.get("user-agent")
}
)
```
### Évolution future
Si exposition Internet devient nécessaire (accès depuis extérieur) :
1. **Ajouter authentification** : Login simple (username/password) + session cookie
2. **Reverse proxy avec auth** : Traefik BasicAuth ou OAuth2 Proxy
3. **VPN** : Accès via WireGuard/Tailscale (recommandé)
4. **Cloudflare Access** : Zero-trust avec auth externe
---
## Notes
Cette décision est **contextuelle** et adaptée à HomeStock :
- ✅ Réseau local domestique contrôlé
- ✅ Mono-utilisateur
- ✅ Données non critiques
Elle serait **inappropriée** pour :
- ❌ Application exposée sur Internet
- ❌ Multi-utilisateurs
- ❌ Données sensibles (bancaires, médicales, secrets)
- ❌ Environnement professionnel
**Analogie** : C'est comme ne pas mettre de serrure sur la porte de sa chambre dans sa propre maison (réseau local) vs mettre une serrure sur la porte d'entrée (exposition Internet).
Si le contexte change (exposition Internet, ajout utilisateurs), cette décision devra être revisitée et l'authentification ajoutée. L'architecture modulaire (ADR-0002) facilite cet ajout futur sans refonte majeure.
**Principe appliqué** : "Security proportionate to risk" - Ne pas sur-sécuriser quand le risque est faible et contrôlé.
---
**Contributeurs** : Gilles (décideur) + Claude Code (architecte)
**Avertissement important** : Cette configuration est sécurisée UNIQUEMENT si le réseau local est isolé d'Internet. Vérifier régulièrement que :
1. Aucun port forwarding n'est configuré sur le routeur
2. Aucun service de tunnel (ngrok, etc.) n'est actif
3. Le firewall du routeur est correctement configuré