Sprint 2 : moteur de documents + sanctuaire

Backend:
- CRUD complet documents/items/versions (update, delete, accept, reject, reorder)
- Service IPFS (upload/retrieve/pin via kubo HTTP API)
- Service sanctuaire : pipeline SHA-256 + IPFS + on-chain (system.remark)
- Verification integrite des entrees sanctuaire
- Recherche par reference (document -> entrees sanctuaire)
- Serialisation deterministe des documents pour archivage
- 14 tests unitaires supplementaires (document service)

Frontend:
- 9 composants : StatusBadge, MarkdownRenderer, DiffView, ItemCard,
  ItemVersionDiff, DocumentList, SanctuaryEntry, IPFSLink, ChainAnchor
- Page detail item avec historique des versions et diff
- Page detail sanctuaire avec verification integrite
- Modal de creation de document + proposition de version
- Archivage document vers sanctuaire depuis la page detail

Documentation:
- API reference mise a jour (9 nouveaux endpoints)
- Guides utilisateur documents et sanctuaire enrichis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-28 13:08:48 +01:00
parent 25437f24e3
commit 2bdc731639
26 changed files with 3452 additions and 397 deletions

View File

@@ -27,7 +27,14 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
| POST | `/{slug}/items` | Ajouter un item au document | Oui |
| GET | `/{slug}/items` | Lister les items d'un document | Non |
| GET | `/{slug}/items/{item_id}` | Obtenir un item avec son historique | Non |
| PUT | `/{slug}/items/{item_id}` | Mettre a jour un item (titre, texte, position, type) | Oui |
| DELETE | `/{slug}/items/{item_id}` | Supprimer un item du document | Oui |
| POST | `/{slug}/items/{item_id}/versions` | Proposer une nouvelle version d'un item | Oui |
| GET | `/{slug}/items/{item_id}/versions` | Lister les versions d'un item | Non |
| PUT | `/{slug}/items/{item_id}/versions/{version_id}/accept` | Accepter une version proposee (applique le texte a l'item) | Oui |
| PUT | `/{slug}/items/{item_id}/versions/{version_id}/reject` | Rejeter une version proposee | Oui |
| PUT | `/{slug}/items/reorder` | Reordonner les items d'un document | Oui |
| POST | `/{slug}/archive` | Archiver le document dans le sanctuaire (hash SHA-256 + IPFS + on-chain) | Oui |
## Decisions (`/api/v1/decisions`)
@@ -73,11 +80,13 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
## Sanctuaire (`/api/v1/sanctuary`)
| Methode | Endpoint | Description | Auth |
| ------- | --------- | ---------------------------------------------------------- | ---- |
| GET | `/` | Lister les entrees du sanctuaire (filtre: entry_type) | Non |
| GET | `/{id}` | Obtenir une entree du sanctuaire | Non |
| POST | `/` | Creer une entree (hash SHA-256, CID IPFS, TX on-chain) | Oui |
| Methode | Endpoint | Description | Auth |
| ------- | --------------------------------- | ---------------------------------------------------------- | ---- |
| GET | `/` | Lister les entrees du sanctuaire (filtre: entry_type) | Non |
| GET | `/{id}` | Obtenir une entree du sanctuaire | Non |
| POST | `/` | Creer une entree (hash SHA-256, CID IPFS, TX on-chain) | Oui |
| GET | `/{id}/verify` | Verifier l'integrite d'une entree (recalcul SHA-256, verification IPFS et on-chain) | Non |
| GET | `/by-reference/{reference_id}` | Obtenir les entrees liees a une entite source par son UUID | Non |
## WebSocket (`/api/v1/ws`)
@@ -91,6 +100,127 @@ Tous les endpoints sont prefixes par `/api/v1`. L'API est auto-documentee via Op
| ------- | -------------- | -------------------------- |
| GET | `/api/health` | Verification de sante (hors versionning) |
## Details des endpoints Sprint 2
### `PUT /api/v1/documents/{slug}/items/{item_id}` -- Mettre a jour un item
Met a jour les champs d'un item existant (titre, texte courant, position, type). Seuls les champs fournis sont mis a jour (mise a jour partielle).
**Corps de la requete** (tous les champs sont optionnels) :
```json
{
"title": "Nouveau titre",
"current_text": "Texte mis a jour...",
"position": "2.1",
"item_type": "rule"
}
```
**Reponse** : `200 OK` avec l'item mis a jour (`DocumentItemOut`).
---
### `DELETE /api/v1/documents/{slug}/items/{item_id}` -- Supprimer un item
Supprime un item d'un document. La suppression est en cascade : toutes les versions associees sont egalement supprimees.
**Reponse** : `204 No Content`.
---
### `GET /api/v1/documents/{slug}/items/{item_id}/versions` -- Lister les versions d'un item
Retourne l'historique complet des versions proposees pour un item, ordonne par date de creation decroissante.
**Parametres de requete** : `skip`, `limit` (pagination standard).
**Reponse** : `200 OK` avec une liste de `ItemVersionOut`.
---
### `PUT /api/v1/documents/{slug}/items/{item_id}/versions/{version_id}/accept` -- Accepter une version
Accepte une version proposee. Le texte propose remplace le texte courant de l'item. Toutes les autres versions en statut `proposed` ou `voting` pour cet item sont automatiquement rejetees.
**Reponse** : `200 OK` avec la version mise a jour (`ItemVersionOut`, statut `accepted`).
---
### `PUT /api/v1/documents/{slug}/items/{item_id}/versions/{version_id}/reject` -- Rejeter une version
Rejette une version proposee. Le texte courant de l'item reste inchange.
**Reponse** : `200 OK` avec la version mise a jour (`ItemVersionOut`, statut `rejected`).
---
### `PUT /api/v1/documents/{slug}/items/reorder` -- Reordonner les items
Modifie l'ordre d'affichage des items dans un document en mettant a jour le champ `sort_order` de chaque item.
**Corps de la requete** :
```json
{
"items": [
{ "item_id": "uuid-1", "sort_order": 0 },
{ "item_id": "uuid-2", "sort_order": 1 },
{ "item_id": "uuid-3", "sort_order": 2 }
]
}
```
**Reponse** : `200 OK` avec la liste des items reordonnes.
---
### `POST /api/v1/documents/{slug}/archive` -- Archiver un document
Archive le document complet dans le sanctuaire. Le processus :
1. Le contenu integral du document (metadonnees + items) est serialise.
2. Un hash SHA-256 est calcule sur le contenu.
3. Le contenu est envoye sur IPFS (CID retourne).
4. Le hash est ancre on-chain via `system.remark` sur Duniter V2.
5. Une entree `sanctuary_entries` est creee avec les references.
6. Le statut du document passe a `archived` et les champs `ipfs_cid` et `chain_anchor` sont mis a jour.
**Reponse** : `200 OK` avec le document mis a jour incluant `ipfs_cid` et `chain_anchor`.
---
### `GET /api/v1/sanctuary/{id}/verify` -- Verifier l'integrite d'une entree
Verifie l'integrite d'une entree du sanctuaire en effectuant trois controles :
1. **Hash SHA-256** : recalcul du hash a partir du contenu source et comparaison avec `content_hash`.
2. **IPFS** : verification que le CID IPFS pointe vers un contenu valide (si disponible).
3. **On-chain** : verification que le hash est present dans le `system.remark` du bloc reference (si disponible).
**Reponse** :
```json
{
"entry_id": "uuid",
"hash_valid": true,
"ipfs_valid": true,
"chain_valid": true,
"verified_at": "2026-02-28T12:00:00Z",
"details": "Tous les controles sont valides."
}
```
---
### `GET /api/v1/sanctuary/by-reference/{reference_id}` -- Entrees par reference
Retourne toutes les entrees du sanctuaire liees a une entite source (document, decision ou session de vote) identifiee par son UUID.
**Parametres de requete** : `skip`, `limit` (pagination standard).
**Reponse** : `200 OK` avec une liste de `SanctuaryEntryOut`.
## Pagination
Les endpoints de liste acceptent les parametres `skip` (offset, defaut 0) et `limit` (max 200, defaut 50).

View File

@@ -38,7 +38,7 @@ Sessions d'authentification (tokens).
### `documents`
Documents de reference modulaires.
Documents de reference modulaires. Le cycle de vie d'un document suit les statuts `draft` -> `active` -> `archived`. Lors de l'archivage (Sprint 2), les champs `ipfs_cid` et `chain_anchor` sont renseignes automatiquement par le service sanctuaire.
| Colonne | Type | Description |
| ------------ | ------------ | ----------------------------------------------------- |
@@ -49,38 +49,38 @@ Documents de reference modulaires.
| version | VARCHAR(32) | Version semantique (defaut "0.1.0") |
| status | VARCHAR(32) | Statut : draft, active, archived |
| description | TEXT | Description du document |
| ipfs_cid | VARCHAR(128) | CID IPFS de la derniere version archivee |
| chain_anchor | VARCHAR(128) | Hash de transaction on-chain |
| ipfs_cid | VARCHAR(128) | CID IPFS de la derniere version archivee (renseigne lors de l'archivage) |
| chain_anchor | VARCHAR(128) | Hash de transaction on-chain (renseigne lors de l'archivage) |
| created_at | TIMESTAMPTZ | Date de creation |
| updated_at | TIMESTAMPTZ | Date de derniere mise a jour |
### `document_items`
Items individuels composant un document (clauses, regles, verifications, etc.).
Items individuels composant un document (clauses, regles, verifications, etc.). Chaque item peut etre modifie, supprime ou reordonne individuellement (Sprint 2). Le champ `current_text` est mis a jour automatiquement lorsqu'une version est acceptee.
| Colonne | Type | Description |
| ------------------- | ------------ | ------------------------------------------------- |
| id | UUID (PK) | Identifiant unique |
| document_id | UUID (FK) | -> documents.id |
| document_id | UUID (FK) | -> documents.id (cascade delete) |
| position | VARCHAR(16) | Numero de position ("1", "1.1", "3.2") |
| item_type | VARCHAR(32) | Type : clause, rule, verification, preamble, section |
| title | VARCHAR(256) | Titre de l'item |
| current_text | TEXT | Texte courant de l'item |
| current_text | TEXT | Texte courant de l'item (mis a jour lors de l'acceptation d'une version) |
| voting_protocol_id | UUID (FK) | -> voting_protocols.id (protocole specifique) |
| sort_order | INTEGER | Ordre de tri |
| sort_order | INTEGER | Ordre de tri (modifiable via endpoint reorder) |
| created_at | TIMESTAMPTZ | Date de creation |
| updated_at | TIMESTAMPTZ | Date de derniere mise a jour |
### `item_versions`
Historique des versions proposees pour chaque item.
Historique des versions proposees pour chaque item. Lors de l'acceptation d'une version (Sprint 2), le `current_text` de l'item parent est remplace par le `proposed_text` de la version, et toutes les autres versions en attente (`proposed`, `voting`) sont automatiquement rejetees.
| Colonne | Type | Description |
| -------------- | ------------ | ------------------------------------------------------ |
| id | UUID (PK) | Identifiant unique |
| item_id | UUID (FK) | -> document_items.id |
| item_id | UUID (FK) | -> document_items.id (cascade delete) |
| proposed_text | TEXT | Texte propose |
| diff_text | TEXT | Diff unifie entre texte courant et propose |
| diff_text | TEXT | Diff unifie entre texte courant et propose (genere automatiquement) |
| rationale | TEXT | Justification de la modification |
| status | VARCHAR(32) | Statut : proposed, voting, accepted, rejected |
| decision_id | UUID (FK) | -> decisions.id (decision associee) |
@@ -240,16 +240,16 @@ Configurations de formules de seuil WoT.
### `sanctuary_entries`
Entrees du sanctuaire (archivage immuable).
Entrees du sanctuaire (archivage immuable). Le champ `reference_id` permet de retrouver toutes les entrees liees a un document, une decision ou une session de vote via l'endpoint `/by-reference/{reference_id}` (Sprint 2). L'endpoint `/verify` recalcule le hash et verifie la coherence IPFS/on-chain.
| Colonne | Type | Description |
| -------------- | ------------ | ------------------------------------------ |
| id | UUID (PK) | Identifiant unique |
| entry_type | VARCHAR(64) | Type : document, decision, vote_result |
| reference_id | UUID | UUID de l'entite source |
| reference_id | UUID | UUID de l'entite source (indexe pour recherche par reference) |
| title | VARCHAR(256) | Titre |
| content_hash | VARCHAR(128) | Hash SHA-256 du contenu |
| ipfs_cid | VARCHAR(128) | CID IPFS |
| ipfs_cid | VARCHAR(128) | CID IPFS (renseigne lors de l'upload) |
| chain_tx_hash | VARCHAR(128) | Hash de la transaction on-chain |
| chain_block | INTEGER | Numero de bloc de la transaction |
| metadata_json | TEXT | Metadonnees JSON supplementaires |
@@ -279,9 +279,10 @@ duniter_identities
documents
|-- 1:N --> document_items
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
document_items
|-- 1:N --> item_versions
|-- 1:N --> item_versions (cascade delete)
|-- N:1 --> voting_protocols
item_versions
@@ -289,6 +290,7 @@ item_versions
decisions
|-- 1:N --> decision_steps
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
decision_steps
|-- N:1 --> vote_sessions
@@ -296,6 +298,7 @@ decision_steps
vote_sessions
|-- 1:N --> votes
|-- N:1 --> voting_protocols
|-- 1:N ..> sanctuary_entries (via reference_id, non FK)
mandates
|-- 1:N --> mandate_steps
@@ -309,4 +312,9 @@ voting_protocols
formula_configs
|-- 1:N --> voting_protocols
sanctuary_entries
|-- reference_id ..> documents | decisions | vote_sessions (lien logique)
```
> **Note Sprint 2** : Les liens `..>` (pointilles) representent des references logiques via le champ `reference_id` de `sanctuary_entries`. Ce ne sont pas des cles etrangeres PostgreSQL car `reference_id` peut pointer vers differentes tables selon le `entry_type`.

View File

@@ -25,7 +25,19 @@ Les documents de reference sont les textes fondateurs de la communaute Duniter/G
3. Cliquez sur le document pour voir la liste de ses items.
4. Chaque item affiche son texte courant, son type et sa position dans le document.
## Proposer une modification
### Voir un item en detail
Pour consulter un item specifique avec tout son historique :
1. Depuis la liste des items du document, cliquez sur l'item souhaite.
2. La vue detaillee affiche :
- Le **texte courant** de l'item.
- Le **type** (clause, regle, verification, preambule, section) et la **position** hierarchique.
- Le **protocole de vote** specifique a cet item (s'il en a un).
- L'**historique des versions** proposees, avec pour chacune son statut (proposee, en vote, acceptee, rejetee).
3. Pour chaque version, vous pouvez consulter le **diff** (differences entre le texte courant et le texte propose) ainsi que la **justification** de l'auteur.
## Proposer une modification (version)
Tout membre authentifie peut proposer une modification a un item de document :
@@ -35,7 +47,39 @@ Tout membre authentifie peut proposer une modification a un item de document :
4. Ajoutez une **justification** expliquant pourquoi cette modification est necessaire.
5. Soumettez. Un diff automatique est genere entre le texte courant et votre proposition.
La proposition passe ensuite par un processus de decision (examen, vote) avant d'etre acceptee ou rejetee.
La proposition cree une nouvelle **version** de l'item. Cette version passe ensuite par un processus de decision (examen, vote) avant d'etre acceptee ou rejetee.
::callout{type="info"}
Plusieurs versions peuvent etre proposees simultanement pour un meme item. Lorsqu'une version est acceptee, toutes les autres versions en attente sont automatiquement rejetees.
::
## Examiner et accepter/rejeter une version
Les membres habilites (selon le protocole de vote associe) peuvent examiner les versions proposees :
### Consulter les versions en attente
1. Ouvrez le document et selectionnez l'item concerne.
2. Consultez la liste des **versions proposees** dans l'onglet historique.
3. Chaque version affiche :
- Le **texte propose** et le **diff** par rapport au texte courant.
- La **justification** fournie par l'auteur.
- Le **statut** actuel (proposee, en vote, acceptee, rejetee).
- L'**identite** du proposant.
### Accepter une version
1. Selectionnez la version a accepter.
2. Cliquez sur **Accepter cette version**.
3. Le texte propose **remplace automatiquement** le texte courant de l'item.
4. Toutes les autres versions en statut `proposee` ou `en vote` pour cet item sont **automatiquement rejetees**.
### Rejeter une version
1. Selectionnez la version a rejeter.
2. Cliquez sur **Rejeter cette version**.
3. Le texte courant de l'item **reste inchange**.
4. La version est archivee avec le statut `rejetee`.
## Cycle de vie d'une proposition
@@ -44,13 +88,57 @@ Proposee --> En vote --> Acceptee --> Texte courant mis a jour
--> Rejetee --> Archivee
```
## Cycle de vie d'un document
Un document suit un cycle de vie en trois etapes :
```
Brouillon --> Actif --> Archive
```
### Brouillon (draft)
Le document est en cours de redaction. Les items peuvent etre ajoutes, modifies, supprimes et reordonnes librement. Le document n'est pas encore soumis au vote permanent.
### Actif (active)
Le document est en vigueur et sous **vote permanent**. Tout membre authentifie peut proposer des modifications aux items via le systeme de versions. Les modifications sont soumises au processus de decision (qualification, examen, vote) avant d'etre appliquees.
### Archive (archived)
Le document a ete archive dans le **Sanctuaire**. Son contenu est fige et preservee de maniere immuable via :
- Un hash SHA-256 pour garantir l'integrite.
- Un stockage sur IPFS pour la distribution decentralisee.
- Un ancrage on-chain via `system.remark` sur Duniter V2.
Un document archive ne peut plus etre modifie. Pour le consulter, rendez-vous dans la section Sanctuaire.
## Archiver un document dans le Sanctuaire
Pour archiver un document actif (necessite une authentification) :
1. Ouvrez le document actif a archiver.
2. Cliquez sur **Archiver dans le Sanctuaire**.
3. Le systeme effectue automatiquement :
- La serialisation complete du document (metadonnees + tous les items).
- Le calcul du hash SHA-256 du contenu.
- L'envoi du contenu sur IPFS.
- L'ancrage du hash on-chain.
4. Le statut du document passe a **Archive**.
5. Les champs **CID IPFS** et **ancrage on-chain** sont renseignes sur le document.
::callout{type="warning"}
L'archivage est une operation irreversible. Une fois archive, le document ne peut plus etre modifie.
::
## Statuts des documents
| Statut | Description |
| -------- | ------------------------------------------------ |
| Brouillon | En cours de redaction, non soumis au vote |
| Actif | Document en vigueur, sous vote permanent |
| Archive | Document archive, plus en vigueur |
| Statut | Description |
| --------- | ------------------------------------------------ |
| Brouillon | En cours de redaction, non soumis au vote |
| Actif | Document en vigueur, sous vote permanent |
| Archive | Document archive dans le Sanctuaire, plus modifiable |
## Versionnage

View File

@@ -41,9 +41,37 @@ La gouvernance exige la transparence et la tracabilite. Le Sanctuaire garantit q
- Le numero de bloc
- La date d'archivage
## Consulter les entrees par document de reference
Pour retrouver toutes les entrees du Sanctuaire liees a un document, une decision ou une session de vote specifique :
1. Depuis la fiche du document (ou de la decision), cliquez sur **Voir dans le Sanctuaire**.
2. La liste affiche toutes les entrees archivees associees a cette entite source.
3. Vous pouvez aussi acceder directement a cette vue via l'URL : `/sanctuaire/par-reference/{id}`.
Cette fonctionnalite permet de retracer l'historique complet d'archivage d'un document au fil de ses modifications adoptees.
## Verification d'integrite
Pour verifier qu'une entree du Sanctuaire est authentique :
### Verification automatique
Glibredecision propose une verification automatique d'integrite pour chaque entree du Sanctuaire :
1. Ouvrez l'entree a verifier dans le Sanctuaire.
2. Cliquez sur **Verifier l'integrite**.
3. Le systeme effectue automatiquement trois controles :
- **Hash SHA-256** : le hash est recalcule a partir du contenu source et compare avec le hash enregistre.
- **IPFS** : le contenu est recupere via le CID IPFS et verifie (si le CID est renseigne).
- **On-chain** : le hash est recherche dans le `system.remark` du bloc reference sur la blockchain Duniter V2 (si le hash de transaction est renseigne).
4. Le resultat affiche pour chaque controle un indicateur **valide** ou **invalide**.
::callout{type="info"}
Si les trois controles sont valides, le contenu est authentique et n'a pas ete modifie depuis son archivage.
::
### Verification manuelle
Pour une verification independante de la plateforme :
1. Recuperez le contenu via IPFS en utilisant le CID affiche.
2. Calculez le hash SHA-256 du contenu telecharge.
@@ -52,6 +80,57 @@ Pour verifier qu'une entree du Sanctuaire est authentique :
Si les trois hash correspondent, le contenu est authentique et n'a pas ete modifie.
## Acces au contenu via IPFS
Chaque entree du Sanctuaire possede un **CID IPFS** (Content Identifier) qui permet d'acceder au contenu archive de maniere decentralisee.
### Utiliser le lien IPFS gateway
Le CID est affiche sous forme de lien cliquable pointant vers une passerelle IPFS publique :
- **Passerelle publique** : `https://ipfs.io/ipfs/{CID}`
- **Passerelle locale** (si vous executez un noeud kubo) : `http://localhost:8080/ipfs/{CID}`
En cliquant sur le lien CID dans l'interface, le contenu archive s'ouvre directement dans votre navigateur.
### Recuperer le contenu via la CLI IPFS
Si vous avez un noeud IPFS local (kubo), vous pouvez recuperer le contenu directement :
```bash
ipfs cat {CID}
```
Ou le telecharger :
```bash
ipfs get {CID} -o document_archive.txt
```
## Comprendre les informations d'ancrage on-chain
Chaque entree du Sanctuaire affiche des informations relatives a son ancrage sur la blockchain Duniter V2 :
| Information | Description |
| -------------------- | -------------------------------------------------------- |
| Hash de transaction | Identifiant unique de la transaction `system.remark` contenant le hash du contenu |
| Numero de bloc | Le bloc de la blockchain dans lequel la transaction a ete incluse |
| Date d'archivage | Horodatage de la creation de l'entree dans le Sanctuaire |
### Verifier sur un explorateur blockchain
Pour verifier l'ancrage on-chain de maniere independante :
1. Copiez le **hash de transaction** affiche sur l'entree du Sanctuaire.
2. Ouvrez un explorateur de la blockchain Duniter V2 (par exemple Polkadot.js Apps connecte au reseau Duniter).
3. Recherchez la transaction par son hash ou parcourez le **bloc** indique.
4. Dans les extrinsics du bloc, reperer l'appel `system.remark` contenant le hash SHA-256 du contenu.
5. Si le hash dans le remark correspond au hash SHA-256 affiche dans le Sanctuaire, l'ancrage est confirme.
::callout{type="tip"}
L'ancrage on-chain fournit une preuve horodatee et infalsifiable de l'existence du contenu a la date du bloc. Meme si la plateforme Glibredecision disparait, la preuve reste verifiable sur la blockchain.
::
## Automatisation
L'archivage dans le Sanctuaire est declenche automatiquement lorsqu'un processus decisionnel est finalise :
@@ -59,3 +138,4 @@ L'archivage dans le Sanctuaire est declenche automatiquement lorsqu'un processus
- Quand une version d'item de document est **acceptee**, le nouveau texte est archive.
- Quand une session de vote est **cloturee**, le resultat detaille est archive.
- Quand une decision est **executee**, l'ensemble de la decision est archive.
- Quand un document est **archive** via le bouton d'archivage, l'integralite du document est archivee dans le Sanctuaire.