La tua missione, se deciderai di accettarla: gli agenti stanno lavorando nel lab, a tre stanze e un salto di rete di distanza, e tu sei in coda con un telefono in mano. Il lavoro non può aspettare che tu torni a sederti. L’approvazione che lo blocca è a un tocco di distanza. L’agente terrà la linea finché qualcuno non risponde, e quel qualcuno sei tu.

Ho già spostato i miei agenti di coding fuori dal laptop, in Ghost Protocol . Ora girano su una macchina sempre accesa nel lab, senza supervisione, e scrivono codice alle 3 di notte mentre il coperchio del laptop è chiuso. Questo ha risolto il problema del compute e ne ha lasciato al suo posto uno più piccolo e più stupido: la console era ancora nel lab.

Un agente che gira senza supervisione non è la stessa cosa di un agente che gira senza essere osservato. Si ferma a fare domande. Chiede il permesso prima di toccare le cose. Finisce una run e vuole una direzione. Se l’unico modo di rispondere è camminare fino a una scrivania, allora una macchina sempre accesa ha solo spostato il collo di bottiglia, non lo ha rimosso.

Questa è l’altra metà: la console in tasca, e un relay tuo che collega le due cose.

Prerequisiti

  • Una macchina Linux o macOS sempre accesa che esegue Pi, con un model provider funzionante.
  • Una mesh VPN privata che colleghi l’host del relay, quella macchina e il tuo telefono (io uso Tailscale).
  • Un posto dove far girare il relay: Docker, o una delle alternative qui sotto.
  • Un iPhone con l’app Un Bien. Questa guida è per iOS; la documentazione del progetto elenca anche macOS.
  • Testato su: un host Proxmox, Docker su Ubuntu dentro container LXC, Pi 1.1 e l’ultima versione di Un Bien al momento della scrittura (ottobre 2026).

I pezzi, e cos’è davvero Un Bien

Un Bien è un client nativo per le sessioni di Pi coding agent , con una propria documentazione di setup . Non è un servizio hosted, non è un login, e non nasce puntato sull’infrastruttura di nessuno. Cinque pezzi, e quello in mezzo lo porti tu:

  • la estensione Pi, che aggiunge il comando /unbien, il client del relay e la agent mesh a qualsiasi sessione Pi da terminale;
  • il relay, un piccolo server WebSocket che ospiti tu, il punto d’incontro tra la tua macchina e il tuo telefono;
  • l’app, il client nativo iOS;
  • il launcher, un daemon opzionale che permette all’app di avviare sessioni anche quando non c’è nessun Pi in esecuzione;
  • la CLI, un client da terminale opzionale che si collega alle stesse sessioni da qualsiasi shell.
phone (Un Bien) ---> relay <--- Pi sessions
                       ^
                       +------- launcher (optional)

every arrow runs inside the mesh VPN

La VPN trasporta il traffico. Il relay lo instrada. Nessuno dei due è crittografia end-to-end, e non esiste un relay di default, ed è voluto. Leggi la prossima sezione prima di decidere dove metterai il tuo.

Il relay: un container sulla macchina del lab

La documentazione di Un Bien consiglia di far girare il relay come servizio systemd o launchd su una macchina sempre accesa. Io lo faccio girare in Docker perché è così che gira il resto del mio lab: un hypervisor Proxmox, servizi in Docker dentro i propri container LXC, tutto raggiungibile su una mesh privata. Un relay è uno in più. Si compila dal repo e parte con una restart policy, così sopravvive ai reboot senza che io faccia niente:

git clone https://github.com/georgeharker/un-bien
docker build -t un-bien-relay ./un-bien/relay
docker run -d --name un-bien-relay -p <tailnet-ip>:3000:3000 \
  -v un-bien-data:/data --restart unless-stopped un-bien-relay

<tailnet-ip> è l’indirizzo Tailscale dell’host (tailscale ip -4). Pubblicare la porta su quell’indirizzo invece che su tutte le interfacce tiene fuori la LAN, ma è un solo controllo, non un isolamento completo: controlla anche il firewall dell’host e le altre porte pubblicate. Se Tailscale non è ancora attivo quando Docker avvia il container al boot, il bind può fallire, quindi riavvia l’host una volta e verifica che il relay torni su. Il volume conserva il database dei membri tra un upgrade e l’altro. Verifica che risponda (da un’altra macchina sulla mesh, perché localhost non corrisponde più all’indirizzo a cui è legato):

