SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-09-08

Cómo acceder a la interfaz web de DSH en un VPS

DSH escucha en 127.0.0.1:3080 y la URL no funciona desde tu portátil. Compara tres formas seguras de acceder al VPS y cuál debes evitar.

Por qué la interfaz web de DSH no se abre desde el portátil

La interfaz web de DSH se enlaza a la interfaz de loopback, por lo que la URL que mostró el terminal sólo funciona en la máquina que la mostró. npx @deepseek-ai/dsh web informa de http://127.0.0.1:3080, y 127.0.0.1 significa «el equipo que lee esta dirección» para cualquier equipo que la lea. El portátil lee esa dirección, consulta su propia interfaz de loopback y no encuentra ningún proceso escuchando allí. Si la dirección es la parte confusa, aquí se explica con más detalle por qué dsh muestra http://127.0.0.1:3080. No cambie la interfaz a la que se enlaza DSH. Cree en su lugar una ruta autenticada desde el portátil hasta la dirección de loopback del VPS.

El valor predeterminado de loopback es correcto, y todos los métodos de esta página lo mantienen. Detrás del puerto 3080 hay un agente que ejecuta comandos de shell en el servidor, y la interfaz web no incluye ninguna página de inicio de sesión. En este caso, loopback es el único control de acceso que tiene DSH: para llegar al socket, ya debe tener un shell en el equipo.

A qué se vincula DSH y cómo comprobarlo

Comprobado el 17 de agosto de 2026, el README de DeepSeek Harness indica que npx @deepseek-ai/dsh web «inicia la interfaz web, que se sirve en http://127.0.0.1:3080 de forma predeterminada». La referencia de la CLI del mismo repositorio documenta --port <num> como una anulación cuyo valor predeterminado es 3080, y --host <addr> como una anulación que «rechaza intencionadamente 0.0.0.0». DSH es una versión preliminar para desarrolladores, y el README advierte en mayúsculas que habrá cambios incompatibles. Confirme ambos valores en su propia instalación en lugar de confiar en cualquier página, incluida esta.

dsh --profile web --dump-config
ss -ltnp | grep 3080

--dump-config imprime el árbol de configuración compuesto sin iniciar el agente, por lo que muestra el host y el puerto que prevalecen tras aplicar todas las capas de parches. ss muestra qué está escuchando realmente en este momento. Una línea correcta tiene este aspecto:

LISTEN 0  511  127.0.0.1:3080  0.0.0.0:*  users:(("node",pid=8123,fd=24))

Lea la dirección situada antes de los dos puntos y nada más. 127.0.0.1:3080 sólo es de bucle local, que es lo que necesita. 0.0.0.0:3080 significa todas las interfaces del equipo, incluida la pública. Si ss no imprime ninguna línea, DSH no se está ejecutando y ningún túnel funcionará hasta que se inicie. Este proceso se explica en instalar y ejecutar DeepSeek Harness en un VPS.

Los indicadores se aplican a una sola ejecución. Para conservar un valor, edite la capa de parches del perfil. dsh --profile <name> inicia el perfil en $DSH_HOME/profiles/<name>, y las capas se aplican en este orden: los parches del paquete, el propio cordis.patch.yml del perfil, el $DSH_HOME/cordis.patch.yml del nivel de usuario y, por último, cualquier superposición --patch. La configuración de escucha se encuentra bajo el complemento @deepseek-ai/dsh-host-webserver, como host y port. Lea su propio árbol compuesto con --dump-config antes de editarlo, porque el perfil web se genera a partir de una plantilla incluida en el primer arranque y la versión preliminar sigue cambiando la estructura de esa plantilla.

La segunda barrera: el límite de confianza de /api

Conseguir que los paquetes lleguen al puerto 3080 resuelve la mitad del problema. DSH aplica una segunda comprobación. Esta comprobación produce el fallo confuso en el que la página carga, aparece el diseño y después nada funciona.

El complemento @deepseek-ai/dsh-client-connection contiene un ajuste trustedHosts, documentado como «Autoridades que esta implementación sirve además de loopback: host:port exactos o host sin puerto, que coincide con cualquier puerto». La barrera rechaza cualquier solicitud /api cuyo encabezado Host no sea de loopback ni figure en esa lista. El navegador muestra el rechazo de esta forma:

