Come un Agente AI Gestisce un Server di Produzione Reale Tramite Ploi
- devops
- ai-agents
- ploi
- security
L'agente crea una chiave SSH effimera tramite l'API di Ploi, la collega al server, esegue il suo compito e poi la elimina. Zero accesso residuo. Ecco la storia completa del deployment di questo portfolio con quel modello.
Questa non e' una storia ipotetica. Il portfolio che stai leggendo e' stato deployato da un agente AI che ha lavorato attraverso l'API di Ploi. L'agente ha fatto il push del repository su GitHub, ha visto fallire il primo deploy, ha diagnosticato il problema con il lockfile, ha patchato lo script di deploy attraverso l'API, ha fatto un nuovo deploy e ha concluso aggiungendo un cron giornaliero di auto-aggiornamento e confermando che pm2 riavvia il sito al reboot. Ha fatto tutto questo senza lasciare nemmeno una credenziale persistente sul server.
Il Pattern in Una Frase
Dai all'agente un token API di Ploi. Digli: usa l'API per creare una chiave SSH effimera sul server X, connettiti, fai cio' che ti serve, poi elimina la chiave. Questo e' l'intero contratto. L'API di Ploi e' il piano di controllo. SSH e' il canale di fuga per tutto cio' che l'API non puo' fare direttamente. La chiave esiste per la durata di un singolo compito e sparisce nel momento in cui il compito finisce.
Tutto il resto, attivare i deploy, patchare gli script di deploy, aggiungere cron job, collegare repository GitHub, gestire SSL, abilitare la supervisione dei demoni, scorre unicamente attraverso la REST API. Nessun SSH necessario per quelle operazioni. La chiave effimera serve solo per interventi chirurgici lato server che non hanno un equivalente nell'API.

