discovery-media-player 0.1.76 → 0.1.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,10 @@
1
1
  # Host contract
2
2
 
3
+ > **This document is an export of the package** — resolve it with
4
+ > `require.resolve("discovery-media-player/contrat")` (and the retention policy with
5
+ > `…/retention`). An exposed path is a promise that survives file reorganizations; reading
6
+ > `node_modules` paths by hand is a guess about our tree, and it broke twice in one day.
7
+
3
8
  What a host application may call, what it must implement, and what will not change without a
4
9
  version bump. If you are integrating the player, this page and [`API.md`](API.md) are the two you
5
10
  need.
@@ -0,0 +1,122 @@
1
+ # Rétention des données
2
+
3
+ Ce document est le **périmètre déclaré** de la rétention : chaque colonne du schéma dont la forme
4
+ peut porter une donnée personnelle y a une politique — *purgée après N* ou *conservée parce que*.
5
+ Une garde de forge énumère les colonnes du schéma **vivant** (`information_schema`, jamais notre
6
+ mémoire du fichier) et refuse toute colonne à forme personnelle absente d'ici : une donnée sans
7
+ politique écrite ne peut pas entrer dans le schéma sans rougir.
8
+
9
+ Le contrat de vérification a deux moitiés, volontairement **indépendantes** (aucun code partagé —
10
+ ni fonction de périmètre, ni filtre) :
11
+
12
+ 1. la **purge** (`server/retention.js`) déclare ce qu'elle a effacé, compte par compte ;
13
+ 2. le **recensement** (`supabase/recensement-retention.sql`, SQL nu) compte ce qui reste dans le
14
+ périmètre revendiqué. Les deux nombres doivent se contredire si l'un ment.
15
+
16
+ > ⚠️ **Fenêtres proposées, à valider par l'exploitant.** Les durées ci-dessous sont des défauts
17
+ > raisonnés (journaux analytiques : 13 mois, comparaison année sur année ; archives de
18
+ > présentation : 12 mois après la fin). Un hôte les ajuste via `config.retention`.
19
+ >
20
+ > ⚠️ **Le balayage automatique est OPT-IN STRICT** : il ne tourne que si l'hôte écrit
21
+ > `config.retention.balayage: true`. Un hôte qui consomme le contexte autonome tel quel hérite de
22
+ > toutes ses capacités par défaut — « rien à brancher parce que rien n'a été débranché » — et une
23
+ > suppression est une décision métier : elle n'agit que là où un exploitant l'a écrite. L'action
24
+ > `retention.run` (hôte de confiance ou admin) reste disponible sans opt-in : l'appeler EST la
25
+ > décision.
26
+
27
+ ## Journaux de lecture (population externe)
28
+
29
+ Finalité : statistiques de lecture d'un document envoyé. **Purge : 13 mois** après l'événement.
30
+
31
+ | colonne | contenu | sort |
32
+ |---|---|---|
33
+ | `commercial_doc_views.recipient_email` | à qui la lecture est attribuée | purgée avec la ligne, 13 mois après `at` |
34
+ | `commercial_doc_views.session_id` | corrèle les vues d'une session | idem |
35
+ | `commercial_doc_views.ua` | navigateur (User-Agent brut) | idem |
36
+ | `commercial_doc_sessions.recipient_email` | attribution de la session | purgée avec la ligne, 13 mois après `last_at` |
37
+ | `commercial_doc_sessions.session_id` | identifiant de session | idem |
38
+ | `commercial_doc_sessions.ip` | **adresse IP en clair** | idem — c'est la donnée la plus sensible du schéma |
39
+ | `commercial_doc_sessions.ua` | User-Agent brut | idem |
40
+ | `commercial_doc_sessions.num_pages` / `commercial_doc_sessions.pages_time` | comportement de lecture page par page | idem |
41
+
42
+ ## Journaux de lecture (équipe interne)
43
+
44
+ Même finalité, population interne. **Purge : 13 mois** après `last_at`.
45
+
46
+ | colonne | sort |
47
+ |---|---|
48
+ | `commercial_doc_internal_sessions.user_email` / `commercial_doc_internal_sessions.user_name` | purgées avec la ligne |
49
+ | `commercial_doc_internal_sessions.session_id` | idem |
50
+ | `commercial_doc_internal_sessions.num_pages` / `commercial_doc_internal_sessions.pages_time` | idem |
51
+
52
+ ## Liens d'envoi (`commercial_doc_shares`)
53
+
54
+ Un lien **vivant** est un enregistrement métier : ses champs restent tant que l'URL distribuée
55
+ doit fonctionner. Un lien **révoqué** ne sert plus personne : **purge 13 mois après révocation**
56
+ (alignée sur les journaux, qui référencent son slug). La révocation est **datée** par
57
+ `commercial_doc_shares.revoked_at` (migration 0013) ; les révoqués d'avant la colonne ont reçu la
58
+ date de la migration — leur horloge démarre là, compter large plutôt qu'inventer. Sans la
59
+ colonne, cette purge-là se tait (sonde de schéma), les autres tournent.
60
+
61
+ | colonne | contenu | sort |
62
+ |---|---|---|
63
+ | `commercial_doc_shares.recipient_email` | qui peut expédier au repartage | conservée tant que le lien vit ; ligne purgée 13 mois après révocation |
64
+ | `commercial_doc_shares.attested_recipient_email` | à qui l'hôte atteste le lien | idem |
65
+ | `commercial_doc_shares.recipient_name` | nom du destinataire | idem |
66
+ | `commercial_doc_shares.created_by` | email du commercial créateur | idem |
67
+ | `commercial_doc_shares.file_name` | nom du fichier (peut porter un nom de personne) | métier, purgé avec la ligne |
68
+
69
+ ## Présentations en direct
70
+
71
+ Une présentation **inactive** (terminée ou abandonnée) est une archive : **purge 12 mois après
72
+ `updated_at`** — la présentation, ses messages, ses présences, et ses pièces jointes du bucket
73
+ `present-attachments` (si l'hôte fournit `storage.remove`, sinon la limite est dite ci-dessous).
74
+
75
+ | colonne | contenu | sort |
76
+ |---|---|---|
77
+ | `doc_presentations.presenter_name` / `doc_presentations.owner_name` | identité du présentateur | purgées avec la ligne, 12 mois après la fin |
78
+ | `doc_presentations.owner_email` / `doc_presentations.owner_user_id` | propriétaire | idem |
79
+ | `doc_presentations.owner_avatar` | URL d'avatar | idem |
80
+ | `doc_presentations.control_hash` | empreinte du jeton de contrôle (pas le jeton) | idem |
81
+ | `doc_presentations.content` | contenu partagé (cartes, médias) | idem |
82
+ | `doc_presentations.file_name` | nom du fichier présenté | idem |
83
+ | `doc_presentation_messages.author_name` / `doc_presentation_messages.author_email` / `doc_presentation_messages.author_avatar` | identité de l'auteur | purgées avec la présentation |
84
+ | `doc_presentation_messages.author_hash` | empreinte du jeton d'auteur | idem |
85
+ | `doc_presentation_messages.body` | corps du message | idem |
86
+ | `doc_presentation_messages.reply_name` / `doc_presentation_messages.reply_text` | citation d'un autre message | idem |
87
+ | `doc_presentation_messages.attachment` | URL de pièce jointe | idem — fichier du bucket inclus quand `storage.remove` existe |
88
+ | `doc_presentation_messages.client_key` | clé d'idempotence d'envoi | idem |
89
+ | `doc_presentation_attendees.name` / `doc_presentation_attendees.email` / `doc_presentation_attendees.avatar` | identité du participant | purgées avec la présentation |
90
+ | `doc_presentation_attendees.attendee_key` | identifiant de présence | idem |
91
+ | `doc_presentation_attendees.pages` | pages vues par le participant | idem |
92
+
93
+ ## Sessions d'agent (`doc_bot_sessions`)
94
+
95
+ Parcours guidé par l'agent : **purge 13 mois** après `last_at`.
96
+
97
+ | colonne | sort |
98
+ |---|---|
99
+ | `doc_bot_sessions.rating` / `doc_bot_sessions.rating_comment` | avis du visiteur — purgés avec la ligne |
100
+ | `doc_bot_sessions.in_tokens` / `doc_bot_sessions.out_tokens` / `doc_bot_sessions.cache_tokens` | volumétrie IA (pas personnelle, mais portée par la ligne) — purgés avec elle |
101
+
102
+ ## Limites de débit (`player_rate_limits`)
103
+
104
+ | colonne | contenu | sort |
105
+ |---|---|---|
106
+ | `player_rate_limits.key` | peut contenir une **IP en clair** (`hshare:<ip>`) ou un email | ligne purgée dès `expires_at` dépassé (opportuniste, à chaque passage) |
107
+
108
+ ## Limites dites plutôt que tues
109
+
110
+ - **Pièces jointes orphelines** : la purge des lignes n'efface le fichier du bucket que si le
111
+ contexte hôte fournit `storage.remove` (capacité optionnelle). Sans elle, l'URL devient
112
+ introuvable depuis le produit mais l'objet survit dans le bucket — c'est dit ici plutôt que
113
+ simulé.
114
+ - **Le recensement ne tourne pas tout seul en production** : c'est un SQL qu'un exploitant lance
115
+ (et que la forge exécute à chaque course sur une base réelle vieillie artificiellement).
116
+ - **« Ce qui existe » a une profondeur temporelle qu'`information_schema` n'a pas** (question du
117
+ second hôte, sans réponse mécanique) : une colonne supprimée du schéma sort du périmètre des
118
+ deux textes, mais sa donnée peut survivre dans un dump, une sauvegarde ou une table d'archive.
119
+ Ce contrat couvre la BASE VIVANTE ; les copies (sauvegardes, exports, dumps de migration) sont
120
+ le périmètre de l'exploitant, nommé ici plutôt que simulé. Corollaire opératoire : supprimer
121
+ une colonne à donnée personnelle est un acte de rétention — sa ligne quitte ce document dans
122
+ le même commit, et les copies antérieures suivent la politique de sauvegarde de l'hôte.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discovery-media-player",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
5
5
  "keywords": [
6
6
  "pdf-viewer",
@@ -36,6 +36,7 @@
36
36
  "LICENSE",
37
37
  "LICENSE-MIT",
38
38
  "docs/HOST-CONTRACT.md",
39
+ "docs/RETENTION.md",
39
40
  "!**/__tests__"
40
41
  ],
41
42
  "exports": {
@@ -43,6 +44,8 @@
43
44
  "./shares": "./server/shares.js",
44
45
  "./presentations": "./server/presentations.js",
45
46
  "./brands": "./server/brands.js",
47
+ "./contrat": "./docs/HOST-CONTRACT.md",
48
+ "./retention": "./docs/RETENTION.md",
46
49
  "./context/standalone": "./context/standalone.js",
47
50
  "./context/storage": "./context/storage.js",
48
51
  "./bridge": {