Documentation

Plateforme SaaS multimodèle
pour documents, graphe et fichiers

RociaDB permet de structurer des données métier en documents, de relier des entités via un graphe, puis d’associer des fichiers au même contexte. Cette page pose une première base de documentation publique : concepts, intégration, clients, sécurité et bonnes pratiques d’usage.

Vue d’ensemble

Un schéma de lecture rapide pour comprendre comment une application SaaS dialogue avec la plateforme et organise ses données.

Client
Application métier
Script ou batch
Accès
Jeton OAuth2 · scope
authorization: Bearer
Services gRPC
DocumentService
GraphService
FileService
TenantService
Stockage
Documents
Nœuds & arêtes
Chunks + metadata
gRPC

DocumentService

Collections documentaires : écriture, lecture, recherche exacte, requête multi-filtres et énumération.

  • PutDoc
  • GetDoc
  • DeleteDoc
  • FindByField
  • ListDoc
  • QueryDoc
  • ListCollections
gRPC

GraphService

Nœuds, arêtes et traversées paginées, avec des IDs fournis par le client.

  • PutNode
  • GetNode
  • AddEdge
  • DeleteEdge
  • NeighborsOut
  • NeighborsIn
  • ListGraphs
  • ListNodes
streaming

FileService

Fichiers chunkés, métadonnées et visibilité atomique via commit marker.

  • Upload
  • Download
  • Stat
  • Delete
  • ListBuckets
  • ListFiles
Transverse

TenantService

Le seul service qui ne soit pas scopé à un tenant : ListTenants énumère l’ensemble du déploiement. Il est séparé des trois services de modèle pour pouvoir porter sa propre politique d’autorisation sans toucher au chemin des données.

  • ListTenants

Démarrage rapide

Quatre étapes pour passer d’un compte vide à un premier document écrit, relu, puis à un parcours d’exploration complet.

1

Obtenir un jeton

Un échange OAuth2 client_credentials contre le fournisseur d’identité. Le jeton vaut 600 secondes.

curl -X POST https://<votre-idp>/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=<client_id>" \
  -d "client_secret=<client_secret>"

# { "access_token": "...", "token_type": "Bearer", "expires_in": 600 }
2

Écrire un document

Le jeton voyage en métadonnée gRPC. Le champ json est du JSON UTF-8 sérialisé, donc encodé en base64 par la transcription JSON de grpcurl.

# {"status":"active"} encodé en base64
grpcurl -H "authorization: Bearer $TOKEN" \
  -proto rocia/v1/rocia.proto \
  -d '{
        "tenant_id": "tenant-a",
        "collection": "users",
        "id": "user-123",
        "json": "eyJzdGF0dXMiOiJhY3RpdmUifQ=="
      }' \
  <hôte>:<port> rocia.v1.DocumentService/PutDoc
3

Relire pour valider l’accès

GetDoc rend le même JSON sérialisé. Un UNAUTHENTICATED ici signifie simplement que le jeton a expiré.

grpcurl -H "authorization: Bearer $TOKEN" \
  -proto rocia/v1/rocia.proto \
  -d '{
        "tenant_id": "tenant-a",
        "collection": "users",
        "id": "user-123"
      }' \
  <hôte>:<port> rocia.v1.DocumentService/GetDoc
4

Explorer sans rien écrire

Les RPC d’énumération suffisent à parcourir un tenant de bout en bout. Un client en scope read peut faire tout ce parcours.

# Documents
ListCollections → ListDoc → GetDoc

# Graphe
ListGraphs → ListNodes → GetNode → NeighborsOut

# Fichiers
ListBuckets → ListFiles → Stat → Download

Modèles de données

La plateforme sépare volontairement document, graphe et fichier. Leur combinaison est explicite et portée par le client.

Collection + id

Document

  • JSON UTF-8 sérialisé, transporté en bytes
  • tenant_id + collection + id
  • ListDoc, FindByField, QueryDoc, ListCollections

Quand un objet doit aussi être traversé dans le graphe, le client crée également un nœud correspondant.

Node + edge