La Storia Reale: Deployment di Questo Portfolio
L'agente ha iniziato con un Hetzner CX22 gia' provisionato da Ploi. Il repository era pronto in locale. Primo compito: fare push al remote GitHub di produzione e collegarlo al sito Ploi. L'agente ha usato l'API di Ploi per associare il repo GitHub al sito, configurare il branch su main e confermare che il webhook fosse in posizione.
Il primo deploy automatico e' partito ed e' fallito. Lo script di deploy chiamava npm ci, ma il lockfile nel repository era stato generato da una versione piu' recente di npm rispetto a quella installata sul server. npm ci e' rigoroso su questo: si rifiuta di installare se il lockfile non corrisponde esattamente alla propria versione.
L'agente ha letto il log del deploy tramite l'API di Ploi, ha identificato l'errore in tre secondi e ha patchato lo script di deploy attraverso una singola chiamata API: sostituire npm ci con npm install. Poi ha attivato un nuovo deploy. Quello ha avuto successo. L'agente non ha avuto bisogno di SSH in nessun punto di questa sequenza. L'API era sufficiente.
Dopo il deploy riuscito, l'agente ha aggiunto due rifiniture: un cron job schedulato alle 3 di mattina che esegue npm install e npm run build e ricarica pm2, e una verifica che pm2 sia configurato come servizio di avvio in modo che il sito sopravviva a un reboot del server. Il cron e' stato creato via API Ploi. La verifica del startup di pm2 ha richiesto una sessione SSH, quindi l'agente ha usato il flusso della chiave effimera.
Il Flusso SSH Effimero, in Codice
Ecco il preciso script shell che l'agente ha usato. Genera una chiave RSA a 4096 bit localmente, registra la meta' pubblica con Ploi, si connette ed esegue il suo comando, e poi elimina la chiave tramite l'API. Nessun materiale della chiave tocca mai un file di configurazione o un gestore di segreti. Vive in memoria e in un file temporaneo per la durata della sessione SSH, poi sparisce.
#!/usr/bin/env bash
# agent-ssh.sh -- ephemeral SSH via Ploi API
# Usage: PLOI_TOKEN=xxx SERVER_ID=4821 SERVER_IP=95.111.42.7 bash agent-ssh.sh
set -euo pipefail
PLOI_TOKEN="$PLOI_TOKEN"
SERVER_ID="$SERVER_ID"
SERVER_IP="$SERVER_IP"
BASE="https://ploi.io/api"
KEY_FILE="/tmp/agent-key-$$.pem"
KEY_NAME="agent-ephemeral-$$"
# 1. Generate an RSA key pair on disk (temp, mode 600)
ssh-keygen -t rsa -b 4096 -N '' -f "$KEY_FILE" -C "$KEY_NAME" >/dev/null 2>&1
chmod 600 "$KEY_FILE"
PUB_KEY="$(cat "$KEY_FILE.pub")"
# 2. Register the public key on the target server via Ploi API
KEY_RESPONSE="$(curl -s -X POST \
-H "Authorization: Bearer $PLOI_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"name":"'"$KEY_NAME"'","key":"'"$PUB_KEY"'"}' \
"$BASE/servers/$SERVER_ID/ssh-keys")"
KEY_ID="$(echo "$KEY_RESPONSE" | python3 -c 'import sys,json; print(json.load(sys.stdin)["sshKey"]["id"])')"
echo "[agent] registered ephemeral key id=$KEY_ID"
# 3. Connect over SSH and run the task
ssh -i "$KEY_FILE" \
-o StrictHostKeyChecking=no \
-o BatchMode=yes \
"ploi@$SERVER_IP" \
"cd /var/www/portfolio/current && npm install --prefer-offline && echo done"
# 4. Delete the key from the server and remove local files
curl -s -X DELETE \
-H "Authorization: Bearer $PLOI_TOKEN" \
-H "Accept: application/json" \
"$BASE/servers/$SERVER_ID/ssh-keys/$KEY_ID"
rm -f "$KEY_FILE" "$KEY_FILE.pub"
echo "[agent] ephemeral key deleted -- server access closed"Il $$ nel nome della chiave incorpora l'ID del processo shell, in modo che esecuzioni concorrenti dell'agente non collidano. La pulizia nel protocollo post-sessione (descritto nella sezione AGENTS.md qui sotto) cattura qualsiasi chiave sopravvissuta a una sessione interrotta controllando l'eta' rispetto al prefisso agent-ephemeral.
Patchare lo Script di Deploy e Aggiungere il Cron via API
Il secondo campione di codice mostra le due chiamate API che l'agente ha fatto dopo aver diagnosticato il deploy fallito: patchare lo script di deploy via PUT, attivare un nuovo deploy e aggiungere il cron di manutenzione giornaliero. Tutte e tre le operazioni sono in un singolo script bash con tre chiamate curl.
#!/usr/bin/env bash
# patch-deploy-and-cron.sh
# Patches the deploy script and adds a daily auto-update cron via Ploi API.
set -euo pipefail
PLOI_TOKEN="$PLOI_TOKEN"
SITE_ID="$SITE_ID"
SERVER_ID="$SERVER_ID"
BASE="https://ploi.io/api"
# ---- 1. Patch the deploy script ----
# The first deploy failed: npm ci rejected the lockfile generated by a newer npm.
# Fix: replace "npm ci" with "npm install" in the deploy script.
NEW_SCRIPT='cd /var/www/portfolio/current
git fetch --depth=1 origin main
git reset --hard origin/main
npm install
npm run build
pm2 reload portfolio --update-env'
curl -s -X PUT \
-H "Authorization: Bearer $PLOI_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"deploy_script":"'"$NEW_SCRIPT"'"}' \
"$BASE/servers/$SERVER_ID/sites/$SITE_ID" | python3 -c 'import sys,json; d=json.load(sys.stdin); print("[patch] site updated:", d.get("site",{}).get("name","ok"))'
# ---- 2. Trigger a fresh deploy ----
curl -s -X POST \
-H "Authorization: Bearer $PLOI_TOKEN" \
-H "Accept: application/json" \
"$BASE/servers/$SERVER_ID/sites/$SITE_ID/deploy" | python3 -c 'import sys,json; print("[deploy] triggered:", json.load(sys.stdin))'
# ---- 3. Add a daily 3am auto-update cron ----
curl -s -X POST \
-H "Authorization: Bearer $PLOI_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"command":"cd /var/www/portfolio/current && npm install && npm run build && pm2 reload portfolio","frequency":"custom","custom_frequency":"0 3 * * *","user":"ploi"}' \
"$BASE/servers/$SERVER_ID/cron-jobs" | python3 -c 'import sys,json; d=json.load(sys.stdin); print("[cron] created id:", d.get("cronJob",{}).get("id","?"))'
echo "[agent] patch, redeploy, and cron setup complete"Nessun SSH, nessuno stato del server, nessuna modifica di file. Lo script di deploy vive in Ploi, il cron vive in Ploi, ed entrambi sono aggiornati attraverso una REST API documentata. Se vuoi verificare cosa e' cambiato e quando, la dashboard di Ploi ha una cronologia completa. Se vuoi ripristinare lo script di deploy, lo fai attraverso la stessa API.
Dare all'Agente Memoria Persistente con AGENTS.md
Un agente AI non ha memoria a lungo termine tra le sessioni a meno che tu non gliene dia una esplicitamente. Ogni volta che inizia una nuova sessione, l'agente legge la directory del progetto. Se hai committato un file AGENTS.md nel repository, l'agente lo legge e conosce immediatamente la topologia del server, le convenzioni di deploy, i modi di fallimento noti e il protocollo di pulizia per le chiavi effimere.
Non e' magia. E' solo un file markdown che l'agente e' istruito a leggere all'inizio di ogni sessione. Ma il risultato e' enorme: niente piu' rispondere alle stesse domande di setup ripetutamente, niente piu' ipotesi conservative su quale endpoint API chiamare, niente piu' dimenticare che npm ci fallira' su questo particolare server.

