实施计划 · v3 · 待审

远程 Dev Container + 可断连的编码 Agent

在 8.222.155.72 上按需拉起隔离开发环境;合上笔记本后 agent 继续跑,开机秒回原 session。
harness = Claude Code,模型 = MiniMax-M3。

2026-07-08 · v3 已纳入 DEPLOYMENT.md 事实、你的四项决定、MiniMax 接入 · 仍未对目标机器做任何写操作
v1 的三个假设被证伪
附带的好消息

接 MiniMax 意味着你本机的 Claude 官方账号和 OAuth 凭证完全不需要上服务器。这台公网机器上永远不会有你的 Anthropic 长期凭证。攻击面反而比 v1 小。

§0你的四项决定 → 架构后果

你的决定直接后果
语言动态
不同容器不同语言
不能造一个胖镜像。走「瘦 base 镜像 + 每项目 devcontainer.json + Features」。这恰好也让 CLI 和 VS Code 共用一套配置。见 §4
MiniMax key 注入
不用 Claude 官方账号
Anthropic 凭证不上服务器(好)。但模型换成 MiniMax-M3,且端点/key 有国内站 vs 国际站的同源问题。见 §5
git + VS Code Remote-SSH
两种都要
代码放 /srv/dev/<proj>,两条路径共用同一 bind mount。但 git 凭证与"离线运行"直接冲突见 §6
保持 root 直登 宿主机不动。但容器内仍必须跑非 root——否则 bind mount 出来的文件全是 root 所有,你在 VS Code 里改不动。

§1基石:三层持久化

Claude Code 只是个普通前台进程。关机杀死它的是 SIGHUP,不是什么状态问题。

