first
This commit is contained in:
185
docs/AGENT.md
Normal file
185
docs/AGENT.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# 📄 agent.md — Mesh Agent (Rust pragmatique)
|
||||
|
||||
## 1. Description
|
||||
**Mesh Agent** est un agent desktop multi-OS (Linux / Windows / macOS) conçu pour compléter Mesh (app web) avec des capacités locales avancées.
|
||||
|
||||
Objectifs :
|
||||
- **Flux lourds P2P** (faible charge serveur)
|
||||
- **Binaires multi-OS** simples à déployer
|
||||
- Terminal/SSH partagé robuste
|
||||
- Partage fichiers/dossiers performant
|
||||
- Notifications directes Gotify
|
||||
|
||||
L’agent communique :
|
||||
- avec le **Mesh Server** (REST + WebSocket) pour auth/permissions/signalisation,
|
||||
- avec les **autres clients/agents** en **P2P** pour les flux data,
|
||||
- avec **Gotify** pour les notifications.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture (control plane vs data plane)
|
||||
- **Control plane** : Mesh Server
|
||||
- Auth
|
||||
- Rooms & ACL
|
||||
- Tokens de capacités (TTL court)
|
||||
- Signalisation (WS)
|
||||
- Événements + notifications Gotify
|
||||
- **Data plane** : P2P
|
||||
- Audio/Vidéo/Écran : WebRTC (côté client web)
|
||||
- Fichiers/Dossiers/Terminal : P2P via **QUIC** (recommandé V1)
|
||||
- fallback possible : WebRTC DataChannel (V2) ou HTTP temporaire (exception)
|
||||
|
||||
Le serveur ne transporte pas de média ni de transferts lourds.
|
||||
|
||||
---
|
||||
|
||||
## 3. Fonctions principales
|
||||
### 3.1 Partage fichiers & dossiers (P2P)
|
||||
- Envoi fichier : chunks + reprise + checksum
|
||||
- Envoi dossier :
|
||||
- mode simple : **zip à la volée**
|
||||
- mode sync (optionnel) : watcher + manifest + diff
|
||||
- Débit contrôlé + backpressure
|
||||
|
||||
### 3.2 Terminal / SSH share (preview + contrôle)
|
||||
- L’agent lance une session locale via **PTY** (bash/pwsh) et peut lancer `ssh user@host`.
|
||||
- Diffuse la sortie terminal en P2P.
|
||||
- Modes :
|
||||
- **preview (lecture seule)** par défaut
|
||||
- **take control** (un seul contrôleur à la fois)
|
||||
|
||||
### 3.3 Notifications
|
||||
- Envoi direct vers Gotify (agent → Gotify)
|
||||
- Notifications OS locales (optionnel V1)
|
||||
|
||||
### 3.4 Intégration OS
|
||||
- Tray icon (optionnel)
|
||||
- Auto-start (optionnel)
|
||||
- Identité machine : `device_id`
|
||||
|
||||
---
|
||||
|
||||
## 4. Stack technique (Rust)
|
||||
### Runtime / réseau
|
||||
- **Rust stable**
|
||||
- **tokio** (async)
|
||||
- **reqwest** (HTTP)
|
||||
- **tokio-tungstenite** (WebSocket)
|
||||
- **quinn** (QUIC P2P) — recommandé pour data plane (fichiers/terminal)
|
||||
|
||||
### Terminal
|
||||
- Unix : **portable-pty** (ou equivalent) pour PTY
|
||||
- Windows : **ConPTY** (crate dédiée / wrapper)
|
||||
|
||||
### FS / sync
|
||||
- notify (watcher cross-platform)
|
||||
- hashing (blake3 recommandé)
|
||||
|
||||
### Config / logs
|
||||
- serde + toml/yaml
|
||||
- tracing + tracing-subscriber
|
||||
|
||||
### Packaging
|
||||
- binaire unique par OS
|
||||
- install : MSI (Windows), deb/rpm (Linux), dmg/pkg (macOS) — V1/V2
|
||||
|
||||
---
|
||||
|
||||
## 5. Structure recommandée
|
||||
```
|
||||
agent/
|
||||
Cargo.toml
|
||||
src/
|
||||
main.rs
|
||||
config/
|
||||
mod.rs
|
||||
mesh/
|
||||
rest.rs
|
||||
ws.rs
|
||||
types.rs
|
||||
p2p/
|
||||
mod.rs
|
||||
quic/
|
||||
endpoint.rs
|
||||
protocol.rs
|
||||
fallback_http/
|
||||
mod.rs
|
||||
share/
|
||||
file_send.rs
|
||||
folder_zip.rs
|
||||
folder_sync/
|
||||
manifest.rs
|
||||
diff.rs
|
||||
watcher.rs
|
||||
terminal/
|
||||
mod.rs
|
||||
pty_unix.rs
|
||||
conpty_windows.rs
|
||||
stream.rs
|
||||
notifications/
|
||||
gotify.rs
|
||||
router.rs
|
||||
os/
|
||||
autostart.rs
|
||||
tray.rs
|
||||
tests/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Protocole & permissions
|
||||
- Toutes les actions P2P sont autorisées par **capability tokens** (TTL court), émis par le serveur :
|
||||
- `share:file`, `share:folder`, `terminal:view`, `terminal:control`
|
||||
- Le P2P (QUIC) utilise un **handshake applicatif** :
|
||||
- échange d’un token de session (issu du serveur)
|
||||
- validation côté pair avant d’accepter un flux
|
||||
|
||||
---
|
||||
|
||||
## 7. Sécurité (résumé)
|
||||
- Les secrets SSH ne sortent jamais de la machine partageuse.
|
||||
- Terminal share : preview-only par défaut.
|
||||
- Chiffrement transport : QUIC (TLS 1.3), WebSocket/TLS.
|
||||
- Logs sans contenu sensible.
|
||||
|
||||
---
|
||||
|
||||
## 8. TODO
|
||||
### MVP (priorité)
|
||||
- [ ] Config + identité `device_id`
|
||||
- [ ] Connexion WS au serveur (auth)
|
||||
- [ ] Réception events (room, terminal, share)
|
||||
- [ ] Notifications Gotify
|
||||
- [ ] Terminal preview (PTY) + stream P2P (QUIC)
|
||||
|
||||
### V1
|
||||
- [ ] Envoi fichier P2P (QUIC)
|
||||
- [ ] Envoi dossier (zip) P2P
|
||||
- [ ] Take control terminal (arbitrage via serveur)
|
||||
- [ ] Diagnostics (mode P2P, erreurs, stats)
|
||||
|
||||
### V2
|
||||
- [ ] Sync dossier (manifest/diff)
|
||||
- [ ] Tray + autostart multi-OS
|
||||
- [ ] Fallback HTTP serveur (temporaire) si P2P impossible
|
||||
- [ ] WebRTC DataChannel agent (si besoin compat)
|
||||
|
||||
---
|
||||
|
||||
## 9. Améliorations futures
|
||||
- `.meshignore`
|
||||
- Diff binaire + dédup
|
||||
- Chiffrement applicatif E2E optionnel
|
||||
- Tunnel SSH (TCP-like) au-dessus de QUIC (avancé)
|
||||
|
||||
---
|
||||
|
||||
## 10. Changelog
|
||||
```
|
||||
0.1.0 – Rust agent skeleton (WS + Gotify)
|
||||
0.2.0 – Terminal share preview (QUIC)
|
||||
0.3.0 – File transfer (QUIC)
|
||||
0.4.0 – Folder zip + take control
|
||||
0.5.0 – Folder sync (beta)
|
||||
```
|
||||
|
||||
306
docs/agent_claude_codex_prompt.md
Normal file
306
docs/agent_claude_codex_prompt.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# Livrables — Agent Rust
|
||||
|
||||
Ce document contient :
|
||||
|
||||
1. le fichier `agent/CLAUDE.md`
|
||||
2. un prompt Codex initial pour générer le squelette compilable de `agent/`
|
||||
|
||||
---
|
||||
|
||||
## 1) `agent/CLAUDE.md`
|
||||
|
||||
```markdown
|
||||
# CLAUDE.md — Mesh Agent (Rust)
|
||||
|
||||
## Portée
|
||||
Ce fichier s’applique **uniquement** au code situé dans `agent/`.
|
||||
Il complète le `CLAUDE.md` racine (vision/architecture). En cas de conflit, le `CLAUDE.md` racine prime.
|
||||
|
||||
---
|
||||
|
||||
## Objectifs de l’agent
|
||||
- Multi-OS (Linux, Windows, macOS)
|
||||
- Faible charge serveur : le serveur ne transporte pas de flux lourds
|
||||
- Data-plane performant :
|
||||
- **QUIC (TLS 1.3)** pour fichiers/dossiers/terminal
|
||||
- fallback HTTP temporaire (exceptionnel)
|
||||
- Terminal share robuste via PTY (preview par défaut)
|
||||
- Notifications directes Gotify
|
||||
|
||||
---
|
||||
|
||||
## Rappels d’architecture
|
||||
- Control plane : Mesh Server (REST + WS)
|
||||
- auth, rooms, ACL
|
||||
- capability tokens TTL court
|
||||
- orchestration des sessions P2P
|
||||
- Data plane : agent ↔ agent
|
||||
- QUIC via `quinn`
|
||||
- handshake applicatif obligatoire `P2P_HELLO`
|
||||
|
||||
---
|
||||
|
||||
## Règles de sécurité (non négociables)
|
||||
- Aucun contournement des capability tokens.
|
||||
- Terminal : **preview-only** par défaut.
|
||||
- Input distant uniquement si :
|
||||
1) serveur a accordé `terminal:control`
|
||||
2) l’agent propriétaire confirme/maintient le contrôleur actif
|
||||
- Ne jamais exfiltrer : clés SSH, mots de passe, variables secrètes.
|
||||
- Logs sans contenu sensible (pas de contenu de terminal en clair dans les logs).
|
||||
|
||||
---
|
||||
|
||||
## Contraintes de code Rust
|
||||
- Rust stable
|
||||
- Async : `tokio`
|
||||
- HTTP : `reqwest`
|
||||
- WebSocket : `tokio-tungstenite`
|
||||
- QUIC : `quinn`
|
||||
- Config : `serde` + `toml` (ou yaml)
|
||||
- Logs : `tracing`
|
||||
|
||||
Qualité :
|
||||
- Interdiction de `unwrap()` / `expect()` en code final
|
||||
- Erreurs explicites (`Result`) + `thiserror` recommandé
|
||||
- Tests stubs acceptés, mais structure prête
|
||||
|
||||
---
|
||||
|
||||
## Structure obligatoire du dossier
|
||||
Ne pas renommer arbitrairement.
|
||||
|
||||
```
|
||||
|
||||
agent/ Cargo.toml src/ main.rs config/ mesh/ p2p/ quic/ share/ terminal/ notifications/ os/ tests/
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Développement : ordre recommandé
|
||||
1) **Squelette compilable** + config + logs
|
||||
2) Client Mesh (REST + WS)
|
||||
3) Notifications Gotify (agent → gotify)
|
||||
4) Terminal preview via PTY (local) + stream (transport abstrait)
|
||||
5) QUIC : endpoint + handshake `P2P_HELLO`
|
||||
6) Terminal preview sur QUIC
|
||||
7) File transfer sur QUIC
|
||||
8) Folder zip sur QUIC
|
||||
9) Terminal take-control (arbitrage via serveur)
|
||||
|
||||
---
|
||||
|
||||
## Consigne de suivi d’avancement
|
||||
À chaque jalon significatif **ou** vers ~80 % d’une session de travail, produire :
|
||||
|
||||
```
|
||||
|
||||
ÉTAT D’AVANCEMENT – Mesh Agent ✔ Terminé :
|
||||
|
||||
- ... ◻ En cours :
|
||||
- ... ✖ À faire :
|
||||
- ... Risques / points bloquants :
|
||||
- ... Prochaine action recommandée :
|
||||
- ...
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gestion du contexte (pour Claude)
|
||||
- Utiliser `/clear` entre tâches distinctes.
|
||||
- Pour revue sécurité/perf : utiliser un sous-agent.
|
||||
|
||||
Le contexte durable doit rester dans :
|
||||
- `CLAUDE.md` (racine)
|
||||
- ce `agent/CLAUDE.md`
|
||||
- `docs/`.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Prompt Codex initial — squelette compilable `agent/`
|
||||
|
||||
```markdown
|
||||
# Codex Prompt — Mesh Agent (Rust) : Skeleton compilable (étape 1)
|
||||
|
||||
Contexte
|
||||
- Projet Mesh : server Python (control plane), client web, agent Rust (data plane QUIC).
|
||||
- Références : `CLAUDE.md` racine + `agent.md` + `agent/CLAUDE.md`.
|
||||
|
||||
Objectif
|
||||
- Générer un squelette **compilable** du dossier `agent/` (Rust stable) avec la structure imposée.
|
||||
- Aucun code “tout-en-un”. Travail en modules minimalistes.
|
||||
|
||||
Règles
|
||||
- Rust stable, tokio, tracing.
|
||||
- Pas de unwrap/expect dans le code final.
|
||||
- Erreurs via Result + thiserror.
|
||||
- Config via serde + toml.
|
||||
|
||||
Livrables attendus
|
||||
1) `agent/Cargo.toml` avec dépendances minimales : tokio, tracing, tracing-subscriber, serde, toml, reqwest, tokio-tungstenite, thiserror.
|
||||
2) Arborescence `src/` avec modules vides mais raccordés :
|
||||
- config/
|
||||
- mesh/ (rest.rs, ws.rs, types.rs)
|
||||
- notifications/ (gotify.rs, router.rs)
|
||||
- terminal/ (mod.rs, stream.rs)
|
||||
- p2p/quic/ (endpoint.rs, protocol.rs)
|
||||
- share/ (file_send.rs, folder_zip.rs)
|
||||
- os/ (autostart.rs, tray.rs)
|
||||
3) `src/main.rs` :
|
||||
- charge config
|
||||
- init tracing
|
||||
- démarre runtime tokio
|
||||
- affiche un log “agent started”
|
||||
4) `agent/README.md` : commandes build/run
|
||||
5) `agent/.env.example` (si utile) + `config.example.toml`
|
||||
|
||||
Ne pas implémenter la logique QUIC/WS complète maintenant : uniquement les stubs d’API et la compilation.
|
||||
|
||||
Stop
|
||||
- Quand tout compile (`cargo build`), terminer et fournir un court résumé des fichiers créés.
|
||||
- Si tu estimes être à ~80 % de l’objectif, produire un état d’avancement au format imposé avant d’aller plus loin.
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Traçabilité des modifications de code (obligatoire)
|
||||
|
||||
Chaque **agent (Claude principal, sous-agent, Codex, ou autre)** qui **crée ou modifie du code** doit **explicitement l’indiquer dans les fichiers concernés**.
|
||||
|
||||
### Règles de traçabilité
|
||||
|
||||
- **Tout fichier créé** doit contenir un commentaire en **début de fichier** indiquant :
|
||||
|
||||
- l’agent auteur,
|
||||
- la date,
|
||||
- l’objectif de la création.
|
||||
|
||||
- **Toute modification d’un fichier existant** doit être signalée :
|
||||
|
||||
- soit par un commentaire en **début de fichier** (si modification importante),
|
||||
- soit par un commentaire **en fin de ligne** ou à proximité immédiate du changement (si modification ponctuelle).
|
||||
|
||||
### Format imposé des commentaires
|
||||
|
||||
#### Création de fichier
|
||||
|
||||
```text
|
||||
// Created by: <agent_name>
|
||||
// Date: YYYY-MM-DD
|
||||
// Purpose: <short description>
|
||||
```
|
||||
|
||||
#### Modification de fichier
|
||||
|
||||
```text
|
||||
// Modified by: <agent_name> — YYYY-MM-DD — <reason>
|
||||
```
|
||||
|
||||
Le format exact peut être adapté au langage (Rust, Python, TypeScript…), mais **les informations doivent toujours être présentes**.
|
||||
|
||||
### Agents concernés
|
||||
|
||||
Cette règle s’applique à :
|
||||
|
||||
- Claude (agent principal),
|
||||
- tous les **sous-agents**,
|
||||
- **Codex** ou tout autre générateur de code,
|
||||
- toute automatisation produisant ou modifiant des fichiers.
|
||||
|
||||
### Objectif
|
||||
|
||||
- garantir une **traçabilité claire** des décisions et des changements,
|
||||
- faciliter les revues (sécurité, performance, architecture),
|
||||
- permettre de comprendre rapidement **qui a fait quoi et pourquoi**.
|
||||
|
||||
Cette consigne est **obligatoire et non optionnelle** pour le projet Mesh.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Traçabilité des modifications de code (obligatoire)
|
||||
|
||||
Toute **création ou modification de code** réalisée pour l’agent Mesh doit indiquer **explicitement l’agent auteur**.
|
||||
|
||||
### Règles
|
||||
|
||||
- **Création de fichier** : commentaire en début de fichier.
|
||||
- **Modification importante** : commentaire en début de fichier.
|
||||
- **Modification ponctuelle** : commentaire en fin de ligne ou à proximité immédiate.
|
||||
|
||||
### Formats imposés (à adapter au langage)
|
||||
|
||||
**Création**
|
||||
|
||||
```text
|
||||
// Created by: <agent_name>
|
||||
// Date: YYYY-MM-DD
|
||||
// Purpose: <short description>
|
||||
```
|
||||
|
||||
**Modification**
|
||||
|
||||
```text
|
||||
// Modified by: <agent_name> — YYYY-MM-DD — <reason>
|
||||
```
|
||||
|
||||
### Agents concernés
|
||||
|
||||
- Claude (principal)
|
||||
- Sous-agents
|
||||
- Codex / générateurs de code
|
||||
|
||||
### Objectif
|
||||
|
||||
Garantir une **traçabilité claire** (qui / quand / pourquoi) pour les revues sécurité, performance et maintenance.
|
||||
|
||||
Cette règle est **non optionnelle** pour l’agent Mesh.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Traçabilité des modifications de code (obligatoire — Agent)
|
||||
|
||||
Cette règle s’applique **spécifiquement au dossier **`` et est prioritaire pour Codex.
|
||||
|
||||
### Exigences
|
||||
|
||||
- Chaque agent (Claude, sous-agent, Codex) doit **signer ses créations et modifications**.
|
||||
- Aucune PR ou génération de code n’est valide sans cette signature.
|
||||
|
||||
### Formats
|
||||
|
||||
**Création de fichier**
|
||||
|
||||
```text
|
||||
// Created by: <agent_name>
|
||||
// Date: YYYY-MM-DD
|
||||
// Purpose: <short description>
|
||||
```
|
||||
|
||||
**Modification**
|
||||
|
||||
```text
|
||||
// Modified by: <agent_name> — YYYY-MM-DD — <reason>
|
||||
```
|
||||
|
||||
### Placement
|
||||
|
||||
- Début de fichier : création ou refactor majeur.
|
||||
- Fin de ligne / bloc : correction ponctuelle.
|
||||
|
||||
### Objectif
|
||||
|
||||
- Auditabilité
|
||||
- Revue facilitée
|
||||
- Historique lisible sans dépendre des commits
|
||||
|
||||
Cette consigne est **obligatoire** pour toute génération de code dans `agent/`.
|
||||
|
||||
590
docs/bundle_2_3_4.md
Normal file
590
docs/bundle_2_3_4.md
Normal file
@@ -0,0 +1,590 @@
|
||||
# Bundle Mesh — (2) infra, (3) prompts Claude, (4) server skeleton
|
||||
|
||||
Ce document contient **les fichiers** demandés, prêts à être copiés dans le dépôt. Chaque section indique le chemin du fichier.
|
||||
|
||||
---
|
||||
|
||||
## 2) `infra/docker-compose.yml` (final, avec reverse-proxy TLS)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
depends_on:
|
||||
- mesh-server
|
||||
|
||||
mesh-server:
|
||||
build: ../server
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MESH_JWT_SECRET=${MESH_JWT_SECRET}
|
||||
- MESH_PUBLIC_URL=${MESH_PUBLIC_URL}
|
||||
- GOTIFY_URL=${GOTIFY_URL}
|
||||
- GOTIFY_TOKEN=${GOTIFY_TOKEN}
|
||||
- STUN_URL=${STUN_URL}
|
||||
- TURN_URL=${TURN_URL}
|
||||
- TURN_REALM=${TURN_REALM}
|
||||
expose:
|
||||
- "8000"
|
||||
|
||||
gotify:
|
||||
image: gotify/server:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- GOTIFY_DEFAULTUSER_NAME=${GOTIFY_DEFAULTUSER_NAME:-admin}
|
||||
- GOTIFY_DEFAULTUSER_PASS=${GOTIFY_DEFAULTUSER_PASS:-adminadmin}
|
||||
ports:
|
||||
- "8080:80" # optionnel: exposer en LAN uniquement
|
||||
volumes:
|
||||
- gotify_data:/app/data
|
||||
|
||||
coturn:
|
||||
image: coturn/coturn:latest
|
||||
restart: unless-stopped
|
||||
network_mode: "host"
|
||||
command: >
|
||||
-n
|
||||
--log-file=stdout
|
||||
--external-ip=${TURN_EXTERNAL_IP}
|
||||
--realm=${TURN_REALM}
|
||||
--user=${TURN_USER}:${TURN_PASS}
|
||||
--listening-port=3478
|
||||
--min-port=49160 --max-port=49200
|
||||
--fingerprint
|
||||
--lt-cred-mech
|
||||
--no-multicast-peers
|
||||
--no-cli
|
||||
|
||||
volumes:
|
||||
gotify_data:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
```
|
||||
|
||||
### `infra/caddy/Caddyfile`
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
email {$CADDY_EMAIL}
|
||||
}
|
||||
|
||||
{$MESH_DOMAIN} {
|
||||
encode gzip
|
||||
|
||||
@ws {
|
||||
path /ws
|
||||
}
|
||||
|
||||
reverse_proxy @ws mesh-server:8000 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-For {remote}
|
||||
}
|
||||
|
||||
reverse_proxy mesh-server:8000 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-For {remote}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `infra/.env.example`
|
||||
|
||||
```env
|
||||
# Public
|
||||
MESH_DOMAIN=mesh.example.com
|
||||
MESH_PUBLIC_URL=https://mesh.example.com
|
||||
CADDY_EMAIL=admin@example.com
|
||||
|
||||
# Server
|
||||
MESH_JWT_SECRET=change_me_super_secret
|
||||
GOTIFY_URL=http://gotify:80
|
||||
GOTIFY_TOKEN=CHANGE_ME_GOTIFY_APP_TOKEN
|
||||
|
||||
# ICE
|
||||
STUN_URL=stun:stun.l.google.com:19302
|
||||
TURN_URL=turn:turn.example.com:3478
|
||||
TURN_REALM=mesh
|
||||
|
||||
# TURN (coturn)
|
||||
TURN_EXTERNAL_IP=203.0.113.10
|
||||
TURN_USER=mesh
|
||||
TURN_PASS=change_me_turn_password
|
||||
```
|
||||
|
||||
### Notes réseau
|
||||
- Ouvrir : **443 TCP** (Mesh via Caddy), **3478 UDP/TCP** (TURN), **49160-49200 UDP** (media relay TURN).
|
||||
- Gotify peut rester LAN-only.
|
||||
|
||||
---
|
||||
|
||||
## 3) Prompts Claude Code découpés
|
||||
|
||||
### `docs/claude-prompt-server.md`
|
||||
|
||||
```markdown
|
||||
# Claude Code Prompt — Mesh Server (FastAPI control plane)
|
||||
|
||||
Génère le squelette du dossier `server/` pour Mesh.
|
||||
|
||||
Contraintes
|
||||
- Python 3.12+
|
||||
- FastAPI
|
||||
- WebSocket /ws
|
||||
- JWT auth
|
||||
- SQLite (MVP)
|
||||
- Server = control plane (aucun média ni fichier transitant)
|
||||
|
||||
Fonctions
|
||||
- POST /auth/login (dev) -> JWT
|
||||
- POST /rooms -> create
|
||||
- POST /rooms/{room_id}/join
|
||||
- POST /caps -> capability token TTL 120s
|
||||
- GET /health
|
||||
|
||||
WebSocket /ws
|
||||
- Auth JWT au handshake
|
||||
- presence
|
||||
- relay des messages: rtc.offer / rtc.answer / rtc.ice
|
||||
- relay contrôle: call.request, screen.share.request, share.file.request, share.folder.request,
|
||||
terminal.share.request, terminal.control.take, terminal.control.release
|
||||
- Validation: refuser relay si capability manquante/expirée pour l’action
|
||||
|
||||
Notifications
|
||||
- Adapter Gotify (GOTIFY_URL + GOTIFY_TOKEN)
|
||||
- Router: notify sur chat.message.created, call.missed, share.completed, terminal.share.started
|
||||
|
||||
Qualité
|
||||
- Typage, structure modules, tests stubs
|
||||
- .env.example + instructions README serveur
|
||||
|
||||
Deliverable
|
||||
- Tous les fichiers nécessaires pour `uvicorn app.main:app --reload`.
|
||||
```
|
||||
|
||||
### `docs/claude-prompt-agent.md`
|
||||
|
||||
```markdown
|
||||
# Claude Code Prompt — Mesh Agent (Python, multi-OS)
|
||||
|
||||
Génère le squelette du dossier `agent/` pour Mesh.
|
||||
|
||||
Contraintes
|
||||
- Python 3.12+
|
||||
- asyncio
|
||||
- httpx + websockets
|
||||
- config fichier (yaml/toml) + env
|
||||
|
||||
Fonctions MVP
|
||||
- Connexion au Mesh Server (REST + WS)
|
||||
- Enregistrement device_id
|
||||
- Réception events
|
||||
- Notifications Gotify directes (gotify_url + token)
|
||||
- Terminal share preview:
|
||||
- spawn PTY local (bash)
|
||||
- capture output
|
||||
- abstraction transport (DataChannel prévu), MVP autorisé sur WS mais interface doit permettre swap
|
||||
|
||||
Bonus V1
|
||||
- File send via WebRTC DataChannel (interfaces + stubs acceptés si lib WebRTC non choisie)
|
||||
- Tray + autostart (stubs par OS)
|
||||
|
||||
Qualité
|
||||
- Modules: core/config, mesh client, notifications, terminal, transfer
|
||||
- Tests stubs
|
||||
- README agent
|
||||
```
|
||||
|
||||
### `docs/claude-prompt-client.md`
|
||||
|
||||
```markdown
|
||||
# Claude Code Prompt — Mesh Client (Web)
|
||||
|
||||
Génère le squelette du dossier `client/`.
|
||||
|
||||
Stack
|
||||
- Vite + React + TypeScript
|
||||
- WebSocket client
|
||||
- WebRTC placeholders
|
||||
- xterm.js terminal viewer
|
||||
|
||||
Écrans MVP
|
||||
- Login
|
||||
- Rooms: create/join
|
||||
- Room view: chat minimal + boutons (call/screen/file/terminal)
|
||||
|
||||
Réseau
|
||||
- REST login
|
||||
- WS connect
|
||||
- Signaling handlers rtc.offer/answer/ice
|
||||
|
||||
Terminal
|
||||
- composant xterm.js capable d’afficher TERM_OUT
|
||||
|
||||
Qualité
|
||||
- code propre, modules lib/api.ts, lib/ws.ts, lib/rtc.ts
|
||||
- README client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) Server skeleton (FastAPI) — fichiers prêts à coller
|
||||
|
||||
### `server/pyproject.toml`
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "mesh-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fastapi>=0.110",
|
||||
"uvicorn[standard]>=0.27",
|
||||
"python-dotenv>=1.0",
|
||||
"pyjwt>=2.8",
|
||||
"pydantic>=2.6",
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[tool.uvicorn]
|
||||
factory = false
|
||||
```
|
||||
|
||||
### `server/app/main.py`
|
||||
|
||||
```python
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import jwt
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
|
||||
JWT_SECRET = os.getenv("MESH_JWT_SECRET", "dev_secret_change_me")
|
||||
JWT_ALG = "HS256"
|
||||
CAP_TTL_SECONDS = int(os.getenv("MESH_CAP_TTL", "120"))
|
||||
|
||||
app = FastAPI(title="Mesh Server", version="0.1.0")
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"] ,
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# In-memory MVP stores (replace by DB later)
|
||||
ROOMS: Dict[str, Dict[str, Any]] = {} # room_id -> {name, members:set(peer_id/user_id)}
|
||||
PEERS: Dict[str, Dict[str, Any]] = {} # peer_id -> {user_id, ws}
|
||||
|
||||
|
||||
# -------------------- Models --------------------
|
||||
class LoginReq(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class LoginResp(BaseModel):
|
||||
token: str
|
||||
|
||||
|
||||
class CreateRoomReq(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
class CreateRoomResp(BaseModel):
|
||||
room_id: str
|
||||
|
||||
|
||||
class JoinRoomReq(BaseModel):
|
||||
room_id: str
|
||||
|
||||
|
||||
class CapsReq(BaseModel):
|
||||
room_id: str
|
||||
peer_id: str
|
||||
caps: list[str]
|
||||
target_peer_id: Optional[str] = None
|
||||
max_size: Optional[int] = None
|
||||
|
||||
|
||||
class CapsResp(BaseModel):
|
||||
cap_token: str
|
||||
exp: int
|
||||
|
||||
|
||||
# -------------------- Helpers --------------------
|
||||
def now_ts() -> int:
|
||||
return int(time.time())
|
||||
|
||||
|
||||
def issue_jwt(payload: dict, ttl_seconds: int) -> str:
|
||||
data = dict(payload)
|
||||
data["exp"] = now_ts() + ttl_seconds
|
||||
return jwt.encode(data, JWT_SECRET, algorithm=JWT_ALG)
|
||||
|
||||
|
||||
def verify_jwt(token: str) -> dict:
|
||||
try:
|
||||
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALG])
|
||||
except jwt.ExpiredSignatureError:
|
||||
raise HTTPException(status_code=401, detail="Token expired")
|
||||
except jwt.InvalidTokenError:
|
||||
raise HTTPException(status_code=401, detail="Invalid token")
|
||||
|
||||
|
||||
def ws_send(ws: WebSocket, msg: dict) -> None:
|
||||
# FastAPI WS requires await; this helper exists for symmetry; see async use.
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def make_event(event_type: str, _from: str, to: str, payload: dict) -> dict:
|
||||
return {
|
||||
"type": event_type,
|
||||
"id": str(uuid.uuid4()),
|
||||
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
"from": _from,
|
||||
"to": to,
|
||||
"payload": payload,
|
||||
}
|
||||
|
||||
|
||||
# -------------------- REST --------------------
|
||||
@app.get("/health")
|
||||
def health() -> dict:
|
||||
return {"ok": True, "ts": now_ts()}
|
||||
|
||||
|
||||
@app.post("/auth/login", response_model=LoginResp)
|
||||
def login(req: LoginReq) -> LoginResp:
|
||||
# Dev-only auth
|
||||
if not req.username or not req.password:
|
||||
raise HTTPException(status_code=400, detail="Missing credentials")
|
||||
token = issue_jwt({"sub": req.username, "user_id": req.username}, ttl_seconds=3600)
|
||||
return LoginResp(token=token)
|
||||
|
||||
|
||||
@app.post("/rooms", response_model=CreateRoomResp)
|
||||
def create_room(req: CreateRoomReq) -> CreateRoomResp:
|
||||
room_id = str(uuid.uuid4())
|
||||
ROOMS[room_id] = {"name": req.name, "members": set()}
|
||||
return CreateRoomResp(room_id=room_id)
|
||||
|
||||
|
||||
@app.post("/rooms/{room_id}/join")
|
||||
def join_room(room_id: str) -> dict:
|
||||
if room_id not in ROOMS:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.post("/caps", response_model=CapsResp)
|
||||
def caps(req: CapsReq) -> CapsResp:
|
||||
if req.room_id not in ROOMS:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
exp = now_ts() + CAP_TTL_SECONDS
|
||||
cap_token = jwt.encode(
|
||||
{
|
||||
"sub": req.peer_id,
|
||||
"room_id": req.room_id,
|
||||
"caps": req.caps,
|
||||
"target_peer_id": req.target_peer_id,
|
||||
"max_size": req.max_size,
|
||||
"exp": exp,
|
||||
},
|
||||
JWT_SECRET,
|
||||
algorithm=JWT_ALG,
|
||||
)
|
||||
return CapsResp(cap_token=cap_token, exp=exp)
|
||||
|
||||
|
||||
# -------------------- WebSocket --------------------
|
||||
async def ws_auth(ws: WebSocket) -> dict:
|
||||
# token can be provided as query param: /ws?token=...
|
||||
token = ws.query_params.get("token")
|
||||
if not token:
|
||||
await ws.close(code=4401)
|
||||
raise HTTPException(status_code=401, detail="Missing token")
|
||||
data = verify_jwt(token)
|
||||
return data
|
||||
|
||||
|
||||
def cap_allows(cap_token: str | None, required_cap: str, room_id: str, from_peer: str, target_peer: str | None) -> bool:
|
||||
if not cap_token:
|
||||
return False
|
||||
try:
|
||||
data = jwt.decode(cap_token, JWT_SECRET, algorithms=[JWT_ALG])
|
||||
except Exception:
|
||||
return False
|
||||
if data.get("room_id") != room_id:
|
||||
return False
|
||||
if data.get("sub") != from_peer:
|
||||
return False
|
||||
caps = set(data.get("caps") or [])
|
||||
if required_cap not in caps:
|
||||
return False
|
||||
tp = data.get("target_peer_id")
|
||||
if target_peer and tp and tp != target_peer:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
CAP_MAP = {
|
||||
# signaling / media control
|
||||
"call.request": "call",
|
||||
"screen.share.request": "screen",
|
||||
# shares
|
||||
"share.file.request": "share:file",
|
||||
"share.folder.request": "share:folder",
|
||||
# terminal
|
||||
"terminal.share.request": "terminal:view",
|
||||
"terminal.control.take": "terminal:control",
|
||||
"terminal.control.release": "terminal:control",
|
||||
# rtc relay is gated by the feature that initiated it; for MVP we accept rtc.* if a cap_token is present
|
||||
"rtc.offer": "rtc",
|
||||
"rtc.answer": "rtc",
|
||||
"rtc.ice": "rtc",
|
||||
}
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(ws: WebSocket):
|
||||
await ws.accept()
|
||||
auth = await ws_auth(ws)
|
||||
peer_id = str(uuid.uuid4())
|
||||
user_id = auth.get("user_id")
|
||||
|
||||
PEERS[peer_id] = {"user_id": user_id, "ws": ws}
|
||||
|
||||
# welcome
|
||||
await ws.send_text(json.dumps(make_event("system.welcome", "server", peer_id, {"peer_id": peer_id, "user_id": user_id})))
|
||||
|
||||
try:
|
||||
while True:
|
||||
raw = await ws.receive_text()
|
||||
msg = json.loads(raw)
|
||||
|
||||
mtype = msg.get("type")
|
||||
payload = msg.get("payload") or {}
|
||||
room_id = payload.get("room_id")
|
||||
target = payload.get("target") or payload.get("target_peer_id")
|
||||
cap_token = payload.get("cap_token")
|
||||
|
||||
# room join bookkeeping (MVP)
|
||||
if mtype == "room.join":
|
||||
if not room_id or room_id not in ROOMS:
|
||||
await ws.send_text(json.dumps(make_event("error", "server", peer_id, {"code": "ROOM_NOT_FOUND"})))
|
||||
continue
|
||||
ROOMS[room_id]["members"].add(peer_id)
|
||||
# broadcast joined
|
||||
for member in list(ROOMS[room_id]["members"]):
|
||||
mws = PEERS.get(member, {}).get("ws")
|
||||
if mws:
|
||||
await mws.send_text(json.dumps(make_event("room.joined", "server", room_id, {"peer_id": peer_id, "room_id": room_id})))
|
||||
continue
|
||||
|
||||
required = CAP_MAP.get(mtype)
|
||||
if required:
|
||||
if not room_id:
|
||||
await ws.send_text(json.dumps(make_event("error", "server", peer_id, {"code": "MISSING_ROOM_ID"})))
|
||||
continue
|
||||
# Special-case rtc.*: require any valid cap_token with "rtc" cap OR reuse the feature cap.
|
||||
if mtype.startswith("rtc."):
|
||||
ok = cap_allows(cap_token, "rtc", room_id, peer_id, target)
|
||||
if not ok:
|
||||
await ws.send_text(json.dumps(make_event("error", "server", peer_id, {"code": "CAP_REQUIRED", "detail": "rtc"})))
|
||||
continue
|
||||
else:
|
||||
ok = cap_allows(cap_token, required, room_id, peer_id, target)
|
||||
if not ok:
|
||||
await ws.send_text(json.dumps(make_event("error", "server", peer_id, {"code": "CAP_REQUIRED", "detail": required})))
|
||||
continue
|
||||
|
||||
# Relay: to specific target peer (typical for rtc.*)
|
||||
if target and target in PEERS:
|
||||
t_ws = PEERS[target]["ws"]
|
||||
await t_ws.send_text(json.dumps(make_event(mtype, peer_id, target, payload)))
|
||||
continue
|
||||
|
||||
# Relay: to room
|
||||
if room_id and room_id in ROOMS:
|
||||
for member in list(ROOMS[room_id]["members"]):
|
||||
mws = PEERS.get(member, {}).get("ws")
|
||||
if mws:
|
||||
await mws.send_text(json.dumps(make_event(mtype, peer_id, room_id, payload)))
|
||||
continue
|
||||
|
||||
# fallback
|
||||
await ws.send_text(json.dumps(make_event("error", "server", peer_id, {"code": "UNROUTABLE"})))
|
||||
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
finally:
|
||||
# cleanup
|
||||
PEERS.pop(peer_id, None)
|
||||
for r in ROOMS.values():
|
||||
r["members"].discard(peer_id)
|
||||
```
|
||||
|
||||
### `server/.env.example`
|
||||
|
||||
```env
|
||||
MESH_JWT_SECRET=change_me
|
||||
MESH_PUBLIC_URL=http://localhost:8000
|
||||
GOTIFY_URL=http://localhost:8080
|
||||
GOTIFY_TOKEN=
|
||||
STUN_URL=stun:stun.l.google.com:19302
|
||||
TURN_URL=turn:turn.example.com:3478
|
||||
TURN_REALM=mesh
|
||||
MESH_CAP_TTL=120
|
||||
```
|
||||
|
||||
### `server/README.md`
|
||||
|
||||
```markdown
|
||||
# Mesh Server (MVP)
|
||||
|
||||
## Run locally
|
||||
```bash
|
||||
cd server
|
||||
python -m venv .venv && . .venv/bin/activate
|
||||
pip install -U pip
|
||||
pip install -e .
|
||||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
- GET /health
|
||||
- POST /auth/login
|
||||
- POST /rooms
|
||||
- POST /caps
|
||||
- WS /ws?token=...
|
||||
|
||||
## Notes
|
||||
- Ce MVP garde les rooms/peers en mémoire.
|
||||
- Remplacer par DB + persistance en V1.
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## À faire ensuite (aligné P2P)
|
||||
- Émettre un vrai `rtc` cap_token par feature (call/screen/share/terminal)
|
||||
- Ajouter stockage DB
|
||||
- Ajouter router Gotify + préférences
|
||||
- Client/Agent: utiliser DataChannel réel
|
||||
|
||||
183
docs/claude(1).md
Normal file
183
docs/claude(1).md
Normal file
@@ -0,0 +1,183 @@
|
||||
# 📄 claude.md — Mesh (spécification générale, Rust pragmatique)
|
||||
|
||||
## 1. Description
|
||||
**Mesh** est une application de communication pour petites équipes (2 à 4 personnes), **self-hosted**, optimisée pour :
|
||||
- **faible charge serveur** (control plane uniquement),
|
||||
- **flux directs P2P** (média + data),
|
||||
- sécurité via autorisations centralisées,
|
||||
- notifications via **Gotify**.
|
||||
|
||||
Fonctionnalités :
|
||||
- chat,
|
||||
- audio / vidéo,
|
||||
- partage d’écran,
|
||||
- partage fichiers & dossiers,
|
||||
- partage de terminal (session SSH “preview” + contrôle),
|
||||
- notifications (serveur + client/agent → Gotify).
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture globale
|
||||
Séparation stricte :
|
||||
- **Control plane** : Mesh Server (Python)
|
||||
- **Media/Data plane** : P2P
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Gotify │
|
||||
└──────▲───────┘
|
||||
│
|
||||
events/notify (server+agent)
|
||||
│
|
||||
Client Web ── P2P (WebRTC RTP) ── Client Web
|
||||
│ \ / │
|
||||
│ \ / │
|
||||
│ \— P2P (QUIC data) ——--/ │
|
||||
│ (agent↔agent) │
|
||||
└────────── WS (signaling) ─────────┘
|
||||
Mesh Server
|
||||
```
|
||||
|
||||
- Audio/Vidéo/Écran : **WebRTC** (navigateurs)
|
||||
- Fichiers/Dossiers/Terminal : **P2P data**
|
||||
- recommandé V1 : **QUIC (TLS 1.3)** via Agent Rust (agent↔agent, agent↔client si support)
|
||||
- option V2 : WebRTC DataChannel (si besoin d’unifier)
|
||||
- fallback exceptionnel : HTTP temporaire via serveur
|
||||
|
||||
---
|
||||
|
||||
## 3. Stack technique (pragmatique)
|
||||
### Serveur (Python)
|
||||
- Python 3.12+
|
||||
- FastAPI
|
||||
- WebSocket `/ws` (signalisation + events)
|
||||
- JWT (auth)
|
||||
- Capabilities tokens TTL court
|
||||
- SQLite (MVP), PostgreSQL (V1)
|
||||
- Adapter notifications : Gotify
|
||||
- Déploiement : Docker
|
||||
|
||||
### Client (Web)
|
||||
- Vite + React + TypeScript (ou équivalent)
|
||||
- WebRTC (call/screen)
|
||||
- UI terminal : xterm.js
|
||||
|
||||
### Agent (Rust)
|
||||
- tokio + reqwest + tokio-tungstenite
|
||||
- QUIC P2P : quinn
|
||||
- Terminal : portable-pty / ConPTY
|
||||
- FS watcher : notify
|
||||
- Logs : tracing
|
||||
|
||||
---
|
||||
|
||||
## 4. Rôles & responsabilités
|
||||
### Mesh Server (control plane)
|
||||
- Auth / sessions
|
||||
- Rooms (2–4) + ACL
|
||||
- Émission de capability tokens (TTL court)
|
||||
- Signalisation WebRTC (offer/answer/ice)
|
||||
- Arbitrage contrôle terminal (“take control”)
|
||||
- Event bus + notifications Gotify
|
||||
|
||||
### Clients (web)
|
||||
- UI + WebRTC call/screen
|
||||
- Chat
|
||||
- Signaling WS
|
||||
- Terminal viewer
|
||||
|
||||
### Agents (Rust)
|
||||
- Terminal share (PTY + stream)
|
||||
- Partage fichiers/dossiers P2P
|
||||
- Sync dossiers (optionnel)
|
||||
- Notifications Gotify
|
||||
|
||||
---
|
||||
|
||||
## 5. Structure du dépôt
|
||||
```
|
||||
mesh/
|
||||
server/
|
||||
client/
|
||||
agent/
|
||||
infra/
|
||||
docs/
|
||||
```
|
||||
|
||||
Docs attendus :
|
||||
- `protocol-events.md`
|
||||
- `signaling.md`
|
||||
- `security.md`
|
||||
- `deployment.md`
|
||||
|
||||
---
|
||||
|
||||
## 6. Règles de permissions (capabilities)
|
||||
Le serveur émet des tokens de capacités (TTL 60–180 s), scoppés :
|
||||
- `room_id`
|
||||
- `peer_id` / `device_id`
|
||||
- `caps[]` ex: `call`, `screen`, `share:file`, `share:folder`, `terminal:view`, `terminal:control`
|
||||
- option : `target_peer_id`, `max_size`, `max_rate`
|
||||
|
||||
Règles :
|
||||
- aucune session P2P ne démarre sans token valide.
|
||||
- terminal : `terminal:view` pour recevoir, `terminal:control` pour envoyer input.
|
||||
|
||||
---
|
||||
|
||||
## 7. Étapes du projet (roadmap)
|
||||
### Phase 1 — Fondation (MVP control plane)
|
||||
- [ ] Server skeleton FastAPI (auth/rooms/ws)
|
||||
- [ ] Protocole events & signalling (WS)
|
||||
- [ ] Capabilities tokens + validation
|
||||
- [ ] Adapter Gotify (server)
|
||||
|
||||
### Phase 2 — Communication (Web)
|
||||
- [ ] Chat (MVP)
|
||||
- [ ] WebRTC audio/vidéo P2P
|
||||
- [ ] Screen share P2P
|
||||
|
||||
### Phase 3 — Agent Rust (data plane)
|
||||
- [ ] Agent Rust skeleton (WS + config + Gotify)
|
||||
- [ ] Terminal share preview (PTY → P2P QUIC)
|
||||
- [ ] Take control (arbitrage via serveur)
|
||||
|
||||
### Phase 4 — Partage data
|
||||
- [ ] File transfer P2P (QUIC)
|
||||
- [ ] Folder zip P2P
|
||||
- [ ] Fallback HTTP (exception)
|
||||
|
||||
### Phase 5 — Sync & polish
|
||||
- [ ] Folder sync (manifest/diff + conflits)
|
||||
- [ ] Packaging multi-OS (MSI/deb/dmg)
|
||||
- [ ] Monitoring minimal + diagnostics
|
||||
|
||||
---
|
||||
|
||||
## 8. TODO global
|
||||
- [ ] RBAC simple (owner/member/guest)
|
||||
- [ ] Quotas (taille, débit)
|
||||
- [ ] Préférences notifications (par room / par event)
|
||||
- [ ] Journaux & rotation
|
||||
- [ ] Tests d’intégration (WS + tokens)
|
||||
|
||||
---
|
||||
|
||||
## 9. Améliorations futures
|
||||
- Mobile native
|
||||
- Federation Mesh↔Mesh
|
||||
- Plugin system
|
||||
- E2E applicatif optionnel
|
||||
- TCP-like tunneling au-dessus de QUIC (SSH tunnel)
|
||||
|
||||
---
|
||||
|
||||
## 10. Changelog
|
||||
```
|
||||
0.1.0 – Architecture validée (Python server + Rust agent)
|
||||
0.2.0 – MVP WebRTC (call/screen)
|
||||
0.3.0 – Agent terminal preview (QUIC)
|
||||
0.4.0 – File/folder transfer (QUIC)
|
||||
0.5.0 – Folder sync (beta)
|
||||
```
|
||||
|
||||
93
docs/deployment.md
Normal file
93
docs/deployment.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 📄 deployment.md — Mesh Deployment (self-hosted)
|
||||
|
||||
## 1. Composants
|
||||
- mesh-server (FastAPI + WS)
|
||||
- coturn (TURN) — fallback NAT strict
|
||||
- gotify (notifications)
|
||||
- (optionnel) reverse proxy (Caddy/Nginx) + TLS
|
||||
|
||||
## 2. Variables d’environnement (exemple)
|
||||
- MESH_PUBLIC_URL=https://mesh.example.com
|
||||
- MESH_JWT_SECRET=...
|
||||
- GOTIFY_URL=https://gotify.example.com
|
||||
- GOTIFY_TOKEN=...
|
||||
- TURN_HOST=turn.example.com
|
||||
- TURN_PORT=3478
|
||||
- TURN_USER=mesh
|
||||
- TURN_PASS=...
|
||||
|
||||
## 3. docker-compose (exemple)
|
||||
Placez ceci dans `infra/docker-compose.yml`.
|
||||
|
||||
services:
|
||||
mesh-server:
|
||||
build: ../server
|
||||
environment:
|
||||
- MESH_JWT_SECRET=${MESH_JWT_SECRET}
|
||||
- GOTIFY_URL=${GOTIFY_URL}
|
||||
- GOTIFY_TOKEN=${GOTIFY_TOKEN}
|
||||
- TURN_URL=${TURN_URL}
|
||||
- STUN_URL=${STUN_URL}
|
||||
ports:
|
||||
- "8000:8000"
|
||||
restart: unless-stopped
|
||||
|
||||
coturn:
|
||||
image: coturn/coturn:latest
|
||||
command: >
|
||||
-n
|
||||
--log-file=stdout
|
||||
--external-ip=${TURN_EXTERNAL_IP}
|
||||
--realm=${TURN_REALM}
|
||||
--user=${TURN_USER}:${TURN_PASS}
|
||||
--listening-port=3478
|
||||
--min-port=49160 --max-port=49200
|
||||
--fingerprint
|
||||
--lt-cred-mech
|
||||
--no-multicast-peers
|
||||
--no-cli
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
|
||||
gotify:
|
||||
image: gotify/server:latest
|
||||
environment:
|
||||
- GOTIFY_DEFAULTUSER_NAME=admin
|
||||
- GOTIFY_DEFAULTUSER_PASS=adminadmin
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- gotify_data:/app/data
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
gotify_data:
|
||||
|
||||
## 4. Notes TURN
|
||||
- TURN peut devenir “lourd” si beaucoup de pairs passent en relay.
|
||||
- Prévoir monitoring trafic + quotas.
|
||||
- Credentials temporaires (V1+) recommandé.
|
||||
|
||||
## 5. Reverse proxy + TLS (recommandé)
|
||||
- Terminer TLS au proxy (Caddy/Nginx).
|
||||
- Forward:
|
||||
- /api → mesh-server
|
||||
- /ws → mesh-server (upgrade websocket)
|
||||
- TURN: idéalement domaine dédié (turn.example.com) + ports ouverts.
|
||||
|
||||
## 6. Ports réseau
|
||||
- Mesh Server: 443 (TLS) / 80 (redirect)
|
||||
- TURN: 3478 UDP/TCP + range UDP (ex 49160-49200)
|
||||
- Gotify: 443/80 (si exposé), sinon LAN only
|
||||
|
||||
## 7. Checks de santé
|
||||
- /health sur mesh-server
|
||||
- gotify UI accessible
|
||||
- test ICE: vérifier host/srflx/relay
|
||||
|
||||
## 8. Exploitation
|
||||
- Sauvegarder:
|
||||
- DB mesh (si sqlite/postgres)
|
||||
- gotify_data
|
||||
- Rotation logs
|
||||
|
||||
172
docs/docs_headers_template.md
Normal file
172
docs/docs_headers_template.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Templates de headers — Traçabilité Mesh
|
||||
|
||||
Ce document définit les **templates de commentaires obligatoires** à utiliser lors de toute **création ou modification de code** dans le projet Mesh.
|
||||
|
||||
Objectifs :
|
||||
|
||||
- traçabilité claire (qui / quand / pourquoi),
|
||||
- auditabilité hors Git,
|
||||
- revues facilitées (sécurité, performance, architecture).
|
||||
|
||||
---
|
||||
|
||||
## Règles générales
|
||||
|
||||
- **Création de fichier** : ajouter un bloc de header en **début de fichier**.
|
||||
- **Modification majeure** : ajouter une ligne `Modified by` en début de fichier (sous le header existant).
|
||||
- **Modification ponctuelle** : ajouter un commentaire **en fin de ligne** ou juste au-dessus du changement.
|
||||
|
||||
### Champs obligatoires
|
||||
|
||||
- `Created by` / `Modified by` : nom de l’agent (ex. `Claude`, `Codex`, `SubAgent:SecurityReview`).
|
||||
- `Date` : format `YYYY-MM-DD`.
|
||||
- `Purpose` / `Reason` : une ligne concise.
|
||||
|
||||
### Champs optionnels
|
||||
|
||||
- `Refs` : issue, ticket, PR, ou référence interne.
|
||||
- `Scope` : module ou sous-système concerné.
|
||||
|
||||
---
|
||||
|
||||
## Noms d’agents recommandés
|
||||
|
||||
- `Claude`
|
||||
- `Codex`
|
||||
- `SubAgent:SecurityReview`
|
||||
- `SubAgent:PerfReview`
|
||||
- `SubAgent:CodeReview`
|
||||
- `Automation:<nom>`
|
||||
|
||||
---
|
||||
|
||||
## Templates par langage
|
||||
|
||||
### Rust (`.rs`)
|
||||
|
||||
**Création (début de fichier)**
|
||||
|
||||
```rust
|
||||
// Created by: <agent_name>
|
||||
// Date: YYYY-MM-DD
|
||||
// Purpose: <short description>
|
||||
// Refs: <optional>
|
||||
```
|
||||
|
||||
**Modification majeure (début de fichier)**
|
||||
|
||||
```rust
|
||||
// Modified by: <agent_name> — YYYY-MM-DD — <reason>
|
||||
```
|
||||
|
||||
**Modification ponctuelle (fin de ligne)**
|
||||
|
||||
```rust
|
||||
let timeout_ms = 1200; // Modified by: Codex — 2025-12-29 — tune retry timeout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Python (`.py`)
|
||||
|
||||
**Création**
|
||||
|
||||
```python
|
||||
# Created by: <agent_name>
|
||||
# Date: YYYY-MM-DD
|
||||
# Purpose: <short description>
|
||||
# Refs: <optional>
|
||||
```
|
||||
|
||||
**Modification ponctuelle**
|
||||
|
||||
```python
|
||||
MAX_CHUNK = 262144 # Modified by: Claude — 2025-12-29 — align with 256KB chunking
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TypeScript / JavaScript (`.ts`, `.tsx`, `.js`)
|
||||
|
||||
**Création**
|
||||
|
||||
```ts
|
||||
// Created by: <agent_name>
|
||||
// Date: YYYY-MM-DD
|
||||
// Purpose: <short description>
|
||||
// Refs: <optional>
|
||||
```
|
||||
|
||||
**Modification ponctuelle**
|
||||
|
||||
```ts
|
||||
const WS_PATH = "/ws"; // Modified by: Codex — 2025-12-29 — keep routing consistent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### HTML
|
||||
|
||||
```html
|
||||
<!--
|
||||
Created by: <agent_name>
|
||||
Date: YYYY-MM-DD
|
||||
Purpose: <short description>
|
||||
Refs: <optional>
|
||||
-->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CSS
|
||||
|
||||
```css
|
||||
/*
|
||||
Created by: <agent_name>
|
||||
Date: YYYY-MM-DD
|
||||
Purpose: <short description>
|
||||
Refs: <optional>
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### YAML (`.yml`, `.yaml`)
|
||||
|
||||
```yaml
|
||||
# Created by: <agent_name>
|
||||
# Date: YYYY-MM-DD
|
||||
# Purpose: <short description>
|
||||
# Refs: <optional>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TOML (`.toml`)
|
||||
|
||||
```toml
|
||||
# Created by: <agent_name>
|
||||
# Date: YYYY-MM-DD
|
||||
# Purpose: <short description>
|
||||
# Refs: <optional>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Markdown (`.md`)
|
||||
|
||||
```markdown
|
||||
<!--
|
||||
Created by: <agent_name>
|
||||
Date: YYYY-MM-DD
|
||||
Purpose: <short description>
|
||||
Refs: <optional>
|
||||
-->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rappel obligatoire
|
||||
|
||||
Ces templates sont **normatifs** pour le projet Mesh. Toute génération de code (Claude, sous-agent, Codex, automatisation) doit les appliquer sans exception.
|
||||
|
||||
93
docs/protocol_events_signaling_update.md
Normal file
93
docs/protocol_events_signaling_update.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 📄 signaling.md — Mesh WebRTC Signaling & ICE Strategy (v2)
|
||||
|
||||
## 1. Objectif
|
||||
Décrire la **signalisation WebRTC** (média) et la stratégie de connexions pour Mesh.
|
||||
|
||||
Mesh utilise :
|
||||
- **WebRTC** pour audio/vidéo/partage écran entre navigateurs
|
||||
- **QUIC P2P** pour transferts data (fichiers/dossiers/terminal) via Agent Rust
|
||||
|
||||
---
|
||||
|
||||
## 2. Rappels d’architecture
|
||||
- **Control plane** : Mesh Server (WS)
|
||||
- **Media plane** : WebRTC (clients web)
|
||||
- **Data plane** : QUIC (agents Rust)
|
||||
|
||||
Le serveur ne transporte pas de média.
|
||||
|
||||
---
|
||||
|
||||
## 3. WebRTC (média) — ICE
|
||||
### STUN (obligatoire)
|
||||
Découverte IP publique.
|
||||
|
||||
### TURN (fallback)
|
||||
Relais en cas d’échec P2P direct.
|
||||
|
||||
Politique candidats :
|
||||
1. host
|
||||
2. srflx
|
||||
3. relay
|
||||
|
||||
Recommandations :
|
||||
- Ne jamais forcer TURN
|
||||
- Logguer si relay utilisé
|
||||
|
||||
---
|
||||
|
||||
## 4. WebRTC — séquence standard
|
||||
1) Client A demande action (call/screen) au serveur
|
||||
2) serveur valide ACL + émet `cap_token`
|
||||
3) échange SDP/ICE via WS (`rtc.offer/answer/ice`)
|
||||
4) flux média direct A↔B
|
||||
|
||||
---
|
||||
|
||||
## 5. QUIC P2P (data) — stratégie
|
||||
- QUIC = TLS 1.3 natif, multiplexing streams, perf élevée
|
||||
- Démarrage : création de session via `p2p.session.request`
|
||||
- Le serveur fournit `session_token` + endpoints
|
||||
- Les pairs se connectent directement (UDP)
|
||||
|
||||
### NAT / connectivité
|
||||
- QUIC peut nécessiter :
|
||||
- UDP sortant autorisé
|
||||
- parfois des contraintes NAT/CGNAT
|
||||
|
||||
Recommandations :
|
||||
- privilégier LAN direct
|
||||
- sur Internet : documenter ouverture/autorisation UDP
|
||||
- fallback HTTP temporaire (exception)
|
||||
|
||||
---
|
||||
|
||||
## 6. Optimisations data
|
||||
- chunking 64–256 KB
|
||||
- backpressure
|
||||
- reprise par offset
|
||||
- hash par chunk + hash final (blake3 recommandé)
|
||||
|
||||
---
|
||||
|
||||
## 7. Sécurité
|
||||
- actions gated by capability token TTL court
|
||||
- QUIC TLS 1.3
|
||||
- terminal : preview-only par défaut, contrôle arbitré via serveur
|
||||
|
||||
---
|
||||
|
||||
## 8. Diagnostics
|
||||
Exposer :
|
||||
- WebRTC : host/srflx/relay
|
||||
- QUIC : latence, débit, retries
|
||||
- erreurs : sessions refusées, tokens expirés
|
||||
|
||||
---
|
||||
|
||||
## 9. Changelog
|
||||
```
|
||||
0.1.0 – WebRTC signaling + ICE
|
||||
0.2.0 – QUIC data-plane sessions (agent Rust)
|
||||
```
|
||||
|
||||
223
docs/protocol_events_v_2(1).md
Normal file
223
docs/protocol_events_v_2(1).md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 📄 protocol-events.md — Mesh Event & Signaling Protocol (v2, Rust pragmatique)
|
||||
|
||||
## 1. Objectif
|
||||
Ce document définit le protocole d’événements Mesh pour :
|
||||
- signalisation WebRTC (média),
|
||||
- orchestration des connexions P2P (data),
|
||||
- autorisations (capabilities),
|
||||
- notifications Gotify.
|
||||
|
||||
Séparation :
|
||||
- **Control plane** : Mesh Server (REST + WebSocket)
|
||||
- **Media plane** : WebRTC (clients web)
|
||||
- **Data plane** : QUIC P2P (agents Rust)
|
||||
|
||||
---
|
||||
|
||||
## 2. Transports
|
||||
- **WSS** : Client/Agent ↔ Mesh Server
|
||||
- **WebRTC** : audio/vidéo/écran (client ↔ client)
|
||||
- **QUIC (TLS 1.3)** : fichiers/dossiers/terminal (agent ↔ agent)
|
||||
|
||||
---
|
||||
|
||||
## 3. Format WS
|
||||
```json
|
||||
{
|
||||
"type": "event.type",
|
||||
"id": "uuid",
|
||||
"timestamp": "ISO-8601",
|
||||
"from": "peer_id|device_id|server",
|
||||
"to": "peer_id|device_id|room_id|server",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
Payload recommandé : `room_id`, `target_peer_id`, `target_device_id`, `cap_token`, `session_id`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Identités
|
||||
- `user_id` : utilisateur
|
||||
- `peer_id` : client web
|
||||
- `device_id` : agent
|
||||
- `room_id` : room (2–4)
|
||||
|
||||
---
|
||||
|
||||
## 5. Capabilities (JWT TTL court)
|
||||
Contenu : `sub`, `room_id`, `caps[]`, `exp` (+ option `target_*`, `max_size`, `max_rate`).
|
||||
|
||||
Caps typiques :
|
||||
- `call`, `screen`
|
||||
- `share:file`, `share:folder`
|
||||
- `terminal:view`, `terminal:control`
|
||||
|
||||
Règles :
|
||||
- aucune session P2P ne démarre sans cap_token valide.
|
||||
- terminal : input interdit sans `terminal:control`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Système
|
||||
### `system.hello`
|
||||
Client/Agent → Server
|
||||
```json
|
||||
{ "peer_type": "client|agent", "version": "x.y.z" }
|
||||
```
|
||||
|
||||
### `system.welcome`
|
||||
Server → Client/Agent
|
||||
```json
|
||||
{ "peer_id": "...", "user_id": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Rooms / présence
|
||||
### `room.join`
|
||||
```json
|
||||
{ "room_id": "..." }
|
||||
```
|
||||
|
||||
### `room.joined`
|
||||
```json
|
||||
{ "peer_id": "...", "role": "member", "room_id": "..." }
|
||||
```
|
||||
|
||||
### `presence.update`
|
||||
```json
|
||||
{ "peer_id": "...", "status": "online|busy|sharing" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Chat
|
||||
### `chat.message.send`
|
||||
```json
|
||||
{ "room_id": "...", "content": "hello" }
|
||||
```
|
||||
|
||||
### `chat.message.created`
|
||||
```json
|
||||
{ "message_id": "...", "from": "...", "content": "hello" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. WebRTC signaling (média)
|
||||
### `rtc.offer`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "sdp": "...", "cap_token": "..." }
|
||||
```
|
||||
|
||||
### `rtc.answer`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "sdp": "...", "cap_token": "..." }
|
||||
```
|
||||
|
||||
### `rtc.ice`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "candidate": {}, "cap_token": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. QUIC P2P sessions (data plane)
|
||||
Les flux data (file/folder/terminal) utilisent une session QUIC orchestrée par le serveur.
|
||||
|
||||
### 10.1 Création
|
||||
#### `p2p.session.request`
|
||||
Agent A → Server
|
||||
```json
|
||||
{
|
||||
"room_id": "...",
|
||||
"target_device_id": "...",
|
||||
"kind": "file|folder|terminal",
|
||||
"cap_token": "...",
|
||||
"meta": { "name": "...", "size": 123 }
|
||||
}
|
||||
```
|
||||
|
||||
#### `p2p.session.created`
|
||||
Server → A et Server → B
|
||||
```json
|
||||
{
|
||||
"session_id": "uuid",
|
||||
"kind": "file|folder|terminal",
|
||||
"expires_in": 180,
|
||||
"auth": {
|
||||
"session_token": "...",
|
||||
"fingerprint": "..."
|
||||
},
|
||||
"endpoints": {
|
||||
"a": { "ip": "x.x.x.x", "port": 45432 },
|
||||
"b": { "ip": "y.y.y.y", "port": 45433 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 Handshake applicatif
|
||||
Premier message sur un stream QUIC :
|
||||
|
||||
`P2P_HELLO`
|
||||
```json
|
||||
{ "t": "P2P_HELLO", "session_id": "...", "session_token": "...", "from_device_id": "..." }
|
||||
```
|
||||
|
||||
Réponse : `P2P_OK` ou `P2P_DENY`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Data messages (sur QUIC)
|
||||
### 11.1 Fichier
|
||||
- `FILE_META` (nom, taille, hash)
|
||||
- `FILE_CHUNK` (offset, bytes)
|
||||
- `FILE_ACK` (last_offset)
|
||||
- `FILE_DONE` (hash final)
|
||||
|
||||
### 11.2 Dossier
|
||||
- `FOLDER_MODE` (zip|sync)
|
||||
- (zip) `ZIP_META` / `ZIP_CHUNK` / `ZIP_DONE`
|
||||
- (sync, V2) `MANIFEST` / `DIFF_CHUNK` / `CONFLICT`
|
||||
|
||||
### 11.3 Terminal
|
||||
- `TERM_OUT` (UTF-8)
|
||||
- `TERM_RESIZE` (cols/rows)
|
||||
- `TERM_IN` (input) — seulement si contrôle accordé
|
||||
|
||||
---
|
||||
|
||||
## 12. Contrôle terminal
|
||||
Arbitrage via WS :
|
||||
- `terminal.control.take`
|
||||
- `terminal.control.granted`
|
||||
- `terminal.control.release`
|
||||
|
||||
---
|
||||
|
||||
## 13. Notifications (Gotify)
|
||||
Événements notifiables :
|
||||
- `chat.message.created`
|
||||
- `call.missed`
|
||||
- `share.completed`
|
||||
- `terminal.share.started`
|
||||
- `agent.offline`
|
||||
|
||||
---
|
||||
|
||||
## 14. Erreurs
|
||||
`error`
|
||||
```json
|
||||
{ "code": "CAP_EXPIRED", "message": "Capability token expired" }
|
||||
```
|
||||
|
||||
Codes suggérés : `CAP_REQUIRED`, `CAP_EXPIRED`, `ROOM_NOT_FOUND`, `P2P_SESSION_DENIED`, `UNROUTABLE`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Changelog
|
||||
```
|
||||
0.1.0 – Base events + WebRTC signaling
|
||||
0.2.0 – QUIC sessions for data plane (file/folder/terminal)
|
||||
```
|
||||
|
||||
223
docs/protocol_events_v_2.md
Normal file
223
docs/protocol_events_v_2.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 📄 protocol-events.md — Mesh Event & Signaling Protocol (v2, Rust pragmatique)
|
||||
|
||||
## 1. Objectif
|
||||
Ce document définit le protocole d’événements Mesh pour :
|
||||
- signalisation WebRTC (média),
|
||||
- orchestration des connexions P2P (data),
|
||||
- autorisations (capabilities),
|
||||
- notifications Gotify.
|
||||
|
||||
Séparation :
|
||||
- **Control plane** : Mesh Server (REST + WebSocket)
|
||||
- **Media plane** : WebRTC (clients web)
|
||||
- **Data plane** : QUIC P2P (agents Rust)
|
||||
|
||||
---
|
||||
|
||||
## 2. Transports
|
||||
- **WSS** : Client/Agent ↔ Mesh Server
|
||||
- **WebRTC** : audio/vidéo/écran (client ↔ client)
|
||||
- **QUIC (TLS 1.3)** : fichiers/dossiers/terminal (agent ↔ agent)
|
||||
|
||||
---
|
||||
|
||||
## 3. Format WS
|
||||
```json
|
||||
{
|
||||
"type": "event.type",
|
||||
"id": "uuid",
|
||||
"timestamp": "ISO-8601",
|
||||
"from": "peer_id|device_id|server",
|
||||
"to": "peer_id|device_id|room_id|server",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
Payload recommandé : `room_id`, `target_peer_id`, `target_device_id`, `cap_token`, `session_id`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Identités
|
||||
- `user_id` : utilisateur
|
||||
- `peer_id` : client web
|
||||
- `device_id` : agent
|
||||
- `room_id` : room (2–4)
|
||||
|
||||
---
|
||||
|
||||
## 5. Capabilities (JWT TTL court)
|
||||
Contenu : `sub`, `room_id`, `caps[]`, `exp` (+ option `target_*`, `max_size`, `max_rate`).
|
||||
|
||||
Caps typiques :
|
||||
- `call`, `screen`
|
||||
- `share:file`, `share:folder`
|
||||
- `terminal:view`, `terminal:control`
|
||||
|
||||
Règles :
|
||||
- aucune session P2P ne démarre sans cap_token valide.
|
||||
- terminal : input interdit sans `terminal:control`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Système
|
||||
### `system.hello`
|
||||
Client/Agent → Server
|
||||
```json
|
||||
{ "peer_type": "client|agent", "version": "x.y.z" }
|
||||
```
|
||||
|
||||
### `system.welcome`
|
||||
Server → Client/Agent
|
||||
```json
|
||||
{ "peer_id": "...", "user_id": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Rooms / présence
|
||||
### `room.join`
|
||||
```json
|
||||
{ "room_id": "..." }
|
||||
```
|
||||
|
||||
### `room.joined`
|
||||
```json
|
||||
{ "peer_id": "...", "role": "member", "room_id": "..." }
|
||||
```
|
||||
|
||||
### `presence.update`
|
||||
```json
|
||||
{ "peer_id": "...", "status": "online|busy|sharing" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Chat
|
||||
### `chat.message.send`
|
||||
```json
|
||||
{ "room_id": "...", "content": "hello" }
|
||||
```
|
||||
|
||||
### `chat.message.created`
|
||||
```json
|
||||
{ "message_id": "...", "from": "...", "content": "hello" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. WebRTC signaling (média)
|
||||
### `rtc.offer`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "sdp": "...", "cap_token": "..." }
|
||||
```
|
||||
|
||||
### `rtc.answer`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "sdp": "...", "cap_token": "..." }
|
||||
```
|
||||
|
||||
### `rtc.ice`
|
||||
```json
|
||||
{ "room_id": "...", "target_peer_id": "...", "candidate": {}, "cap_token": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. QUIC P2P sessions (data plane)
|
||||
Les flux data (file/folder/terminal) utilisent une session QUIC orchestrée par le serveur.
|
||||
|
||||
### 10.1 Création
|
||||
#### `p2p.session.request`
|
||||
Agent A → Server
|
||||
```json
|
||||
{
|
||||
"room_id": "...",
|
||||
"target_device_id": "...",
|
||||
"kind": "file|folder|terminal",
|
||||
"cap_token": "...",
|
||||
"meta": { "name": "...", "size": 123 }
|
||||
}
|
||||
```
|
||||
|
||||
#### `p2p.session.created`
|
||||
Server → A et Server → B
|
||||
```json
|
||||
{
|
||||
"session_id": "uuid",
|
||||
"kind": "file|folder|terminal",
|
||||
"expires_in": 180,
|
||||
"auth": {
|
||||
"session_token": "...",
|
||||
"fingerprint": "..."
|
||||
},
|
||||
"endpoints": {
|
||||
"a": { "ip": "x.x.x.x", "port": 45432 },
|
||||
"b": { "ip": "y.y.y.y", "port": 45433 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 Handshake applicatif
|
||||
Premier message sur un stream QUIC :
|
||||
|
||||
`P2P_HELLO`
|
||||
```json
|
||||
{ "t": "P2P_HELLO", "session_id": "...", "session_token": "...", "from_device_id": "..." }
|
||||
```
|
||||
|
||||
Réponse : `P2P_OK` ou `P2P_DENY`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Data messages (sur QUIC)
|
||||
### 11.1 Fichier
|
||||
- `FILE_META` (nom, taille, hash)
|
||||
- `FILE_CHUNK` (offset, bytes)
|
||||
- `FILE_ACK` (last_offset)
|
||||
- `FILE_DONE` (hash final)
|
||||
|
||||
### 11.2 Dossier
|
||||
- `FOLDER_MODE` (zip|sync)
|
||||
- (zip) `ZIP_META` / `ZIP_CHUNK` / `ZIP_DONE`
|
||||
- (sync, V2) `MANIFEST` / `DIFF_CHUNK` / `CONFLICT`
|
||||
|
||||
### 11.3 Terminal
|
||||
- `TERM_OUT` (UTF-8)
|
||||
- `TERM_RESIZE` (cols/rows)
|
||||
- `TERM_IN` (input) — seulement si contrôle accordé
|
||||
|
||||
---
|
||||
|
||||
## 12. Contrôle terminal
|
||||
Arbitrage via WS :
|
||||
- `terminal.control.take`
|
||||
- `terminal.control.granted`
|
||||
- `terminal.control.release`
|
||||
|
||||
---
|
||||
|
||||
## 13. Notifications (Gotify)
|
||||
Événements notifiables :
|
||||
- `chat.message.created`
|
||||
- `call.missed`
|
||||
- `share.completed`
|
||||
- `terminal.share.started`
|
||||
- `agent.offline`
|
||||
|
||||
---
|
||||
|
||||
## 14. Erreurs
|
||||
`error`
|
||||
```json
|
||||
{ "code": "CAP_EXPIRED", "message": "Capability token expired" }
|
||||
```
|
||||
|
||||
Codes suggérés : `CAP_REQUIRED`, `CAP_EXPIRED`, `ROOM_NOT_FOUND`, `P2P_SESSION_DENIED`, `UNROUTABLE`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Changelog
|
||||
```
|
||||
0.1.0 – Base events + WebRTC signaling
|
||||
0.2.0 – QUIC sessions for data plane (file/folder/terminal)
|
||||
```
|
||||
|
||||
64
docs/security.md
Normal file
64
docs/security.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 📄 security.md — Mesh Security Model
|
||||
|
||||
## 1. Principes
|
||||
- **Server = control plane** : auth, ACL, signaling WebRTC, événements, notifications.
|
||||
- **Clients/Agents = data plane** : média + fichiers + terminal en P2P (WebRTC).
|
||||
- Le serveur ne transporte pas de flux média ni de transferts lourds (sauf fallback explicite).
|
||||
|
||||
## 2. Identités
|
||||
- user_id : utilisateur
|
||||
- peer_id : instance client web
|
||||
- device_id : agent desktop
|
||||
- room_id : salon (2–4 personnes)
|
||||
|
||||
## 3. Authentification
|
||||
- JWT (access token court recommandé, refresh optionnel).
|
||||
- WS authentifié (JWT dans query param sécurisé ou header lors du handshake).
|
||||
- Rotation et révocation (à prévoir V1/V2).
|
||||
|
||||
## 4. Autorisations : Capability Tokens
|
||||
Le serveur émet des tokens à TTL court (ex 120s) contenant:
|
||||
- room_id
|
||||
- peer_id (ou device_id)
|
||||
- caps : ["call", "screen", "share:file", "share:folder", "terminal:view", "terminal:control"]
|
||||
- contraintes optionnelles : max_size, max_rate, target_peer_id
|
||||
|
||||
Règles:
|
||||
- aucun offer WebRTC n’est accepté/relayé sans capability valide associée à l’action.
|
||||
- un viewer en terminal n’a pas le droit d’envoyer `TERM_IN` sans `terminal:control`.
|
||||
|
||||
## 5. WebRTC sécurité
|
||||
- Chiffrement natif DTLS/SRTP (média) + DTLS (DataChannel).
|
||||
- Vérification fingerprint SDP côté client (V1+).
|
||||
- ICE: STUN + TURN (TURN = bande passante sensible; limiter via quotas).
|
||||
|
||||
## 6. Terminal share (SSH preview)
|
||||
- Le SSH tourne **sur la machine du partageur** (agent), via PTY.
|
||||
- Aucun secret SSH (clé, mot de passe) n’est transmis aux viewers.
|
||||
- Mode par défaut : **preview (read-only)**.
|
||||
- “Take control” : un seul contrôleur à la fois, arbitrage via serveur (capability).
|
||||
- Option V2: masquage best-effort de secrets (non garanti).
|
||||
|
||||
## 7. Notifications (Gotify)
|
||||
- Le serveur et l’agent peuvent notifier Gotify.
|
||||
- Ne jamais inclure de secrets dans le texte des notifications.
|
||||
- Niveau de détail configurable (ex: “Nouveau message” vs contenu).
|
||||
|
||||
## 8. Journalisation
|
||||
- Server logs: auth failures, room joins, capability issuance, signaling events (sans contenu média).
|
||||
- Agent logs: démarrage/arrêt de partage, erreurs sync, diagnostics ICE.
|
||||
- Politique de rétention courte (ex 7–14 jours) + rotation.
|
||||
|
||||
## 9. Menaces & mitigations (résumé)
|
||||
- Usurpation peer_id → JWT + WS auth + device_id registration.
|
||||
- Replay d’offers → capability TTL court + nonce/session_id.
|
||||
- Accès non autorisé à une room → ACL server-side + tokens scoped room_id.
|
||||
- Exfiltration via terminal → preview-only par défaut + contrôle explicite.
|
||||
- TURN abuse → quotas, credentials temporaires, rate limiting.
|
||||
|
||||
## 10. Roadmap sécurité
|
||||
- Refresh tokens + révocation
|
||||
- RBAC (owner/member/guest)
|
||||
- E2E applicatif optionnel (au-dessus de WebRTC)
|
||||
- Attestation device (optionnel)
|
||||
|
||||
144
docs/signaling.md
Normal file
144
docs/signaling.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# 📄 signaling.md — Mesh WebRTC Signaling & ICE Strategy
|
||||
|
||||
## 1. Objectif
|
||||
Ce document décrit la **signalisation WebRTC**, la stratégie **ICE / STUN / TURN**, et les règles d’optimisation des flux pour Mesh.
|
||||
|
||||
Objectifs :
|
||||
- Connexions **client ↔ client** directes par défaut
|
||||
- Charge serveur minimale
|
||||
- Fallback robuste (NAT strict)
|
||||
- Sécurité et contrôle centralisé
|
||||
|
||||
---
|
||||
|
||||
## 2. Rappels d’architecture
|
||||
- **Control plane** : Mesh Server (WebSocket)
|
||||
- **Media/Data plane** : WebRTC P2P
|
||||
- Le serveur **ne transporte aucun flux média ou fichier**
|
||||
|
||||
```
|
||||
Client A ── P2P (RTP/DataChannel) ── Client B
|
||||
│ ▲
|
||||
└────── WS ─────┘
|
||||
Mesh Server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. WebRTC : rôles
|
||||
- **Offerer** : initiateur du flux (call/share/file/terminal)
|
||||
- **Answerer** : pair cible
|
||||
- **Signaling server** : Mesh Server (WS JSON)
|
||||
|
||||
---
|
||||
|
||||
## 4. ICE configuration
|
||||
### STUN (obligatoire)
|
||||
Utilisé pour la découverte d’IP publiques.
|
||||
|
||||
Exemples :
|
||||
- `stun:stun.l.google.com:19302`
|
||||
- `stun:stun.mesh.local:3478`
|
||||
|
||||
### TURN (fallback)
|
||||
Utilisé uniquement si le P2P direct échoue.
|
||||
|
||||
- Recommandé : **coturn**
|
||||
- Transport : UDP en priorité, TCP en fallback
|
||||
|
||||
```
|
||||
turn:turn.mesh.local:3478?transport=udp
|
||||
turn:turn.mesh.local:3478?transport=tcp
|
||||
```
|
||||
|
||||
Authentification :
|
||||
- Credentials temporaires (REST API TURN)
|
||||
- TTL court
|
||||
|
||||
---
|
||||
|
||||
## 5. Politique ICE Mesh
|
||||
Priorité des candidats :
|
||||
1. host
|
||||
2. srflx (STUN)
|
||||
3. relay (TURN)
|
||||
|
||||
Règles :
|
||||
- Ne jamais forcer TURN
|
||||
- Logguer si relay utilisé (diagnostic)
|
||||
|
||||
---
|
||||
|
||||
## 6. Séquence de signalisation (exemple : partage écran)
|
||||
|
||||
1) Client A → Server : `screen.share.request`
|
||||
2) Server : vérifie droits + émet capability token
|
||||
3) Server → Client B : `screen.share.granted`
|
||||
4) A ↔ Server ↔ B : échange SDP (offer/answer)
|
||||
5) A ↔ Server ↔ B : ICE candidates
|
||||
6) A ↔ B : flux P2P direct
|
||||
|
||||
---
|
||||
|
||||
## 7. DataChannel (fichiers, terminal)
|
||||
|
||||
### Paramètres recommandés
|
||||
- ordered: true
|
||||
- maxPacketLifeTime: null
|
||||
- maxRetransmits: null
|
||||
|
||||
### Sous-canaux logiques
|
||||
- `file-transfer`
|
||||
- `folder-sync`
|
||||
- `terminal-stream`
|
||||
- `terminal-input`
|
||||
|
||||
---
|
||||
|
||||
## 8. Optimisations performance
|
||||
- Chunking (64–256 KB)
|
||||
- Backpressure (bufferedAmount)
|
||||
- Pause/reprise
|
||||
- Hash par chunk
|
||||
|
||||
---
|
||||
|
||||
## 9. Fallback HTTP (exceptionnel)
|
||||
Si WebRTC impossible :
|
||||
- Upload temporaire serveur
|
||||
- URL signée + expiration
|
||||
- Nettoyage automatique
|
||||
|
||||
---
|
||||
|
||||
## 10. Sécurité
|
||||
- DTLS / SRTP natif WebRTC
|
||||
- Capability token requis avant offer
|
||||
- Vérification fingerprint SDP
|
||||
- TTL court TURN
|
||||
|
||||
---
|
||||
|
||||
## 11. Diagnostics
|
||||
Expose localement :
|
||||
- mode ICE utilisé (host/srflx/relay)
|
||||
- latence RTT
|
||||
- débit moyen
|
||||
- pertes
|
||||
|
||||
---
|
||||
|
||||
## 12. TODO
|
||||
- [ ] ICE restarts
|
||||
- [ ] QUIC (WebTransport)
|
||||
- [ ] TCP-over-DataChannel
|
||||
- [ ] Priorisation flux (media > data)
|
||||
|
||||
---
|
||||
|
||||
## 13. Changelog
|
||||
```
|
||||
0.1.0 – Base signaling + ICE
|
||||
0.2.0 – DataChannel optimisation
|
||||
```
|
||||
|
||||
161
docs/signaling_v_2.md
Normal file
161
docs/signaling_v_2.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# 📄 signaling.md — Mesh Signaling & Connectivity (v2)
|
||||
|
||||
## 1. Objectif
|
||||
|
||||
Documenter :
|
||||
|
||||
- la **signalisation WebRTC** (média) via Mesh Server,
|
||||
- la stratégie **ICE/STUN/TURN**,
|
||||
- la stratégie **data-plane QUIC** (agents Rust) et ses contraintes réseau,
|
||||
- les optimisations et diagnostics.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
- **Control plane** : Mesh Server (WS)
|
||||
- **Media plane** : WebRTC (clients web)
|
||||
- **Data plane** : QUIC P2P (agents Rust)
|
||||
|
||||
Le serveur ne transporte aucun flux média ou transfert lourd.
|
||||
|
||||
---
|
||||
|
||||
## 3. WebRTC (média) — ICE
|
||||
|
||||
### STUN (obligatoire)
|
||||
|
||||
Découverte IP publique.
|
||||
|
||||
### TURN (fallback)
|
||||
|
||||
Relais si P2P direct échoue.
|
||||
|
||||
Politique candidats :
|
||||
|
||||
1. host
|
||||
2. srflx
|
||||
3. relay
|
||||
|
||||
Recommandations :
|
||||
|
||||
- Ne pas forcer TURN
|
||||
- Logguer quand relay est utilisé
|
||||
- Credentials temporaires TURN (V1+)
|
||||
|
||||
---
|
||||
|
||||
## 4. WebRTC — séquence
|
||||
|
||||
1. Action (call/screen) demandée au serveur
|
||||
2. Serveur valide ACL + émet `cap_token`
|
||||
3. Échange SDP/ICE via WS (`rtc.offer/answer/ice`)
|
||||
4. Flux média direct A↔B
|
||||
|
||||
---
|
||||
|
||||
## 5. QUIC P2P (data plane) — stratégie
|
||||
|
||||
- QUIC = TLS 1.3 natif + multiplexing streams
|
||||
- Sessions orchestrées via `p2p.session.request/created`
|
||||
- Connexion directe agent↔agent sur UDP
|
||||
|
||||
### Contraintes réseau
|
||||
|
||||
- UDP sortant requis.
|
||||
- Certains NAT/CGNAT peuvent compliquer la traversée.
|
||||
|
||||
Stratégie :
|
||||
|
||||
- privilégier LAN
|
||||
- documenter l’ouverture/autorisation UDP côté WAN
|
||||
- fallback exceptionnel : HTTP temporaire via serveur
|
||||
|
||||
---
|
||||
|
||||
## 6. Optimisations transferts
|
||||
|
||||
- chunks 64–256 KB
|
||||
- backpressure
|
||||
- reprise par offset
|
||||
- hashing blake3 (chunk + final)
|
||||
|
||||
---
|
||||
|
||||
## 7. Sécurité
|
||||
|
||||
- Actions gated by capability token TTL court.
|
||||
- QUIC : TLS 1.3.
|
||||
- Terminal : preview-only par défaut, contrôle arbitré par serveur.
|
||||
|
||||
---
|
||||
|
||||
## 8. Diagnostics
|
||||
|
||||
Exposer :
|
||||
|
||||
- WebRTC : host/srflx/relay
|
||||
- QUIC : latence, débit, pertes, retries
|
||||
- erreurs : sessions refusées, tokens expirés
|
||||
|
||||
---
|
||||
|
||||
## 9. Changelog
|
||||
|
||||
```
|
||||
0.1.0 – WebRTC signaling + ICE
|
||||
0.2.0 – QUIC data-plane sessions (agent Rust)
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Consignes de gestion du contexte et des fichiers CLAUDE.md (obligatoire)
|
||||
|
||||
### Utilisation de plusieurs fichiers `CLAUDE.md`
|
||||
|
||||
- Utilisez **plus d’un fichier **`` dans le projet.
|
||||
- Conservez un ``** général à la racine** du dépôt Mesh (vision globale, règles communes).
|
||||
- Ajoutez des ``** spécifiques dans les sous-dossiers** (`server/`, `agent/`, `client/`, `infra/`, etc.) afin de fournir à Claude un **contexte ciblé et local**.
|
||||
- Chaque fichier `CLAUDE.md` de sous-dossier doit compléter le fichier racine, sans le contredire.
|
||||
|
||||
### Gestion de la fenêtre de contexte
|
||||
|
||||
Au fil d’une session longue, la fenêtre de contexte de Claude peut se saturer, entraînant une perte de précision ou l’oubli d’instructions importantes.
|
||||
|
||||
Pour éviter cela, appliquer systématiquement les pratiques suivantes :
|
||||
|
||||
#### 1. Réinitialisation stratégique du contexte
|
||||
|
||||
- Utiliser régulièrement la commande :
|
||||
```
|
||||
/clear
|
||||
```
|
||||
- En particulier :
|
||||
- entre deux tâches distinctes,
|
||||
- entre conception et implémentation,
|
||||
- entre implémentation et revue.
|
||||
|
||||
Cette commande efface l’historique courant et permet de repartir sur une **ardoise vierge**, en s’appuyant uniquement sur les fichiers `CLAUDE.md` pertinents.
|
||||
|
||||
#### 2. Utilisation de sous-agents
|
||||
|
||||
Pour les flux de travail complexes ou multi-étapes, déléguer explicitement à des **sous-agents**.
|
||||
|
||||
Exemple :
|
||||
|
||||
> « Tu viens d’écrire le code du partage de fichiers. Maintenant, utilise un sous-agent pour effectuer une revue de sécurité de ce code. »
|
||||
|
||||
Avantages :
|
||||
|
||||
- séparation claire des responsabilités,
|
||||
- contexte dédié pour chaque analyse,
|
||||
- réduction du bruit dans la conversation principale.
|
||||
|
||||
### Principe fondamental
|
||||
|
||||
Le **contexte de référence du projet Mesh doit toujours être porté par les fichiers **``, et non par l’historique de la conversation. L’historique n’est qu’un support temporaire.
|
||||
|
||||
Ces consignes sont **obligatoires** pour toute utilisation de Claude dans le cadre du projet Mesh.
|
||||
|
||||
265
docs/tooling_precommit_vscode_snippets.md
Normal file
265
docs/tooling_precommit_vscode_snippets.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Tooling Mesh — Pre-commit (headers) + Snippets VS Code
|
||||
|
||||
Ce document fournit :
|
||||
1) un **contrôle automatique** (pre-commit) qui vérifie la présence des headers de traçabilité Mesh
|
||||
2) des **snippets VS Code** pour insérer rapidement les headers
|
||||
|
||||
Référence normative : `docs/headers-template.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1) Pre-commit : vérification des headers
|
||||
|
||||
### 1.1 Fichiers à ajouter
|
||||
|
||||
#### `.pre-commit-config.yaml` (à la racine)
|
||||
```yaml
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: mesh-traceability-headers
|
||||
name: Mesh traceability headers check
|
||||
entry: python3 scripts/check_trace_headers.py
|
||||
language: system
|
||||
types_or: [python, javascript, typescript, rust, yaml, toml, markdown, css, html]
|
||||
pass_filenames: true
|
||||
```
|
||||
|
||||
#### `scripts/check_trace_headers.py`
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# Created by: Claude — 2025-12-29
|
||||
# Purpose: Validate Mesh traceability headers in repo files
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# File extensions we validate
|
||||
VALID_EXTS = {
|
||||
".rs",
|
||||
".py",
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".jsx",
|
||||
".yml",
|
||||
".yaml",
|
||||
".toml",
|
||||
".md",
|
||||
".css",
|
||||
".html",
|
||||
".htm",
|
||||
}
|
||||
|
||||
# For markdown we allow HTML comment headers
|
||||
HEADER_PATTERNS = [
|
||||
re.compile(r"^\s*//\s*Created by:\s*.+$", re.IGNORECASE),
|
||||
re.compile(r"^\s*#\s*Created by:\s*.+$", re.IGNORECASE),
|
||||
re.compile(r"^\s*/\*\s*$", re.IGNORECASE),
|
||||
re.compile(r"^\s*<!--\s*$", re.IGNORECASE),
|
||||
]
|
||||
|
||||
REQUIRED_FIELDS = [
|
||||
re.compile(r"Created by:\s*.+", re.IGNORECASE),
|
||||
re.compile(r"Date:\s*\d{4}-\d{2}-\d{2}", re.IGNORECASE),
|
||||
re.compile(r"Purpose:\s*.+", re.IGNORECASE),
|
||||
]
|
||||
|
||||
|
||||
def is_text_file(path: Path) -> bool:
|
||||
# basic heuristic: try decode as utf-8
|
||||
try:
|
||||
path.read_text(encoding="utf-8")
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def has_required_header(text: str) -> bool:
|
||||
# Check within first 40 lines
|
||||
head = "\n".join(text.splitlines()[:40])
|
||||
if not any(p.search(head) for p in HEADER_PATTERNS):
|
||||
return False
|
||||
return all(p.search(head) for p in REQUIRED_FIELDS)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
files = [Path(a) for a in argv[1:] if a and not a.startswith("-")]
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
bad: list[str] = []
|
||||
|
||||
for f in files:
|
||||
if not f.exists() or f.is_dir():
|
||||
continue
|
||||
if f.suffix.lower() not in VALID_EXTS:
|
||||
continue
|
||||
if not is_text_file(f):
|
||||
continue
|
||||
|
||||
txt = f.read_text(encoding="utf-8", errors="replace")
|
||||
if not has_required_header(txt):
|
||||
bad.append(str(f))
|
||||
|
||||
if bad:
|
||||
sys.stderr.write("\nMesh header check failed. Missing/invalid traceability header in:\n")
|
||||
for b in bad:
|
||||
sys.stderr.write(f"- {b}\n")
|
||||
sys.stderr.write("\nExpected header fields near top of file: Created by, Date (YYYY-MM-DD), Purpose.\n")
|
||||
sys.stderr.write("See: docs/headers-template.md\n\n")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv))
|
||||
```
|
||||
|
||||
#### `scripts/check_trace_headers.py` (permissions)
|
||||
```bash
|
||||
chmod +x scripts/check_trace_headers.py
|
||||
```
|
||||
|
||||
### 1.2 Installation pre-commit
|
||||
```bash
|
||||
pipx install pre-commit # ou pip install pre-commit
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
### 1.3 Notes
|
||||
- Ce hook vérifie uniquement les **headers de création** (Created by / Date / Purpose) dans les premières lignes.
|
||||
- La traçabilité des **modifications ponctuelles** (fin de ligne) reste une règle humaine + revue.
|
||||
- Si tu veux imposer aussi `Modified by` dans certaines conditions, on peut ajouter une règle (par ex. pour fichiers existants dans Git).
|
||||
|
||||
---
|
||||
|
||||
## 2) Snippets VS Code
|
||||
|
||||
### 2.1 Fichier snippets à ajouter
|
||||
Créer : `.vscode/mesh.code-snippets`
|
||||
|
||||
```json
|
||||
{
|
||||
"Mesh Header — Rust": {
|
||||
"prefix": "mesh-header-rs",
|
||||
"body": [
|
||||
"// Created by: ${1:AgentName}",
|
||||
"// Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"// Purpose: ${2:Short description}",
|
||||
"// Refs: ${3:optional}",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for Rust files"
|
||||
},
|
||||
"Mesh Header — Python": {
|
||||
"prefix": "mesh-header-py",
|
||||
"body": [
|
||||
"# Created by: ${1:AgentName}",
|
||||
"# Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"# Purpose: ${2:Short description}",
|
||||
"# Refs: ${3:optional}",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for Python files"
|
||||
},
|
||||
"Mesh Header — TS/JS": {
|
||||
"prefix": "mesh-header-ts",
|
||||
"body": [
|
||||
"// Created by: ${1:AgentName}",
|
||||
"// Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"// Purpose: ${2:Short description}",
|
||||
"// Refs: ${3:optional}",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for TypeScript/JavaScript files"
|
||||
},
|
||||
"Mesh Header — YAML": {
|
||||
"prefix": "mesh-header-yaml",
|
||||
"body": [
|
||||
"# Created by: ${1:AgentName}",
|
||||
"# Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"# Purpose: ${2:Short description}",
|
||||
"# Refs: ${3:optional}",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for YAML files"
|
||||
},
|
||||
"Mesh Header — TOML": {
|
||||
"prefix": "mesh-header-toml",
|
||||
"body": [
|
||||
"# Created by: ${1:AgentName}",
|
||||
"# Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"# Purpose: ${2:Short description}",
|
||||
"# Refs: ${3:optional}",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for TOML files"
|
||||
},
|
||||
"Mesh Header — Markdown": {
|
||||
"prefix": "mesh-header-md",
|
||||
"body": [
|
||||
"<!--",
|
||||
"Created by: ${1:AgentName}",
|
||||
"Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"Purpose: ${2:Short description}",
|
||||
"Refs: ${3:optional}",
|
||||
"-->",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for Markdown files"
|
||||
},
|
||||
"Mesh Header — CSS": {
|
||||
"prefix": "mesh-header-css",
|
||||
"body": [
|
||||
"/*",
|
||||
"Created by: ${1:AgentName}",
|
||||
"Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"Purpose: ${2:Short description}",
|
||||
"Refs: ${3:optional}",
|
||||
"*/",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for CSS files"
|
||||
},
|
||||
"Mesh Header — HTML": {
|
||||
"prefix": "mesh-header-html",
|
||||
"body": [
|
||||
"<!--",
|
||||
"Created by: ${1:AgentName}",
|
||||
"Date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
|
||||
"Purpose: ${2:Short description}",
|
||||
"Refs: ${3:optional}",
|
||||
"-->",
|
||||
""
|
||||
],
|
||||
"description": "Insert Mesh traceability header for HTML files"
|
||||
},
|
||||
"Mesh Modified Tag": {
|
||||
"prefix": "mesh-mod",
|
||||
"body": [
|
||||
"Modified by: ${1:AgentName} — ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} — ${2:reason}"
|
||||
],
|
||||
"description": "Insert a Mesh modified-by tag"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Activation
|
||||
Les snippets dans `.vscode/*.code-snippets` sont automatiquement pris en compte par VS Code.
|
||||
|
||||
Utilisation :
|
||||
- taper `mesh-header-` puis sélectionner le snippet.
|
||||
|
||||
---
|
||||
|
||||
## 3) Recommandation Mesh
|
||||
- Appliquer le hook pre-commit sur tous les développeurs.
|
||||
- Dans Codex, fixer `AgentName` à une valeur stable (ex. `Codex`).
|
||||
- Pour les sous-agents : `SubAgent:SecurityReview`, etc.
|
||||
|
||||
Reference in New Issue
Block a user