Graphe

  • IDs fournis par le client
  • AddEdge échoue si une extrémité manque
  • NeighborsIn / NeighborsOut paginés

Il n’y a pas de suppression de nœud exposée dans le proto v1. Le nettoyage des arêtes reste explicite côté client.

Bucket + file_id

Fichier

  • Upload en streaming client
  • Chunks de 1 MiB au plus
  • Métadonnées : size_bytes, content_type, checksum

Un fichier n’est visible qu’après écriture du commit final. Les uploads partiels orphelins sont éligibles au GC.

Les champs JSON sont des bytes

json et values_json ne sont pas des chaînes : ce sont des bytes portant du JSON UTF-8 sérialisé. Le client encode avant l’envoi et décode à la réception.

Quatre services : trois orientés modèle — document, graphe, fichier — et un service transverse aux tenants. Tous partagent les mêmes conventions de pagination, d’idempotence et d’erreur.

Documents

Écriture, lecture, suppression, recherche et énumération des collections, avec pagination et total_count.

PutDoc · GetDoc · DeleteDoc · FindByField · ListDoc · QueryDoc · ListCollections

Graphes

Nœuds et arêtes, traversée paginée dans les deux sens, énumération des graphes et de leurs nœuds.

PutNode · GetNode · AddEdge · DeleteEdge · NeighborsOut · NeighborsIn · ListGraphs · ListNodes

Fichiers

Upload et téléchargement streamés, métadonnées, suppression, énumération des buckets et des fichiers.

Upload · Download · Stat · Delete · ListBuckets · ListFiles

Pagination

PageRequest(limit, cursor) avec limit de 1 à 200, 50 par défaut. La réponse rend un next_cursor opaque.

curseur de session
QueryDoc v1

Requête documentaire avancée

QueryDoc complète ListDoc et FindByField pour les cas où plusieurs filtres, un tri stable et une pagination sur résultat filtré sont nécessaires.

Opérateurs

EQ, IN et CONTAINS, combinés en AND.

Tri

Tri explicite multi-champs, ASC ou DESC, avec tie-breaker stable id asc si absent.

Curseur

Opaque, à rejouer tel quel — mais valable le temps d’une session de pagination, pas à conserver entre deux visites.

Total count

Le serveur renvoie total_count avant pagination.

QueryDocRequest {
  tenant_id: "tenant-a",
  collection: "users",
  filters: [
    {
      field: "organization_id",
      operator: QUERY_OPERATOR_EQ,
      values_json: ["\"org-1\""]
    },
    {
      field: "status",
      operator: QUERY_OPERATOR_IN,
      values_json: ["\"active\"", "\"draft\""]
    },
    {
      field: "name",
      operator: QUERY_OPERATOR_CONTAINS,
      values_json: ["\"prod\""]
    }
  ],
  sort: [
    { field: "name", direction: SORT_DIRECTION_ASC }
  ],
  page: { limit: 50, cursor: "" }
}

Erreurs

Chaque statut gRPC porte une métadonnée reason qui précise la cause. C’est sur elle qu’un client branche sa logique de reprise, pas sur le seul code.

Code gRPCreasonQuand
INVALID_ARGUMENTinvalid_argumentChamp manquant, limit hors bornes, curseur illisible, JSON invalide
NOT_FOUNDnot_foundDocument, nœud, arête ou fichier absent
ALREADY_EXISTSalready_existsConflit d’unicité
PERMISSION_DENIEDpermission_deniedÉcriture demandée par un client en lecture seule
UNAUTHENTICATEDunauthenticatedJeton absent, expiré, mal signé ou d’un autre émetteur
INTERNALinternalDéfaillance du stockage

Sécurité, environnements et exploitation

Cette section rassemble les éléments publics utiles pour exploiter la plateforme proprement dans un contexte SaaS, sans entrer dans les détails internes d’implémentation.

Jeton OAuth2

Échange client_credentials contre le fournisseur d’identité, puis authorization: Bearer <jwt> en métadonnée gRPC.

Claims

Requis : iss, sub, exp. Le jeton porte aussi iat, scope et cv. Le tenant_id voyage dans la requête, pas dans le jeton.