Ecco l'AGENTS.md effettivo committato in questo repository del portfolio. Documenta il server, la correzione dello script di deploy applicata, la pianificazione del cron e il protocollo di pulizia.
# AGENTS.md -- Infrastructure memory for AI coding agents
## Server
- Provider: Hetzner CX22, Ubuntu 24.04 LTS
- Ploi server ID: 4821 (env: PLOI_SERVER_ID)
- Public IP: 95.111.42.7 (env: SERVER_IP)
- Site ID: 9103 (env: PLOI_SITE_ID)
- Stack: Node 20, nginx, pm2, PostgreSQL 16
## Deploy
Trigger via Ploi API:
POST https://ploi.io/api/servers/4821/sites/9103/deploy
Authorization: Bearer PLOI_TOKEN
Deploy script uses "npm install" (NOT "npm ci" -- lockfile version mismatch on first deploy).
Zero-downtime via pm2 reload.
## Cron
Daily auto-update at 03:00 UTC:
cd /var/www/portfolio/current && npm install && npm run build && pm2 reload portfolio
## Ephemeral SSH
Use agent-ssh.sh for any direct server access. Script mints a key via API, runs the task,
and deletes the key. Do NOT leave persistent keys on the server.
## Post-session cleanup
GET https://ploi.io/api/servers/4821/ssh-keys
DELETE any key with name prefix "agent-ephemeral" older than 60 minutes.Tratta AGENTS.md come un documento vivo. Ogni volta che l'agente cambia qualcosa di significativo, aggiorna il file nello stesso commit. Quando cambi lo script di deploy, registra il cambiamento e il motivo. Quando aggiungi un cron, documentalo. Il file costa poco da mantenere e fa partire ogni futura sessione dell'agente da una posizione informata piuttosto che da zero.
Il Modello di Sicurezza: Minimo Privilegio, Durata Minima
Il token API di Ploi da' all'agente controllo sul piano di gestione del server: script di deploy, cron, siti, demoni, database, SSL. Non da' all'agente una shell. La chiave SSH effimera da' all'agente una shell per una finestra delimitata. Questi sono due livelli di fiducia diversi e l'agente usa ciascuno esattamente quando e' appropriato.
- Il token API non tocca mai il server. Autentica su Ploi, che poi istruisce il server. Se il token viene compromesso, un attaccante puo' riconfigurare i tuoi siti. Non puo' ottenere una shell.
- La chiave SSH effimera tocca il server direttamente ma viene eliminata immediatamente dopo l'uso. La sua finestra di esposizione e' di minuti, non giorni.
- Il token API dovrebbe essere limitato alle risorse minime necessarie. Ploi supporta restrizioni per token per server.
- Se la sessione dell'agente si interrompe dopo la creazione della chiave SSH ma prima di eliminarla, il protocollo di pulizia di AGENTS.md gestisce la situazione: controllare tutte le chiavi con il prefisso effimero e eliminare quelle piu' vecchie di un'ora.
- Nessuna chiave SSH di proprieta' umana deve essere aggiunta al server per il lavoro dell'agente. L'agente gestisce il proprio accesso interamente attraverso l'API.
Questo e' il modello corretto per l'automazione guidata da agenti. L'alternativa, dare all'agente una chiave SSH longeva o una password sudo, significa che una sessione dell'agente compromessa ha accesso persistente equivalente al root al tuo server di produzione. Le chiavi effimere significano che il raggio di esplosione di qualsiasi singola sessione compromessa e' limitato e automaticamente azzerato.
Un agente AI dovrebbe avere il minimo accesso necessario per la durata minima necessaria. L'API di Ploi rende tutto cio' banale da applicare.
Cosa Puo' Fare l'API di Ploi Senza SSH
Vale la pena essere concreti su quanto l'agente puo' fare senza mai aprire una connessione SSH. L'API di Ploi copre l'intera superficie della gestione del server per le applicazioni web tipiche.
- Creare e configurare siti, impostare la document root, collegarsi a un repository GitHub o GitLab.
- Leggere, scrivere e attivare script di deploy.
- Aggiungere, modificare ed eliminare cron job.
- Gestire i worker daemon di supervisor: creare, avviare, fermare, riavviare.
- Creare e distruggere database e utenti di database.
- Emettere e rinnovare certificati SSL via Let's Encrypt.
- Leggere i log di deploy per la diagnosi.
- Aggiungere e rimuovere chiavi SSH per qualsiasi utente sul server.
- Riavviare servizi come nginx, php-fpm e Redis attraverso l'API.
SSH e' riservato per operazioni non esposte attraverso l'API: eseguire comandi arbitrari, ispezionare file di log in tempo reale, o eseguire operazioni sul filesystem una tantum. In pratica, per un portfolio Next.js o Node standard come questo, l'agente ha avuto bisogno di SSH esattamente una volta: per verificare che pm2 fosse registrato come servizio di avvio systemd. Tutto il resto e' passato attraverso l'API.
Questa e' l'architettura. API prima di tutto, SSH come ultima risorsa, chiavi effimere quando SSH e' necessario, e un AGENTS.md committato che da' ad ogni futura sessione il contesto di cui ha bisogno per agire senza chiedere. Il server rimane pulito. L'agente rimane verificabile. E il deploy fallito al primo tentativo e' stato corretto ed e' in esecuzione in produzione, interamente da un agente che non ha mai detenuto una credenziale persistente.