# 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 1. [Panoramica del provider](#1-panoramica-del-provider) 2. [Architettura Aeterna](#2-architettura-aeterna) 3. [Autenticazione JWT](#3-autenticazione-jwt) 4. [Ingest – Upload SIP](#4-ingest--upload-sip) 5. [Formato SIP BagIt (RFC 8493)](#5-formato-sip-bagit-rfc-8493) 6. [Polling stato ingest](#6-polling-stato-ingest) 7. [Disseminazione (DIP)](#7-disseminazione-dip) 8. [Mapping stati Aeterna → PecHub](#8-mapping-stati-aeterna--pechub) 9. [Configurazione in PecHub](#9-configurazione-in-pechub) 10. [Esempi curl completi](#10-esempi-curl-completi) 11. [Note operative](#11-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: ```json { "email": "matteo@idrainformatica.it", "password": "...", "tenant_slug": "pechub" } ``` Risposta 200: ```json { "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 ``` --- ## 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 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 ```json { "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 ``` ### Risposta ```json { "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 ``` --- ## 7. Disseminazione (DIP) ### Richiesta generazione DIP ``` POST /api/v1/packages/{package_id}/disseminate Authorization: Bearer Content-Type: application/json {"note": "Richiesto da PecHub Archival Module"} ``` Risposta 202: ```json { "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 ``` Quando `is_available: true`, il DIP e' scaricabile. ### Download DIP ``` GET /api/v1/packages/{package_id}/dip/download Authorization: Bearer ``` Risposta: stream ZIP del DIP. --- ## 8. Mapping stati Aeterna → PecHub ```python _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 ``` Risposta: ```json { "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 ```bash 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 ```bash # 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 < /tmp/testbag/manifest-sha256.txt <