---
title: Schema de base de donnees
description: Tables et relations de la base de donnees PostgreSQL
---

# Schema de base de donnees

libreDecision utilise PostgreSQL 16 avec SQLAlchemy 2.0 en mode asynchrone (asyncpg). Toutes les cles primaires sont des UUID v4.

## Tables

### `duniter_identities`

Identites Duniter V2 connues de la plateforme.

| Colonne        | Type         | Description                                    |
| -------------- | ------------ | ---------------------------------------------- |
| id             | UUID (PK)    | Identifiant unique                             |
| address        | VARCHAR(64)  | Adresse SS58 Duniter (unique, indexee)         |
| display_name   | VARCHAR(128) | Nom d'affichage                                |
| wot_status     | VARCHAR(32)  | Statut WoT : member, pending, revoked, unknown |
| is_smith       | BOOLEAN      | Membre Smith (forgeron)                        |
| is_techcomm    | BOOLEAN      | Membre du Comite Technique                     |
| created_at     | TIMESTAMPTZ  | Date de creation                               |
| updated_at     | TIMESTAMPTZ  | Date de derniere mise a jour                   |

### `sessions`

Sessions d'authentification (tokens).

| Colonne      | Type         | Description                        |
| ------------ | ------------ | ---------------------------------- |
| id           | UUID (PK)    | Identifiant unique                 |
| token_hash   | VARCHAR(128) | Hash du token (unique, indexe)     |
| identity_id  | UUID (FK)    | -> duniter_identities.id           |
| created_at   | TIMESTAMPTZ  | Date de creation                   |
| expires_at   | TIMESTAMPTZ  | Date d'expiration                  |

