SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-21

علت نمایش http://127.0.0.1:3080 در dsh چیست؟

عبارت http://127.0.0.1:3080 در dsh به دلیل اتصال Web UI به localhost است. برای دسترسی از راه دور، از SSH tunnel استفاده کنید و از انتشار عمومی پورت 3080 بپرهیزید.

معنای dsh web: http://127.0.0.1:3080 چیست

هنگامی که پروفایل وب DeepSeek Harness را روی یک VPS اجرا می‌کنید، دو خط چاپ شده و سپس برنامه منتظر می‌ماند:

dsh web: http://127.0.0.1:3080
Ready.

127.0.0.1 آدرس loopback است. این آدرسی است که یک ماشین برای برقراری ارتباط با خودش از آن استفاده می‌کند. سوکتی که به 127.0.0.1 متصل (bind) شده باشد، فقط اتصالات را از پردازش‌های همان ماشین می‌پذیرد و نه از هیچ جای دیگر. بنابراین، آن خط دو نکته را همزمان به شما می‌گوید: رابط کاربری وب در کجا در حال گوش دادن (listening) است و چه کسی اجازه دسترسی به آن را دارد. فقط ماشینی که dsh روی آن در حال اجراست.

به همین دلیل است که وقتی URL را در مرورگر لپ‌تاپ خود کپی می‌کنید، هیچ اتفاقی نمی‌افتد. 127.0.0.1 لپ‌تاپ شما، همان لپ‌تاپ خودتان است. اما Harness روی 127.0.0.1 سرور VPS در حال گوش دادن است که ماشینی متفاوت با پشتهٔ loopback مجزا می‌باشد. هیچ مشکلی وجود ندارد. شما باید اتصال را به آن سمت هدایت کنید.

فایل README رسمی، مقدار پیش‌فرض را به صراحت بیان می‌کند: «این دستور رابط کاربری وب را اجرا می‌کند که به‌طور پیش‌فرض روی http://127.0.0.1:3080 سرویس‌دهی می‌شود.» آدرس bind از افزونه میزبان وب‌سرور یعنی @deepseek-ai/dsh-host-webserver می‌آید که کلید host آن به این صورت مستند شده است: «میزبان گوش‌دهنده؛ دو مقدار پشتیبانی‌شده loopback و all-interfaces هستند.» تا زمانی که آن را تغییر ندهید، مقدار پیش‌فرض همان loopback است. اگر مفهوم پورت‌ها برای شما جدید است، نحوه عملکرد پورت‌ها در لینوکس مدل آدرس-به‌علاوه-پورت که همه این‌ها بر پایه آن استوار است را پوشش می‌دهد.

چرا رابط کاربری وب فقط به localhost متصل می‌شود

dsh یک ابزار عامل (agent) است. زبانه مرورگر، یک سطح کنترل برای فرآیندی است که دستورات shell را اجرا می‌کند، فایل‌ها را در دایرکتوری کاری انتخابی شما می‌خواند و می‌نویسد، و از کلید API مدل شما استفاده می‌کند. هر کسی که بتواند آن صفحه را بارگذاری کند، می‌تواند تمام این کارها را با دسترسی کاربری که dsh را اجرا کرده است، انجام دهد.

بنابراین پورت 3080 یک داشبورد فقط‌خواندنی نیست. بارگذاری آن صفحه به معنای اجرای دستورات روی سرور است.

رابط کاربری وب را باز می‌کنید و مستقیماً به لیست نشست‌ها (session list) هدایت می‌شوید. هیچ صفحه ورود به سیستمی وجود ندارد، زیرا نسخه پیش‌نمایش توسعه‌دهنده، فاقد حساب کاربری و احراز هویت از راه دور است. در حالت loopback این موضوع منطقی است: سیستم‌عامل کنترل دسترسی را بر عهده دارد و فقط فرآیندهای محلی اجازه عبور دارند. اگر همان سرور را روی 0.0.0.0 در یک VPS با IP عمومی متصل کنید، همان صفحه به کل اینترنت پاسخ می‌دهد، بدون اینکه هیچ محافظی جلوی آن باشد. اسکنرهای خودکار به‌طور مداوم پورت‌های غیرمعمول را بررسی می‌کنند، بنابراین فرض کنید پورت 3080 در صورت انتشار، بلافاصله کشف می‌شود.