curl -s http://<tailnet-ip>:3000/health   # 200 OK

Niente Docker sul tuo host? Due alternative arrivano allo stesso punto, un relay che riparte da solo: la guida Homebrew su una macchina Mac o Linux, oppure cargo install un-bien-relay seguito da /unbien install relay da dentro Pi.

Sappi dov’è il confine di fiducia. Il relay è una pipe di instradamento, non un confine di riservatezza. La VPN cifra il traffico in transito, ma il relay stesso serve testo in chiaro e il suo operatore può vedere il plaintext che instrada: Un Bien non è cifrato end-to-end. Per questo non esiste un relay di default. Tratta l’host del relay come infrastruttura fidata e limita l’accesso di conseguenza.

Il perimetro è la mesh

Niente qui apre una porta verso internet. Non è proprio la stessa cosa di niente raggiungibile: se non leghi la porta pubblicata come sopra, il relay ascolta su ogni interfaccia IPv4 del suo host, quindi la LAN su cui sta può parlargli che tu lo volessi o no. L’host del relay, le macchine di lavoro e il telefono stanno tutti sulla stessa mesh VPN, quindi il telefono raggiunge il relay su un overlay cifrato a prescindere dalla rete a cui è collegata ciascuna estremità. Nel mio caso è Tailscale, ma conta più la proprietà che il prodotto: qualsiasi overlay basato su WireGuard dà la stessa postura, e l’HTTP in chiaro dentro il tunnel va bene perché il tunnel è la cifratura.

La cifratura però non è controllo degli accessi. La VPN protegge il trasporto tra i tuoi dispositivi. Se un telefono possa guidare una macchina lo decidono livelli separati: il pairing dei dispositivi, chi può raggiungere l’host e l’opt-in al remote launch, di cui parlo più avanti.

Questa singola decisione è ciò che fa funzionare tutto il resto. L’URL del relay che usa il telefono è il nome privato sulla mesh dell’host del relay, non un indirizzo pubblico, e si risolve allo stesso modo sia che io sia a casa, in un bar o su un treno.

Punta Pi al relay e fai il pairing

Sulla macchina di lavoro, installa Pi, poi l’estensione, che entra come qualsiasi altro pacchetto Pi:

npm install -g --ignore-scripts @earendil-works/pi-coding-agent
pi install npm:@geohar/un-bien

Poi, da dentro Pi, accedi a un model provider e punta l’estensione al relay:

/login
/model
/unbien set-relay http://<relay-host>:3000
/unbien
/unbien pair

/unbien lancia il wizard di primo avvio (nome dell’agente, sessione di default, se questo terminale entra nel relay) e a ogni esecuzione successiva si limita a rientrare nella mesh e ad avviare il client del relay. /unbien pair stampa un QR code; scansionalo dall’app e la macchina è tua.

Due cose da sapere prima di fare il pairing di qualsiasi cosa.

Il pairing è per macchina, non per sessione. Fai il pairing una volta e ogni processo Pi su quella macchina accetta il dispositivo, compreso il daemon del launcher. Non stai approvando una sessione, stai approvando una macchina.

La tua identità non lascia mai i tuoi dispositivi. L’app genera una Owner key al primo avvio e la tiene lì. Non c’è nessun account e nessun flusso di recupero, ed è questo il punto: nessun operatore può riemetterla. Il backup è iCloud Keychain, e perdere la chiave significa perdere l’identità.

Il codice di pairing è monouso e di breve durata. Se scade, esegui di nuovo /unbien pair. /unbien devices elenca i dispositivi accoppiati; /unbien revoke ne toglie uno.

Non hai l’app a portata di mano? La CLI si collega alle stesse sessioni da qualsiasi shell:

npm install -g @geohar/unbien-cli
unbien connect '<unbien://pair?t=…&epk=…>'   # first run: pair with the machine
unbien connect --list
unbien connect --session-name <name>

La CLI fa il pairing separatamente dall’app, quindi una shell nuova ha bisogno di quella stringa unbien:// una volta, incollata da sotto il QR, prima che --list restituisca qualcosa.

Come ci si sente davvero a controllare da remoto