transport failure for /api/host.describe: HTTP 403

Por tanto, cualquier proxy que coloque delante de DSH debe declarar el nombre que el usuario escribe en el navegador. La CLI acepta --trusted-host <authority> y puede repetirse:

dsh web --trusted-host dsh.example.com --trusted-host dsh.example.com:8443

La barrera compara el encabezado Host como una cadena literal. Por eso aparecen las dos formas anteriores. Para ella, dsh.example.com y dsh.example.com:8443 son dos autoridades distintas. Lo mismo ocurre con localhost:3080 y 127.0.0.1:3080. Un error 403 que no puede explicar casi siempre se debe a una diferencia de escritura entre la barra de direcciones y la lista de confianza.

La barrera comprueba el origen. No realiza autenticación. El propio navegador impide que una página establezca un encabezado Host, por lo que esta comprobación evita que una página de otro sitio controle su agente. Cualquier cliente que no sea un navegador puede escribir ese encabezado libremente. Considere trustedHosts un ajuste de compatibilidad para proxies, nunca un control de seguridad.

Opción 1: un reenvío local de SSH

Use esta opción primero, porque no añade nada al servidor.

ssh -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vps

-L abre un listener en el portátil y reenvía cada conexión a 127.0.0.1:3080 según se resuelva en el VPS. -N significa «no ejecutar ningún comando remoto», por lo que la sesión sólo transporta el reenvío. Escribir el lado local como 127.0.0.1:3080 en lugar de usar un 3080 sin más es intencionado: fija el listener del portátil en loopback, de modo que una configuración GatewayPorts en la configuración del cliente SSH no pueda volver a publicar el agente en la red a la que está conectado el portátil.

Abra ahora http://localhost:3080 en el navegador. Aquí hay dos ventajas inmediatas. La cabecera Host identifica una autoridad loopback, por lo que la protección /api se supera sin ninguna configuración. Además, los navegadores tratan http://localhost como un contexto seguro. Esto es importante porque la aplicación web DSH llama a crypto.randomUUID() durante el arranque, y los navegadores sólo exponen esa función mediante HTTPS o en un origen loopback.

Añada -f para que ssh pase a segundo plano cuando el reenvío esté activo:

ssh -f -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vps

La contrapartida real. No hay ningún listener nuevo en una dirección pública y no cambia ninguna regla del firewall, por lo que este método ofrece el menor aumento posible de exposición. Todo el control de acceso depende de la configuración SSH. Por tanto, SSH con autenticación exclusiva mediante claves y el acceso por contraseña deshabilitado es un requisito, no un complemento opcional. El coste es que el túnel pertenece a una sola sesión de cliente. Se cierra cuando el portátil entra en suspensión, debe reiniciarlo manualmente y no le ofrece acceso desde un teléfono.

Opción 2: Tailscale serve en el VPS

Tailscale crea una red privada entre tus propios dispositivos. Al instalarlo en el VPS, esa máquina obtiene una dirección 100.x.y.z a la que sólo tus dispositivos pueden acceder mediante enrutamiento.

Unirse a esa red no basta. Aquí es donde suelen producirse los problemas. DSH no escucha en la dirección 100.x.y.z porque escucha en loopback. Un navegador que accede a http://100.x.y.z:3080 recibe un rechazo de conexión porque ningún socket está asociado a esa dirección.

tailscale serve es el componente que conecta ambos extremos. Se ejecuta en el VPS, acepta tráfico de tu red privada y lo envía mediante proxy a una dirección local:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
sudo tailscale serve --bg --https=443 localhost:3080
tailscale serve status

tailscale serve status muestra la URL con el formato https://<machine>.<tailnet>.ts.net/. Inicia DSH con ese nombre como confiable. De lo contrario, /api devuelve 403, exactamente como se describió antes:

dsh web --trusted-host your-vps.your-tailnet.ts.net

Esto también elimina el problema del contexto seguro. Tailscale termina el HTTPS real con un certificado emitido para ese nombre, por lo que crypto.randomUUID() está disponible y la interfaz se inicia en el navegador del teléfono. Nada llega a Internet pública porque serve publica sólo dentro de tu red privada.