پورت 3080 را در فایروال خود باز نکنید و وب‌سرور host را روی یک VPS عمومی به 0.0.0.0 تنظیم نکنید. این ترکیب، دسترسی اجرای دستورات روی سرور شما را به اولین کسی که متصل شود، واگذار می‌کند.

همین استدلال برای هر runtime عاملی که روی سرور قرار می‌دهید صدق می‌کند، به همین دلیل است که اجرای ایمن یک عامل برنامه‌نویسی روی VPS با همین قانون شروع می‌شود: پورت کنترل عامل باید خصوصی باقی بماند و ابزاری که به آن اعتماد دارید، شما را به آن متصل کند.

چگونه رابط کاربری وب dsh را از لپ‌تاپ خود باز کنم؟

سه روش مطمئن برای این کار وجود دارد و در هر سه روش، harness همچنان روی loopback باقی می‌ماند.

  • یک تونل SSH. هیچ سرویس جدیدی روی اینترفیس عمومی گوش نمی‌دهد و شما از قبل اعتبارنامه‌های لازم را دارید. این روشی است که باید استفاده کنید.
  • یک شبکه overlay خصوصی، تا رابط کاربری از دستگاه‌های خودتان قابل دسترسی باشد و برای دیگران نامرئی بماند.
  • یک reverse proxy که TLS (امنیت لایه انتقال) را خاتمه می‌دهد و پیش از هدایت هر درخواستی، درخواست رمز عبور می‌کند.

تفاوت این روش‌ها در نحوه رساندن مرورگر شما به loopback است. در هیچ‌کدام از این روش‌ها نیازی نیست که harness از روی loopback خارج شود.

دسترسی به آن با SSH tunnel

این دستور را روی لپ‌تاپ خود اجرا کنید، نه روی VPS:

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

اجازه دهید در حال اجرا باقی بماند، سپس http://127.0.0.1:3080 را در مرورگر محلی خود باز کنید. رابط کاربری وب بارگذاری می‌شود.

آرگومان -L شامل سه فیلد است که با دو نقطه از هم جدا شده‌اند. اولی پورتی است که باید روی لپ‌تاپ شما باز شود. دومی و سومی آدرس و پورتی هستند که هر اتصال باید به آن هدایت شود. نکته مهم این است: 127.0.0.1 در فیلد میانی، توسط سرور SSH روی VPS و پس از رسیدن ترافیک شما به آنجا، ترجمه (resolve) می‌شود. این یعنی آدرس loopback مربوط به VPS، نه لپ‌تاپ شما. دقیقاً به همین دلیل است که آدرس dsh چاپ شده و تونل کار می‌کند، در حالی که اتصال مستقیم مرورگر کار نمی‌کند.

-N به SSH می‌گوید که هیچ دستور از راه دوری را اجرا نکند، بنابراین شما فقط یک forwarder دریافت می‌کنید و نه یک shell. برای یک تونل در پس‌زمینه که در صورت شکست، با صدای بلند خطا می‌دهد (به جای سکوت):

ssh -N -f -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -L 3080:127.0.0.1:3080 you@your-vps

-f پس از احراز هویت، آن را به پس‌زمینه می‌فرستد. ExitOnForwardFailure=yes از آنچه به نظر می‌رسد مهم‌تر است: بدون آن، SSH حتی زمانی که forward نتواند تنظیم شود با موفقیت متصل می‌شود، بنابراین شما یک نشست فعال دارید اما تونلی که کار نمی‌کند و هیچ هشداری هم دریافت نمی‌کنید. ServerAliveInterval=30 هر 30 ثانیه یک keepalive می‌فرستد تا تونل‌های غیرفعال از timeoutهای NAT (ترجمه آدرس شبکه) در روترهای کافه‌ها و هتل‌ها جان سالم به در ببرند.

