API Desktop Agent
Endpoints pour le client desktop ConformVault — registration, heartbeat, sync, SSE, conflict reporting
API Desktop Agent
Disponible depuis filesecure v3.29.0 (avril 2026). Authentification par token de device (
X-Device-Token).
L'API Desktop Agent permet au client ConformVault Desktop (Tauri) de synchroniser un dossier local avec le serveur. Elle est distincte de la Developer API (clé API publique) et de l'API interne (JWT user) — elle utilise un token long-vie scopé à un device, créé une seule fois lors de l'enregistrement.
Vue d'ensemble du flow
1. POST /api/v1/agent/register [JWT user] → device_token (cvdt_…)
2. POST /api/v1/agent/heartbeat [X-Device-Token] → ping périodique (60s)
3. GET /api/v1/agent/config [X-Device-Token] → paramètres sync (interval, chunk size)
4. GET /api/v1/files/delta [X-Device-Token] → polling incrémental
5. GET /api/v1/events/stream [X-Device-Token] → SSE push temps réel
6. POST /api/v1/files/upload-init [X-Device-Token] → resumable upload : init
PUT /api/v1/files/upload-part [X-Device-Token] → resumable upload : chunk
POST /api/v1/files/upload-complete [X-Device-Token] → resumable upload : finalisation
7. POST /api/v1/sync/conflict [X-Device-Token] → reporter un conflit localRegistration
POST /api/v1/agent/register
Enregistre un nouveau device pour l'utilisateur courant et retourne un device_token long-vie.
Authentification : Authorization: Bearer <jwt-user>
Corps de requête :
{
"device_name": "Olivier-MacBook",
"platform": "darwin",
"arch": "arm64",
"app_version": "0.5.1",
"capabilities": {
"sync": true,
"resumable_upload": true,
"sse": true,
"max_chunk_mb": 10
}
}| Champ | Type | Description |
|---|---|---|
device_name | string | Nom lisible du device (max 120 chars). Pré-rempli avec le hostname. |
platform | string | Une de : darwin, windows, linux. |
arch | string | Architecture CPU (x86_64, aarch64, etc.). |
app_version | string | Version du client desktop. |
capabilities | object (optionnel) | Capacités déclarées par le client. Stocké tel quel en JSONB côté serveur. |
Réponse 201 :
{
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"device_token": "cvdt_a1b2c3d4e5f6...",
"expires_at": null,
"created_at": "2026-04-28T10:00:00Z"
}Important : Le device_token est retourné une seule fois. Le serveur stocke uniquement son hash SHA-256 et ne peut pas le récupérer. Le client doit le persister immédiatement (OS keychain recommandé).
Erreurs :
400 Bad Request—device_namemanquant ou plateforme invalide401 Unauthorized— JWT invalide ou expiré
Heartbeat
POST /api/v1/agent/heartbeat
Met à jour le last_seen_at du device. À appeler toutes les 60 secondes par défaut (configurable via agent/config).
Authentification : X-Device-Token: cvdt_…
Corps de requête :
{
"app_version": "0.5.1",
"sync_status": "idle"
}Réponse 200 :
{
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"last_seen_at": "2026-04-28T10:01:00Z",
"paused": false,
"server_time": "2026-04-28T10:01:01Z"
}Erreurs :
401 Unauthorized— Token de device invalide ou révoqué. Le client doit re-register.
Configuration distante
GET /api/v1/agent/config
Retourne les paramètres de sync poussés par le serveur (l'admin peut les ajuster par device).
Authentification : X-Device-Token: cvdt_…
Réponse 200 :
{
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"paused": false,
"selective_sync_paths": ["/Documents", "/Projects"],
"bandwidth_cap_kbps": 8192,
"sync_interval_secs": 30,
"max_chunk_mb": 10
}| Champ | Description |
|---|---|
paused | Sync globalement en pause pour ce device. |
selective_sync_paths | Chemins relatifs à synchroniser uniquement (vide = tout). |
bandwidth_cap_kbps | Plafond upload/download en kilobits/s (0 = illimité). |
sync_interval_secs | Cadence du polling delta (min 5s, défaut 30s). |
max_chunk_mb | Taille de chunk recommandée pour les resumable uploads. |
Delta sync
GET /api/v1/clients/:client_id/files/delta
Retourne les fichiers ajoutés / modifiés / supprimés depuis un timestamp donné, avec pagination keyset.
Authentification : X-Device-Token: cvdt_…
Paramètres query :
| Paramètre | Type | Description |
|---|---|---|
since | string (RFC3339) | Timestamp seuil (requis). |
cursor | string (optionnel) | Curseur opaque pour la page suivante. |
limit | int | Nombre max d'éléments (défaut 100, max 200). |
include_deleted | bool | Inclure les fichiers supprimés (défaut true). |
Réponse 200 :
{
"files": [
{
"id": "uuid",
"name": "rapport.pdf",
"size": 1048576,
"encrypted_size": 1049000,
"mime_type": "application/pdf",
"version": 3,
"checksum": "sha256:…",
"folder_id": "uuid",
"encrypted_metadata": "base64",
"deleted_at": null,
"updated_at": "2026-04-28T10:00:00Z",
"created_at": "2026-04-15T08:00:00Z"
}
],
"next_cursor": "opaque-cursor-or-null",
"has_more": true,
"fetched_at": "2026-04-28T10:01:00Z"
}Le client doit suivre le next_cursor jusqu'à has_more=false, puis utiliser fetched_at comme nouveau since au prochain round.
SSE — Stream d'événements temps réel
GET /api/v1/events/stream
Flux Server-Sent Events qui pousse les changements en temps réel (alternative au polling pour les clients connectés).
Authentification : X-Device-Token: cvdt_…
Headers :
Accept: text/event-streamLast-Event-ID: <id>(optionnel, pour reprendre après déconnexion)
Format des événements :
event: file.created
data: {"file_id":"uuid","client_id":"uuid","name":"a.pdf","version":1}
id: 42
event: file.updated
data: {"file_id":"uuid","client_id":"uuid","version":3}
id: 43
event: file.deleted
data: {"file_id":"uuid","client_id":"uuid"}
id: 44
event: space.activity
data: {"space_id":"uuid","action":"document_uploaded","actor_id":"uuid"}
id: 45Reconnexion : Le client doit reconnecter avec backoff exponentiel borné (1s → 60s) en cas de coupure, en envoyant Last-Event-ID pour reprendre.
Erreurs :
401— Token invalide → re-register503— Service en surcharge (rare)
Resumable upload (3 étapes)
POST /api/v1/clients/:client_id/files/upload-init
Crée une session d'upload et retourne le plan de chunks.
Corps :
{
"file_name": "rapport.pdf",
"total_size": 52428800,
"chunk_size": 5242880,
"folder_id": "uuid (optionnel)",
"mime_type": "application/pdf",
"checksum": "sha256:…"
}Réponse 200 :
{
"session_id": "uuid",
"chunk_size": 5242880,
"total_chunks": 10
}Le serveur peut renvoyer un chunk_size différent de la requête (clamp à max_chunk_mb).
PUT /api/v1/clients/:client_id/files/upload-part
Upload un chunk individuel.
Query params :
session_id: UUID retourné parupload-initpart: index du chunk, 0-based
Headers : Content-Type: application/octet-stream
Corps : bytes du chunk (taille = chunk_size, sauf le dernier chunk qui peut être plus petit).
Réponse 200 : {"part": 0}
Stratégie de retry : 5xx et 429 → backoff exponentiel jitter (cap 30s, max 5 retries). 4xx (autre que 429) → abort immédiat.
POST /api/v1/clients/:client_id/files/upload-complete
Finalise l'upload et crée l'enregistrement fichier.
Corps :
{
"session_id": "uuid",
"encrypted_file_key": "base64 (optionnel)",
"checksum": "sha256:… (optionnel)"
}Réponse 200 : payload du fichier créé (incluant id).
Conflict reporting
POST /api/v1/clients/:client_id/sync/conflict
Notifie le serveur qu'un conflit a été détecté localement et résolu (server-wins par défaut côté client).
Corps :
{
"file_id": "uuid",
"conflict_type": "edit_edit",
"local_version": "v3-local",
"server_version": "v3-server",
"device_id": "uuid"
}Types de conflit :
edit_edit— modifié des deux côtésdelete_edit— supprimé localement, modifié serveuredit_delete— modifié localement, supprimé serveur
Réponse 200 : {"id": "uuid", "ok": true, "recorded_at": "..."}
Le serveur ne tranche pas — il enregistre la résolution dans le journal d'audit + l'activité du space.
SDKs officiels
Les trois SDKs implémentent ces endpoints :
| SDK | Module | Version min |
|---|---|---|
| Go | github.com/secuaas/conformvault-sdk-go/agent | v0.7.0 |
| Python | conformvault.agent + conformvault.sync | v0.8.0 |
| TypeScript | @secuaas/conformvault/agent | v2.5.0 |
Exemple Go : examples/desktop-sync/main.go du repo conformvault-sdk-go.
Exemple Python : examples/desktop_sync.py du repo conformvault-sdk-python.
Sécurité
- Le
device_tokenn'est jamais journalisé en clair côté serveur — seul son hash SHA-256 est stocké. - Un device peut être révoqué via
DELETE /api/v1/devices/:id(interface utilisateur dans Settings → Devices). - Le
X-Device-Tokenn'autorise que les endpoints/api/v1/agent/*,/api/v1/files/delta,/api/v1/files/upload-*,/api/v1/events/stream,/api/v1/sync/conflict. Les opérations sensibles (admin, users, billing) restent JWT-only. - Toutes les communications sont en TLS 1.2+ (cert Let's Encrypt via cert-manager k8s).
Observabilité
Les métriques Prometheus sont exposées sur /metrics :
conformvault_agent_register_total— compteur de registrationsconformvault_agent_heartbeat_total{device_id}— heartbeats par deviceconformvault_files_delta_duration_seconds— histogramme du temps de réponseconformvault_events_stream_clients— gauge des consumers SSE actifsconformvault_upload_chunk_duration_seconds— histogramme par chunkconformvault_sync_conflicts_total{conflict_type}— compteur de conflits par type
Le dashboard Grafana est disponible à https://grafana.secuaas.ovh/d/conformvault-api.