工具大全
Claude 指南作者:Coocon2026年8月29日32 次阅读约 10 分钟阅读

把家里的 Mac mini 变成 24 小时在线的 Claude Code 工作站:claudecodeui + SSH 反向隧道,手机浏览器随时接管

家里的 Mac mini 是常年开机的:Claude Code 在上面同时管着五六个项目的工作区,长任务一跑就是几十分钟。问题是人不总在家——在外面想看一眼任务跑到哪了、批准一个权限请求、给它派个新活,怎么办?

目标很具体:在任何有浏览器的设备上打开一个网址,登录,就能看到并操作 Mac mini 上的 Claude Code 会话。不装客户端、不连 VPN,公司电脑和手机都能用。

这套方案上线一周,每天在用。本文按真实决策顺序写:先讲两个选型(Web 界面选什么、通道选什么),再给完整落地配置,然后是实际效果截图和运行数据,最后是已经踩过的坑。

选型一:Web 界面用什么

「浏览器里操作 Claude Code」有四条路,我逐一排除到最后一个:

官方 Web 版(claude.ai/code):最先排除。它跑在 Anthropic 的云沙箱里,操作的是沙箱里的代码副本——而我要的是操作 Mac mini 本地的工作区:本地的仓库、本地的 .env、本地已经登录好的各种 CLI。两者根本不是一个东西。

ttyd + tmux(终端 Web 化):架构上完全可行,把 tmux 会话通过 ttyd 挂到网页上就是一个远程终端。但在手机上用过五分钟就知道问题在哪:触屏敲终端是折磨,方向键、Ctrl 组合键、滚动回看历史输出,每一样都很别扭。它给你的是「一块远程屏幕」,而不是「为移动端设计的操作界面」。

code-server(VS Code Web 版):能用,但绕。它本体是编辑器,Claude Code 还得在它的集成终端里跑——等于套了一层重壳(内存占用以 GB 计)来获得一个更差的终端体验,移动端布局也基本不可用。

claudecodeui(开源,13.5k stars):最终选择。决定性的一条是它的数据模型——

claudecodeui 不维护自己的会话状态,它直接读 ~/.claude/projects/ 下的会话 JSONL 文件。这意味着你在终端里开的 Claude Code 会话,网页上看到的是同一份;反过来在网页上发起的会话,回到电脑上 claude --resume 也能接着聊。Web 界面只是同一份数据的另一个视图,不产生「网页会话」和「终端会话」两套账。

这一条直接命中我的核心场景:外出时接管的就是出门前在终端里开的那个会话,而不是另起炉灶。其余加分项:

  • 移动端优先的 UI:项目列表抽屉、会话流、底部输入框,是按手机屏幕设计的,不是桌面布局硬缩
  • 自带认证:JWT 登录 + bcrypt 密码,登录接口天然适合在 nginx 层再加限流(后文有配置)
  • 不止聊天:内置文件浏览器(带编辑)、git 面板、真·终端(WebSocket),聊天解决不了的时候还有兜底
  • 开源可改:这点一周内就兑现了——上线第二天撞上一个上游 bug,fork 下来自己修了(见「踩过的坑」)

实测足迹很轻:Node 单进程,RSS 稳定在 170MB 左右,对一台还要跑正经任务的 Mac mini 来说无感。

选型二:为什么 SSH 反向隧道,而不是 Tailscale

家庭宽带没有公网 IP,这是前提。「从外面够到家里的机器」教科书答案是 Tailscale,但对这个具体需求——任何浏览器可达的 Web 入口——SSH 反向隧道在四个维度上更合适:

1. 访问端零安装(决定性)。 Tailscale 的模型是「设备入网」:每台访问设备都要装客户端、登录账号。手机可以装,公司电脑往往不让装,临时借用的设备更不可能。SSH 隧道 + nginx 的产出是一个普通 HTTPS 网址,任何有浏览器的东西都能用。「零安装」不是锦上添花,它决定了这个入口在多少真实场景下可用。

2. 链路自己可控。 Tailscale 靠 NAT 打洞直连,打不通就回落 DERP 中继——国内运营商级 NAT 加上对端网络限制,打洞成功率并不乐观,回落后流量绕 Tailscale 的海外节点,延迟和稳定性都不受你控制。SSH 隧道的链路是固定且自选的:Mac mini → 你挑的 VPS → 你。线路好不好买 VPS 之前就知道,出问题 ssh -v 一眼看穿,没有打洞、中继、第三方控制面这些「今天怎么又慢了」的玄学层。