La lista Sessions contiene due tipi di riga, e la differenza è tutto il workflow remoto.

Una sessione è un agente già in esecuzione, con la sua working directory. Toccala e sei dentro la trascrizione: i tuoi messaggi, il ragionamento del modello collassato, la prosa dell’assistente renderizzata in markdown con codice evidenziato, e una tool card per ogni chiamata, etichettata dal tool (bash, edit) con un segno di successo. Una card edit ha un selettore Diff / Content, così puoi leggere la patch o il file risultante senza lasciare il telefono.

Una macchina è una macchina accoppiata senza sessione Pi attiva, mostrata con uno spinner mentre l’app cerca il daemon del launcher. Quella riga è il percorso di avvio remoto, ed è la differenza tra osservare il lab e operarlo.

Dentro una sessione, i controlli che contano:

  • L’ask sheet. Quando un agente si ferma a chiedere, la domanda arriva come un vero sheet con spiegazioni per ogni opzione. Scegli, aggiungi una nota se serve, invia, e il turno continua. Una domanda in attesa mette un badge sulla sessione in Home, quindi un ask che scatta mentre guardi altrove non è invisibile. Può rispondere uno qualsiasi dei due lati: rispondi dal terminale e lo sheet sul telefono si chiude da solo.
  • Invia subito o accoda. Due pulsanti di invio, e la distinzione è quella che ti evita di interrompere una run lunga. La freccia invia subito; il vassoio trattiene la tua istruzione finché il turno corrente non finisce.
  • Il pulsante stop. Mentre un turno è in esecuzione, l’invio diventa uno stop rosso. È il tuo interrupt, da un telefono, senza uccidere la sessione.
  • Il menu di controllo. Modello, livello di thinking, uso dei token e se il contesto è stato compattato, modificabili a sessione in corso. Ho abbassato di un livello un modello costoso da una sala d’attesa perché il lavoro rimasto era meccanico.
  • Subagent. Quando l’agente delega, il subagent compare in tre posti insieme: come tool card nella trascrizione, nel pannello Agents e annidato sotto il suo genitore nella lista Sessions. Toccarlo apre la trascrizione del subagent, leggibile come una sessione a sé.
  • Fork e branch. Tieni premuto su un messaggio per ripartire da lì, o come nuova sessione o come nuovo percorso dentro questa.

Una trappola, ed è una trappola di iOS: mettere l’app in background sospende il socket del relay, quindi i push in tempo reale durante quella finestra si perdono, e gli ask rimasti molto a lungo senza risposta scadono sulla macchina dopo dieci minuti con un avviso. Non si perde nulla, perché tornando in primo piano viene riproposto tutto ciò che è ancora in sospeso. Ma se una domanda smette di comparire, rispondi dal terminale e vai avanti con la run.

Leggi quella lista come una gerarchia, non come un tour delle funzioni. L’ask sheet è ciò che uso ogni giorno. Il cambio di modello è ciò a cui ricorro quando una run supera il modello economico con cui l’avevo iniziata. Fork e branch sono ciò che dimentico di avere finché una direzione non si rivela sbagliata. Tutto risponde a una sola domanda a cui non puoi rispondere da una scrivania dove non sei seduto: questa run va bene, o ha bisogno di me?

Avviare una sessione quando non gira niente

Collegarsi è la parte facile. Il caso interessante è una macchina inattiva alle 2 di notte: niente Pi, niente sessione, solo una scatola.

È a questo che serve il daemon del launcher. È un peer leggero della mesh e non una sessione Pi, quindi riusa l’identità accoppiata e la configurazione del relay della macchina, entra in una control room a livello di macchina, annuncia di poter avviare sessioni e, su richiesta, lancia una finestra pi in tmux (o herdr).

⚠️ Leggi questo prima di abilitare il remote launch. Sono in gioco tre permessi separati: connettersi (il pairing), avviare (un opt-in per directory) ed eseguire tool (per niente limitato). Un Pi avviato esegue Bash, Edit e Write senza chiedere, quindi configura prima i permessi dei tool di Pi. Dettagli alla fine di questa sezione.

/unbien install launcher

Non usare /unbien install all qui. La documentazione è esplicita: sia /unbien install nudo sia /unbien install all installano anche un servizio relay, quindi su una macchina che già esegue il relay Docker ne ottieni un secondo che litiga per la porta 3000.

