npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hi-dsh/tailscale

v0.1.0

Published

Tailnet-only remote access for a loopback-bound dsh web server: identity-gated loopback proxy plus an automatic tailscale serve HTTPS route. 让 dsh 在不暴露任何对外端口的前提下,从你的 Tailscale 虚拟局域网内安全访问。

Readme

@hi-dsh/tailscale

让 dsh web 只对你的 Tailscale 虚拟局域网开放——装完即可用,不需要任何配置项、不需要复制 token、不需要加 --trusted-host。

装好后打开 https://<你的节点>.<你的tailnet>.ts.net/ 就是 DSH 界面。没有加入你 tailnet 的计算机在网络上根本碰不到它。


它解决的是什么问题

dsh web 默认只监听 127.0.0.1,而且故意拒绝 --host 0.0.0.0:它后面跑的是会执行 shell 命令的 agent,一个可达的源站就等于远程代码执行。所以"安全地远程访问"只有一条路——源站留在环回地址,前面放一道门。

但这道门不能只靠 tailscale serve 裸转发,因为 DSH 自己还有两道门:

  1. Host/Origin 信任栅栏(/api):只接受 loopback 的 Host,或显式声明为可信的权威。tailscale serve 会把 Host: <节点>.<tailnet>.ts.net 原样透传,于是 /api 直接 403——这是"我明明配了 serve 却打不开"的根因。
  2. 浏览器会话认证:每个进程随机生成一个 launch token,用一个 GET /?token=… 换发绑定 authority 的签名 Cookie,之后 /、/api、WebSocket 都要靠它。没有它就 401。

本插件把这两件事一起解决,且不修改 DSH 任何内部状态:它在进程内起一个只监听 127.0.0.1 的代理,验证 Tailscale 身份之后才把 Host 重写成环回权威,并复用 DSH 自己的 token 交换流程完成首次配对。

安全模型

四层,逐层独立成立:

| 层 | 机制 | 挡住什么 | |---|---|---| | 网络 | tailscale serve 的监听只存在于 tailnet 接口上,且只应答 Host 为本节点 MagicDNS 名的请求(直接用 tailnet IP 访问返回 400) | 未加入 tailnet 的计算机(你要求的那条硬保证) | | 身份 | 每个请求必须带 tailscaled 注入的 tailscale-user-login;再用 tailscale whois 回查 x-forwarded-for 的归属登录名并核对;默认白名单为本机 Tailscale 账号所有者 | 同一 tailnet 内的其他账号;伪造头(本机之外的进程无法产生该头) | | 传输 | 代理只绑 127.0.0.1,不新开任何对外端口;TLS 由 Tailscale 用真实证书终止 | 局域网扫描、端口暴露 | | 应用 | DSH 自己的 Host 栅栏与会话 Cookie 认证原样保留,代理只是替已验证身份重放其配对流程 | CSRF / DNS rebinding;未认证的 API 访问 |

需要显式说明的边界:本机进程仍等同于完全信任(环回监听上的身份头可被本机代码伪造)。这不增加任何新暴露面——本机代码本来就能直连 127.0.0.1:3080。真正的信任边界是"tailnet 成员"和"本机",两者都在你手里。

安装

# 1. 装进 web profile
dsh plugin --profile web add @hi-dsh/tailscale

#    (本地开发时改用仓库目录:dsh plugin --profile web add link:/path/to/hi-dsh-tailscale)

# 2. 把包名加入 profile 的 dsh.profile.bundles 数组
#    编辑 ~/.dsh/profiles/web/package.json:
#      "dsh": { "profile": { "bundles": [
#        "@deepseek-ai/dsh-base",
#        "@deepseek-ai/dsh-web-app",
#        "@hi-dsh/tailscale"
#      ] } }

# 3. 重启 dsh web
dsh web

启动日志会出现一行:

dsh tailscale: https://your-node.your-tailnet.ts.net/  (tailnet only; identity: [email protected])

前置条件只有两个,都与你是否已经会用 Tailscale 一致:

  • 本机装了 Tailscale 并已登录;
  • tailnet 已启用 MagicDNS 与 HTTPS 证书(Tailscale 管理台 → DNS → HTTPS Certificates)。 未启用时 tailscale serve --https 会失败,插件会记录原因并保持关闭状态,不会影响 DSH 正常启动。

使用

用任何已加入 tailnet 的设备(手机、笔记本)打开上面那行 URL 即可。第一次会自动跳转一次完成配对,之后该浏览器 30 天内免配对。

  • 不需要把 token 发到手机上;
  • 不需要 --trusted-host;
  • 不需要改 Tailscale ACL(要按人收窄见下方配置);
  • 不需要固定端口。

配置(全部可选)

在 ~/.dsh/profiles/web/cordis.patch.yml 里按 id 覆盖:

- id: hi-dsh-tailscale
  config:
    httpsPorts: [8443]                    # 默认 [443, 8443, 4443],按序自检回退
    allowLogins: [[email protected], [email protected]]
    # listenPort: 3081                   # 默认 0 = 自动(优先用 dsh 端口 +1…+8)
    # verifyWhois: true                  # 默认开;关掉只校验身份头
    # tailscaleBin: /usr/local/bin/tailscale
    # printUrl: true
    # replaceDeadRoutes: true            # 默认开:接管指向已死环回端口的旧路由
    # logRequests: true                  # 默认开:每次经代理的请求打一行(方法/路径/状态/耗时/字节)
    # enabled: false                     # 临时关闭整个插件

allowLogins 不写时自动取本机 Tailscale 账号所有者(tailscale status --json 的 Self.UserID → User[].LoginName),所以个人 tailnet 无需任何配置。

关于端口:为什么不是简单的 --https=443

插件不会盲目占用 443。对每个候选端口它都做一件事:配好之后用真实 HTTPS 请求探测自己的探针路径,只有确认应答来自本代理才采用,否则立刻回滚换下一个。

这不是过度设计。在开发这台机器上,Docker 正监听 *:443:tailscale serve --https=443 会"配置成功",但 TLS 握手被 Docker 接走并重置,远端完全连不上——没有自检就会静默失效。插件实测行为:

port 443  : probe 000  → rolled back
port 8443 : probe 303  → claimed

同理,若某端口上已存在指向其他存活服务的 serve 路由,插件会明确跳过并打印冲突对象,绝不覆盖别人的服务。

幂等与生命周期

  • 监听端口优先取 dsh端口+1…+8,因此重启后通常复用同一个端口,serve 路由无需改动。
  • 进程被强杀(SIGKILL)导致路由残留时,下次启动会识别出"指向已死环回端口"的旧路由并接管(replaceDeadRoutes)。
  • 插件停止时(DSH 退出、profile 重载)会关闭监听,并撤掉本次由它安装的 serve 路由;若复用的是已存在的路由则保持不动。
  • 客户端断开会被传导到上游:浏览器放弃一个流式响应(手机上很常见——链路抖动后重连)时,代理立即结束对应的上游请求。没有这一步,每次重连都会在上游留下一个永远在产生数据、却没人读取的流,越积越多。test/abort-propagation.mjs 用一个永不结束的源站锁住这条行为。

故障排查

| 现象 | 原因与处理 | |---|---| | 启动日志没有 dsh tailscale: 行 | 未在 web profile 挂载,或 enabled: false;确认 dsh.profile.bundles 里有它 | | 日志出现 tailscale CLI not found | 没装 Tailscale,或 CLI 不在 PATH;macOS 用 GUI 安装时插件会自动找 /Applications/Tailscale.app/Contents/MacOS/Tailscale | | this node has no MagicDNS name | Tailscale 未登录/未连接,或 tailnet 未启用 MagicDNS | | no usable HTTPS port among … | 候选端口都被占用或被自检否决;看上面几行日志里每个端口的探测结果,必要时换 httpsPorts | | 端口上显示 already serves …; leaving it alone | 该端口有别的服务;换端口,或先 tailscale serve --https=<port> off | | 浏览器 403 且页面显示 @hi-dsh/tailscale: refused (…) | 身份没通过:换用白名单内的 Tailscale 账号、确认 tailscale whois 能解析来源地址 | | 浏览器 401 | 走到了源站但没配对成功;清掉该站点 Cookie 后重新打开 URL |

已知边界

  • 不做 Funnel:插件只配置 serve(仅 tailnet)。把 DSH 暴露到公网与它的安全前提(源站本应不可达)直接冲突。
  • 多实例:同一节点上跑多个 dsh web 时,先启动的实例会占用 443/8443/4443;后启动的实例会发现端口上是指向存活服务的路由并跳过,日志会说明冲突对象。可显式给第二个实例配不同的 httpsPorts。
  • 依赖 DSH 公开契约:connection.authenticatedUrl()(取 launch token)与 /api 的 Host 栅栏语义。它不读写私有字段,也不伪造 Cookie。

English summary

@hi-dsh/tailscale gives a loopback-bound dsh web a tailnet-only HTTPS URL with no configuration. It starts an identity-gated proxy on 127.0.0.1, points a tailscale serve --https route at it, and prints https://<node>.<tailnet>.ts.net/. Every request must carry a tailscaled-issued identity header, cross-checked with tailscale whois; only then is Host rewritten to the loopback origin so DSH's own Host/Origin fence and browser-session authentication still apply. First visit pairs itself through DSH's existing token exchange, so there is no token to copy and no --trusted-host to add. Machines outside the tailnet cannot reach it at all.

License

MIT