3. 暴露面是一个端口,不是一台整机。 Tailscale 入网后整台机器对网内可达(所有监听端口),信任边界是「账号安全 + ACL 写对」。反向隧道暴露的是恰好一个端口上的恰好一个服务,且该端口在 VPS 上只监听回环——公网唯一能碰的是 nginx 上那一个 URL。审计是「三道门锁好了没」,不是「整张网的 ACL 有没有漏」。

4. 复用已有资产。 我的 VPS 上 nginx、证书自动续期、日志体系都是现成的,这套方案只新增一个 vhost 和一条隧道;Tailscale 则要引入新账号体系 + 每台设备一个常驻 agent + 一套 ACL 配置语言。

反过来,什么时候该选 Tailscale:只从自己的设备访问且每台都能装客户端;要的是整机能力(远程桌面、SMB、直接 SSH);没有 VPS 也不想维护。以及一条要诚实说的——本方案的入口是公网可达的,安全完全依赖认证那几道门;Tailscale 的服务只在私有网络内可见,天然少一类风险。Web 界面没有可靠认证的话,不要用本方案裸奔。

顺带一句 Cloudflare Tunnel:思路与本方案相同(从内网拨出、边缘做入口)且免费,没有 VPS 的话是最好替代;有 VPS 的话,自己的 nginx 在超时策略、限流、日志上都更可控。

架构

[Mac mini 家中]                      [VPS 公网]                      [任意浏览器]
claudecodeui                SSH 反向隧道        nginx (TLS+限流)
127.0.0.1:18300   ────────►  127.0.0.1:18300  ────────►  https://code.example.com
(只监听回环)      ssh -R       (只监听回环)      反向代理

三个要点:Web 界面只监听回环(局域网都碰不到);隧道由 Mac mini 主动拨出(所以家里不需要公网 IP、不需要改光猫);VPS 上的隧道落点同样只监听回环,公网唯一入口是 nginx。

以下配置中域名、IP、用户名均为占位符。

落地:Mac mini 侧

claudecodeui 安装与守护

git clone https://github.com/siteboon/claudecodeui.git ~/tools/claudecodeui
cd ~/tools/claudecodeui
npm install && npm run build

.env 三行,关键是绑回环:

SERVER_PORT=18300
HOST=127.0.0.1
NODE_ENV=production

用 launchd 做成开机自起的常驻服务,~/Library/LaunchAgents/com.example.claudecodeui.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.example.claudecodeui</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/node</string>
    <string>dist-server/server/index.js</string>
  </array>
  <key>WorkingDirectory</key><string>/Users/YOUR_USER/tools/claudecodeui</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    <key>HOME</key><string>/Users/YOUR_USER</string>
  </dict>
  <key>KeepAlive</key><true/>
  <key>RunAtLoad</key><true/>
  <key>StandardOutPath</key><string>/Users/YOUR_USER/tools/logs/claudecodeui/server.log</string>
  <key>StandardErrorPath</key><string>/Users/YOUR_USER/tools/logs/claudecodeui/server.err.log</string>
</dict>
</plist>

一个容易踩的点:launchd 环境里没有你的 shell 配置PATHHOME 必须显式写,否则 node 找不到、claude CLI 也找不到。

反向隧道,同样交给 launchd

macOS 上不需要 autossh,launchd 的 KeepAlive 就是最好的守护。~/Library/LaunchAgents/com.example.ccui-tunnel.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.example.ccui-tunnel</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/ssh</string>
    <string>-N</string>
    <string>-o</string><string>ExitOnForwardFailure=yes</string>
    <string>-o</string><string>ServerAliveInterval=30</string>
    <string>-o</string><string>ServerAliveCountMax=3</string>
    <string>-R</string><string>127.0.0.1:18300:127.0.0.1:18300</string>
    <string>tunnel@203.0.113.10</string>
  </array>
  <key>KeepAlive</key><true/>
  <key>RunAtLoad</key><true/>
  <key>ThrottleInterval</key><integer>15</integer>
</dict>
</plist>

参数为什么这么配:

  • -R 127.0.0.1:18300:127.0.0.1:18300:两端端口取一致省心;显式写 127.0.0.1: 前缀,确保 VPS 上的落点不对公网监听
  • ExitOnForwardFailure=yes:转发建立失败时让 ssh 干脆退出,而不是揣着一条废连接假装活着——退出了 launchd 才知道要拉起重连。不配这条,断线后隧道假活,你在外面干着急
  • ServerAliveInterval=30 + CountMax=3:90 秒内判定断线退出,宽带闪断、光猫重拨靠它触发自愈
  • ThrottleInterval=15:断网期间别疯狂重试

密钥注意:launchd 环境下 ssh 走默认路径找 ~/.ssh/id_ed25519,且不能有 passphrase(或提前进 Keychain),否则无人值守时连不上。

