File size: 10,170 Bytes
95cbd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# Déploiement institutionnel — Picarones

> Sprint A11 du plan de remédiation institutionnelle (item M-7).
>
> Ce guide cible les **DSI de bibliothèques nationales et services
> d'archives** qui souhaitent héberger Picarones sur leur propre
> infrastructure (intranet, derrière SSO, avec stockage centralisé)
> plutôt que sur HuggingFace Space public.
>
> Pour le déploiement HuggingFace Space ou un usage local rapide,
> voir [`INSTALL.md`](../../INSTALL.md).

## Pré-requis

### Système

- **Linux x86_64 ou ARM64** (Debian 12+, RHEL 9+, Ubuntu 22.04+
  LTS, Rocky 9+).
- **Python 3.11 ou 3.12** (3.13 informationnel — Sprint A1).
- **Tesseract OCR ≥ 5.3** (avec packs `fra`, `lat`, `eng` au
  minimum).
- **3 GB RAM par worker** (le ProcessPool spawne un sous-processus
  par moteur ; profil mémoire dominé par Pillow + jiwer).
- **5 GB de disque** pour l'application + 50 GB recommandés pour
  les uploads et la base SQLite des jobs.

### Réseau

- **Sortant** : optionnel (HF Datasets / Gallica / HTR-United
  uniquement si vous activez les imports distants).
- **Entrant** : un seul port HTTP (défaut 7860) à exposer derrière
  votre reverse proxy (Nginx, Apache, Traefik).

### Optionnel

- **PostgreSQL 14+** (en remplacement de SQLite si vous montez en
  multi-instance — voir § Architecture cible).
- **Reverse proxy SSO** (Shibboleth, CAS, OIDC, OAuth2 proxy).
- **Stack observabilité** (Prometheus + Grafana, ou ELK/Loki).

## Architecture cible

### Mono-instance (recommandé pour < 10 utilisateurs simultanés)

```
[Utilisateur] → [Reverse proxy SSO] → [Picarones Docker] → [SQLite jobs.db]

                                    [Volume persistant uploads/]

                                    [Volume persistant reports/]
```

Configuration minimale, rétro-compatible avec le déploiement
HuggingFace Space. Le reverse proxy ajoute l'authentification
(SSO institutionnel) et la terminaison TLS.

### Multi-instance (charge > 50 jobs/h)

```
[Utilisateur] → [Load balancer + SSO] → [Picarones × N]

                                  [PostgreSQL jobs (centralisé)]

                                  [Volume NFS uploads/ partagé]
```

Notes :
- **PostgreSQL** : `JobStore` (Sprint 25) utilise SQLite par défaut.
  Pour PostgreSQL, dériver une classe `PostgresJobStore` qui
  implémente la même API (`create_job`, `update_progress`,
  `get_job`, etc.). À défaut, partager la BD SQLite via NFS ne
  fonctionne pas — le mode WAL exige un filesystem local.
- **Volume NFS** pour `uploads/` et `reports/` afin que tous les
  workers voient les mêmes fichiers.
- **Sticky sessions** sur le LB pour SSE (les progress streams
  doivent rester sur le même worker).

## Configuration

Toutes les variables sont documentées dans
[`.env.example`](../../.env.example). Les principales pour un
déploiement institutionnel :

```bash
# Sécurité (Sprints A4 + 24)
PICARONES_PUBLIC_MODE=        # vide ou 0 = mode dev (autorise OCR cloud)
PICARONES_CSRF_REQUIRED=1     # OBLIGATOIRE derrière SSO
PICARONES_CSRF_SECRET="$(openssl rand -hex 32)"

# Restrictions
PICARONES_BROWSE_ROOTS="/var/lib/picarones/uploads:/data/corpus"
PICARONES_MAX_UPLOAD_MB=500
PICARONES_MAX_CONCURRENT_JOBS=8
PICARONES_RATE_LIMIT_PER_HOUR=0   # 0 = illimité (le SSO gère l'identité)

# Persistance
PICARONES_JOBS_DB=/var/lib/picarones/jobs.sqlite

# RGPD (Sprint A11)
PICARONES_UPLOAD_RETENTION_DAYS=7
PICARONES_LOG_IP_RETENTION_HOURS=24
```

