15 KiB
Integrazione Aeterna – Archiviazione Sostitutiva PecHub
Documento tecnico sull'integrazione di PecHub con Aeterna, piattaforma di conservazione digitale conforme E-ARK gestita da Idra Informatica.
Indice
- Panoramica del provider
- Architettura Aeterna
- Autenticazione JWT
- Ingest – Upload SIP
- Formato SIP BagIt (RFC 8493)
- Polling stato ingest
- Disseminazione (DIP)
- Mapping stati Aeterna → PecHub
- Configurazione in PecHub
- Esempi curl completi
- Note operative
1. Panoramica del provider
| Campo | Valore |
|---|---|
| Provider | Idra Informatica srl |
| Piattaforma | Aeterna v0.1.0 |
| URL applicazione | https://aeterna.idrainformatica.it |
| Endpoint API | https://api.aeterna.idrainformatica.it |
| Documentazione | https://api.aeterna.idrainformatica.it/docs |
| OpenAPI JSON | https://api.aeterna.idrainformatica.it/openapi.json |
| Standard | E-ARK CSIP 2.1.0, BagIt RFC 8493, PREMIS 3.0, METS 1.12 |
Credenziali tenant PecHub
| Campo | Valore |
|---|---|
| Org. name | pechub |
| Tenant slug | pechub |
| Username | matteo@idrainformatica.it |
| Password | (cifrata nel DB PecHub) |
| Tenant ID | 366d3d51-b25d-46fc-8f9c-9bc28d902620 |
2. Architettura Aeterna
Aeterna e' una piattaforma multi-tenant FastAPI (Python). Ogni organizzazione (tenant) ha:
- Container MinIO dedicato per lo storage isolato
- Collection Apache Solr dedicata per la ricerca full-text
- RBAC (Role-Based Access Control) per-tenant
Il ciclo di vita di un documento archiviale in Aeterna:
SIP (upload) → AIP (ingest pipeline) → DIP (disseminazione)
| | |
BagIt ZIP PREMIS events ZIP scaricabile
multipart METS 1.12 generato
form-data validazione E-ARK
3. Autenticazione JWT
Aeterna usa JWT Bearer token, NON HTTP Basic. Il token ha durata 3600 secondi (1 ora). Esiste un refresh token.
Login
POST /api/v1/auth/login
Content-Type: application/json
Body:
{
"email": "matteo@idrainformatica.it",
"password": "...",
"tenant_slug": "pechub"
}
Risposta 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"user": {
"id": "e3cac60b-d942-4590-94fe-932c0e14e836",
"email": "matteo@idrainformatica.it",
"full_name": "Matteo Giustini",
"is_platform_admin": false,
"tenant_id": "366d3d51-b25d-46fc-8f9c-9bc28d902620",
"permissions": [
"ingest:submit", "ingest:manage", "packages:read",
"packages:create", "dissemination:read", "dissemination:download",
"preservation:manage", "audit:read", "settings:manage", ...
]
}
}
Utilizzo del token nelle richieste successive:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Refresh token
POST /api/v1/auth/refresh
Content-Type: application/json
{"refresh_token": "eyJhbGciOiJIUzI1NiIs..."}
Verifica identita'
GET /api/v1/auth/me
Authorization: Bearer <token>
4. Ingest – Upload SIP
L'endpoint di ingest accetta pacchetti SIP in due formati:
- E-ARK CSIP v2.2.0 (ZIP con METS.xml in root)
- BagIt RFC 8493 (ZIP con bagit.txt + data/) — formato scelto per PecHub
Endpoint
POST /api/v1/ingest/upload
Authorization: Bearer <token>
Content-Type: multipart/form-data
Campi form-data
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
file |
file | si | ZIP SIP (E-ARK CSIP o BagIt) |
title |
string | si | Titolo del pacchetto |
description |
string | no | Descrizione |
creator |
string | no | Nome del produttore |
submission_agreement |
string | no | ID o URL dell'accordo di versamento |
ead3_file |
file | no | EAD3 finding aid XML (GAP-09) |
eac_cpf_file |
file | no | EAC-CPF authority record XML |
Risposta 202 Accepted
{
"package_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"pid": "urn:pechub:aip:a1b2c3d4",
"status": "UPLOADED",
"task_id": "celery-task-uuid",
"message": "SIP uploaded successfully. Processing started.",
"submitted_at": "2026-06-18T09:00:00Z"
}
Il package_id e' l'identificatore da usare per polling e DIP.
5. Formato SIP BagIt (RFC 8493)
PecHub costruisce pacchetti BagIt in memoria (build_bagit_sip in conservatore_client.py).
Struttura ZIP generata
pechub-pec-{message_id}/
bagit.txt # Dichiarazione BagIt (obbligatorio)
bag-info.txt # Metadati descrittivi (opzionale)
manifest-sha256.txt # Checksum SHA-256 dei file in data/
data/
{message_id}.eml # Messaggio PEC grezzo
Contenuto bagit.txt
BagIt-Version: 1.0
Tag-File-Character-Encoding: UTF-8
Contenuto bag-info.txt (esempio)
Bag-Software-Agent: PecHub Archival Module
Bagging-Date: 2026-06-18
External-Identifier: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Source-Organization: PecHub
Description: PEC oggetto del messaggio (max 500 char)
Contact-Email: mittente@pec.it
External-Description: PEC a destinatario@pec.it
Bag-Group-Identifier: 2026-06-18
Contenuto manifest-sha256.txt
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/a1b2c3d4-e5f6.eml
Rilevamento automatico BagIt da Aeterna
Aeterna rileva automaticamente il formato BagIt dalla presenza di bagit.txt
nella root del bag all'interno dello ZIP. Non e' necessario specificare il formato.
La pipeline verifica i checksum del manifest prima dell'ingest.
6. Polling stato ingest
Dopo l'upload, la pipeline di Aeterna processa il pacchetto in modo asincrono.
Endpoint status
GET /api/v1/ingest/{package_id}/status
Authorization: Bearer <token>
Risposta
{
"package_id": "a1b2c3d4-...",
"pid": "urn:pechub:aip:a1b2c3d4",
"status": "PROCESSING",
"task_id": "celery-task-uuid",
"pipeline_stage": "format_identification",
"progress_pct": 40,
"steps": [
{"name": "validation", "status": "completed", ...},
{"name": "format_identification", "status": "running", ...},
{"name": "virus_scan", "status": "pending", ...}
],
"is_valid": null,
"validation_errors": 0,
"error_message": null,
"submitted_at": "2026-06-18T09:00:00Z",
"completed_at": null
}
Stati possibili
| Status Aeterna | Significato | Stato PecHub |
|---|---|---|
UPLOADED |
SIP ricevuto, elaborazione non iniziata | uploading |
VALIDATING |
Validazione E-ARK/BagIt in corso | uploading |
PROCESSING |
Pipeline ingest in corso | uploading |
INGESTING |
AIP in fase di creazione | uploading |
ACTIVE |
AIP attivo, conservazione completata | confirmed |
FAILED |
Pipeline fallita con errori | failed |
REJECTED |
Pacchetto non conforme agli standard | rejected |
DELETED |
Soft-delete | rejected |
Report completo
GET /api/v1/ingest/{package_id}/report
Authorization: Bearer <token>
7. Disseminazione (DIP)
Richiesta generazione DIP
POST /api/v1/packages/{package_id}/disseminate
Authorization: Bearer <token>
Content-Type: application/json
{"note": "Richiesto da PecHub Archival Module"}
Risposta 202:
{
"id": "dip-uuid",
"pid": "urn:pechub:dip:...",
"status": "PROCESSING",
"size_bytes": null,
"is_available": false
}
Verifica stato DIP
GET /api/v1/packages/{package_id}/dip
Authorization: Bearer <token>
Quando is_available: true, il DIP e' scaricabile.
Download DIP
GET /api/v1/packages/{package_id}/dip/download
Authorization: Bearer <token>
Risposta: stream ZIP del DIP.
8. Mapping stati Aeterna → PecHub
_STATUS_MAP = {
"UPLOADED": "uploading",
"VALIDATING": "uploading",
"PROCESSING": "uploading",
"INGESTING": "uploading",
"ACTIVE": "confirmed", # conservazione completata
"FAILED": "failed",
"REJECTED": "rejected",
"DELETED": "rejected",
}
Il versamento_id in PecHub corrisponde al package_id di Aeterna (UUID v4).
9. Configurazione in PecHub
Impostazioni tenant
Dalla pagina Impostazioni → Archiviazione Sostitutiva:
| Campo | Valore per Aeterna |
|---|---|
| Modalita' | Produzione |
| Identificativo conservatore | aeterna |
| URL endpoint API | https://api.aeterna.idrainformatica.it |
| Tenant Slug | pechub |
| Username | matteo@idrainformatica.it |
| Password | (da fornire, viene cifrata AES-256-GCM) |
Riconoscimento automatico del provider
Il factory ConservatoreClient.from_tenant_credentials() rileva Aeterna se:
conservatore_id == "aeterna"(case-insensitive), OPPURE"aeterna"e' presente nell'URL endpoint, OPPURE"idrainformatica"e' presente nell'URL endpoint
Se rilevato come Aeterna, usa AeternaConservatoreClient (JWT + BagIt).
Altrimenti usa ProductionConservatoreClient (HTTP Basic, standard AgID legacy).
Test connessione
Dopo aver configurato e salvato le impostazioni, usare il pulsante "Testa connessione" in Impostazioni → Archiviazione Sostitutiva.
Oppure via API:
POST /api/v1/settings/test-conservatore
Authorization: Bearer <pechub-token>
Risposta:
{
"success": true,
"message": "Connessione ad Aeterna riuscita (utente: matteo@idrainformatica.it)",
"latency_ms": 342,
"provider_info": {
"platform": "Aeterna",
"tenant_slug": "pechub",
"user_email": "matteo@idrainformatica.it",
"permissions_count": 20
}
}
10. Esempi curl completi
Login
TOKEN=$(curl -s -X POST https://api.aeterna.idrainformatica.it/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"matteo@idrainformatica.it","password":"...","tenant_slug":"pechub"}' \
| jq -r .access_token)
echo "Token: $TOKEN"
Upload SIP BagIt
# Crea un BagIt minimo di test
mkdir -p /tmp/testbag/data
echo "Hello PEC" > /tmp/testbag/data/test.eml
SHA=$(sha256sum /tmp/testbag/data/test.eml | awk '{print $1}')
cat > /tmp/testbag/bagit.txt <<EOF
BagIt-Version: 1.0
Tag-File-Character-Encoding: UTF-8
EOF
cat > /tmp/testbag/manifest-sha256.txt <<EOF
$SHA data/test.eml
EOF
# ZIP del bag
cd /tmp && zip -r testbag.zip testbag/
# Upload
curl -s -X POST https://api.aeterna.idrainformatica.it/api/v1/ingest/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/tmp/testbag.zip;type=application/zip" \
-F "title=Test PEC da PecHub" \
-F "description=Messaggio di test" \
-F "creator=PecHub" \
| jq .
Polling status
PACKAGE_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -s https://api.aeterna.idrainformatica.it/api/v1/ingest/$PACKAGE_ID/status \
-H "Authorization: Bearer $TOKEN" \
| jq '{status:.status, stage:.pipeline_stage, pct:.progress_pct}'
Lista pacchetti
curl -s "https://api.aeterna.idrainformatica.it/api/v1/packages?limit=10" \
-H "Authorization: Bearer $TOKEN" \
| jq '.items[] | {id:.id, title:.title, status:.status}'
Report ingest
curl -s https://api.aeterna.idrainformatica.it/api/v1/ingest/$PACKAGE_ID/report \
-H "Authorization: Bearer $TOKEN" \
| jq .
Richiesta DIP
curl -s -X POST \
"https://api.aeterna.idrainformatica.it/api/v1/packages/$PACKAGE_ID/disseminate" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"note":"Richiesto da PecHub"}' \
| jq .
Download DIP
curl -L -o /tmp/dip.zip \
"https://api.aeterna.idrainformatica.it/api/v1/packages/$PACKAGE_ID/dip/download" \
-H "Authorization: Bearer $TOKEN"
11. Note operative
Dimensione pacchetti
Non ci sono limiti documentati. I file EML di PEC sono tipicamente 10-500 KB. Il timeout HTTP nel client e' impostato a 120 secondi per l'upload.
Concorrenza
Ogni AeternaConservatoreClient gestisce il token JWT in memoria.
Se il worker usa piu' istanze concorrenti, ogni istanza effettua il suo login.
Il token dura 3600s, il rinnovo automatico avviene 60s prima della scadenza.
Retry policy
In caso di fallimento dell'upload, il batch ArchivalBatch in PecHub
ha max_attempts=3 e un next_retry_at con back-off esponenziale.
Standard di riferimento
- E-ARK CSIP v2.1.0 — Common Specification for Information Packages
- E-ARK SIP/AIP/DIP — Submission/Archival/Dissemination Information Package
- BagIt RFC 8493 — formato file standard per trasferimento dati
- PREMIS 3.0 — metadati di preservazione
- METS 1.12 — Metadata Encoding and Transmission Standard
- Dublin Core / EAD — metadati descrittivi
Codice sorgente rilevante
| File | Descrizione |
|---|---|
worker/app/archival/conservatore_client.py |
Client completo con AeternaConservatoreClient, BagIt builder, factory |
worker/scripts/test_aeterna_transmission.py |
Script di test standalone |
backend/app/api/v1/settings.py |
Endpoint test-conservatore |
backend/app/models/tenant_settings.py |
Modello DB con conservatore_tenant_slug |
backend/app/services/tenant_settings_service.py |
Servizio credenziali |
backend/alembic/versions/0020_add_conservatore_tenant_slug.py |
Migrazione DB |
frontend/src/pages/Settings/SettingsPage.tsx |
UI configurazione conservatore |
frontend/src/api/settings.api.ts |
Client API frontend |
Esecuzione script di test
# Dal server, dentro il container worker
docker exec -it pechub-worker-1 \
python /app/scripts/test_aeterna_transmission.py
# I risultati vengono salvati in /tmp/aeterna_test_results.json
Documento generato il 2026-06-18. Versione Aeterna: 0.1.0.