آنچه باید ببینید

روی VPS، تأیید کنید که چه چیزی واقعاً در حال گوش دادن است:

ss -ltnp | grep 3080

یک نتیجه سالم، آدرس loopback را نشان می‌دهد:

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

اگر ستون آدرس محلی به جای آن 0.0.0.0:3080 را نشان می‌دهد، رابط کاربری وب روی تمام اینترفیس‌ها از جمله اینترفیس عمومی در دسترس است. آن را متوقف کنید و پیش از انجام هر کار دیگری، bind را اصلاح کنید. اگر ss سوکت را چاپ می‌کند اما فیلد users: را خالی می‌گذارد، آن را با sudo اجرا کنید، زیرا در غیر این صورت نام پردازش برای سوکتی که متعلق به کاربر دیگری است، مخفی می‌ماند.

وقتی تونل از شروع خودداری می‌کند

SSH این پیام را چاپ کرده و خارج می‌شود:

bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080

این مشکل مربوط به لپ‌تاپ شماست، نه سرور. چیزی در سیستم محلی شما قبلاً پورت 3080 را اشغال کرده است، که اغلب یک تونل قدیمی است که فراموش کرده‌اید. به جای آن یک پورت محلی آزاد انتخاب کنید:

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

فقط فیلد اول تغییر کرد، بنابراین اکنون به http://127.0.0.1:3081 مرور می‌کنید در حالی که harness همچنان روی 3080 گوش می‌دهد. این دو عدد هرگز نیازی ندارند که با هم یکی باشند.

اگر تونل بالا می‌آید اما مرورگر گزارش می‌دهد که اتصال رد شده یا پاسخ خالی است، ترافیک به VPS رسیده اما در مقصد چیزی پیدا نکرده است. یا dsh خارج شده است، یا پورت متفاوتی را bind کرده است. با ss -ltnp | grep 3080 روی سرور بررسی کنید.

یک نکته دیگر که کاربران را در اینجا دچار مشکل می‌کند: یک npx @deepseek-ai/dsh web در پیش‌زمینه با بسته شدن shell از بین می‌رود، بنابراین به محض اینکه log out کنید، harness متوقف می‌شود. آن را داخل tmux یا تحت یک systemd user service اجرا کنید، که همان مشکلی است که در نگه داشتن یک coding agent در حال اجرا روی VPS حل شده است. در حالی که روی بخش SSH کار می‌کنید، ایمن‌سازی SSH روی VPS ارزش انجام دادن دارد، زیرا تونل باعث می‌شود ورود SSH شما تنها درب ورودی به agent باشد.

دسترسی از طریق یک شبکه overlay خصوصی

یک شبکه overlay به VPS و لپ‌تاپ شما آدرس‌هایی در یک شبکه خصوصی می‌دهد که فقط دستگاه‌های شما به آن ملحق می‌شوند. Tailscale انتخاب رایجی است و دستور serve آن دقیقاً برای این مورد مناسب است: tailscaled روی VPS اجرا می‌شود و به localhost:3080 متصل می‌گردد، بنابراین harness روی loopback باقی می‌ماند و شما هیچ تغییری در نحوه پیکربندی dsh ایجاد نمی‌کنید.

tailscale serve --bg localhost:3080
tailscale serve status

در این حالت، رابط کاربری از طریق نام دستگاه شما در tailnet و با پروتکل HTTPS در دسترس است، بدون اینکه هیچ پورتی روی اینترفیس عمومی باز باشد. این کار مستلزم فعال بودن گواهی‌های HTTPS برای tailnet شماست، در غیر این صورت serve گواهی معتبری برای ارائه نخواهد داشت. برای غیرفعال کردن مجدد آن، دستور را با off تکرار کنید:

tailscale serve --https=443 off

