SCHEMA.md
... ...
@@ -0,0 +1,31 @@
1
+# Wiki Schema
2
+
3
+## Domain
4
+Hermes VPS — configuration, skills, conteneurs, sécurité
5
+
6
+## Conventions
7
+- File names: lowercase, hyphens, no spaces
8
+- Every page starts with YAML frontmatter
9
+- Use [[wikilinks]] to link between pages (minimum 2 outbound links)
10
+- Update `updated` date on every change
11
+- Every new page must be added to index.md
12
+
13
+## Frontmatter
14
+```yaml
15
+---
16
+title: Page Title
17
+created: YYYY-MM-DD
18
+updated: YYYY-MM-DD
19
+type: entity | concept | comparison | query
20
+tags: []
21
+sources: []
22
+---
23
+```
24
+
25
+## Tag Taxonomy
26
+- infra: Infrastructure, serveurs, réseaux
27
+- security: Sécurité, firewall, hardening
28
+- container: Docker, conteneurs, Traefik
29
+- hermes: Hermes Agent, config, outils
30
+- ai: IA, LLM, Claude Code
31
+- skill: Skills, automatisation
concepts/billetweb-api.md
... ...
@@ -0,0 +1,364 @@
1
+# API BilletWeb — Référence complète
2
+
3
+> **Plateforme :** [BilletWeb.fr](https://www.billetweb.fr) — billetterie en ligne
4
+> **Compte :** Parc de la Luge (user=268537)
5
+> **Dernière mise à jour :** 29/05/2026
6
+
7
+---
8
+
9
+## 1. Authentification
10
+
11
+L'API BilletWeb utilise une authentification par **query parameters** (pas de token Bearer).
12
+
13
+```
14
+user=<user_id>
15
+key=<api_key>
16
+version=1
17
+```
18
+
19
+L'API key se trouve dans le compte BilletWeb : **Réglages → Outils → API**.
20
+
21
+> ⚠️ **Pas de JSON-RPC, pas de REST auth standard** — clé en query string.
22
+
23
+---
24
+
25
+## 2. Endpoints
26
+
27
+### 2.1 Lister les événements
28
+
29
+```
30
+GET https://www.billetweb.fr/api/user/events
31
+ ?user=<user_id>
32
+ &key=<api_key>
33
+ &version=1
34
+```
35
+
36
+Retourne la liste des événements du compte.
37
+
38
+**Réponse :**
39
+```json
40
+[
41
+ {
42
+ "id": 1350897,
43
+ "name": "Parc de la Luge - Billetterie",
44
+ "date_start": "2026-01-01",
45
+ "date_end": "2026-12-31",
46
+ "status": "active"
47
+ }
48
+]
49
+```
50
+
51
+### 2.2 Lister les participants
52
+
53
+```
54
+GET https://www.billetweb.fr/api/event/{event_id}/attendees
55
+ ?user=<user_id>
56
+ &key=<api_key>
57
+ &version=1
58
+ &since=<YYYY-MM-DD HH:MM:SS>
59
+```
60
+
61
+**Paramètres :**
62
+
63
+| Paramètre | Description |
64
+|-----------|-------------|
65
+| `event_id` | ID de l'événement BilletWeb |
66
+| `since` | Optionnel — filtre les ventes après cette date (format `YYYY-MM-DD HH:MM:SS`) |
67
+| `ticket_id` | Optionnel — filtre par type de billet |
68
+
69
+**Réponse (tableau de participants) :**
70
+```json
71
+[
72
+ {
73
+ "id": "12345678",
74
+ "barcode": "6743097169",
75
+ "ref": "BW-XXXXXX",
76
+ "ticket": "Luge - Forfait 4 tours",
77
+ "ticket_id": 12345,
78
+ "name": "Jean Dupont",
79
+ "email": "jean@email.com",
80
+ "used": "0",
81
+ "date": "2026-05-15 10:30:00",
82
+ "price": "11.00",
83
+ "event_id": "1350897"
84
+ }
85
+]
86
+```
87
+
88
+**Champs importants :**
89
+
90
+| Champ | Type | Description |
91
+|-------|------|-------------|
92
+| `id` | string | ID unique de la commande (référence de commande) |
93
+| `barcode` | string | **Code barre unique** (servira de lien entre BilletWeb et Odoo) |
94
+| `ref` | string | Référence BilletWeb (format `BW-XXXXXX`) |
95
+| `ticket` | string | Nom du billet |
96
+| `ticket_id` | int | ID du type de billet |
97
+| `name` | string | Nom du participant |
98
+| `email` | string | Email du participant |
99
+| `used` | string | `"0"` = pas encore composté, `"1"` = déjà utilisé |
100
+| `date` | string | Date d'achat |
101
+| `price` | string | Prix payé |
102
+| `event_id` | string | ID de l'événement |
103
+
104
+### 2.3 Composter un billet (check)
105
+
106
+```
107
+POST https://www.billetweb.fr/api/event/{event_id}/check
108
+ ?user=<user_id>
109
+ &key=<api_key>
110
+ &version=1
111
+
112
+Body: {"code": "6743097169"}
113
+```
114
+
115
+**Comportement :**
116
+- Valide que le billet existe et n'a pas déjà été utilisé
117
+- **Composte** le billet (le marque comme `used=1`)
118
+- **Idempotent ?** Non — une fois composté, un deuxième appel échoue
119
+
120
+**Réponse succès :**
121
+```json
122
+{"success": true, "message": "Billet composté avec succès"}
123
+```
124
+
125
+**Réponse erreur (billet déjà utilisé) :**
126
+```json
127
+{"error": "Ce billet a déjà été utilisé"}
128
+```
129
+
130
+---
131
+
132
+## 3. Événements du Parc de la Luge
133
+
134
+| ID BilletWeb | Nom | Événement Odoo |
135
+|:-----------:|------|:--------------:|
136
+| `1350897` | Parc de la Luge - Billetterie | Event ID 1 |
137
+| `1353693` | BON CADEAU | Event ID 2 |
138
+
139
+---
140
+
141
+## 4. Mapping produits (18 billets)
142
+
143
+| Intitulé BilletWeb | Code barre Odoo |
144
+|-------------------|-----------------|
145
+| Journée Luge illimitée | `BW-LUGE-JOUR` |
146
+| Luge - Forfait 4 tours | `BW-LUGE-4T` |
147
+| Luge - Forfait 6 tours | `BW-LUGE-6T` |
148
+| Luge - Forfait 10 tours | `BW-LUGE-10T` |
149
+| Luge - Forfait 12 tours | `BW-LUGE-12T` |
150
+| Poney | `BW-PONEY` |
151
+| Mon ticket Promo | `BW-PROMO` |
152
+| OFFRE LIMITÉE - SPÉCIALE TRIBU | `BW-LUGE-TRIBU` |
153
+| Tir à l'arc - 20 min | `BW-TIR-20` |
154
+| Tir à l'arc - 40 min | `BW-TIR-40` |
155
+| Kids électrique - 10 min | `BW-KIDS-E10` |
156
+| Trottinettes ETT - 20 min | `BW-TROTT-20` |
157
+| Bon cadeau Luge 4 tours | `BW-CADEAU-LUGE4` |
158
+| Bon cadeau Luge 3 tours | `BW-CADEAU-LUGE3` |
159
+| Bon cadeau Poney | `BW-CADEAU-PONEY` |
160
+| Bon cadeau Kids électrique | `BW-CADEAU-KIDS` |
161
+| Bon cadeau Tir à l'arc 20 min | `BW-CADEAU-TIR20` |
162
+| Bon cadeau Trottinettes ETT | `BW-CADEAU-TROTT` |
163
+
164
+---
165
+
166
+## 5. Architecture de synchronisation
167
+
168
+```
169
+┌─────────────┐ ┌──────────────┐ ┌──────────────┐
170
+│ BilletWeb │◄────│ n8n.n8n-1 │────►│ Sync Server │
171
+│ (API REST) │ │ (orchestre) │ │ (Python) │
172
+└─────────────┘ └──────┬───────┘ └──────┬────────┘
173
+ │ │
174
+ │ Schedule: 15 min │ POST /sync
175
+ │ ▼
176
+ │ ┌────────────────┐
177
+ │ │ sync_billetweb │
178
+ │ │ (Python) │
179
+ │ └───────┬────────┘
180
+ │ │
181
+ │ ▼
182
+ │ ┌────────────────┐
183
+ └──────────►│ Odoo Online │
184
+ │ (JSON-RPC) │
185
+ └────────────────┘
186
+```
187
+
188
+### Composants
189
+
190
+| Service | Technologie | Port | Accès |
191
+|---------|------------|------|-------|
192
+| n8n | n8n v2.21.7 Docker | 5678 | `https://n8n.delgard.cloud` |
193
+| Sync Server | Python 3.12-slim | 8855 | Interne (Docker network) |
194
+| Traefik | Reverse proxy | 80/443 | Let's Encrypt |
195
+
196
+### Flux de sync (script `sync_billetweb.py`)
197
+
198
+```
199
+1. 🔒 Charger tous les barcodes Odoo existants (anti-doublon)
200
+2. 🌐 GET BilletWeb /api/event/{id}/attendees (2 events)
201
+3. Pour chaque attendee nouveau :
202
+ a. Trouver/créer client (res.partner)
203
+ b. Trouver produit (product.product par barcode BW-*)
204
+ c. Trouver/créer commande (sale.order)
205
+ d. Créer ligne (sale.order.line)
206
+ e. Créer inscription (event.registration)
207
+ f. Lier ligne → inscription
208
+4. 📊 Résultat : "X nouveaux importés"
209
+```
210
+
211
+---
212
+
213
+## 6. Webhook de compostage (Odoo → BilletWeb)
214
+
215
+Quand un billet est scanné au PDV Odoo (`state: open → done`), un webhook composte le billet BilletWeb.
216
+
217
+```
218
+Odoo (Automated Action)
219
+ │ POST webhook
220
+ ▼
221
+n8n Webhook Trigger
222
+ │ POST /webhook/billetweb-compost
223
+ ▼
224
+BilletWeb API
225
+ │ POST /api/event/{id}/check
226
+ ▼
227
+Billet composté ✅
228
+```
229
+
230
+### Webhook URL
231
+
232
+```
233
+https://n8n.delgard.cloud/webhook/billetweb-compost
234
+```
235
+
236
+### Payload attendu
237
+
238
+```json
239
+{
240
+ "event_id": 1350897,
241
+ "barcode": "6743097169",
242
+ "name": "Jean Dupont",
243
+ "state": "done"
244
+}
245
+```
246
+
247
+### Configuration Odoo (Automated Action)
248
+
249
+1. Activer le mode développeur
250
+2. Technique → Automatisation → Règles d'automatisation → Créer
251
+3. **Modèle :** `event.registration`
252
+4. **Déclencheur :** « Lors de la mise à jour » + champ `state`
253
+5. **Action :** « Envoyer une notification webhook »
254
+6. **URL :** `https://n8n.delgard.cloud/webhook/billetweb-compost`
255
+7. **Headers :** `Content-Type: application/json`
256
+8. **Champs :** `barcode`, `name`, `state`, `event_id`
257
+
258
+### Test manuel
259
+
260
+```bash
261
+curl -X POST https://n8n.delgard.cloud/webhook/billetweb-compost \
262
+ -H "Content-Type: application/json" \
263
+ -d '{"event_id": 1350897, "barcode": "6743097169", "name": "Test", "state": "done"}'
264
+# → {"message":"Workflow was started"}
265
+```
266
+
267
+---
268
+
269
+## 7. Pièges et bonnes pratiques
270
+
271
+### Import initial : respecter le champ `used`
272
+
273
+| BilletWeb `used` | État réel | Odoo `state` correct |
274
+|:----------------:|-----------|:--------------------:|
275
+| `"0"` | Pas encore venu | `open` |
276
+| `"1"` | Déjà composté | `done` |
277
+
278
+> ⚠️ Ne pas tout mettre en `done` par défaut — l'import initial du 26/05 avait ce bug.
279
+
280
+**Correction post-import :**
281
+```python
282
+# Repasser en "open" les billets used=0
283
+bw_unused = {a['barcode'] for a in bw_attendees if a['used'] == '0'}
284
+to_open = odoo_call("event.registration", "search_read",
285
+ [["barcode", "in", list(bw_unused)], ["state", "=", "done"]])
286
+odoo_call("event.registration", "write",
287
+ [[r['id'] for r in to_open], {"state": "open"}])
288
+
289
+# Repasser en "done" les billets used=1
290
+bw_used = {a['barcode'] for a in bw_attendees if a['used'] == '1'}
291
+to_done = odoo_call("event.registration", "search_read",
292
+ [["barcode", "in", list(bw_used)], ["state", "=", "open"]])
293
+odoo_call("event.registration", "write",
294
+ [[r['id'] for r in to_done], {"state": "done"}])
295
+```
296
+
297
+### Anti-doublon : cache vs Odoo direct
298
+
299
+- **Toujours requêter Odoo directement** pour la liste des barcodes existants
300
+- Ne pas se fier uniquement au fichier cache (`sync_cache.json`) — il peut être vidé
301
+- Le cache est une optimisation, pas une source de vérité
302
+
303
+### Commandes multi-tickets
304
+
305
+Ne pas bloquer la création d'une commande si `client_order_ref` existe déjà :
306
+
307
+```python
308
+# ✅ Réutiliser la commande existante
309
+existing_so = odoo_call("sale.order", "search_read",
310
+ [[["client_order_ref", "=", ref]]], {"fields": ["id"]})
311
+if existing_so:
312
+ so = existing_so[0]["id"] # Ajouter des lignes à la commande existante
313
+else:
314
+ so = odoo_call("sale.order", "create", [{...}]) # Créer nouvelle commande
315
+```
316
+
317
+### Rate limiting BilletWeb
318
+
319
+L'API BilletWeb n'a pas de limite documentée, mais espacer les appels d'au moins 1 seconde en cas de bulk.
320
+
321
+---
322
+
323
+## 8. Maintenance
324
+
325
+### Sync manuelle
326
+
327
+```bash
328
+docker exec n8n-sync-server-1 python3 /app/sync_billetweb.py
329
+```
330
+
331
+### Réinitialiser le cache anti-doublon
332
+
333
+```bash
334
+docker exec n8n-sync-server-1 rm -f /root/.hermes/scripts/sync_cache.json
335
+# Prochaine sync : recharge TOUS les barcodes depuis Odoo
336
+```
337
+
338
+### Logs
339
+
340
+```bash
341
+docker logs n8n-n8n-1 --tail 50
342
+docker logs n8n-sync-server-1 --tail 50
343
+docker exec n8n-sync-server-1 cat /tmp/sync_result.json
344
+```
345
+
346
+### Workflows n8n
347
+
348
+| Nom | ID | Status |
349
+|-----|-----|--------|
350
+| BilletWeb → Odoo Sync | `n4r0JG3BvHKi9nmC` | ✅ Actif (toutes les 15 min) |
351
+| Compost BilletWeb (webhook) | `V1TCykwcoSWlBHyX` | ✅ Actif |
352
+
353
+---
354
+
355
+## 9. Fichiers associés
356
+
357
+| Fichier | Emplacement |
358
+|---------|------------|
359
+| sync_billetweb.py (Docker) | `/docker/n8n-gdkj/sync-server/sync_billetweb.py` |
360
+| sync_billetweb.py (host) | `/root/.hermes/scripts/sync_billetweb.py` |
361
+| server.py (sync-server) | `/docker/n8n-gdkj/sync-server/server.py` |
362
+| docker-compose n8n | `/docker/n8n-gdkj/docker-compose.yml` |
363
+| Workflow n8n (export) | `/root/workspace/n8n/workflow-billetweb-odoo.json` |
364
+| Architecture doc | `/root/workspace/n8n/ARCHITECTURE.md` |
concepts/odoo-api.md
... ...
@@ -0,0 +1,367 @@
1
+# API Odoo Online (SaaS) — Référence complète
2
+
3
+> **Instance :** Parc de la Luge — Odoo Online Standard
4
+> **Dernière mise à jour :** 29/05/2026
5
+
6
+---
7
+
8
+## 1. Authentification
9
+
10
+### Endpoint
11
+
12
+```
13
+POST https://<subdomain>.odoo.com/jsonrpc
14
+```
15
+
16
+### Login (JSON-RPC)
17
+
18
+```python
19
+payload = {
20
+ "jsonrpc": "2.0", "method": "call",
21
+ "params": {
22
+ "service": "common", "method": "login",
23
+ "args": [DB, "login@email.com", "api_key_or_password"]
24
+ }, "id": 1
25
+}
26
+uid = requests.post(ODOO_URL, json=payload).json()["result"]
27
+```
28
+
29
+> **⚠️ ERREUR CLASSIQUE :** `/web/session/authenticate` → `AccessDenied` sur SaaS.
30
+> Toujours utiliser `common.login` sur `/jsonrpc`.
31
+
32
+### API Key
33
+
34
+Générée dans Odoo : **Profil → Preferences → Account Security → API Keys**.
35
+
36
+---
37
+
38
+## 2. Helper générique
39
+
40
+```python
41
+def odoo_call(model, method, *args):
42
+ payload = {
43
+ "jsonrpc": "2.0", "method": "call",
44
+ "params": {
45
+ "service": "object",
46
+ "method": "execute_kw",
47
+ "args": [DB, UID, PWD, model, method] + list(args)
48
+ }, "id": 1
49
+ }
50
+ r = requests.post(ODOO_URL, json=payload, timeout=30)
51
+ data = r.json()
52
+ if "error" in data:
53
+ msg = data['error']['data']['message'][:200]
54
+ raise Exception(f"Odoo error: {msg}")
55
+ return data.get("result")
56
+```
57
+
58
+---
59
+
60
+## 3. Opérations CRUD
61
+
62
+### search_read — Rechercher + lire
63
+
64
+```python
65
+records = odoo_call("res.partner", "search_read",
66
+ [[["email", "=", "x@y.com"]]], # domain (list of tuples)
67
+ {"fields": ["id", "name", "email"]}) # fields dict
68
+```
69
+
70
+### search_count — Compter
71
+
72
+```python
73
+count = odoo_call("sale.order", "search_count",
74
+ [[["state", "=", "sale"]]])
75
+```
76
+
77
+### create — Créer (⚠️ un seul dict)
78
+
79
+```python
80
+new_id = odoo_call("sale.order", "create",
81
+ [{"name": "ORDER-REF", "partner_id": 42, ...}]) # ONE dict, PAS une liste
82
+```
83
+
84
+### write — Modifier
85
+
86
+```python
87
+odoo_call("sale.order", "write",
88
+ [[42], {"state": "sale"}]) # [[id], {values}]
89
+```
90
+
91
+### unlink — Supprimer
92
+
93
+```python
94
+odoo_call("sale.order", "unlink", [[42]])
95
+```
96
+
97
+### fields_get — Découvrir les champs
98
+
99
+```python
100
+fields = odoo_call("sale.order", "fields_get", [],
101
+ {"attributes": ["string", "type", "required"]})
102
+required = [(k, v) for k, v in fields.items() if v.get("required")]
103
+```
104
+
105
+---
106
+
107
+## 4. Modèles clés
108
+
109
+### 4.1 sale.order — Commande
110
+
111
+| Champ | Type | Obligatoire | Notes |
112
+|-------|------|:----------:|-------|
113
+| `name` | char | ✅ | Référence commande |
114
+| `partner_id` | m2o → res.partner | ✅ | Client |
115
+| `partner_invoice_id` | m2o → res.partner | ✅ | Adresse facturation |
116
+| `partner_shipping_id` | m2o → res.partner | ✅ | Adresse livraison |
117
+| `company_id` | m2o → res.company | ✅ | Généralement `1` |
118
+| `date_order` | datetime | ✅ | Format `"2026-01-01 00:00:00"` |
119
+| `picking_policy` | selection | ✅ | `"direct"` |
120
+| `client_order_ref` | char | | Référence externe (ex: BilletWeb) |
121
+| `state` | selection | | `draft` → `sent` → `sale` → `done` → `cancel` |
122
+
123
+Création minimale :
124
+
125
+```python
126
+odoo_call("sale.order", "create", [{
127
+ "name": "ORDER-REF",
128
+ "partner_id": partner_id,
129
+ "partner_invoice_id": partner_id,
130
+ "partner_shipping_id": partner_id,
131
+ "company_id": 1,
132
+ "date_order": "2026-01-01 00:00:00",
133
+ "picking_policy": "direct",
134
+}])
135
+```
136
+
137
+### 4.2 sale.order.line — Ligne de commande
138
+
139
+| Champ | Type | Notes |
140
+|-------|------|-------|
141
+| `order_id` | m2o → sale.order | ID commande |
142
+| `product_id` | m2o → product.product | ID produit |
143
+| `product_uom_qty` | float | Quantité (défaut: 1) |
144
+| `price_unit` | float | Prix unitaire |
145
+| `registration_ids` | o2m → event.registration | Lie les inscriptions |
146
+
147
+### 4.3 res.partner — Client
148
+
149
+| Champ | Type | Notes |
150
+|-------|------|-------|
151
+| `name` | char | Nom complet |
152
+| `email` | char | Email (unique par client) |
153
+| `phone` | char | Téléphone |
154
+| `barcode` | char | Code barre |
155
+
156
+### 4.4 product.product — Produit
157
+
158
+| Champ | Type | Notes |
159
+|-------|------|-------|
160
+| `name` | char | Nom |
161
+| `barcode` | char | Code barre (utilisé pour mapper BilletWeb) |
162
+| `list_price` | float | Prix de vente |
163
+| `active` | bool | `False` pour désactiver sans supprimer |
164
+
165
+### 4.5 event.registration — Inscription
166
+
167
+| Champ | Type | Notes |
168
+|-------|------|-------|
169
+| `event_id` | m2o → event.event | Événement |
170
+| `name` | char | Nom participant |
171
+| `email` | char | Email |
172
+| `barcode` | char | **Code barre BilletWeb** |
173
+| `state` | selection | `draft` → `open` → `done` → `cancel` |
174
+| `event_ticket_id` | m2o → event.event.ticket | Type de billet |
175
+
176
+Création :
177
+
178
+```python
179
+reg_id = odoo_call("event.registration", "create", [{
180
+ "event_id": event_id,
181
+ "name": "Jean Dupont",
182
+ "email": "jean@email.com",
183
+ "barcode": "1234567890",
184
+ "state": "open",
185
+ "event_ticket_id": ticket_id,
186
+}])
187
+```
188
+
189
+Lier à une ligne de commande :
190
+
191
+```python
192
+odoo_call("sale.order.line", "write",
193
+ [[line_id], {"registration_ids": [[6, 0, [reg_id]]]}])
194
+```
195
+
196
+### 4.6 event.event.ticket — Type de billet
197
+
198
+```python
199
+odoo_call("event.event.ticket", "create", [{
200
+ "name": "Luge - Forfait 4 tours",
201
+ "event_id": event_id,
202
+ "product_id": product_id,
203
+ "price": 11.0,
204
+ "seats_limited": False,
205
+}])
206
+```
207
+
208
+### 4.7 pos.order — Commande PDV
209
+
210
+| Champ | Type | Notes |
211
+|-------|------|-------|
212
+| `name` | char | Référence session |
213
+| `date_order` | datetime | Date commande |
214
+| `amount_total` | float | Total TTC |
215
+| `amount_tax` | float | Total TVA |
216
+| `state` | selection | `draft` → `paid` → `done` |
217
+| `lines` | o2m → pos.order.line | Lignes |
218
+
219
+---
220
+
221
+## 5. Flux BilletWeb → Odoo
222
+
223
+Ordre correct pour importer un participant :
224
+
225
+```
226
+1. res.partner — find by email, or create
227
+2. product.product — find by barcode (BW-*)
228
+3. event.event.ticket — find by product_id
229
+4. sale.order — create with partner_id
230
+5. sale.order.line — create with order_id + product_id
231
+6. event.registration — create with event_id + barcode + event_ticket_id
232
+7. sale.order.line — write registration_ids to link
233
+```
234
+
235
+---
236
+
237
+## 6. TVA — Réunion (DOM)
238
+
239
+| Taux | Usage |
240
+|------|-------|
241
+| **8,5%** | Standard DOM |
242
+| **2,1%** | Réduit |
243
+| 0% | Exonéré |
244
+
245
+```python
246
+odoo_call("account.tax", "create", [{
247
+ "name": "TVA 8.5% (Réunion)",
248
+ "amount": 8.5,
249
+ "amount_type": "percent",
250
+ "type_tax_use": "sale",
251
+ "tax_group_id": group_id,
252
+ "price_include": False,
253
+}])
254
+```
255
+
256
+> ⚠️ `l10n_fr_account` installe les taux métropole (20%, 10%, 5.5%).
257
+> Désactiver avec `write([[id], {"active": False}])`.
258
+
259
+---
260
+
261
+## 7. Modules de localisation française
262
+
263
+| Module | Rôle |
264
+|--------|------|
265
+| `l10n_fr` | Localisation France |
266
+| `l10n_fr_account` | Plan comptable + taxes |
267
+| `l10n_fr_pos_cert` | Certification **NF 525** (obligatoire en France) |
268
+| `l10n_fr_reports` | Rapports comptables |
269
+
270
+Installation via API :
271
+
272
+```python
273
+mid = odoo_call("ir.module.module", "search_read",
274
+ [[["name", "=", "l10n_fr"]]], {"fields": ["id"]})[0]["id"]
275
+odoo_call("ir.module.module", "button_immediate_install", [[mid]])
276
+```
277
+
278
+---
279
+
280
+## 8. Pièges et bonnes pratiques
281
+
282
+### Emails (limite 5/jour sur SaaS)
283
+
284
+```python
285
+# Désactiver TOUS les emails après import
286
+odoo_call("ir.mail_server", "write", [all_ids], {"active": False})
287
+odoo_call("fetchmail.server", "write", [all_ids], {"active": False})
288
+odoo_call("mail.template", "write", [all_ids], {"auto_delete": True})
289
+odoo_call("ir.cron", "write", [ids], {"active": False}) # jobs mail/email/send
290
+odoo_call("event.event", "write", [[1,2]], {"auto_confirm": False})
291
+```
292
+
293
+### Produits de démo
294
+
295
+Ne pas `unlink` (référencés par les moyens de paiement PDV). Utiliser :
296
+
297
+```python
298
+odoo_call("product.product", "write", [[pid], {"active": False}])
299
+```
300
+
301
+### Groupes de taxes — country_id
302
+
303
+Ne PAS mettre `country_id` sauf s'il correspond au pays de la société :
304
+
305
+```python
306
+# ✅ OK
307
+odoo_call("account.tax.group", "create", [{"name": "TVA 8.5%"}])
308
+
309
+# ❌ Erreur si société = Réunion (187)
310
+odoo_call("account.tax.group", "create", [{"name": "TVA", "country_id": 75}])
311
+```
312
+
313
+### create() — un seul dict
314
+
315
+L'API JSON-RPC Odoo Online attend **un dict** pour `create`, pas une liste de dicts :
316
+
317
+```python
318
+# ✅ OK
319
+odoo_call("sale.order", "create", [{"name": "X", ...}])
320
+
321
+# ❌ Erreur silencieuse
322
+odoo_call("sale.order", "create", [[{"name": "X", ...}]])
323
+```
324
+
325
+### Anti-doublon
326
+
327
+Trois niveaux de protection :
328
+
329
+```python
330
+# 1. Charger tous les barcodes existants au démarrage
331
+existing = odoo_call("event.registration", "search_read", [[]],
332
+ {"fields": ["barcode"]})
333
+done = {r["barcode"] for r in existing if r.get("barcode")}
334
+
335
+# 2. Vérifier le barcode avant création
336
+if barcode in done:
337
+ continue
338
+
339
+# 3. Réutiliser les commandes existantes (ne pas bloquer)
340
+existing_so = odoo_call("sale.order", "search_read",
341
+ [[["client_order_ref", "=", ref]]], {"fields": ["id"]})
342
+if existing_so:
343
+ so = existing_so[0]["id"] # RÉUTILISER
344
+else:
345
+ so = odoo_call("sale.order", "create", [{...}])
346
+```
347
+
348
+---
349
+
350
+## 9. Limites Odoo Online (SaaS)
351
+
352
+| Possible | Impossible |
353
+|----------|-----------|
354
+| API JSON-RPC (`/jsonrpc`) | Accès SSH au serveur |
355
+| CRUD sur tous les modèles standards | Modules customs (pas de `__manifest__.py`) |
356
+| Modules Studio (custom fields, vues) | Accès direct PostgreSQL |
357
+| Imports CSV/XML | Modifications du core Odoo |
358
+| Webhooks sortants (Automatisations) | Installation de packages Python |
359
+
360
+---
361
+
362
+## 10. Événements Odoo du Parc de la Luge
363
+
364
+| ID | Nom |
365
+|----|-----|
366
+| 1 | Parc de la Luge — Billetterie |
367
+| 2 | BON CADEAU |
concepts/odoo-saas-limites.md
... ...
@@ -0,0 +1,32 @@
1
+# Contraintes Odoo Online (SaaS)
2
+
3
+> **Dernière mise à jour** : 2026-05-27
4
+> **Périmètre** : Parc de la Luge — Odoo Online Standard
5
+
6
+## Ce qui est possible
7
+
8
+- API JSON-RPC : `https://<instance>.odoo.com/jsonrpc`
9
+- CRUD sur tous les modèles standards (sale.order, product.product, res.partner...)
10
+- Modules Studio (custom fields, vues)
11
+- Imports CSV/XML
12
+- Webhooks sortants (via Automatisations)
13
+
14
+## Ce qui est IMPOSSIBLE
15
+
16
+- **Pas d'accès au serveur** — pas de SSH, pas de fichiers Python custom
17
+- **Pas de modules customs** au sens classique (pas de `__manifest__.py` sur le serveur)
18
+- **Pas d'accès direct à la base PostgreSQL**
19
+- **Pas de modifications du core Odoo**
20
+
21
+## Conséquences pour les projets
22
+
23
+- Toute customisation passe par l'API JSON-RPC ou le Studio
24
+- Les extensions Chrome interagissent avec le DOM, pas avec le backend
25
+- Les automatismes passent par n8n (sur le VPS) qui appelle l'API
26
+- Les modules customs Odoo ne sont pas possibles sans Odoo.sh ou on-premise
27
+
28
+## Authentification API
29
+
30
+- API key : générée dans le profil utilisateur Odoo
31
+- Session : login via `/web/session/authenticate`
32
+- Pas d'OAuth2 natif
concepts/security-vps.md
... ...
@@ -0,0 +1,59 @@
1
+---
2
+title: Sécurité VPS
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: concept
6
+tags: [security, infra]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Sécurité VPS
12
+
13
+## Firewall (UFW)
14
+- **Statut** : Actif
15
+- **Politique** : Deny incoming, Allow outgoing
16
+- **Ports autorisés** : 22 (SSH), 80 (HTTP), 443 (HTTPS)
17
+- **Logging** : Low
18
+
19
+## Fail2ban
20
+- **Jail active** : sshd
21
+- **Règles** : 3 tentatives max en 10 min → ban 1 heure
22
+- **Bilan actuel** : 36 échecs totaux, 8 bans totaux, 0 actif
23
+
24
+## Hardening SSH
25
+| Paramètre | Valeur |
26
+|---|---|
27
+| PermitRootLogin | yes (terminaux Hostinger) |
28
+| PasswordAuthentication | yes (terminaux Hostinger) |
29
+| MaxAuthTries | 3 |
30
+| LoginGraceTime | 30s |
31
+| X11Forwarding | no |
32
+| TCPForwarding | no |
33
+| HostbasedAuth | no |
34
+| PermitEmptyPasswords | no |
35
+
36
+## Kernel Hardening
37
+- `net.ipv4.conf.all.send_redirects = 0`
38
+- `net.ipv6.conf.all.accept_redirects = 0`
39
+- `net.ipv4.conf.all.accept_source_route = 0`
40
+- `net.ipv4.tcp_syncookies = 1`
41
+- `net.ipv4.conf.all.log_martians = 1`
42
+
43
+## Isolation Docker
44
+Tous les conteneurs bindés sur `127.0.0.1` uniquement :
45
+
46
+| Service | Port |
47
+|---------|------|
48
+| [[searxng]] | `127.0.0.1:62761` |
49
+| [[n8n]] | `127.0.0.1:5678` |
50
+| [[crawl4ai]] | `127.0.0.1:32771` |
51
+| [[qdrant]] | `127.0.0.1:32770` |
52
+| [[hermes-webui]] | `127.0.0.1:8787` |
53
+
54
+Seul [[traefik]] est en host network (ports 80/443) — unique point d'entrée externe.
55
+Docker bypass UFW → le binding `127.0.0.1` est la seule protection efficace.
56
+
57
+## Mises à jour
58
+- **Unattended-upgrades** : Actif (sécurité uniquement)
59
+- Dernière mise à jour : 2026-05-29
decisions/dashboard-public-api-patch.md
... ...
@@ -0,0 +1,25 @@
1
+# Dashboard API public patch
2
+
3
+## Contexte
4
+Hermes Workspace a besoin d'accéder aux APIs du dashboard Hermes (sessions, skills, config, mcp, memory, jobs) sans authentification.
5
+Par défaut, le dashboard génère un token éphémère à chaque démarrage et protège toutes les APIs sauf `/api/status` et quelques autres.
6
+
7
+## Solution appliquée
8
+Patch dans `/usr/local/lib/hermes-agent/hermes_cli/web_server.py` — ajout des routes à `_PUBLIC_API_PATHS` :
9
+
10
+```python
11
+"/api/config",
12
+"/api/sessions",
13
+"/api/skills",
14
+"/api/mcp",
15
+"/api/memory",
16
+"/api/jobs",
17
+```
18
+
19
+## Risques
20
+- Le dashboard est bindé sur `127.0.0.1:9119` → seuls les processus locaux peuvent y accéder
21
+- Les APIs restent protégées des accès externes (le dashboard n'est pas exposé sur internet)
22
+- Le patch sera perdu après une mise à jour de Hermes → réappliquer si nécessaire
23
+
24
+## Résultat
25
+Workspace passe de `mode=portable` à `mode=zero-fork` avec sessions, skills, config, memory, jobs disponibles.
entities/conteneurs-docker.md
... ...
@@ -0,0 +1,50 @@
1
+---
2
+title: Conteneurs Docker
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: concept
6
+tags: [container, infra]
7
+sources: [CONTAINERS.md]
8
+confidence: high
9
+---
10
+
11
+# Conteneurs Docker
12
+
13
+## Inventaire complet
14
+
15
+| Stack | Conteneur | Image | Rôle | Port local |
16
+|-------|-----------|-------|------|------------|
17
+| traefik | traefik-traefik-1 | traefik:latest | Reverse proxy + HTTPS | `:80`, `:443` |
18
+| searxng-dm6i | searxng-dm6i-searxng-1 | ghcr.io/searxng/searxng:latest | Recherche web privée | `127.0.0.1:62761→8080` |
19
+| n8n-gdkj | n8n-n8n-1 | docker.n8n.io/n8nio/n8n | Automatisation | `127.0.0.1:5678` |
20
+| n8n-gdkj | n8n-sync-server-1 | n8n-sync-server | Serveur Python sync | `8855` (interne) |
21
+| crawl4ai-fhxz | crawl-crawl4ai-1 | unclecode/crawl4ai:latest | Extraction web LLM | `127.0.0.1:32771→11235` |
22
+| qdrant-5hvs | qdrant-qdrant-1 | qdrant/qdrant:latest | Base vectorielle | `127.0.0.1:32770→6333` |
23
+| hermes-webui | hermes-webui-proxy | nginx:alpine | Proxy WebUI | `127.0.0.1:8787` |
24
+| wiki | wiki-wiki-1 | nginx:alpine (custom) | Wiki web Docsify | `127.0.0.1:4570→80` |
25
+
26
+## Réseau
27
+
28
+- **Traefik** : Host network — seul point d'entrée externe
29
+- **Tous les autres** : Bridge, ports bindés sur `127.0.0.1` uniquement
30
+- **n8n-sync-server** : Réseau interne n8n, port 8855 non exposé
31
+
32
+## Sécurité
33
+
34
+Aucune exposition directe sur internet. Docker bypass UFW → tous les ports bindés
35
+sur `127.0.0.1`. Seul Traefik (host network) est joignable de l'extérieur sur 80/443.
36
+
37
+## Gestion
38
+
39
+```bash
40
+# Déploiement complet
41
+for dir in docker/*/; do (cd "$dir" && docker compose up -d); done
42
+
43
+# Stack individuelle
44
+cd docker/searxng-dm6i && docker compose up -d
45
+```
46
+
47
+## Configuration
48
+
49
+Chaque stack a son `docker-compose.yml` dans `/root/hermes-config/docker/<nom-stack>/`.
50
+Les backups des volumes sont dans `/root/hermes-config/docker-volumes/` (n8n_data, searxng-config, sync_cache).
entities/crawl4ai.md
... ...
@@ -0,0 +1,28 @@
1
+---
2
+title: Crawl4AI
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [container, hermes, ai]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Crawl4AI
12
+
13
+## Overview
14
+Extracteur web optimisé LLM, sortie Markdown propre. Version 0.8.6.
15
+
16
+## Setup
17
+- **Conteneur** : `crawl-crawl4ai-1`
18
+- **Stack** : `docker/crawl4ai-fhxz/`
19
+- **Image** : unclecode/crawl4ai:latest
20
+- **Port** : `127.0.0.1:32771→11235`
21
+- **Endpoints** : `/crawl`, `/md`, `/llm/{url}`
22
+
23
+## Pipeline type
24
+[[searxng]] (cherche) → Crawl4AI (extrait en markdown) → [[hermes-agent]] (analyse)
25
+
26
+## Utilisation par Hermes
27
+Intégré nativement via `web_extract()` — aucune config nécessaire.
28
+L'agent enchaîne automatiquement `web_search()` → `web_extract()`.
entities/hermes-agent.md
... ...
@@ -0,0 +1,76 @@
1
+---
2
+title: Hermes Agent
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [hermes, ai]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Hermes Agent
12
+
13
+## Overview
14
+Agent IA autonome open-source par Nous Research, installé sur [[vps-srv1672918]].
15
+Point d'entrée : interface WebUI, [[telegram-bot]], ou CLI.
16
+
17
+## Configuration
18
+- **Provider** : DeepSeek (api.deepseek.com/v1)
19
+- **Modèle principal** : `deepseek-v4-pro` (orchestrateur, chef de projet)
20
+- **Modèle rapide** : `deepseek-v4-flash` (chercheur, dev, reviewer)
21
+- **Vision** : Google Gemini 2.5 Flash (via Google AI Studio)
22
+- **Fallback** : `nvidia/nemotron-3-super-120b-a12b:free` via OpenRouter
23
+- **Python** : 3.11.15, venv `/usr/local/lib/hermes-agent/venv/`
24
+- **Binaire** : `~/.local/bin/hermes`
25
+- **Config** : `~/.hermes/config.yaml`
26
+- **Env** : `~/.hermes/.env`
27
+
28
+## Architecture Kanban
29
+
30
+5 profils collaborant via un board Kanban :
31
+
32
+```
33
+default (V4 Pro) → Orchestrateur — définit projets, crée tâches
34
+chef-de-projet (V4 Pro) → Coordonne un projet, décompose en sous-tâches
35
+chercheur (V4 Flash) → Recherche web/docs
36
+dev (V4 Flash) → Code, scripts, implémentation
37
+reviewer (V4 Flash) → Tests, validation, quality gates
38
+```
39
+
40
+- **Board** : `kanban.db` (SQLite)
41
+- **Dispatcher** : intégré à la gateway
42
+- **Nesting** : max_spawn_depth=2
43
+- **Wiki partagé** : `/root/wiki/` — accessible à tous les profils
44
+
45
+## Gateway
46
+
47
+- **Telegram** : [[telegram-bot]] @HeermesJod974Bot — 6 topics
48
+- **Webhooks** : supportés
49
+- **Cron** : jobs programmés (backup toutes les 6h, script `auto-backup.sh`)
50
+
51
+## Services intégrés
52
+
53
+| Service | Rôle | Accès |
54
+|---------|------|-------|
55
+| [[searxng]] | Recherche web (web_search) | `127.0.0.1:62761` |
56
+| [[crawl4ai]] | Extraction contenu (web_extract) | `127.0.0.1:32771` |
57
+| [[qdrant]] | Base vectorielle (RAG) | `127.0.0.1:32770` |
58
+| [[n8n]] | Automatisations externes | `127.0.0.1:5678` |
59
+
60
+## Outils actifs
61
+
62
+Terminal, File, Memory, Skills, Kanban, Cron, Web Search/Extract,
63
+Code Execution, Vision, Session Search, Delegation, Telegram
64
+
65
+## Mémoire
66
+
67
+- `~/.hermes/memories/MEMORY.md` — notes techniques, conventions
68
+- `~/.hermes/memories/USER.md` — profil utilisateur, préférences
69
+- Pas de backend externe (tout en fichiers)
70
+
71
+## Backup
72
+
73
+- Repo GitHub privé : `github.com/Jod97426/hermes-config`
74
+- Script `backup.sh` : sauvegarde tout (config, profils, skills, kanban, wiki, docker, cron)
75
+- Script `auto-backup.sh` : cron toutes les 6h + màj README + push
76
+- Script `restore.sh` : restauration complète VPS vierge
entities/hermes-webui.md
... ...
@@ -0,0 +1,46 @@
1
+---
2
+title: Hermes WebUI
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [hermes, container]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Hermes WebUI
12
+
13
+## Overview
14
+Interface web pour [[hermes-agent]], accessible en HTTPS via [[traefik]].
15
+
16
+## Setup
17
+- **Source** : `/opt/hermes-webui` (GitHub: nesquena/hermes-webui)
18
+- **Service** : systemd `hermes-webui`, auto-restart, démarre au boot
19
+- **Python** : `/usr/local/lib/hermes-agent/venv/bin/python3`
20
+- **Écoute** : `0.0.0.0:8787` (protégé par UFW)
21
+- **Proxy** : Conteneur `hermes-webui-proxy` (nginx:alpine) en host network
22
+
23
+## Accès
24
+- **Public** : `https://hermes.srv1672918.hstgr.cloud` (Traefik → nginx → :8787)
25
+- **Local** : `http://127.0.0.1:8787`
26
+
27
+## Fonctionnalités
28
+- Chat avec streaming SSE
29
+- Gestion des sessions multiples
30
+- Multi-provider LLM
31
+- Workspace file browser
32
+- 8 thèmes + custom CSS
33
+- Mobile responsive
34
+- Panneau Insights (token usage)
35
+
36
+## Stack Docker
37
+```yaml
38
+# docker/hermes-webui/docker-compose.yml
39
+services:
40
+ proxy:
41
+ image: nginx:alpine
42
+ network_mode: host
43
+ volumes:
44
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
45
+```
46
+Le conteneur proxy relaie vers `127.0.0.1:8787` (le service systemd).
entities/kanban.md
... ...
@@ -0,0 +1,47 @@
1
+---
2
+title: Kanban
3
+created: 2026-05-29
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [hermes, workflow]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Kanban
12
+
13
+## Overview
14
+Système de gestion de projet interne à [[hermes-agent]], basé sur un board Kanban
15
+SQLite et 5 profils collaboratifs.
16
+
17
+## Profils
18
+
19
+| Profil | Modèle | Rôle |
20
+|--------|--------|------|
21
+| default | deepseek-v4-pro | Orchestrateur — définit projets, crée tâches |
22
+| chef-de-projet | deepseek-v4-pro | Coordonne UN projet, crée sous-tâches |
23
+| chercheur | deepseek-v4-flash | Recherche web/docs (tools: web, file, clarify) |
24
+| dev | deepseek-v4-flash | Code, scripts (tools: terminal, file, code, web) |
25
+| reviewer | deepseek-v4-flash | Tests, validation (tools: terminal, file, code, web) |
26
+
27
+## Flux type
28
+
29
+```
30
+User → default (définit le projet)
31
+ → Kanban: task pour chef-de-projet
32
+ → chef-de-projet crée sous-tâches
33
+ → chercheur (analyse)
34
+ → dev (implémentation)
35
+ → reviewer (validation)
36
+```
37
+
38
+## Configuration
39
+- **Board** : `kanban.db` (SQLite)
40
+- **Dispatcher** : intégré à la gateway Hermes
41
+- **Nesting** : max_spawn_depth=2 (un orchestrateur peut déléguer à un autre)
42
+- **Wiki** : `/root/wiki/` partagé entre tous les profils
43
+
44
+## Fichiers clés
45
+- `~/.hermes/config.yaml` → section `delegation`
46
+- `~/.hermes/profiles/{chef-de-projet,chercheur,dev,reviewer}/` → configs restreintes
47
+- `/root/wiki/` → mémoire collective
entities/n8n.md
... ...
@@ -0,0 +1,36 @@
1
+---
2
+title: n8n
3
+created: 2026-05-29
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [container, automation]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# n8n
12
+
13
+## Overview
14
+Plateforme d'automatisation low-code, intégrée avec Odoo Online pour le Parc de la Luge.
15
+
16
+## Setup
17
+- **Stack** : `docker/n8n-gdkj/`
18
+- **Conteneur principal** : `n8n-n8n-1` — image `docker.n8n.io/n8nio/n8n`
19
+- **Conteneur sync** : `n8n-sync-server-1` — serveur Python custom (port 8855 interne)
20
+- **Port** : `127.0.0.1:5678`
21
+- **Accès public** : `https://n8n-gdkj.srv1672918.hstgr.cloud` (via [[traefik]])
22
+- **Volume** : `n8n_n8n_data` — workflows, credentials, données
23
+
24
+## Workflows principaux
25
+- **BilletWeb → Odoo** : Sync des ventes de billets
26
+- **Odoo → externe** : Webhooks et automatisations
27
+- **Scripts Python** : Serveur sync pour opérations complexes
28
+
29
+## Sécurité
30
+- Port bindé sur `127.0.0.1`
31
+- Accès web via Traefik avec `noindex`, HSTS, Permissions-Policy
32
+- Secure cookie désactivé (cookie same-site car reverse proxy)
33
+
34
+## Backup
35
+- Volume `n8n_n8n_data` sauvegardé dans `/root/hermes-config/docker-volumes/n8n_n8n_data.tar.gz`
36
+- Workflows exportables en JSON depuis l'interface
entities/qdrant.md
... ...
@@ -0,0 +1,35 @@
1
+---
2
+title: Qdrant
3
+created: 2026-05-29
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [container, ai, search]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Qdrant
12
+
13
+## Overview
14
+Base de données vectorielle pour la recherche sémantique et RAG (Retrieval-Augmented Generation).
15
+
16
+## Setup
17
+- **Conteneur** : `qdrant-qdrant-1`
18
+- **Stack** : `docker/qdrant-5hvs/`
19
+- **Image** : qdrant/qdrant:latest
20
+- **Port** : `127.0.0.1:32770→6333` (API REST)
21
+- **Port gRPC** : `6334` (interne, non exposé)
22
+
23
+## Utilisation
24
+- Stockage d'embeddings pour la recherche contextuelle
25
+- Utilisé par les pipelines RAG de [[hermes-agent]]
26
+- Intégré nativement dans les flows de recherche
27
+
28
+## API
29
+```bash
30
+# Health check
31
+curl http://127.0.0.1:32770/health
32
+
33
+# List collections
34
+curl http://127.0.0.1:32770/collections
35
+```
entities/searxng.md
... ...
@@ -0,0 +1,28 @@
1
+---
2
+title: SearXNG
3
+created: 2026-05-19
4
+updated: 2026-05-19
5
+type: entity
6
+tags: [container, hermes]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# SearXNG
12
+
13
+## Overview
14
+Métamoteur de recherche privé, utilisé par [[hermes-agent]] comme backend de recherche.
15
+
16
+## Setup
17
+- **Conteneur** : `searxng-dm6i-searxng-1`
18
+- **Image** : ghcr.io/searxng/searxng:latest
19
+- **Port** : `127.0.0.1:62761`
20
+- **Config** : Volume Docker `searxng-dm6i_searxng-config`
21
+- **Formats** : HTML + JSON
22
+
23
+## Utilisation
24
+```bash
25
+curl "http://127.0.0.1:62761/search?format=json&q=requête"
26
+```
27
+
28
+Remplace Tavily/Exa pour la recherche web.
entities/telegram-bot.md
... ...
@@ -0,0 +1,42 @@
1
+---
2
+title: Telegram Bot
3
+created: 2026-05-29
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [hermes, telegram]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Telegram Bot
12
+
13
+## Overview
14
+Bot Telegram connecté à la gateway [[hermes-agent]] pour interagir avec l'agent
15
+via messagerie instantanée.
16
+
17
+## Identifiants
18
+- **Username** : @HeermesJod974Bot
19
+- **User ID** : 7738179065 (Jérôme Delgard)
20
+- **Groupe** : -1003298293378
21
+
22
+## Topics du groupe
23
+
24
+| # | Nom | Thread ID | Usage |
25
+|---|------|-----------|-------|
26
+| 🏠 | Général | 3 | Discussion générale |
27
+| 📦 | Projets | 6 | Suivi des projets |
28
+| 🚨 | Alertes | 9 | Notifications critiques |
29
+| 🦾 | DevOps | 11 | Infra, déploiements |
30
+| 🚀 | Releases | 12 | Mises en production |
31
+| 💾 | Sauvegardes | 68 | Notifs backup auto |
32
+
33
+## Intégration
34
+- **Gateway** : La gateway Hermes consomme les updates Telegram
35
+- **Notifications cron** : Le job de backup (toutes les 6h) notifie dans le topic 💾 Sauvegardes
36
+- **Config** : `TELEGRAM_BOT_TOKEN` dans `.env`, `platforms.telegram` dans `config.yaml`
37
+- **Fallback** : `python-telegram-bot` v22.6 disponible pour scripts custom
38
+
39
+## Pièges
40
+- La gateway consomme les updates → la stopper avant d'utiliser `getUpdates` directement
41
+- `allowed_users` doit être défini via variable d'environnement (pas `hermes config set`)
42
+- `extra: {}` vide dans la config Telegram → crash gateway TEMPFAIL 75
entities/traefik.md
... ...
@@ -0,0 +1,36 @@
1
+---
2
+title: Traefik
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [container, infra]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# Traefik
12
+
13
+## Overview
14
+Proxy inverse et équilibreur de charge, gère HTTPS et routage pour tous les services exposés.
15
+
16
+## Setup
17
+- **Conteneur** : `traefik-traefik-1`
18
+- **Stack** : `docker/traefik/`
19
+- **Réseau** : Host network (partage les ports de l'hôte)
20
+- **Entrypoints** : HTTP (80), HTTPS (443)
21
+- **Certificats** : Let's Encrypt (⚠️ rate-limité sur le wildcard `*.hstgr.cloud`)
22
+- **Provider** : Docker (labels sur les conteneurs)
23
+
24
+## Routes actives
25
+
26
+| Domaine | Service | Backend |
27
+|---------|---------|---------|
28
+| `hermes.srv1672918.hstgr.cloud` | [[hermes-webui]] | `127.0.0.1:8787` |
29
+| `n8n-gdkj.srv1672918.hstgr.cloud` | [[n8n]] | `127.0.0.1:5678` |
30
+
31
+## Sécurité
32
+
33
+- Robots tag `noindex, nofollow` sur n8n
34
+- HSTS activé sur n8n
35
+- Permissions-Policy restrictives
36
+- Tous les services backend en `127.0.0.1`
entities/vps-srv1672918.md
... ...
@@ -0,0 +1,46 @@
1
+---
2
+title: VPS Hostinger srv1672918
3
+created: 2026-05-19
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [infra, hermes]
7
+sources: []
8
+confidence: high
9
+---
10
+
11
+# VPS Hostinger srv1672918
12
+
13
+## Overview
14
+Serveur VPS Hostinger Ubuntu 24.04 LTS hébergeant [[hermes-agent]], [[telegram-bot]], [[traefik]], et 6 stacks Docker (7 conteneurs).
15
+
16
+## Hardware
17
+- **Hostname** : srv1672918
18
+- **OS** : Ubuntu 24.04.4 LTS (Noble Numbat)
19
+- **Kernel** : 6.8.0-117-generic
20
+- **CPU** : AMD EPYC 7543P — 2 cores
21
+- **RAM** : 7.8 GiB
22
+- **Disque** : 96 GB — 25 GB utilisé (26%), 72 GB libre
23
+- **IP** : 72.61.195.45 / 2a02:4780:28:6b4c::1
24
+
25
+## Réseau
26
+- **Fuseau** : Indian/Reunion (UTC+4)
27
+- **Proxy inverse** : [[traefik]] (ports 80, 443)
28
+- **Firewall** : UFW actif, deny incoming par défaut
29
+- **SSH** : Port 22, protégé par Fail2ban
30
+- **Accès** : Terminal web Hostinger + SSH
31
+
32
+## Services déployés
33
+
34
+| Service | Rôle | Type | Accès |
35
+|---------|------|------|-------|
36
+| [[hermes-agent]] | Agent IA autonome | Python/systemd | CLI + [[telegram-bot]] + [[hermes-webui]] |
37
+| [[hermes-webui]] | Interface web | Docker (nginx) | `https://hermes.srv1672918.hstgr.cloud` |
38
+| [[telegram-bot]] | Bot @HeermesJod974Bot | Hermes Gateway | Groupe privé 6 topics |
39
+| [[searxng]] | Recherche web privée | Docker | `127.0.0.1:62761` |
40
+| [[n8n]] | Automatisation | Docker | `127.0.0.1:5678` |
41
+| [[crawl4ai]] | Extraction web LLM | Docker | `127.0.0.1:32771` |
42
+| [[qdrant]] | Base vectorielle | Docker | `127.0.0.1:32770` |
43
+| [[traefik]] | Reverse proxy + HTTPS | Docker | Ports 80/443 |
44
+| [[kanban]] | Gestion de projets Hermes | SQLite | Interne (5 profils) |
45
+| Fail2ban | Protection SSH | systemd | Jail sshd |
46
+| Unattended-upgrades | Mises à jour auto | systemd | Sécurité uniquement |
entities/wiki-web.md
... ...
@@ -0,0 +1,53 @@
1
+---
2
+title: Wiki Web
3
+created: 2026-05-29
4
+updated: 2026-05-29
5
+type: entity
6
+tags: [wiki, container, hermes, infra]
7
+sources: [/docker/wiki/]
8
+---
9
+
10
+# Wiki Web (Docsify)
11
+
12
+Interface web du wiki partagé Hermes, accessible publiquement (protégé par noindex).
13
+
14
+## URLs
15
+
16
+- **Production** : `https://wiki.delgard.cloud`
17
+- **Locale** : `http://127.0.0.1:4570`
18
+
19
+## Stack
20
+
21
+- **Base** : `nginx:alpine` (image custom dans `/docker/wiki/Dockerfile`)
22
+- **Renderer** : [Docsify](https://docsify.js.org) (markdown côté client, zero build)
23
+- **Source** : `/root/wiki/` monté en read-only (`:ro`)
24
+- **Reverse proxy** : Traefik avec Let's Encrypt
25
+
26
+## Fichiers
27
+
28
+```
29
+/docker/wiki/
30
+├── .env # TZ, TRAEFIK_HOST, COMPOSE_PROJECT_NAME
31
+├── Dockerfile # FROM nginx:alpine
32
+├── docker-compose.yml # Port 4570:80, labels Traefik
33
+├── index.html # Docsify SPA avec thème sombre
34
+└── nginx.conf # try_files → SPA fallback
35
+```
36
+
37
+## Particularités
38
+
39
+- **Read-only** : le volume `/root/wiki` est en `:ro` → pas de modification via le web
40
+- **Recherche** : activée côté client (docsify search plugin)
41
+- **Wikilinks** : les `[[liens]]` sont gérés nativement par Docsify
42
+- **SEO** : `X-Robots-Tag: noindex, nofollow` (wiki privé)
43
+- **Syntax highlighting** : bash, python, yaml, json, docker
44
+
45
+## Commandes
46
+
47
+```bash
48
+# Redémarrer après modif du wiki
49
+docker compose -f /docker/wiki/docker-compose.yml restart
50
+
51
+# Reconstruire l'image
52
+docker compose -f /docker/wiki/docker-compose.yml up -d --build
53
+```
index.md
... ...
@@ -0,0 +1,28 @@
1
+# Wiki Index — Hermes Agent System
2
+
3
+> Mémoire collective partagée entre tous les profils.
4
+> Dernière mise à jour : 2026-05-29
5
+
6
+## concepts/ — Savoir durable et contraintes
7
+- `odoo-saas-limites.md` — Ce qui est possible/impossible avec Odoo Online
8
+- `security-vps.md` — Sécurité VPS complète
9
+
10
+## entities/ — APIs, services, infrastructure
11
+- `vps-srv1672918.md` — Infrastructure VPS Hostinger
12
+- `hermes-agent.md` — Framework d'agents (config, profils, outils)
13
+- `kanban.md` — Système de gestion de projet interne
14
+- `telegram-bot.md` — Bot @HeermesJod974Bot et topics
15
+- `conteneurs-docker.md` — Inventaire des 7 conteneurs Docker
16
+- `traefik.md` — Reverse proxy et certificats HTTPS
17
+- `searxng.md` — Moteur de recherche web privé
18
+- `crawl4ai.md` — Extraction web optimisée LLM
19
+- `qdrant.md` — Base vectorielle pour RAG
20
+- `n8n.md` — Plateforme d'automatisation BilletWeb/Odoo
21
+- `hermes-webui.md` — Interface web de chat
22
+- `wiki-web.md` — Interface web du wiki (Docsify)
23
+
24
+## decisions/ — Choix architecturaux
25
+*(à remplir au fil des projets)*
26
+
27
+## patterns/ — Solutions éprouvées
28
+*(à remplir au fil des projets)*
log.md
... ...
@@ -0,0 +1,38 @@
1
+# Wiki Log
2
+
3
+> Chronological record of all wiki actions. Format: `## [YYYY-MM-DD] action | subject`
4
+
5
+## [2026-05-29] update | Mise à jour complète de l'infrastructure
6
+
7
+## [2026-05-29] create | Conteneur wiki-web (Docsify + nginx)
8
+- Créé le conteneur Wiki Web dans `/docker/wiki/`
9
+- Stack : nginx:alpine + Docsify (renderer markdown côté client)
10
+- Exposé sur `wiki.delgard.cloud` via Traefik/Let's Encrypt
11
+- Volume `/root/wiki` monté en read-only
12
+- Updated entities/vps-srv1672918.md — kernel 117, disque 26%, 12 services listés
13
+- Updated entities/conteneurs-docker.md — 7 conteneurs, ports exacts, n8n+Qdrant
14
+- Updated entities/hermes-agent.md — Kanban, profils, Telegram, cron, backup
15
+- Updated entities/traefik.md — routes n8n+webui, HSTS
16
+- Updated entities/crawl4ai.md — port 32771, v0.8.6
17
+- Updated entities/hermes-webui.md — proxy Docker nginx
18
+- Updated concepts/security-vps.md — ports exacts, bilan Fail2ban
19
+- Created entities/n8n.md — automatisation BilletWeb/Odoo
20
+- Created entities/qdrant.md — base vectorielle RAG
21
+- Created entities/telegram-bot.md — bot, topics, pièges
22
+- Created entities/kanban.md — 5 profils, flux, config
23
+- Updated index.md — 11 entités, 2 concepts
24
+
25
+## [2026-05-19] create | Wiki initialized
26
+- Domain: Hermes VPS — configuration, skills, conteneurs, sécurité
27
+- Structure created with SCHEMA.md, index.md, log.md
28
+
29
+## [2026-05-19] ingest | Initialisation complète du wiki
30
+- Created entities/vps-srv1672918.md — fiche serveur
31
+- Created entities/hermes-agent.md — config agent IA
32
+- Created entities/conteneurs-docker.md — inventaire conteneurs
33
+- Created entities/hermes-webui.md — interface web
34
+- Created entities/searxng.md — recherche privée
35
+- Created entities/crawl4ai.md — extraction web
36
+- Created entities/traefik.md — reverse proxy
37
+- Created concepts/security-vps.md — hardening complet
38
+- Updated index.md — 7 entités, 1 concept