Signature

ES256, kid obligatoire dans l’en-tête, résolu contre un JWKS mis en cache — le fournisseur d’identité reste hors du chemin de requête.

Émetteur

L’issuer est épinglé côté service : un jeton valide émis ailleurs est refusé.

Scopes

read, read write et admin. Une écriture demandée en lecture seule répond PERMISSION_DENIED. admin ne contient pas write.

Durée de vie

600 secondes. Renouvelez avant l’échéance, ou dès qu’un appel répond UNAUTHENTICATED.

Tenant isolation

Le tenant_id fait partie du contrat applicatif et structure l’isolation des données.

Secrets

Le client_secret vit dans le backend appelant. Il ne doit jamais atteindre un navigateur ni un dépôt de code.

Bonnes pratiques

01
Limiter le nombre de requêtes

Regrouper les appels et paginer plutôt qu’enchaîner des requêtes unitaires évite de saturer les quotas et la latence côté client.

02
Préférer ListDoc à QueryDoc

Sans filtre à appliquer, ListDoc coûte le prix de la page demandée et son total_count est gratuit. QueryDoc, lui, doit charger tous les candidats pour le calculer — ne bouclez donc jamais dessus pour compter.

03
Rejouer sans dupliquer

Les sept RPC d’écriture acceptent un request_id : un rejeu portant le même identifiant et la même opération est ignoré. Les marqueurs expirent au bout de 24 h.

04
Faire tourner les comptes de service

La rotation se fait côté fournisseur d’identité et incrémente le cv du client. Elle prend effet aussitôt sur l’administration, et sous 10 minutes sur le plan de données — le temps qu’expirent les jetons déjà émis.

05
Donner le scope juste

Un service qui ne fait que lire reçoit read. Réserver read write aux appelants qui écrivent vraiment limite la portée d’un secret qui fuite.

06
Attention à la surcharge du client

Éviter d’empiler des couches de code applicatif au-dessus du client généré : cela alourdit la maintenance et masque le comportement réel des appels.

07
Préférer les pull requests

Si un besoin n’est pas couvert, ouvrir une pull request plutôt que de surcharger son propre code : les dépôts sont publics et les PR sont examinées régulièrement.

08
Utiliser la sauvegarde & restauration intégrées

S’appuyer sur les mécanismes de sauvegarde et de restauration fournis par la plateforme plutôt que de reconstruire son propre outillage à coups d’appels API.

Performances & limites

Les plafonds ci-dessous sont appliqués à la frontière de l’API. Pour les capacités et les tarifs par nœud, voir les tarifs publics, qui font seuls autorité sur la grille en vigueur.

Valider

Environnement de test

Destiné à l’expérimentation, la validation d’un modèle de données ou d’une intégration. Pensé pour itérer avant de passer en exploitation, sans exigence de continuité de service.

Opérer

Environnement de production

Destiné à l’exploitation réelle, avec des exigences plus élevées de continuité et de supervision. Le multi-node y répond à des besoins de résilience et de montée en charge.

PlafondValeurPortée
Taille d’une page1 à 200limit hors bornes ⇒ INVALID_ARGUMENT ; 50 par défaut
Document JSON2 MioPar document, sur PutDoc et PutNode
Fichier5 GioTaille totale déclarée à l’upload
Chunk d’upload1 MioTaille maximale d’un message du flux Upload
Durée d’un jeton600 sRenouvellement à la charge du client
Marqueur d’idempotence24 hAu-delà, un rejeu de request_id est réexécuté

Débit vs latence, par taille de nœud

valeurs indicatives

Tendance illustrative du point où la latence p95 se dégrade selon le débit. Ces courbes ne sont pas des mesures : elles montrent la forme du décrochage, à affiner avec de vrais relevés.

seuil p95 · 50 ms05001000150020002500 requêtes / seconde 0100200 ms~560 req/s
Petit nœud · 1 CPU / 2 Go
Nœud moyen · 2 CPU / 4 Go
Grand nœud · 4 CPU / 8 Go

Un nœud, une capacité complète