از serve استفاده کنید، هرگز از funnel استفاده نکنید. قابلیت Funnel همان هدف را در اینترنت عمومی منتشر می‌کند که شما را دوباره به وضعیت یک agent احراز هویت نشده روی یک پورت باز برمی‌گرداند. این دو دستور بسیار شبیه به هم هستند اما عملکردهای متضادی دارند، بنابراین پیش از تایپ هر کدام، تفاوت بین Tailscale Serve و Funnel را مطالعه کنید. Tailscale به عنوان یک شبکه خصوصی به نحوه راه‌اندازی آن می‌پردازد.

دسترسی از طریق reverse proxy با احراز هویت رمز عبور

این گزینه‌ای است که در واقع یک پورت را روی اینترنت منتشر می‌کند، بنابراین احراز هویت تنها مانع بین یک غریبه و اجرای دستورات روی سرور شماست. زمانی این روش را انتخاب کنید که چندین نفر به رابط کاربری نیاز دارند و ایجاد تونل برای هر کدام غیرعملی است.

برنامه اصلی روی 127.0.0.1:3080 باقی می‌ماند. nginx روی همان سرور اجرا می‌شود، بنابراین می‌تواند به loopback دسترسی داشته باشد و روی پورت 443 با یک گواهی و یک فایل رمز عبور گوش می‌دهد.

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 Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 3600s;
        proxy_buffering off;
    }
}

فایل رمز عبور را ایجاد کرده و تنظیمات را reload کنید:

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

nginx -t باید syntax is ok و به دنبال آن test is successful را چاپ کند. reload کردن با یک فایل خراب، ناموفق خواهد بود و پیکربندی در حال اجرا را دست‌نخورده باقی می‌گذارد؛ بنابراین به‌جای restart کورکورانه، خطا را بخوانید.

سه مورد از آن خطوط proxy صرفاً برای تزئین نیستند. هدرهای Upgrade و Connection اجازه می‌دهند handshake مربوط به WebSocket انجام شود و بدون آن‌ها، صفحه بارگذاری می‌شود اما هرگز به‌روزرسانی نمی‌شود. proxy_read_timeout 3600s جایگزین پیش‌فرض 60 ثانیه‌ای می‌شود که در غیر این صورت باعث قطع شدن اجرای طولانی agent در میانه پاسخ شده و رابط کاربری را منجمد نشان می‌دهد. proxy_buffering off خروجی مدل را به‌محض رسیدن به مرورگر می‌فرستد، به‌جای اینکه آن را تا تکمیل پاسخ نگه دارد. پیکربندی reverse proxy در nginx، خط به خط بقیه موارد را توضیح می‌دهد و انتخاب بین nginx، Caddy و Traefik انجام همین کار با گواهی‌های خودکار را پوشش می‌دهد.

صرف‌نظر از اینکه کدام proxy را انتخاب می‌کنید، پورت 3080 را در فایروال بسته نگه دارید تا تنها مسیر ورود، از طریق مسیر احراز هویت‌شده باشد. اصول فایروال ufw قوانین مربوطه را پوشش می‌دهد. احراز هویت پایه (Basic Authentication) روی TLS یک حداقل است، نه یک مدل امنیتی کامل: هر کسی که آن رمز عبور را داشته باشد، به یک shell روی سرور شما دسترسی دارد. هر زمان که می‌توانید، تونل را ترجیح دهید.

چگونه پورت گوش‌دهی وب در dsh را تغییر دهم؟

--port متعلق به برنامه وب است، نه لانچر. مستندات CLI این مثال را مستقیماً ارائه می‌دهد:

dsh --profile web --port 8080

dsh web یک نام مستعار برای --profile web است، بنابراین dsh web --port 8080 همان دستور است. لانچر فقط پرچم‌های (flags) مربوط به خود را تجزیه می‌کند و هر چیزی بعد از آن‌ها را به پروفایل در حال اجرا می‌سپارد. بنابراین پرچم‌های لانچر باید در ابتدا قرار بگیرند و اولین توکنی که لانچر تشخیص نمی‌دهد، شروع آرگومان‌های برنامه است. --port را بعد از پروفایل قرار دهید، هرگز قبل از آن ننویسید.

