Domenica 25 ottobre ho reso pubblico il repository di Plenipo, la libreria che fa approvare dall’EUDI Wallet un mandato per gli agenti AI, e ho pubblicato la versione 0.4 su npm, con licenza Apache 2.0. Il mio vero primo progetto OpenSource, dopo LocalAI.
Nella stessa giornata il primo wallet reale, la Wallet API di walt.id, ha completato tutto il flusso, dalla richiesta del mandato fino alla verifica, e la sandbox è andata online con una vetrina che mostra in diretta cosa succede quando un agente prova a pagare.
In pratica una persona autorizza un agente con un mandato firmato dal proprio wallet, e qualunque servizio, una banca per esempio, verifica quel mandato prima che l’agente agisca. Lo racconto qui perché è un esperimento, con i suoi limiti dichiarati, e perché la domanda da cui nasce mi sembra più grande del codice che ho scritto per rispondere.
Un registro che non dice chi c’è dietro
Tutto parte da un fastidio. Quest’anno, studiando l’EUDI Wallet dentro un progetto di sperimentazione, mi sono messo a guardare come reti di carte e startup statunitensi stanno costruendo i registri degli agenti AI, gli elenchi che servono a un commerciante per riconoscere chi bussa alla sua API. Funzionano, ti dicono quale software ha agito e con quale chiave, ma quasi mai rispondono alla domanda che interessava a me: chi c’è dietro quell’agente, e con quale autorità lo ha mandato a spendere?
Nel frattempo in Europa sta succedendo una cosa che fuori dagli addetti ai lavori si racconta poco. Il regolamento eIDAS 2 chiede a ogni Stato membro di offrire ai cittadini un EUDI Wallet entro la fine del 2026, con dentro il PID, l’identità digitale con valore legale, e il protocollo con cui il wallet presenta le credenziali, OpenID4VP, prevede un meccanismo chiamato transaction data che permette alla persona di approvare in modo vincolante un contenuto mostrato a schermo, con una firma che copre esattamente quei byte.
Ho messo insieme le due cose. Se l’EUDI Wallet sa già firmare quello che leggo, può firmare anche il perimetro dentro cui un agente lavora per me, senza inventare identità nuove né chiavi radice nuove.
Il mandato come oggetto da firmare
Il nome viene da plenipotenziario, chi agisce con pieni poteri per conto di un altro, e il motto del progetto lo rovescia: «Mandates, not full powers». Un mandato Plenipo è un oggetto piccolo e chiuso, con le azioni permesse (pagare bollette, comprare biglietti del treno), i beneficiari ammessi, un tetto per singola operazione e uno complessivo, una data di inizio e una di scadenza, la chiave pubblica dell’agente e il numero massimo di sub-deleghe.
Le regole sono poche e testarde. Ogni mandato ha una scadenza obbligatoria, per default al massimo novanta giorni, e non può durare più del PID che lo ha firmato; se l’agente passa il lavoro a un sub-agente, il figlio può solo stringere il perimetro del padre, e un campo che il sistema non conosce viene rifiutato invece di essere ignorato, perché potrebbe essere un vincolo che qualcuno voleva applicare. La revoca passa da una Token Status List, lo standard IETF che l’EUDI Wallet usa già per sospendere le credenziali, e vale dal controllo successivo.
Ho lavorato molto su un dettaglio che sembra minore: quello che la persona vede deve coincidere con quello che viene applicato. Il mandato viaggia in JSON canonico, il wallet ne firma l’hash, e qualunque differenza fra il testo mostrato e il testo verificato porta al rifiuto. In Pelle Digitale avevo provato a descrivere le interfacce come la superficie dove si negozia la fiducia fra noi e le macchine, e qui quella superficie è una schermata di consenso dell’EUDI Wallet, dove la frase che approvi diventa letteralmente la regola.
Dal PID nell’EUDI Wallet al bonifico rifiutato
Il percorso completo ha diversi passaggi, ma si racconta in poche righe. La piattaforma che ospita l’agente prepara il mandato e lo trasforma in una richiesta OpenID4VP, la persona apre il link o inquadra il QR, il wallet mostra il mandato e risponde con il PID e la firma; del PID, per default, la richiesta chiede soltanto nome e cognome. Da lì l’agente porta con sé la catena, cioè la radice firmata dalla persona più le eventuali deleghe, e firma ogni singola chiamata legandola a quella catena precisa e a un nonce che vale una volta sola.
Dall’altra parte la banca, o qualunque servizio che riceve la richiesta, ricontrolla tutto in un ordine stabilito, dalla radice alle deleghe, dal tempo alla firma dell’invocazione, poi l’ambito e la revoca, e solo alla fine riserva l’importo in modo atomico contro ogni tetto della catena. Se qualcosa non torna arriva un rifiuto con un codice preciso, LIMIT_TOTAL o COUNTERPARTY_NOT_PERMITTED o REVOKED, e il denaro resta dov’è. La libreria si integra come guardia attorno a uno strumento MCP oppure come gateway HTTP davanti a un’API esistente.
Quello che mi ha convinto di più l’ho visto nella sandbox, in uno degli scenari scriptati. Un documento contiene un’istruzione nascosta che prova a far pagare un beneficiario diverso da quello vero, il modello può anche cascarci, e la banca rifiuta comunque, perché quel beneficiario nel mandato non c’è. Ne avevo scritto parlando di tool, skill e MCP: ogni strumento che un agente usa ha un gemello organizzativo, e una delega di potere con una soglia è proprio quel gemello, scritto in una forma che una macchina può verificare.
544 test e nessun audit esterno
Oggi Plenipo è alla versione 0.4, con 544 test nella libreria (compresi vettori di prova e test di fuzzing) e 56 nella sandbox, e una CI che gira su Node 22 e 24 con Postgres e Redis. Ci sono gli store condivisi per chi lavora su più istanze, un gateway HTTP con errori in formato RFC 9457, un registro di audit a catena di hash, l’export verso OPA e Cedar per chi ha già un motore di policy, e la gestione delle chiavi in KMS o HSM.
Sul fronte dei formati ho provato a parlare le lingue che i wallet parleranno. Oltre al tipo proprio di Plenipo c’è una mappatura verso l’e-mandate di TS12, la specifica europea per i pagamenti con il wallet, e un modello sperimentale per le persone giuridiche, dove chi firma per un’azienda presenta anche un’attestazione di ruolo e il mandato non può uscire dai poteri che quel ruolo gli dà.
Il codice ha passato due giri di revisione di sicurezza fatti con agenti AI indipendenti, che hanno trovato 35 problemi, sette gravi, tutti corretti con un test che riproduce l’attacco. Lo scrivo per quello che è: due revisioni automatiche non valgono un audit esterno, che non c’è ancora, e il README avverte di non usare Plenipo per proteggere denaro o dati reali. Wallet, PID provider e banca della sandbox sono simulati, la crittografia no.
Il passo che mi ha fatto più piacere è arrivato domenica, quando la Wallet API 2 di walt.id ha completato il flusso sia con il tipo di mandato di Plenipo sia con un e-mandate TS12 legato al PID. Va detto con precisione, perché chi lavora nei pagamenti lo noterebbe subito: quella seconda modalità serve ai wallet configurabili e ai test, mentre i wallet certificati TS12 legheranno l’e-mandate a un’attestazione della banca del pagatore, un passaggio che dipende dai PSP e che ancora non esiste.
Una scelta che passa dal codice pubblico
Ho deciso di aprire tutto, codice, sandbox e specifiche, con licenza Apache 2.0. Un meccanismo di fiducia che nessuno può leggere chiede esattamente il tipo di fede che vorrebbe eliminare, e chi dovrà accettare un pagamento firmato da un agente deve poter verificare ogni riga prima ancora di sedersi a parlarne.
Con la sandbox pubblica ho voluto rendere il tutto toccabile. Si collega Claude come connettore personalizzato all’endpoint MCP della sandbox, gli si chiede di pagare una bolletta, lui chiede un mandato al wallet di test e restituisce un codice di sei caratteri, e su plenipo.net/demo si vede in tempo reale ogni decisione della banca, con i soldi finti e i controlli veri. È il modo più rapido che ho trovato per spiegare a chi lavora nei pagamenti cosa succede quando l’agente sbaglia, o quando qualcuno prova a farlo sbagliare.
Qualche giorno fa avevo scritto della minimizzazione dei dati come scelta su quanto potere diamo all’AI. Un mandato è la stessa scelta presa dall’altro lato, quanto potere diamo all’agente quando esce a spendere per noi.
Il prossimo passo è un telefono
La sperimentazione adesso deve uscire dal laptop. Il prossimo passo è il wallet di riferimento europeo su emulatore Android, che può dichiarare tipi di transaction data propri ma va modificato per mostrare il mandato nella schermata di consenso, e poi IT-Wallet, dove serve un dialogo con chi gestisce lo schema perché oggi non c’è supporto pubblico per tipi personalizzati.
Lato mercato guardo in due direzioni. Le banche e i PSP, che con TS12 dovranno decidere se e come accettare l’e-mandate per il commercio fatto dagli agenti, e le imprese, dove un amministratore che affida un acquisto a un agente lo deve fare dentro i poteri di firma che ha davvero, con un registro che lo attesti. Su entrambi i fronti mancano ancora pezzi normativi, dagli atti di esecuzione sugli European Business Wallet a una regola comune per le attestazioni SCA, e Plenipo per ora riempie i vuoti con formati propri dichiarati come tali.
Non ho ancora capito quale di queste porte si aprirà per prima, e alcune scelte, il legame fra l’attestazione della banca e il PID per esempio, non spetta a me prenderle da solo. Fra pochi mesi però milioni di europei avranno in tasca un’identità con valore legale, e mi sembra il punto giusto da cui far partire la fiducia verso gli agenti, prima che qualcun altro la definisca altrove con regole che non possiamo leggere.
Dove trovare Plenipo
- Codice sorgente e documentazione: github.com/plenipo-net/plenipo
- Pacchetto npm: plenipo
- Sito del progetto: plenipo.net
- Demo dal vivo: plenipo.net/demo
- Sandbox pubblica: sandbox.plenipo.net
- Vulnerabilità: segnalazione privata dal repository GitHub
Chi lavora in una banca, in un PSP o su un wallet e vuole provarlo su un caso concreto mi trova dalla pagina contatti.
Riferimenti: Regolamento (UE) 2024/1183 (eIDAS 2), OpenID4VP 1.0, EUDI TS12.