Chaque nœud dispose de ses propres CPU, mémoire et stockage. La topologie retenue multiplie donc la capacité — et le coût.

Single-node → multi-node

La plupart des projets démarrent en single-node, puis évoluent vers du multi-node quand la disponibilité ou la charge deviennent critiques.

Support

Le support standard est inclus par défaut. Le support premium accompagne les architectures multi-node lorsque la disponibilité devient critique.

Monitoring, logs et alertes

Métriques, logs et alertes sont consultables directement depuis le tableau de bord de chaque environnement, sans intégration externe à configurer.

Métriques exposées

Servies au format Prometheus sur un écouteur dédié. Les quatre premières familles couvrent le trafic et les flux ; les quatre suivantes instrumentent QueryDoc, dont le coût dépend de la requête écrite par le client.

  • rocia_request_total
  • rocia_request_latency_ms
  • rocia_index_scans_total
  • rocia_stream_bytes_total
  • rocia_query_doc_total
  • rocia_query_doc_filter_count
  • rocia_query_doc_sort_count
  • rocia_query_doc_total_count

Logs corrélés

Chaque appel produit une ligne structurée, qui relie une erreur, une requête, un tenant et le client authentifié qui l’a émise.

  • tenant_id
  • request_id
  • client_id
  • service
  • method
  • status
  • latency_ms

Règles d’alerte

Des règles évaluées en continu déclenchent des alertes que le tableau de bord présente comme actives tant qu’elles ne sont pas retombées, avec l’historique de leurs déclenchements.

Canaux de notification

Les alertes sont routées vers des canaux configurables, testables avant mise en service pour vérifier qu’un destinataire reçoit bien ce qu’on lui adresse.

Ce que ça permet côté SaaS

Construire des tableaux de bord d’usage, suivre les SLO, identifier les régressions et investiguer plus vite sans exposer les détails d’implémentation aux clients finaux.

Clients & génération de code

Aucun SDK n’est publié à ce jour. Le fichier .proto est la surface de référence : chaque langage y génère son client avec son outillage standard, et un SDK publié plus tard n’en changera pas le contrat. Les commandes ci-dessous supposent le fichier rangé en proto/rocia/v1/rocia.proto.

Intégration applicative

Python

grpcio-tools

python -m grpc_tools.protoc \
  -I proto \
  --python_out=. \
  --grpc_python_out=. \
  proto/rocia/v1/rocia.proto
API / backend

TypeScript

buf + ts-proto

protoc \
  -I proto \
  --plugin=protoc-gen-ts_proto \
  --ts_proto_out=src/generated \
  --ts_proto_opt=outputServices=grpc-js \
  proto/rocia/v1/rocia.proto
Services critiques

Rust

tonic-build

// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::configure()
        .build_server(false)
        .compile_protos(
            &["proto/rocia/v1/rocia.proto"],
            &["proto"],
        )?;
    Ok(())
}

Questions fréquentes

Les points qui structurent aujourd’hui l’usage réel de la plateforme.

Comment relier un document et un nœud graphe ?

Les stores restent séparés au niveau du proto. Si un objet doit exister dans les deux modèles, le client écrit le document puis le nœud, en partageant l’identifiant ou en portant un lien explicite dans le JSON.

Est-ce que la plateforme synchronise automatiquement document et graphe ?

Non en v1. La cohérence entre les représentations est à la charge du client ou du workflow applicatif.

Quels opérateurs QueryDoc sont disponibles ?

EQ, IN et CONTAINS, combinés en AND, avec un tri multi-champs. Les opérateurs numériques et de plage, ainsi que le OR, restent hors périmètre à ce stade.

Pourquoi mon écriture répond-elle PERMISSION_DENIED ?

Le client appelle avec un scope read. Seul un scope read write autorise PutDoc, DeleteDoc, PutNode, AddEdge, DeleteEdge, Upload et Delete.

Existe-t-il des SDK prêts à installer ?

Pas encore. Le proto est la surface de référence, et chaque langage génère son client à partir de lui. Les SDK publiés viendront ensuite, sans changer cette surface.