别让 Mac mini 睡着

sudo pmset -a sleep 0 disksleep 0   # 机器永不睡眠(显示器随意)
sudo pmset -a autorestart 1          # 断电恢复后自动开机

落地:VPS 侧

nginx:TLS + WebSocket + 登录限流

这是线上真实在跑的配置(域名脱敏)。两个容易漏的点都标了注释——WebSocket 升级头和长连接超时:

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

# 登录接口防爆破:每 IP 每分钟 10 次
limit_req_zone $remote_addr zone=ccui_auth:1m rate=10r/m;

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

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

    # 登录接口单独限流——入口在公网上,这是必须的
    location /api/auth/login {
        limit_req zone=ccui_auth burst=5 nodelay;
        proxy_pass http://127.0.0.1:18300;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location / {
        proxy_pass http://127.0.0.1:18300;
        proxy_http_version 1.1;
        # 聊天流和内置终端都是 WebSocket,这两行不能少
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        # 长会话别被 60s 默认超时掐断
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        client_max_body_size 50m;
    }
}

证书 certbot 正常签发。如果你的 443 已被其他服务占用(比如跑着 SNI 分流),把 vhost 挂到内部端口、由分流层转发即可,架构不变。

收紧隧道账号权限

我图省事复用了已有的运维账号。如果新开专用账号(更推荐),/etc/ssh/sshd_config 加一段 Match 把权限钉死:

Match User tunnel
    AllowTcpForwarding remote
    PermitListen 127.0.0.1:18300
    PermitTTY no
    X11Forwarding no
    AllowAgentForwarding no

配合 nologin shell + 仅密钥登录,就算密钥泄露,对方拿到的也只是「能往你 VPS 回环的 18300 挂个转发」的权限。

验证

# VPS 上:隧道落点必须只在回环监听
ss -tlnp | grep 18300     # 应为 127.0.0.1:18300,绝不能是 0.0.0.0

# 公网直连端口必须不通
curl -m 5 http://203.0.113.10:18300/   # 应超时/拒绝

实际效果

手机浏览器打开域名,登录页(自带 JWT 认证,nginx 层还有限流):

claudecodeui 手机端登录页:用户名密码表单

登录后左侧抽屉是项目列表——这里列出的就是 ~/.claude/projects 里的真实项目,终端里用过 Claude Code 的目录自动出现,带会话数和星标:

手机端项目列表抽屉:多个项目各带会话数、星标、搜索框

点进项目打开会话,消息流、工具调用(Read/Bash 折叠展示)、底部输入框和模型选择,在手机上是完整可操作的——看状态、批权限、发指令都在这个界面完成:

手机端会话界面:用户消息、Claude 的 Read 与 Bash 工具调用记录、底部输入框

桌面浏览器则是完整双栏布局,右上还能切换文件浏览器、git 面板和内置终端:

桌面端界面:左侧项目列表含路径与会话数,右侧工作区

一周运行数据(真实读数,不是估计):

指标 实测
隧道稳定性 单条 ssh 进程连续在线 5 天,错误日志 0 行,零人工干预
claudecodeui 内存 RSS ≈ 170MB(连续运行 3.5 天后读数)
断线自愈 心跳 90s 判死 + launchd 秒级拉起,感知上「两分钟内自动恢复」
手机端实际用途 看任务进度、批准权限请求、发后续指令;重活回电脑

踩过的坑

<synthetic> 占位符当成模型名显示。 上线第二天遇到:某个会话在 API 报错后,claudecodeui 的会话列表把模型名显示成了 <synthetic>。查下来是 Claude Code 的行为——它会往会话 JSONL 里写入本地合成的占位行(API 错误占位等),这些行的 model 字段是 "<synthetic>",而 claudecodeui 扫描会话模型时不加区分地采信了。fork 之后修掉(尖括号包裹的值一律视为占位符跳过),三个 commit 解决。这也是「开源可改」在选型里值一分的原因:闭源工具遇到这种碍眼 bug 只能等。

launchd 的环境是干净的。 两个 plist 都因此栽过小跟头:不写 PATH 时 node/claude 找不到;ssh 密钥带 passphrase 时无人值守拉不起来。规律是一条:launchd 里跑的东西,所有依赖显式声明,别指望 shell 配置。

WebSocket 头漏配的症状很迷惑。 页面能打开、能登录、项目列表也有,但聊天发不出去、终端连不上——因为 HTTP 请求都正常,只有 WebSocket 升级被 nginx 吃了。如果你看到「界面正常但一切实时功能都死着」,先查 Upgrade/Connection 两个头。