No sustituyas tailscale funnel aquí. Pertenece a la misma familia de comandos, pero está orientado a Internet pública. Pondría un agente con acceso al shell en un nombre de host que cualquiera puede resolver. Lee cómo serve mantiene el tráfico dentro de tu tailnet mientras funnel lo publica antes de ejecutar cualquiera de los dos. Para la versión de toda esta configuración adaptada al teléfono, consulta cómo acceder desde el teléfono a un agente autoalojado.

La desventaja es que añade una dependencia. Cada dispositivo que necesite la interfaz debe unirse a la red, y un servidor de coordinación que no administras decide quién puede acceder a ella. Si esto no es aceptable, un servidor de control Headscale autoalojado utiliza el mismo protocolo en hardware que administras.

Opción 3: un proxy inverso TLS con autenticación

Use esta opción cuando el navegador no pueda unirse a una red privada, por ejemplo, desde una máquina que usted no administra. Ahora está publicando un nombre de host en Internet, por lo que la autenticación debe ser real y debe proporcionarla el proxy. DSH no proporciona ninguna.

TLS significa seguridad de la capa de transporte y es el cifrado que se utiliza detrás de https://. Configure nginx para usar el puerto de loopback y coloque una contraseña delante. El bloque map pertenece al contexto http, así que debe tener su propio archivo en /etc/nginx/conf.d/:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Después, configure el sitio:

server {
    listen 443 ssl;
    server_name dsh.example.com;

    ssl_certificate     /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;

    auth_basic           "dsh";
    auth_basic_user_file /etc/nginx/dsh.htpasswd;

    location / {
        proxy_pass http://127.0.0.1:3080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600s;
        proxy_buffering    off;
    }
}

Cree el archivo de contraseñas, pruebe la configuración y recargue nginx:

sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/dsh.htpasswd you
sudo nginx -t && sudo systemctl reload nginx

nginx -t debería mostrar syntax is ok y test is successful. Si muestra un error, nginx seguirá sirviendo la configuración anterior, por lo que nada se habrá interrumpido todavía. Después, inicie DSH con el nombre público como nombre de confianza, porque proxy_set_header Host $host reenvía dsh.example.com y el mecanismo de protección lo rechaza en caso contrario:

dsh web --trusted-host dsh.example.com

Hay dos directivas fundamentales. Las cabeceras Upgrade y Connection mantienen abierta la conexión de larga duración de la interfaz. Sin ellas, nginx la cierra y la interfaz se reconecta en un bucle mientras muestra un estado obsoleto. proxy_read_timeout 3600s evita que nginx interrumpa una ejecución del agente que tarde más de los 60 segundos predeterminados. El motivo del resto de la configuración se explica en una configuración de proxy inverso de nginx explicada directiva por directiva, y la elección del proxy se compara en nginx frente a Caddy y Traefik.

Debe tener claro qué ofrece basic auth. Detiene los escáneres oportunistas y es mucho mejor que dejar un puerto abierto. Sin embargo, sólo proporciona una contraseña compartida delante de un shell, sin un segundo factor ni una forma de revocar el acceso de una sola persona. Cualquiera que la conozca puede ejecutar comandos como el usuario con el que se ejecuta DSH. Si varias personas necesitan acceso, coloque delante un proxy de identidad real y lea las reglas de seguridad para ejecutar un agente de programación en un VPS antes de ampliar aún más el acceso.

Qué ocurre si se enlaza a 0.0.0.0 y se abre el puerto

El atajo evidente es volver a enlazar el servicio y abrir el firewall. DSH bloquea la primera parte. --host <addr> rechaza intencionadamente 0.0.0.0, y el plugin @deepseek-ai/dsh-host-webserver documenta host como un valor que sólo acepta 127.0.0.1 o 0.0.0.0, por lo que hay un valor compatible y otro que la CLI rechaza. Existen plugins de la comunidad que eliminan esta comprobación. Se distribuyen con advertencias, y las advertencias son correctas. La misma capa de plugins resulta mucho más útil cuando se usa en sentido contrario, para que los límites de gasto y las reglas de permisos de las herramientas restrinjan lo que el agente puede hacer en lugar de ampliar dónde escucha.