## Intégration SSO

Picarones n'implémente **pas** de mécanisme d'authentification
natif — l'authentification est déléguée à votre reverse proxy.
Pattern recommandé : header trusté `X-Remote-User`.

### Nginx + Shibboleth (université, Renater)

```nginx
location / {
    auth_request /shibauthorizer;
    proxy_set_header X-Remote-User $http_remote_user;
    proxy_set_header X-Remote-Groups $http_remote_groups;
    proxy_pass http://picarones-backend:7860;

    # SSE long-polling
    proxy_buffering off;
    proxy_read_timeout 24h;
}
```

### Apache + CAS (CRU, ESR français)

```apache
<Location />
    AuthType CAS
    Require valid-user
    RequestHeader set X-Remote-User %{REMOTE_USER}s
    ProxyPass http://localhost:7860/
    ProxyPassReverse http://localhost:7860/
</Location>
```

### Traefik + OIDC (déploiements modernes)

```yaml
http:
  middlewares:
    oidc-auth:
      forwardAuth:
        address: "http://oauth2-proxy:4180/auth"
        trustForwardHeader: true
        authResponseHeaders:
          - X-Auth-Request-User
          - X-Auth-Request-Email

  routers:
    picarones:
      rule: "Host(`picarones.institution.fr`)"
      service: picarones
      tls:
        certResolver: letsencrypt
      middlewares:
        - oidc-auth
```

## Sauvegarde et restauration

### Composants à sauvegarder

| Élément | Chemin | Stratégie |
|---|---|---|
| BD jobs | `/var/lib/picarones/jobs.sqlite*` | Snapshot quotidien (mode WAL : sauvegarder `.sqlite`, `.sqlite-wal`, `.sqlite-shm` ensemble) |
| Uploads | `/var/lib/picarones/uploads/` | Snapshot hebdomadaire, rétention 30 jours (cf. RGPD) |
| Rapports | `/var/lib/picarones/reports/` | Snapshot hebdomadaire, rétention illimitée (artefacts citables) |
| Historique longitudinal | `/var/lib/picarones/history.sqlite` | Snapshot quotidien |
| Configuration | `/etc/picarones/`, `.env` | Versionner dans le système de gestion de config (Ansible, Salt) |

### Restauration

```bash
# Arrêt du service
systemctl stop picarones

# Restauration BD
cp backups/jobs.sqlite-2026-05-01.sqlite /var/lib/picarones/jobs.sqlite

# Restauration uploads
rsync -av backups/uploads-2026-05-01/ /var/lib/picarones/uploads/

# Redémarrage
systemctl start picarones

# Vérification : marquage des jobs orphelins
curl http://localhost:7860/api/status
```

Les jobs `running` au moment du snapshot sont automatiquement
marqués `interrupted` au redémarrage (Sprint 26). Le tableau de
bord sera donc cohérent.

## Migration de schéma BD

Picarones évolue sa BD via une stratégie **append-only** —
nouvelles colonnes ajoutées avec `ALTER TABLE ADD COLUMN ...
DEFAULT NULL`. Aucune migration destructive entre versions
mineures.

Pour vérifier la compatibilité d'une BD existante avec une
nouvelle version :

```bash
sqlite3 jobs.sqlite "PRAGMA table_info(jobs);" > current_schema.txt
# Comparer avec docs/schema/jobs.sqlite.X.Y.Z.sql versionné
```

Si une migration majeure est nécessaire (changement de moteur SQL,
structure incompatible), elle sera annoncée 2 versions mineures
avant et un script de migration sera fourni dans `scripts/migrate/`.

## Observabilité

### Logs structurés