### `documents`

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                                           |
| ------------ | ------------ | ----------------------------------------------------- |
| id           | UUID (PK)    | Identifiant unique                                    |
| slug         | VARCHAR(128) | Identifiant lisible (unique, indexe)                  |
| title        | VARCHAR(256) | Titre du document                                     |
| doc_type     | VARCHAR(64)  | Type : licence, engagement, reglement, constitution   |
| 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 (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.). 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 (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 (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 (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. 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 (cascade delete)                  |
| proposed_text  | TEXT         | Texte 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)                    |
| proposed_by_id | UUID (FK)    | -> duniter_identities.id (auteur de la proposition)    |
| created_at     | TIMESTAMPTZ  | Date de creation                                       |

### `decisions`

Processus decisionnels multi-etapes. Le cycle de vie suit un ordre strict de statuts gere par le service `decision_service.advance_decision()`.

| Colonne             | Type         | Description                                              |
| ------------------- | ------------ | -------------------------------------------------------- |
| id                  | UUID (PK)    | Identifiant unique                                       |
| title               | VARCHAR(256) | Titre de la decision                                     |
| description         | TEXT         | Description                                              |
| context             | TEXT         | Contexte additionnel (cadrage)                           |
| decision_type       | VARCHAR(64)  | Type : runtime_upgrade, document_change, mandate_vote, parameter_change, custom |
| status              | VARCHAR(32)  | Statut : draft, qualification, review, voting, executed, closed |
| voting_protocol_id  | UUID (FK)    | -> voting_protocols.id                                   |
| created_by_id       | UUID (FK)    | -> duniter_identities.id                                 |
| created_at          | TIMESTAMPTZ  | Date de creation                                         |
| updated_at          | TIMESTAMPTZ  | Date de derniere mise a jour                             |

**Workflow des statuts** : `draft` -> `qualification` -> `review` -> `voting` -> `executed` -> `closed`. La transition est geree par le service `advance_decision` qui complete l'etape active, active la suivante, et avance le statut global quand toutes les etapes sont terminees. La suppression n'est autorisee qu'en statut `draft`.

### `decision_steps`

Etapes d'un processus decisionnel. Chaque etape est traitee sequentiellement selon `step_order`. Le service `advance_decision` complete l'etape `active` et active la prochaine `pending`.

| Colonne          | Type         | Description                                              |
| ---------------- | ------------ | -------------------------------------------------------- |
| id               | UUID (PK)    | Identifiant unique                                       |
| decision_id      | UUID (FK)    | -> decisions.id (cascade delete)                         |
| step_order       | INTEGER      | Ordre de l'etape (0-indexed, determine la sequence)      |
| step_type        | VARCHAR(32)  | Type : qualification, review, vote, execution, reporting |
| title            | VARCHAR(256) | Titre de l'etape                                         |
| description      | TEXT         | Description                                              |
| status           | VARCHAR(32)  | Statut : pending, active, completed, skipped             |
| vote_session_id  | UUID (FK)    | -> vote_sessions.id (renseigne via create-vote-session pour les etapes de type `vote`) |
| outcome          | TEXT         | Resultat de l'etape                                      |
| created_at       | TIMESTAMPTZ  | Date de creation                                         |

**Types d'etapes** : `qualification` (verification recevabilite), `review` (examen communautaire), `vote` (session de vote formelle), `execution` (mise en oeuvre), `reporting` (compte-rendu). Les etapes de type `vote` sont les seules a pouvoir avoir un `vote_session_id` renseigne.

### `vote_sessions`

Sessions de vote avec snapshot des tailles WoT et decompte en temps reel.

| Colonne             | Type         | Description                                    |
| ------------------- | ------------ | ---------------------------------------------- |
| id                  | UUID (PK)    | Identifiant unique                             |
| decision_id         | UUID (FK)    | -> decisions.id                                |
| item_version_id     | UUID (FK)    | -> item_versions.id                            |
| voting_protocol_id  | UUID (FK)    | -> voting_protocols.id                         |
| wot_size            | INTEGER      | Taille WoT au debut de la session              |
| smith_size          | INTEGER      | Taille Smith au debut de la session            |
| techcomm_size       | INTEGER      | Taille TechComm au debut de la session         |
| starts_at           | TIMESTAMPTZ  | Date de debut                                  |
| ends_at             | TIMESTAMPTZ  | Date de fin                                    |
| status              | VARCHAR(32)  | Statut : open, closed, tallied                 |
| votes_for           | INTEGER      | Nombre de votes pour                           |
| votes_against       | INTEGER      | Nombre de votes contre                         |
| votes_total         | INTEGER      | Nombre total de votes                          |
| smith_votes_for     | INTEGER      | Votes pour des membres Smith                   |
| techcomm_votes_for  | INTEGER      | Votes pour des membres TechComm                |
| threshold_required  | FLOAT        | Seuil calcule requis                           |
| result              | VARCHAR(32)  | Resultat : adopted, rejected, null             |
| chain_recorded      | BOOLEAN      | Enregistre sur la blockchain                   |
| chain_tx_hash       | VARCHAR(128) | Hash de la transaction on-chain                |
| created_at          | TIMESTAMPTZ  | Date de creation                               |

### `votes`

Votes individuels avec preuve cryptographique.

| Colonne          | Type         | Description                                  |
| ---------------- | ------------ | -------------------------------------------- |
| id               | UUID (PK)    | Identifiant unique                           |
| session_id       | UUID (FK)    | -> vote_sessions.id                          |
| voter_id         | UUID (FK)    | -> duniter_identities.id                     |
| vote_value       | VARCHAR(32)  | Valeur : for, against, ou niveau nuance      |
| nuanced_level    | INTEGER      | Niveau nuance (0-5) pour les votes nuances   |
| comment          | TEXT         | Commentaire optionnel                        |
| signature        | TEXT         | Signature Ed25519 du payload                 |
| signed_payload   | TEXT         | Payload signe (pour verification)            |
| voter_wot_status | VARCHAR(32)  | Statut WoT du votant au moment du vote       |
| voter_is_smith   | BOOLEAN      | Le votant est-il forgeron                    |
| voter_is_techcomm| BOOLEAN      | Le votant est-il membre TechComm             |
| is_active        | BOOLEAN      | Vote actif (false si remplace)               |
| created_at       | TIMESTAMPTZ  | Date de creation                             |

### `mandates`

Mandats assignes a des membres. Le cycle de vie suit un ordre strict de statuts gere par le service `mandate_service.advance_mandate()`. La revocation (`POST /mandates/{id}/revoke`) permet de passer directement au statut terminal `revoked`.

| Colonne       | Type         | Description                                             |
| ------------- | ------------ | ------------------------------------------------------- |
| id            | UUID (PK)    | Identifiant unique                                      |
| title         | VARCHAR(256) | Titre du mandat                                         |
| description   | TEXT         | Description                                             |
| mandate_type  | VARCHAR(64)  | Type : techcomm, smith, custom                          |
| status        | VARCHAR(32)  | Statut : draft, candidacy, voting, active, reporting, completed, revoked |
| mandatee_id   | UUID (FK)    | -> duniter_identities.id (titulaire du mandat, renseigne via `POST /mandates/{id}/assign`) |
| decision_id   | UUID (FK)    | -> decisions.id (decision associee, optionnel)          |
| starts_at     | TIMESTAMPTZ  | Date de debut (renseignee lors de l'assignation)        |
| ends_at       | TIMESTAMPTZ  | Date de fin (renseignee lors de l'assignation)          |
| created_at    | TIMESTAMPTZ  | Date de creation                                        |
| updated_at    | TIMESTAMPTZ  | Date de derniere mise a jour                            |

**Workflow des statuts** : `draft` -> `candidacy` -> `voting` -> `active` -> `reporting` -> `completed`. Les statuts terminaux sont `completed` et `revoked`. La suppression n'est autorisee qu'en statut `draft`. La revocation est possible depuis les statuts `active` ou `reporting`.

### `mandate_steps`

Etapes du cycle de vie d'un mandat. Chaque etape est traitee sequentiellement selon `step_order`. Le service `advance_mandate` complete l'etape `active` et active la prochaine `pending`.

| Colonne          | Type         | Description                                                   |
| ---------------- | ------------ | ------------------------------------------------------------- |
| id               | UUID (PK)    | Identifiant unique                                            |
| mandate_id       | UUID (FK)    | -> mandates.id (cascade delete)                               |
| step_order       | INTEGER      | Ordre de l'etape (0-indexed, determine la sequence)           |
| step_type        | VARCHAR(32)  | Type : formulation, candidacy, vote, assignment, reporting, completion, revocation |
| title            | VARCHAR(256) | Titre de l'etape                                              |
| description      | TEXT         | Description                                                   |
| status           | VARCHAR(32)  | Statut : pending, active, completed, skipped                  |
| vote_session_id  | UUID (FK)    | -> vote_sessions.id (renseigne via create-vote-session pour les etapes de type `vote`) |
| outcome          | TEXT         | Resultat de l'etape                                           |
| created_at       | TIMESTAMPTZ  | Date de creation                                              |

**Types d'etapes** : `formulation` (definition du mandat), `candidacy` (depot des candidatures), `vote` (election par vote WoT), `assignment` (attribution au candidat elu), `reporting` (compte-rendu), `completion` (fin normale), `revocation` (fin anticipee). Les etapes de type `vote` sont les seules a pouvoir avoir un `vote_session_id` renseigne.

### `voting_protocols`

Protocoles de vote reutilisables.

| Colonne            | Type         | Description                                     |
| ------------------ | ------------ | ----------------------------------------------- |
| id                 | UUID (PK)    | Identifiant unique                              |
| name               | VARCHAR(128) | Nom du protocole                                |
| description        | TEXT         | Description                                     |
| vote_type          | VARCHAR(32)  | Type de vote : binary, nuanced                  |
| formula_config_id  | UUID (FK)    | -> formula_configs.id                           |
| mode_params        | VARCHAR(64)  | Parametres compacts ("D30M50B.1G.2T.1")         |
| is_meta_governed   | BOOLEAN      | Le protocole est-il sous meta-gouvernance       |
| created_at         | TIMESTAMPTZ  | Date de creation                                |

### `formula_configs`

Configurations de formules de seuil WoT.

| Colonne                   | Type         | Description                               |
| ------------------------- | ------------ | ----------------------------------------- |
| id                        | UUID (PK)    | Identifiant unique                        |
| name                      | VARCHAR(128) | Nom de la configuration                   |
| description               | TEXT         | Description                               |
| duration_days             | INTEGER      | Duree du vote en jours                    |
| majority_pct              | INTEGER      | Pourcentage de majorite (0-100)           |
| base_exponent             | FLOAT        | Exposant de base B                        |
| gradient_exponent         | FLOAT        | Exposant de gradient G                    |
| constant_base             | FLOAT        | Base constante C                          |
| smith_exponent            | FLOAT        | Exposant Smith S (null si non requis)     |
| techcomm_exponent         | FLOAT        | Exposant TechComm T (null si non requis)  |
| nuanced_min_participants  | INTEGER      | Participants minimum (vote nuance)        |
| nuanced_threshold_pct     | INTEGER      | Seuil positif % (vote nuance)            |
| created_at                | TIMESTAMPTZ  | Date de creation                          |

### `sanctuary_entries`

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 (indexe pour recherche par reference) |
| title          | VARCHAR(256) | Titre                                      |
| content_hash   | VARCHAR(128) | Hash SHA-256 du contenu                    |
| 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           |
| created_at     | TIMESTAMPTZ  | Date de creation                           |

### `blockchain_cache`

Cache des donnees blockchain pour eviter les appels RPC repetes.

| Colonne      | Type         | Description                      |
| ------------ | ------------ | -------------------------------- |
| id           | UUID (PK)    | Identifiant unique               |
| cache_key    | VARCHAR(256) | Cle de cache (unique, indexee)   |
| cache_value  | JSONB        | Valeur en cache                  |
| fetched_at   | TIMESTAMPTZ  | Date de recuperation             |
| expires_at   | TIMESTAMPTZ  | Date d'expiration du cache       |

## Diagramme des relations

```
duniter_identities
    |-- 1:N --> sessions
    |-- 1:N --> votes (voter_id)
    |-- 1:N --> item_versions (proposed_by_id)
    |-- 1:N --> decisions (created_by_id)
    |-- 1:N --> mandates (mandatee_id)

documents
    |-- 1:N --> document_items
    |-- 1:N ..> sanctuary_entries (via reference_id, non FK)

document_items
    |-- 1:N --> item_versions (cascade delete)
    |-- N:1 --> voting_protocols

item_versions
    |-- N:1 --> decisions

decisions
    |-- 1:N --> decision_steps
    |-- 1:N ..> sanctuary_entries (via reference_id, non FK)

decision_steps
    |-- N:1 --> vote_sessions

vote_sessions
    |-- 1:N --> votes
    |-- N:1 --> voting_protocols
    |-- 1:N ..> sanctuary_entries (via reference_id, non FK)

mandates
    |-- 1:N --> mandate_steps
    |-- N:1 --> decisions

mandate_steps
    |-- N:1 --> vote_sessions

voting_protocols
    |-- N:1 --> formula_configs

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`.