به جای فرض کردن، URL چاپ شده توسط دستور را بخوانید، زیرا آن خط آدرسی را گزارش می‌دهد که سرور واقعاً به آن متصل شده است. سپس آخرین فیلد تونل خود را مطابق با آن به‌روزرسانی کنید:

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

برای تغییر دائمی، پورت به جای خط فرمان، در پیکربندی پروفایل قرار دارد. پروفایل‌های web و headless در اولین استفاده از قالب‌های پیش‌فرض در مسیر ~/.dsh به‌طور خودکار مقداردهی اولیه می‌شوند. برای مشاهده آنچه پس از ترکیب تمام لایه‌ها واقعاً اعمال شده است:

dsh --dump-config

پلاگین وب‌سرور دقیقاً دو کلید host و port را در اختیار می‌گذارد. تنظیم port روی 0 از سیستم‌عامل درخواست یک پورت آزاد می‌کند، که در مستندات به عنوان "صفر درخواست یک پورت تخصیص‌یافته توسط سیستم‌عامل" ثبت شده است. این کار تضمین می‌کند که هرگز با تداخل مواجه نشوید، اما برای تونل مناسب نیست، زیرا شماره پورت با هر بار راه‌اندازی مجدد تغییر می‌کند.

چرا dsh با خطای address already in use شکست می‌خورد؟

به این دلیل که یک پردازش دیگر در حال حاضر آن آدرس و پورت را در اختیار دارد، بنابراین هسته سیستم‌عامل درخواست bind دوم را رد می‌کند. Node این وضعیت را به شکل زیر گزارش می‌دهد:

Error: listen EADDRINUSE: address already in use 127.0.0.1:3080

پیش از هر تغییری، پردازش مالک را پیدا کنید:

sudo ss -ltnp | grep 3080

فیلد users:(("node",pid=1042,fd=21)) نام پردازش و PID آن را مشخص می‌کند. پاسخ معمول، وجود یک dsh قدیمی است که تصور می‌کردید متوقف شده، اما اغلب همچنان در یک پنجره tmux جداشده (detached) در حال اجراست. آن را با kill 1042 متوقف کنید، یا نمونه جدید را روی پورت دیگری اجرا نمایید. توجه داشته باشید که 127.0.0.1:3080 و 0.0.0.0:3080 نیز با یکدیگر تداخل دارند، زیرا bind کردن روی تمام اینترفیس‌ها، شامل loopback نیز می‌شود.

نسخه را ثابت (Pin) کنید، زیرا این یک پیش‌نمایش توسعه‌دهنده است

فایل README در این باره صریح است: DeepSeek Harness در مرحله پیش‌نمایش توسعه‌دهنده قرار دارد و به‌سرعت در حال تغییر است، بنابراین تغییرات ناسازگار با نسخه‌های قبلی رخ خواهد داد.

دستور npx @deepseek-ai/dsh web هر بار که آن را اجرا می‌کنید، به جدیدترین نسخه منتشرشده اشاره می‌کند. سروری که یک هفته به آن دست نزده‌اید، ممکن است در راه‌اندازی بعدی با یک CLI متفاوت و پرچم‌های (flags) تغییریافته اجرا شود. نسخه را ثابت کنید تا راه‌اندازی مجدد (restart) به معنای ارتقا نباشد:

npx @deepseek-ai/dsh@0.1.0-rc.7 web

تا آگوست 2026، بسته منتشرشده نسخه 0.1.0-rc.7 است. پیش از پذیرش، بررسی کنید که یک دستور npx ساده چه نسخه‌ای را دریافت می‌کند:

npm view @deepseek-ai/dsh version

در نسخه‌های پیش‌نمایش، پرچم‌ها بین لانچر و اپلیکیشن وب جابه‌جا می‌شوند. اگر --port دیگر مطابق آنچه در این راهنما توضیح داده شده عمل نمی‌کند، به‌جای حدس زدن، از خود اپلیکیشن لیست پرچم‌هایش را بخواهید:

dsh --profile web --help

برای خودِ نصب، راه‌اندازی فضای کاری و کلید مدل، به نصب DeepSeek Harness روی یک VPS مراجعه کنید. برای یک راهنمای کوتاه‌تر فقط برای مرحله دسترسی، دسترسی به رابط کاربری وب dsh روی یک VPS بدون ذکر دلایل فنی، تونل را پوشش می‌دهد.

FAQ

چرا نمی‌توانم http://127.0.0.1:3080 را در مرورگر لپ‌تاپ خود باز کنم؟

زیرا 127.0.0.1 به دستگاهی اشاره دارد که پشت آن نشسته‌اید. رابط کاربری وب DeepSeek Harness به آدرس loopback سرور مجازی (VPS) متصل (bind) شده است، بنابراین فقط پردازش‌های روی همان VPS می‌توانند به آن متصل شوند. لپ‌تاپ شما loopback جداگانه خود را دارد و هیچ سرویسی روی پورت 3080 آن گوش نمی‌دهد. پورت را از طریق SSH با دستور ssh -N -L 3080:127.0.0.1:3080 you@your-vps فوروارد کنید و سپس http://127.0.0.1:3080 را به‌صورت محلی باز کنید. بخش میانی آرگومان -L در سمت سرور تحلیل می‌شود و همین موضوع باعث می‌شود که به harness اشاره کند.

آیا اتصال رابط کاربری وب dsh به 0.0.0.0 روی یک VPS عمومی امن است؟

خیر. رابط کاربری وب، سطح کنترل عاملی است که دستورات shell را اجرا کرده و فایل‌ها را با دسترسی کاربری که dsh را اجرا می‌کند، ویرایش می‌نماید؛ نسخه پیش‌نمایش توسعه‌دهنده نیز هیچ صفحه ورودی ندارد. اتصال به تمام رابط‌ها روی یک IP عمومی به این معناست که هر کسی که به پورت 3080 دسترسی پیدا کند، می‌تواند روی سرور شما دستور اجرا کند. اتصال را روی 127.0.0.1 نگه دارید، پورت 3080 را در فایروال ببندید و از یک تونل SSH، یک شبکه خصوصی (overlay network) یا یک reverse proxy که نیاز به رمز عبور دارد، استفاده کنید.

چگونه رابط کاربری وب dsh را پس از بستن نشست SSH فعال نگه دارم؟

یک npx @deepseek-ai/dsh web که در پیش‌زمینه اجرا می‌شود، فرزند shell ورود شماست و با خروج از آن shell کشته می‌شود. آن را درون یک نشست tmux شروع کرده و با Ctrl-b d جدا (detach) کنید، یا آن را به‌عنوان یک systemd user service با قابلیت lingering فعال اجرا نمایید. تونل و harness مستقل از یکدیگر هستند: تا زمانی که harness دارای والدی باشد که پس از خروج شما زنده بماند، می‌توانید تونل SSH را هر چند بار که بخواهید بدون تأثیر بر harness در حال اجرا، قطع و وصل کنید.

چرا رابط کاربری وب dsh هنگام اجرای طولانی‌مدت عامل (agent) در پشت nginx متوقف می‌شود؟

زیرا مقدار پیش‌فرض proxy_read_timeout در nginx برابر با 60 ثانیه است؛ بنابراین اگر اتصالی به مدت یک دقیقه داده‌ای ارسال نکند، آن را می‌بندد که در مراحل طولانی اجرای عامل، این اتفاق به‌راحتی رخ می‌دهد. مقدار proxy_read_timeout 3600s; را در بلاک location تنظیم کنید. دستور proxy_buffering off; را اضافه کنید تا خروجی به‌محض رسیدن به مرورگر ارسال شود و هدرهای Upgrade و Connection را با proxy_http_version 1.1; ارسال کنید تا handshake مربوط به WebSocket با موفقیت انجام شود. بدون این هدرها، صفحه بارگذاری می‌شود اما هرگز به‌روزرسانی دریافت نمی‌کند.