Esto es lo que se obtiene realmente al forzarlo. El puerto 3080 responde en la dirección IP pública. No hay ninguna página de inicio de sesión. La barrera /api inspecciona una cabecera Host que cualquier cliente que no sea un navegador establece por su cuenta, por lo que incluir su dirección en trustedHosts no cambia nada para un atacante que controle curl. Lo que ha publicado es un agente que ejecuta comandos de shell con su usuario, junto con las credenciales almacenadas en $DSH_HOME, donde DSH guarda perfiles y claves de API. Eso permite la ejecución remota de código en su servidor y además puede generar cargos en la cuenta de su proveedor. Los escáneres recorren continuamente todo el espacio de direcciones, por lo que una IP no anunciada no es una medida de control. Todos los secretos que el agente puede leer salen junto con el shell.

Todos los métodos anteriores existen para que nunca tenga que hacer esto. El reenvío SSH es la opción predeterminada adecuada para una persona con un solo portátil. Cambie a tailscale serve cuando intervenga un teléfono o cuando quiera un certificado real, y reserve el reverse proxy público para el caso en que un navegador que usted no administra tenga que acceder a la interfaz, con autenticación real delante.

FAQ

¿Por qué http://127.0.0.1:3080 no se abre desde mi portátil?

Porque 127.0.0.1 significa «la máquina que lee esta dirección». DSH la muestra en el VPS, donde es correcta. Tu portátil lee la misma cadena y consulta su propia interfaz de loopback, donde no hay ningún proceso escuchando. Comprueba el lado del servidor con ss -ltnp | grep 3080 en el VPS. Una línea que muestre 127.0.0.1:3080 significa que DSH se está ejecutando y está enlazado deliberadamente a loopback. Lo que necesitas es un túnel o un proxy, no una dirección de enlace distinta.

¿Puedo ejecutar dsh web con --host 0.0.0.0?

No. La referencia de la CLI del repositorio, comprobada el 17 August 2026, documenta --host <addr> como una opción de sustitución que rechaza intencionadamente 0.0.0.0. La razón es que DSH ejecuta comandos de shell y no incluye ninguna página de inicio de sesión. Por tanto, enlazarlo a todas las interfaces publica un shell sin autenticación en tu dirección IP pública. Los parches de la comunidad eliminan esta comprobación. Si aplicas uno, el firewall y la autenticación que DSH no proporciona quedan bajo tu responsabilidad.

¿Por qué todas las llamadas a /api devuelven HTTP 403 detrás de mi proxy inverso?

La barrera de confianza /api rechaza cualquier petición cuyo encabezado Host no sea una dirección de loopback ni figure en trustedHosts. Detrás de un proxy, ese encabezado contiene tu nombre público, por lo que la barrera lo rechaza y el navegador registra transport failure for /api/host.describe: HTTP 403. Inicia DSH con --trusted-host <your name> y respeta exactamente la misma escritura, incluido el puerto cuando la URL lo contiene, porque la comparación se hace como una coincidencia literal de cadenas.

¿Por qué la interfaz de DSH se carga, pero nunca termina de iniciarse mediante HTTP sin cifrar?

La aplicación web llama a crypto.randomUUID() durante el arranque. Los navegadores sólo exponen esa función en un contexto seguro: HTTPS o un origen de loopback como http://localhost. Si se sirve mediante HTTP sin cifrar desde una dirección IP independiente, la función no está definida. Por eso fallan las llamadas que dependen de ella y la interfaz nunca se completa. Servirla mediante HTTPS con tailscale serve o mediante un proxy inverso TLS resuelve el problema. También lo resuelve acceder a ella mediante un reenvío SSH en http://localhost.

¿Qué método debe usar un único desarrollador?

El reenvío local de SSH. No añade nada al servidor, no modifica ninguna regla del firewall y reutiliza la clave SSH en la que ya confías. Ejecuta ssh -f -N -L 127.0.0.1:3080:127.0.0.1:3080 you@your-vps y abre http://localhost:3080. Pasa a tailscale serve cuando quieras usar la interfaz desde un teléfono o necesites un acceso que siga disponible aunque el portátil entre en suspensión.