Cosa crea l’installer dipende dal tipo di macchina, e questa dipendenza vale la pena di capirla prima di fidarsi. Su macOS diventa un launchd agent. Su una macchina Linux bare-metal diventa un servizio systemd --user, e c’è un secondo interruttore da ricordare: loginctl enable-linger "$USER". Senza, logind tratta il tuo logout come la fine dell’account e abbatte lo user manager, portandosi via il launcher. Il daemon muore con la tua ultima sessione SSH e la macchina sparisce in silenzio dalla lista, senza che venga loggato nulla di abbastanza rumoroso da farti accorgere.

Se invece gli agenti girano dentro container Docker, come i miei, niente di tutto questo si applica, perché non c’è systemd in cui installare. Un container normalmente non ha un’istanza systemd --user, quindi l’installer del servizio non ha nulla con cui parlare. Per quel caso il launcher viene distribuito come binario autonomo in foreground, senza alcun service manager di mezzo:

npm install -g @geohar/un-bien-launcher
unbien-launcher

In un container o in una sessione tmux nuda, esegui il launcher in foreground e lascia che sia ciò che già supervisiona quella macchina a tenerlo vivo. Stesso requisito, meccanismo diverso.

Il remote launch è opt-in per directory. Imposta allow_remote_launch: true nel .pi/un-bien/config.json delle directory in cui sei disposto a far partire lavoro, oppure defaults.allow_remote_launch in ~/.pi/extensions/un-bien.json per consentirlo a livello di macchina. Il solo pairing non dà al telefono l’esecuzione di codice, perché il telefono non può allargare da solo quell’opt-in. Quello che dà è il controllo: un Pi avviato eredita la stessa configurazione di una run interattiva, quindi l’esecuzione dei tool non è limitata, esattamente come dice l’avviso qui sopra. Accoppia un telefono a una macchina del lab solo se ti andrebbe bene che qualcuno rispondesse ai suoi prompt.

Opzionale: la agent mesh

La stessa infrastruttura porta una seconda capacità che non costa nulla in più da attivare: una agent mesh. Le sessioni Pi su una macchina si trovano tra loro tramite un broker locale, e il relay estende questo schema tra macchine, solo per lo stesso owner. Le sessioni si scoprono con list_peers e si scambiano messaggi con agent_send, che per un invio unicast restituisce un ACK di consegna invece di bloccarsi in attesa di una risposta. Un agente che lavora sul front end può chiedere all’agente che lavora sulle API se un campo è ancora nullable. Gli indirizzi sono chiavi di instradamento opache: copiali tali e quali da list_peers, non costruirne mai uno a mano.

Test di accettazione

Cinque controlli, in ordine. Se uno fallisce, fermati lì e sistemalo prima di andare avanti.

  1. Relay: curl -s http://<tailnet-ip>:3000/health restituisce 200 da un’altra macchina sulla mesh.
  2. Pairing: dopo /unbien pair, la sessione Pi compare nell’app.
  3. Ask: fai in modo che l’agente ti chieda qualcosa, e rispondi dal telefono.
  4. Launch (solo se l’hai abilitato): avvia una sessione nuova dalla riga della macchina mentre non gira nessun Pi.
  5. Durabilità: esci da ogni sessione SSH e riavvia l’host una volta, poi verifica che la macchina compaia ancora nell’app.

Dove morde

Due cose che preferisco tu legga qui piuttosto che scoprire alle 3 di notte:

  • I permessi di Pi sono il vero cancello. Una sessione avviata esegue i suoi tool senza chiedere, quindi il pairing non è il controllo.
  • Il launcher è durevole quanto ciò che lo supervisiona. Su un host systemd, dimenticare linger somiglia in tutto e per tutto all’agente che crolla, quando in realtà è il daemon che muore con la tua ultima sessione SSH. Su un host a container, il supervisor che ti sei costruito da solo è il pezzo più probabile da trovare silenziosamente mancante.

Missione compiuta: la console ha lasciato la scrivania ed è finita nella tua tasca, e gli agenti continuano a lavorare mentre tu sei lontano da loro. Prossimo obiettivo: la mesh sotto tutto questo, dove sessioni Pi indipendenti si passano messaggi e coordinano il lavoro senza di te.