Picarones logge en **format texte simple** par défaut. Pour ELK /
Loki / Datadog, ajouter un wrapper JSON :

```python
# /etc/picarones/logging.conf
[handler_json]
class = pythonjsonlogger.jsonlogger.JsonFormatter
format = %(asctime)s %(levelname)s %(name)s %(message)s
```

Variable d'env : `PICARONES_LOG_FORMAT=json` (à implémenter dans
un sprint ultérieur — actuellement les logs sont en plain text).

### Métriques Prometheus (recommandé)

L'exposition Prometheus n'est pas livrée par défaut. Pour
l'ajouter, monter un conteneur sidecar `prometheus_client_python`
qui expose :

- `picarones_jobs_total{status="..."}`
- `picarones_jobs_duration_seconds`
- `picarones_uploads_size_bytes_total`
- `picarones_engine_invocations_total{engine="..."}`

Voir `docs/operations/observability.md` (à venir Sprint A11+).

### Healthcheck

Un endpoint `/health` minimal (Sprint A4 / M-3) répond en < 50 ms
sans toucher à la BD ni aux engines. Configurer le LB pour le
cibler avec un timeout court (5 s).

## Sécurité réseau

### Liste blanche réseau

Si vos engines cloud sont activés (`PICARONES_PUBLIC_MODE` non
défini), autoriser en sortie uniquement :

| Domaine | Usage |
|---|---|
| `api.openai.com` | OpenAI / GPT-4o |
| `api.anthropic.com` | Claude |
| `api.mistral.ai` | Mistral OCR + LLM |
| `vision.googleapis.com` | Google Vision |
| `*.cognitiveservices.azure.com` | Azure Doc Intelligence |
| `huggingface.co` | Imports HF Datasets (optionnel) |
| `gallica.bnf.fr` | Imports Gallica (optionnel) |

### Politique de mots de passe

Les clés API LLM/OCR sont passées **en variables d'environnement
uniquement** (jamais sur le filesystem en clair). Voir [`SECURITY.md`](../../SECURITY.md).

## Mise à l'échelle

| Charge | Configuration |
|---|---|
| < 5 jobs/h, < 5 utilisateurs | Mono-instance, SQLite, 2 vCPU / 4 GB RAM |
| 5–50 jobs/h, < 20 utilisateurs | Mono-instance, SQLite, 4 vCPU / 8 GB RAM, ProcessPool 8 workers |
| > 50 jobs/h | Multi-instance derrière LB, PostgreSQL centralisé, NFS uploads |
| > 500 jobs/h | Considérer un orchestrateur de tâches dédié (Celery + Redis), hors scope Picarones |

## Checklist déploiement

- [ ] Tesseract installé avec packs de langues nécessaires.
- [ ] Variables d'env configurées (mode dev/public, CSRF, browse roots).
- [ ] Volume persistant pour `uploads/`, `reports/`, BD.
- [ ] Reverse proxy SSO en place (CSRF activé !).
- [ ] Backup automatique configuré (cron quotidien minimum).
- [ ] Healthcheck `/health` configuré dans le LB.
- [ ] TLS terminé au reverse proxy.
- [ ] Liste blanche réseau si engines cloud actifs.
- [ ] Rétention RGPD configurée
      (cf. [`data-retention-rgpd.md`](data-retention-rgpd.md)).
- [ ] Audit RGAA externe planifié si prestation publique
      (cf. [`ACCESSIBILITY.md`](../../ACCESSIBILITY.md)).
- [ ] Issue tracking institutionnel (Mantis, JIRA, Redmine)
      synchronisé avec les issues GitHub si pertinent.

## Aide et support

- Issues GitHub étiquetées `deployment` :
  <https://github.com/maribakulj/Picarones/issues?q=label%3Adeployment>
- Pour un support contractualisé (SLO renforcés, intégration
  spécifique), contractualiser une prestation séparément (modalités
  hors-projet, à définir au cas par cas).

---

*Dernière mise à jour : 2 mai 2026 (Sprint A11).*