DeepSeek Harness su VPS: come tenerlo privato
Installa DeepSeek Harness su un VPS Linux, fissa la versione npm, scopri cosa può fare un plugin e accedi alla UI web sulla porta 3080 con un tunnel SSH.
Che cos'è DeepSeek Harness
DeepSeek Harness (dsh) è un runtime per agenti Node.js che puoi eseguire su un VPS (virtual private server). Il modo sicuro per eseguirlo consiste nel vincolarlo a 127.0.0.1 e raggiungerlo dal browser tramite un tunnel SSH (secure shell). Espone una UI web (user interface) sulla porta 3080 invece di funzionare in un terminale. Questo server web non richiede una password propria. Di conseguenza, una porta 3080 pubblicata mette a disposizione di chiunque la trovi un agente in grado di leggere i tuoi file ed eseguire comandi con l'account del tuo utente Linux.
DeepSeek lo ha rilasciato il 13 agosto 2026 con licenza MIT, come pacchetto npm @deepseek-ai/dsh. Il progetto si presenta come un'anteprima per sviluppatori e specifica che sono previste modifiche incompatibili. Ogni numero di versione riportato di seguito è riferito a uno snapshot di agosto 2026. Controlla quindi il repository prima di copiarne i contenuti su un server importante.
Un principio ricorre nell'intero design: tutto è un plugin. L'adapter del modello, il registro degli strumenti, il log delle sessioni, la sandbox, lo scheduler e lo stesso ciclo dell'agente sono plugin caricati in un unico contesto condiviso, e ciascuno può essere sostituito. Non esiste un core privilegiato che i plugin si limitano a estendere. È questo che rende interessante provare l'harness, ma è anche il punto in cui si trova l'unico rischio concreto.
Un harness non è un modello
Il harness esegue il ciclo dell’agente. Il ragionamento avviene in un modello esterno, quindi nulla funziona finché non si fornisce una chiave API (application programming interface) oppure l’indirizzo di un endpoint del modello ospitato autonomamente.
La configurazione si trova nell’interfaccia, in Settings e poi Models. Il catalogo include schede preconfigurate per i principali provider API (DeepSeek, OpenAI, Anthropic), nelle quali è possibile incollare una chiave. L’opzione interessante è "Add a custom provider": richiede un ID del provider, un nome visualizzato, un URL di base, un protocollo API e una credenziale. Utilizza il protocollo compatibile con OpenAI, quindi funziona con qualsiasi gateway o server locale che implementi questo protocollo. I provider personalizzati possono anche interrogare l’endpoint compatibile con OpenAI GET /models per compilare automaticamente l’elenco dei modelli.
In questo modo si indirizza il harness verso un modello eseguito sullo stesso VPS. Ollama espone un’API compatibile con OpenAI all’indirizzo http://127.0.0.1:11434/v1/ e richiede di compilare il campo della chiave API con una stringa qualsiasi, per convenzione ollama, perché il campo è obbligatorio e viene poi ignorato. Stabilire se un modello abbastanza piccolo da entrare nel VPS sia adatto a eseguire un agente è la questione più complessa. La differenza tra Ollama e vLLM come server locale per modelli determina quanta RAM richiede la soluzione.
Le chiavi immesse nell’interfaccia sono di sola scrittura. Il harness le memorizza in $DSH_HOME/.credentials.yaml e conserva soltanto un riferimento alla credenziale in settings.yaml. $DSH_HOME usa per impostazione predefinita ~/.dsh. Tratta quel file come un file contenente password, perché lo è: chiunque possa leggerlo può utilizzare il tuo budget API.
Cosa serve prima dell’installazione
- una VPS con Ubuntu 24.04 o un’altra distribuzione Linux aggiornata, con accesso SSH
- Node.js 22.19 o versione successiva della linea 22.x, oppure Node.js 24 o versione successiva, cioè le versioni con cui il progetto viene compilato e testato
- un normale account utente, non
root, perché l’agent esegue i comandi shell con l’utente che ha avviato il processo pnpmnel PATH se prevedi di installare plugin, perché il comando del plugin lo esegue tramite una shell- la porta 3080 chiusa nel firewall e nel firewall di rete separato del provider
Il pacchetto nodejs fornito da Ubuntu è più vecchio della versione richiesta dall’harness. Installa quindi Node da NodeSource o tramite nvm, invece di usare apt install nodejs. Se la VPS è appena stata installata, conviene mettere in sicurezza SSH prima di qualsiasi altra operazione: il tunnel da cui stai per dipendere è sicuro quanto il server SSH che lo gestisce.
Installare DeepSeek Harness su un VPS con una versione bloccata
node --version
npx @deepseek-ai/dsh@0.1.0-rc.6 webnpx scarica il pacchetto ed esegue il relativo binario dsh. web è un alias di --profile web, che avvia l'applicazione web; il processo stampa l'indirizzo su cui è in ascolto. Il valore predefinito è http://127.0.0.1:3080.
Bloccare la versione è importante. npx @deepseek-ai/dsh web risolve il tag indicato da latest nel momento in cui viene eseguito e il progetto ha già pubblicato diversi release candidate, indicando che sono previste modifiche incompatibili. 0.1.0-rc.6 è il valore a cui puntava latest il 13 August 2026. Una versione bloccata garantisce che il server configurato oggi si comporti allo stesso modo il mese prossimo. L'aggiornamento diventa così una decisione esplicita, non una conseguenza inattesa.
Per l'uso quotidiano, installalo una volta invece di risolvere nuovamente la versione a ogni avvio.
npm install -g @deepseek-ai/dsh@0.1.0-rc.6
dsh --profile web --helpVale la pena eseguire anche la seconda riga, perché il launcher e l'applicazione web hanno set di flag distinti. dsh --help mostra le opzioni del launcher. dsh --profile web --help mostra i flag accettati dall'applicazione web: è qui che si trovano --port, --host e il parametro ripetibile --trusted-host.
Ora verifica su quale indirizzo è in ascolto.
ss -tlnp | grep 3080La colonna dell'indirizzo locale dovrebbe contenere 127.0.0.1:3080. Se contiene 0.0.0.0:3080, l'interfaccia è raggiungibile da Internet. Arresta il processo prima di eseguire qualsiasi altra operazione.
Perché non devi mai pubblicare la porta 3080
Il web server non dispone di un livello di autenticazione. La sua configurazione espone un host di ascolto e una porta di ascolto, e questa è l’intera superficie esposta. Il controllo degli accessi per le distribuzioni non locali è un’impostazione separata per gli host attendibili, non una schermata di login.
Ora considera cosa si trova dietro quella porta. L’agente modifica i file nell’area di lavoro ed esegue comandi shell, mentre le credenziali del provider sono archiviate su disco accanto all’agente. Una porta 3080 aperta equivale quindi a una shell remota con un’interfaccia di chat, eseguita con l’utente che l’ha avviata e con la tua chiave API associata. Non serve alcun exploit. È sufficiente conoscere il numero della porta, e gli scanner individuano le porte nel giro di poche ore dalla connessione di un host.
Anche la CLI (interfaccia a riga di comando) conferma questa impostazione. A partire da 0.1.0-rc.6, non supporta deliberatamente --host 0.0.0.0 ed esce con un errore di utilizzo invece di avviarsi. Questo rifiuto è una funzione di sicurezza: non cercare quindi una patch per rimuoverlo.
Esistono altre due configurazioni ragionevoli quando un tunnel non è adatto. Inserisci il server in una rete overlay privata, in modo che disponga di un indirizzo raggiungibile tramite routing solo dai tuoi dispositivi: è ciò che offre un server di controllo Headscale self-hosted. Oppure anteponi un reverse proxy che autentichi la richiesta prima che raggiunga la porta 3080, ad esempio un server single sign-on Authentik che esegue la forward auth. Un reverse proxy privo di autenticazione non è un controllo di sicurezza. È soltanto un URL più lungo.
Accedere all’interfaccia web tramite un tunnel SSH
Eseguire questo comando sul laptop, non sul server.
ssh -N -L 3080:127.0.0.1:3080 you@your-server-L apre la porta 3080 sul laptop e inoltra tutto ciò che vi si connette attraverso la sessione SSH crittografata. La parte 127.0.0.1:3080 viene risolta sul server, quindi la connessione arriva all’harness dall’interfaccia loopback, esattamente come se si operasse direttamente sulla macchina. -N indica di non avviare una shell remota, perché serve soltanto il forwarding.
Quindi aprire http://127.0.0.1:3080 nel browser locale. Se la porta 3080 è già occupata sul laptop, modificare il numero a sinistra: ssh -N -L 3180:127.0.0.1:3080 you@your-server, quindi aprire http://127.0.0.1:3180. Il numero a sinistra è locale, mentre quello a destra appartiene al server. Pertanto, si modifica soltanto il numero a sinistra.
Salvarlo in ~/.ssh/config e non inserirlo più manualmente.
Host dsh
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
LocalForward 3080 127.0.0.1:3080A questo punto, ssh -N dsh avvia il tunnel. Se il browser segnala che la connessione è stata rifiutata, di solito il tunnel è attivo ma sull’estremità remota non è in ascolto alcun servizio, perché SSH inoltra la porta indipendentemente dal fatto che l’harness sia in esecuzione. Verificare il server con il comando ss riportato sopra.
Mantieni l’harness in esecuzione dopo il logout
Un comando npx termina insieme alla shell. Un servizio utente systemd continua a funzionare e riavvia l’harness dopo un arresto anomalo o un riavvio.
loginctl enable-linger $USER
mkdir -p ~/.config/systemd/user
command -v dshenable-linger è importante perché i servizi utente normalmente si arrestano al termine dell’ultima sessione. Senza questa impostazione, l’harness termina non appena chiudi il tunnel. Usa il percorso assoluto restituito da command -v dsh e inseriscilo nell’unità, perché systemd non cerca nel PATH costruito dalla shell di login.
[Unit]
Description=DeepSeek Harness web UI
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/projects/site
ExecStart=/usr/local/bin/dsh web
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetWorkingDirectory non è un dettaglio estetico. Il processo dsh usa la directory da cui viene avviato come posizione predefinita nel filesystem. Se il servizio viene avviato nella directory sbagliata, l’agente usa un workspace predefinito errato. Puoi comunque scegliere il workspace nell’interfaccia utente.
systemctl --user daemon-reload
systemctl --user enable --now dsh
systemctl --user status dshUn’unità che non si avvia usa quasi sempre un percorso ExecStart errato oppure una versione di Node rifiutata dal binario. journalctl --user -u dsh -n 50 indica quale dei due problemi si è verificato. Lo stesso schema permette di mantenere in esecuzione qualsiasi agente di coding su un VPS, con modalità di errore identiche.
Cosa può fare un plugin
Un plugin è un modulo che aggiunge servizi, eventi tipizzati ed effetti reversibili a un contesto condiviso. È importante esaminare con attenzione i punti di estensione:
- registrare un provider del modello su
ctx.llm - aggiungere strumenti accessibili al modello su
ctx.tools - fornire il backend della shell dietro
ctx.shell - fornire l'accesso al filesystem o applicare policy dietro
ctx.fs - registrare comandi dell'utente su
ctx.commands - eseguire attività in background tramite
ctx.jobs - avvolgere i processi generati con un backend
ctx.sandbox - intercettare richieste e chiamate agli strumenti tramite gli eventi
agent/*etools/* - estendere lo stato persistente della sessione
- controllare l'interfaccia tramite
ctx.agents
Leggi questo elenco dal punto di vista di un attaccante. Un plugin può fornire il livello filesystem e il livello shell, oltre a inserirsi nel percorso di ogni chiamata allo strumento eseguita dal modello. Nessuna finestra di autorizzazione separa un plugin da questi punti di integrazione, perché un plugin è normale codice Node caricato nello stesso processo di tutto il resto. Installare un plugin significa eseguire codice di terze parti con i permessi del tuo agent e i permessi del tuo agent corrispondono a quelli del tuo utente Unix.
È la stessa decisione sulla fiducia che prendi quando colleghi un server MCP a un agent su un VPS, dove MCP è il model context protocol. Per questo eseguire in sicurezza un coding agent su un VPS parte dall'account con cui viene eseguito, non dal modello. Per lo stesso motivo gli attacchi alla supply chain di npm hanno un impatto così grave sui server: la compromissione avviene durante l'installazione e non viene mostrata alcuna richiesta di conferma.
Da dove provengono i plugin
I plugin risiedono nei profili. Un profilo è una composizione denominata memorizzata in $DSH_HOME, che per impostazione predefinita è ~/.dsh, e ogni directory del profilo contiene i plugin esterni che installa. La CLI li gestisce inoltrando gli argomenti direttamente a pnpm e usando la directory del profilo come directory di lavoro.
dsh plugin --profile web add github:deepseek-harness/turtle-ui
dsh plugin --profile web remove turtle-uiPoiché gli argomenti raggiungono pnpm senza modifiche, add, remove, update e why si comportano come in qualsiasi progetto pnpm, e un plugin può essere un pacchetto npm o un riferimento GitHub. pnpm deve essere già disponibile nel PATH. Con Node 22 e versioni successive, corepack enable pnpm lo inserisce nel PATH.
L'individuazione dei plugin avviene tramite un topic GitHub. Gli autori dei plugin aggiungono il topic dsh-plugin al proprio repository; per trovare i plugin disponibili si consulta quindi quel topic. Un topic è un'etichetta che l'autore applica al proprio repository. Nessuno lo esamina né lo firma, e la pagina del topic ordina i repository in base alle stelle, che misurano la popolarità, non la sicurezza.
Quattro abitudini rendono questa gestione sostenibile. Leggete il codice sorgente prima dell'installazione, perché la maggior parte dei plugin è abbastanza piccola da poter essere esaminata in dieci minuti. Bloccate la versione o il commit esatto invece di seguire un branch. Eseguite l'harness con un utente che non possieda altro, su un VPS che siate disposti a ricreare. Assegnate all'agent una propria API key con un proprio limite di spesa, separata dalla key usata dai servizi di produzione.
Se preferite confrontare le architetture prima di sceglierne una, l'harness multi-agent Omnigent affronta lo stesso problema con una struttura diversa; le differenze diventano evidenti quando entrano in gioco i plugin.
Che cosa si rompe per primo
Node è troppo vecchio. Il progetto richiede Node 22.19 e versioni successive della serie 22.x, oppure Node 24 o versioni successive, e la CI esegue i test su queste versioni. Un runtime più vecchio non si avvia perché il codice usa sintassi e API che non supporta. Esegui node --version prima di qualsiasi altra operazione.
La porta 3080 è già occupata. Potrebbe essere in uso da un secondo harness, da un processo rimasto attivo o da un'applicazione indipendente che usa anch'essa la porta 3080. Individuala con ss -tlnp | grep 3080, quindi arrestala oppure avvia l'harness su un'altra porta con dsh web --port 3180. --port appartiene all'applicazione web, quindi va eseguito dopo web.
Il browser non riesce a connettersi tramite il tunnel. Verifica di avere aperto 127.0.0.1 e non l'indirizzo pubblico del server, perché la porta inoltrata esiste solo sul laptop. Verifica quindi che l'harness sia in ascolto sul server, poiché SSH configura l'inoltro anche se dall'altra parte non risponde alcun processo.
dsh plugin si interrompe immediatamente. Il comando è un wrapper per pnpm, quindi l'assenza del binario pnpm ne provoca l'interruzione prima dell'avvio di qualsiasi attività dei plugin.
L'agent non riesce a vedere il progetto. Per impostazione predefinita, il workspace corrisponde alla directory da cui è stato avviato il processo. Di conseguenza, un'unità il cui WorkingDirectory è la directory home assegna all'agent la directory home. Seleziona il workspace nell'interfaccia oppure correggi l'unità e ricaricala.
FAQ
È sicuro esporre la web UI di DeepSeek Harness sulla porta 3080?
No. Il server web non dispone di un sistema di accesso proprio e l'agente che lo esegue modifica file ed esegue comandi shell con l'account che ha avviato il processo; la chiave API del provider è archiviata sullo stesso disco. Mantieni il listener su 127.0.0.1 e raggiungilo tramite un tunnel SSH. Funziona anche una rete overlay privata oppure un reverse proxy che autentica ogni richiesta prima che raggiunga la porta. A partire dalla versione 0.1.0-rc.6, la CLI rifiuta --host 0.0.0.0 e termina con un errore di utilizzo: questo indica chiaramente come gli autori valutano questa scelta.
Mi serve una chiave API DeepSeek o posso usare un modello locale?
Entrambe le opzioni funzionano, perché harness è un runtime e non un modello. In Settings, quindi Models, puoi incollare una chiave nella scheda di un provider del catalogo oppure scegliere "Add a custom provider" e specificare un URL di base che supporti il protocollo compatibile con OpenAI. Un server Ollama locale risponde su http://127.0.0.1:11434/v1/ e accetta qualsiasi stringa nel campo della chiave API. Le chiavi vengono salvate in $DSH_HOME/.credentials.yaml, che per impostazione predefinita è ~/.dsh/.credentials.yaml.
Che cosa permette effettivamente di fare a un plugin l'installazione di un plugin DeepSeek Harness?
Le stesse operazioni consentite all'account che esegue harness. Un plugin è codice Node caricato nello stesso processo e i punti di estensione includono il backend shell, il livello del filesystem, il registro degli strumenti e gli eventi che racchiudono ogni chiamata agli strumenti. Nulla isola un plugin da questi punti di integrazione, a meno che non sia il plugin stesso a fornire il sandboxing. Leggi il codice sorgente prima dell'installazione ed esegui harness con un account che non possieda dati o risorse importanti.
Quale versione devo installare e continuerà a funzionare?
Installa una versione esatta, ad esempio npx @deepseek-ai/dsh@0.1.0-rc.6 web. Questa è la versione indicata dal tag latest il 13 agosto 2026. Il progetto si definisce un'anteprima per sviluppatori e dichiara che sono previste modifiche incompatibili, quindi un comando senza versione fissata può comportarsi in modo diverso da un giorno all'altro. Controlla il repository prima dell'aggiornamento e considera probabile che le chiavi di configurazione e le interfacce dei plugin cambino mentre la versione inizia ancora con 0.