安全清单

上线前逐项过:

  • Mac mini 的 Web 界面只监听 127.0.0.1
  • VPS 隧道落点只监听 127.0.0.1ss -tlnp 验证)
  • 公网直连隧道端口不通,只有 443 经 nginx 可达
  • nginx 对登录接口限流(本方案入口在公网,防爆破不是可选项)
  • 隧道账号最小权限(nologin + Match 限制 + 仅密钥)
  • ExitOnForwardFailure=yes 已配置
  • claudecodeui 密码强度足够且不复用

常见问题 FAQ

家里宽带没有公网 IP,这方案能用吗?

能,这正是反向隧道的意义。连接方向是 Mac mini 主动拨出到 VPS,家里这头只要能上网就行——公网 IP、端口映射、光猫桥接统统不需要。

网页上看到的会话和终端里的是同一份吗?

是。claudecodeui 直接读 ~/.claude/projects/ 的会话文件,不维护独立状态。终端开的会话网页能接着看,网页发起的会话回电脑 claude --resume 也在。

隧道断了需要人工干预吗?

不需要。ServerAliveInterval 心跳 90 秒内发现断线,ExitOnForwardFailure 保证 ssh 干脆退出,launchd KeepAlive 随即拉起。实测五天宽带闪断数次,全部自愈,错误日志零行。

为什么不直接在 VPS 上跑 Claude Code?

工作区在 Mac mini 上:本地仓库、本地凭据、登录好的各种 CLI。而且 mini 的算力内存是买断的,VPS 同规格月月花钱。这个架构里 VPS 只当「门牌」,最便宜的机器都够。

和 Cloudflare Tunnel 比呢?

同思路(内网拨出 + 边缘入口)且免费,没有 VPS 时是最佳替代。差异:流量过 Cloudflare 边缘(多一个第三方,部分地区速度不稳)、WebSocket 长连接受其超时策略约束。有自己的 nginx 就更可控。

手机上真能干活吗?

分场景。看进度、批权限、发指令、读 diff:完全够用,这占外出场景的九成。大段写代码:不现实,屏幕和键盘摆在那。实际模式是「移动端监工派活,重活回电脑」。

相关文章

ANTHROPIC_BASE_URL 设了却不生效

在 zshrc 里 export 了 ANTHROPIC_BASE_URL 指向第三方中转站,Claude Code 却依然走 Google Vertex;同一台机器上,launchd 启动的 Web UI 干脆说未认证。两个坑的根子都不在中转站,而在「你以为环境变量设上了」。

claude-code中转站+2
pitfalls2026年8月24日3 min
126

24G Mac mini 跑 Qwen3.8-27B + DFlash 2:实测 1.8x,以及为什么到不了官方的 3x

DFlash 2 发布一周后,我在一台 24G 的 Mac mini M4 上把 Qwen3.8-27B 加投机解码完整跑通:4-bit 量化下从 6.5 tok/s 提到 11.7–12.2 tok/s,稳定 1.8–1.9 倍。这篇记录完整部署命令、三轮对照实测数据、24GB 的内存账,以及官方 2.7–3.4x 数字在消费级 Mac 上打折的三个具体原因。

qwendflash+6
ai-tutorials2026年8月23日5 min
266

Claude 没有写那个打印机驱动:14KB 胶水代码、一个 Linux 容器,和 HP 自己的二进制

一条「Claude 给只支持 Windows 的 HP 打印机写了 macOS 驱动」的推文拿到 262 万阅读。把仓库拉下来一看:Shell 6497 字节、Python 7638 字节、Dockerfile 550 字节,没有一行 C,真正做编码的是 HP 官方 Linux 驱动里的 rastertospl 二进制,跑在 Mac 上的 Linux 容器里。HN 上一群人指出了这件事。但这篇文章想说的不是拆台——四小时会话里真正有含金量的部分(读打印机吐出的错误页、绕过 CUPS 沙箱、libusb 直写),以及那个决定性转折点是人提出来的、不是 Claude 想到的,才是 AI 干脏活能力边界的准确刻度。

claude-code开源+6
claude2026年8月19日11 min
243

MCP Server 把 CPU 吃满 100%:一次 Cloudflare MCP 失控进程的排查与止血

Mac 风扇狂转、CPU 100%,定位到元凶是 Cloudflare 的 stdio MCP server:两个进程各吃满一个核,背后还堆着一排历史会话残留的僵尸实例。本文复盘现象、定位过程、stdio MCP 的进程模型缺陷,以及为什么低频运维操作用 REST API 比挂一个常驻 MCP 更合理。

mcpclaude-code+2
pitfalls2026年8月18日4 min
286