机制挡住什么挡不住什么
L1容器内 tmux server,claude 跑在 tmux window 里SSH 断线、合盖、关机、网络抖动容器被删 / 进程被 OOM kill
L2容器 PID1 ≠ claudeclaude 一退整个容器消失宿主机重启(除非配 --restart
L3~/.claude 挂 named volume容器重建后彻底丢历史进程被杀时正在进行的那一轮
好消息:L2 白送

devcontainer 规范里,基于 image / dockerFile 的配置默认 overrideCommand: true,容器 PID1 会被替换成一个无限 sleep 循环。用 devcontainer.json 就自动拿到 L2,不用自己 sleep infinity

别把 L3 当 L1 的等价物

claude --resume 能捞回历史对话,但捞不回"关机瞬间正跑到一半的那次工具调用"。真正让 agent 在你睡觉时干活的只有 L1。L3 是兜底。

§2架构

Windows 笔记本
  │
  │  ssh myserver              (22,密钥,root)
  │  ssh -L 5173:localhost:5173 myserver     ← dev server 走隧道,不开安全组
  ▼
8.222.155.72   Alibaba Cloud Linux 4 · 新加坡
  │
  ├─ nginx  :80 :443  ────► jinghao.site  【生产站点,不许碰】
  │     └─ acme.sh cron,靠 :80 的 /.well-known/ 续期
  │
  ├─ dockerd            (unix socket only;容器一律不 publish 到 0.0.0.0)
  │
  ├─ /etc/devbox/minimax.env      chmod 600, root:root   ← MiniMax key + BASE_URL
  │                                                        (无任何 Anthropic 凭证)
  │  出网 → api.minimaxi.com  【新加坡→国内站,待实测,见 §5 坑一】
  │
  └─ 容器 devbox-<project>
       │  PID1 = sleep loop           ← L2(devcontainer 默认给的)
       │  以非 root 用户 dev 运行,uid 对齐宿主
       │
       ├─ tmux server                 ← L1
       │    └─ session "main"
       │         └─ claude ..........  ← agent 在这里跑
       │
       ├─ /workspaces/<proj>          ← bind mount ← /srv/dev/<proj>  (git clone 在这)
       │
       └─ /home/dev/.claude           ← named volume  (L3)
            └─ projects/<cwd>/*.jsonl   会话历史

重连:

ssh -t myserver docker exec -it -u dev devbox-myproj tmux attach -t main

§3与生产站点共存 硬约束

这台机器上跑着一个活的、有 TLS 证书自动续期的站点。以下每条都不是建议,是约束。

§4多语言:让 devcontainer.json 当唯一事实源

你要"不同容器不同语言",又要"CLI 和 VS Code 两种入口"。如果分别配,必然漂移成两套系统。

解法是把语言声明推进每个项目的 .devcontainer/devcontainer.json,用官方 Features 声明式装工具链:

// /srv/dev/myproj/.devcontainer/devcontainer.json
{
  "name": "myproj",
  "image": "devbox-base:latest",          // 瘦 base:claude-code + tmux + git + rg/fd/jq
  "features": {
    "ghcr.io/devcontainers/features/go:1":     { "version": "1.23" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" }
  },
  "remoteUser": "dev",
  "runArgs": ["--env-file", "/etc/devbox/anthropic.env"],
  "mounts": [
    "source=devbox-claude-myproj,target=/home/dev/.claude,type=volume"   // L3
  ]
}

然后两个入口读同一份配置:

入口命令
终端 / tmuxdevcontainer up --workspace-folder /srv/dev/myproj
VS CodeRemote-SSH 连上 → 打开 /srv/dev/myprojReopen in Container
这里有一个待实测的假设

VS Code 的 Dev Containers 扩展应当能复用 CLI 已创建的容器(两者都用 devcontainer.local_folder 这个 label 做查找)。但我没在你这台机器上验证过。 如果它不复用而是新建一个,两个入口就会各起一个容器、tmux session 对不上,整个"重连原 session"的体验就断了。

Phase 3 必须实测这一条。 若不复用,退路是:VS Code 只当编辑器,claude 一律从 devbox attach 进 tmux 跑。

Features 从 ghcr.io 拉取,需要出网。新加坡节点应该没问题,但 Phase 0 要测。

§5MiniMax 接入

Claude Code 可以指向任何 Anthropic 协议兼容的网关。MiniMax 提供了一个。配置不难,难的是它带来的四件事。

按官方文档(platform.minimaxi.com/docs/token-plan/claude-code):

# /etc/devbox/minimax.env      chmod 600, root:root
ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
ANTHROPIC_AUTH_TOKEN=<你的 MiniMax key>
ANTHROPIC_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M3
CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000     # M3 是 1M 上下文
是 AUTH_TOKEN,不是 API_KEY

文档给的是 ANTHROPIC_AUTH_TOKEN。这两个变量在 Claude Code 里走的路径不同 —— 前者直接设 Authorization 头,后者会触发"检测到 API key"的确认流程。指向第三方网关时用文档给的那个,别顺手改成 ANTHROPIC_API_KEY

坑一 · 端点与 key 必须同源,而你的机器在新加坡

文档里有两个端点:

你给的链接是 minimaxi.com(国内站),key 也从 platform.minimaxi.com/user-center/payment/token-plan 取。国内站与国际站是两套独立账号体系,key 通常不通用。

而你这台 ECS 在 ap-southeast-1(新加坡)。于是有两个必须弄清的问题:

  1. 从新加坡能否连通 api.minimaxi.com?延迟多少?—— Phase 0 我来实测
  2. 你手上那把 key 属于哪一边?—— 只有你知道
坑二 · 容器里跑的不是 Claude

Claude Code 是 harness,MiniMax-M3 是模型。三个模型槽位(Opus / Sonnet / Haiku)全部映射到 M3 —— 包括 Claude Code 内部拿"小快模型"做的那些活。

这不是贬低 M3。这是一个必须摆在桌面上的事实:你在 §7 里要开 --dangerously-skip-permissions,也就是把无人监督的 shell 执行权,交给一个你尚未在这个 harness 里摸过底的模型。 Claude Code 的工具调用约定是围绕 Claude 调优的;换模型后的失败模式(误删文件、循环重试、卡在错误的假设上)需要你自己积累经验。

建议:前几次不要无人值守。 先盯着它跑,看清它在你的项目里怎么用工具,再放它单飞。这条比任何配置都重要。

坑三 · 计费模式:文档没说,我不猜

那份文档里没有任何关于订阅制 / 按量计费 / 速率限制 / 并发限制的说明。我不知道你的 token plan 是预付费额度包还是后付费。所以我也收回 v2 里"去 Anthropic Console 设 spend limit"那句 —— 那是错的,你根本不用 Anthropic 计费。

但无论哪种模式,「无人值守」+「跳过权限确认」都会消耗它。跑偏一整夜的结果,要么是账单,要么是额度归零。开工前去 MiniMax 控制台确认计费模式,看有没有额度告警可设。

坑四 · WebSearch 不开箱可用

文档说联网搜索需要另外按 MCP 教程配置。容器里的 claude 默认没有 web search 能力。如果你的工作流依赖它(查文档、查报错),Phase 5 要单独处理。

key 怎么交给服务器

别把 key 粘贴到这个对话里

聊天内容会写进你 Windows 上的 ~/.claude/projects/*.jsonl(明文落盘),并作为对话的一部分发送到模型服务商。一个能自主执行命令的 agent 的对话记录里躺着一把付费 key,是不必要的暴露。

你自己 ssh 上去写,我全程不需要看见它:

ssh myserver
mkdir -p /etc/devbox && chmod 700 /etc/devbox

cat > /etc/devbox/minimax.env <<'EOF'
ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
ANTHROPIC_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M3
ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M3
CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000
EOF

# key 单独追加,read -rs 让它不进 shell history
read -rs KEY && printf 'ANTHROPIC_AUTH_TOKEN=%s\n' "$KEY" >> /etc/devbox/minimax.env
unset KEY
chmod 600 /etc/devbox/minimax.env
key 会在你想不到的地方留痕

§6git 凭证 × 离线运行 = 冲突

你选了 git 同步。那么当 agent 在你合盖之后想 git push 时,会发生什么?

SSH agent forwarding(ForwardAgent yes)只在你连着的时候有效。 你一合盖,forwarded socket 就没了。agent 执行 git push → 认证失败 → 要么报错,要么卡在密码提示上,然后一整晚什么都没干。

方案做法评价
约定 claude 不 push commit 到本地分支,你回来自己 push。写进 CLAUDE.md 推荐。 无凭证暴露;agent 的产出你回来能审一遍再推
服务器侧 deploy key 为该 repo 生成专用 SSH key,只给写权限 可行。每个 repo 一把,权限最小化
fine-grained PAT GitHub token 写进 /etc/devbox/ 可行但又多一份长期凭证在公网机器上
agent forwarding 依赖你本地的 ssh-agent 与本方案的核心目标直接矛盾,不要用

§7两个必须校准的预期

一 · agent 不会永远跑下去

Claude 把当前这轮任务干完就会停下来等输入。"关机后继续运行" = 它能把手头这轮跑完,不是 24 小时自主工作。 心理预期是后者的话,方案再完美也会失望。

二 · 权限询问会把无人值守的 agent 卡死

你人不在,它弹 Allow Bash(npm install)?,然后永远等在那里。你回来看到一个一动没动的进度条。

解法是容器内使用 --dangerously-skip-permissions —— 这正是 dev container 存在的意义,隔离的一次性环境里这个 flag 是合理的。Anthropic 官方 devcontainer 参考实现就是这个思路,配 init-firewall.sh 做出网白名单。注意白名单里要放的是 api.minimaxi.com,不是 api.anthropic.com

但请把它和 §5 坑二、坑三连起来读:跳过权限 + 换了个未摸底的模型 + 无人看管 + 计费模式未知,四者叠加。头几次盯着跑,不是客套话。

加分项 · Stop hook 通知

配一个 Stop hook:claude 跑完 → 打 webhook → 手机收消息(ntfy / 飞书 / Slack)。否则你不知道什么时候该开机去看结果,只能瞎猜。

§8分阶段

Phase 0 — 侦察 只读

DEPLOYMENT.md 已经替我回答了一半:OS = Alibaba Cloud Linux 4,内核 6.6.102,磁盘 40G/22%,SELinux disabled,firewalld inactive,iptables INPUT ACCEPT。仍然未知的:

Phase 1 — 装 Docker 碰生产机

Phase 2 — base 镜像 纯构建

devbox-base:latest@anthropic-ai/claude-code、tmux(set -g mouse on)、git、ripgrep、fd、jq、gh。非 root 用户 devuid/gid 与宿主对齐。语言工具链一律不装 —— 交给各项目的 Features。

Phase 3 — devbox 脚本 + 验收 核心交付

命令作用
devbox up <proj>devcontainer up + 建 tmux session main
devbox attach <proj>一路 attach 进 tmux
devbox ls列出所有 devbox 及 tmux session 状态
devbox down / nuke停容器(留 volume) / 连 volume 一起删
验收 · 这三条不过整个方案不成立

① 断连持久化:attach 进去让 claude 跑个几分钟的任务 → 直接合盖(不要优雅退出)→ 五分钟后开机 devbox attach → 断言任务已跑完,能向上滚动看到你不在时的中间输出。

② VS Code 容器复用:CLI 起容器 → VS Code Remote-SSH → Reopen in Container → 断言没有新建第二个容器,且 VS Code 终端里 tmux attach 能接上 CLI 那个 session。(见 §4 的待实测假设)

③ 生产站点无损:全程 https://jinghao.site/ 返回 200,acme-challenge 路径返回 200 明文。

Phase 4 — MiniMax 接入 你亲自做

见 §5「key 怎么交给服务器」。命令我给你,你自己执行,我不接触 key。之后 devcontainer.json 里只出现 --env-file /etc/devbox/minimax.env 这个路径,不出现 key 本身。

验收:容器里 claude -p "回复 OK" 能拿到 MiniMax 的响应;/status 显示的模型是 MiniMax-M3

动手前先去 MiniMax 控制台确认计费模式(§5 坑三)。

Phase 5 — 配置与 hook 迭代

settings.json / CLAUDE.md / commands/ / agents/ 放进一个 git repo,容器启动时 symlink 进 ~/.claude/,可版本化。CLAUDE.md 里写死"不要 git push"(§6)。加 Stop hook 通知。

§9诚实清单:我现在还不知道的事