单向物理隔离环境下无法运行composer命令或发起网络请求,镜像同步必须通过导出机定时构建静态文件、u盘/光盘手动拷贝、导入机nginx托管三步闭环完成;禁止使用composer config或satis直连内网源,因内外网完全断连,所有元数据和zip包须离线搬运。

单向物理隔离环境下,根本不能运行 composer 命令或任何网络出向请求——镜像同步必须脱离 Composer 运行时,靠离线介质搬运静态文件。所谓“同步”,实际是定时导出 + 手动拷贝 + 服务托管三步闭环。
为什么不能用 composer config 或 Satis 直连内网源
单向物理隔离(如光闸、网闸)意味着:外部网络无法发起 HTTP 请求到内网,内网机器也无法访问外网域名(packagist.org、github.com 等全不可达)。所有依赖元数据和 ZIP 包必须由外部机器生成后,通过 U 盘、光盘或摆渡系统导入内网。
常见错误现象包括:
-
composer install卡在Loading composer repositories,且日志里出现curl error 7或连接超时 - 手动运行
satis build报错Could not fetch https://packagist.org/packages.json - 即使配置了
repo.packagist,composer config -g repo.packagist输出为空或仍指向官方源
必须用两台机器分角色完成:导出机 + 导入机
导出机(可联网)负责生成完整静态镜像;导入机(纯内网)只托管并提供 HTTP 访问。两者之间无进程通信,仅靠文件拷贝交接。
导出机上操作要点:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 用
satis构建,但repositories必须全部设为type: "package"或本地type: "path",禁止任何vcs或远程 URL;否则构建会失败 -
satis.json中必须显式启用"require-all": true和完整"archive"块:{"directory": "dist", "format": "zip", "skip-dev": true} - 构建完成后,整个输出目录(含
packages.json和所有dist/xxx.zip)必须整体打包,不能只拷packages.json - 验证 ZIP 可直连:在导出机上用
python3 -m http.server 8000启一个临时服务,然后curl -I http://localhost:8000/dist/monolog/monolog/2.10.0/monolog-2.10.0.zip,确认返回200和Content-Type: application/zip
导入机 Nginx 配置必须满足三个硬性响应条件
内网机器上部署的 Nginx 不是简单挂个目录就行,它要骗过 Composer 对 MIME 类型和路径结构的校验。
-
root指令必须精确指向 Satis 输出目录的根,例如root /var/www/mirror;,不能多一层/web或少一层 - 必须显式声明 JSON 响应头:
types { application/json json; },否则packages.json返回text/plain,Composer 直接跳过 - ZIP 文件路径必须与
packages.json中记录的dist.url字段完全一致——Satis 默认生成的是相对路径如dist/monolog/monolog/2.10.0/monolog-2.10.0.zip,Nginx 必须能按此路径 200 返回 - 浏览器或
curl能打开http://mirror.internal/packages.json和任意一个dist/xxx.zip链接,且状态码都是200
项目级配置是唯一可靠生效方式
全局配置 composer config -g 在导入机上毫无意义:不同用户、Docker 容器、CI runner 都读不到同一份 ~/.composer/config.json,且物理隔离下也禁止写入远程配置。
必须在每个项目的 composer.json 中硬编码镜像源:
"repositories": [
{"packagist.org": false},
{
"type": "composer",
"url": "http://mirror.internal/"
}
]
注意:
-
url末尾不加/会导致请求变成http://mirror.internalpackages.json→404 -
{"packagist.org": false}必须是独立对象,不能合并进下一项 - 改完立刻删掉
vendor/和composer.lock,否则旧 lock 里的dist.url仍指向外网地址,校验失败 - 若项目已有私有包,它们的仓库定义必须放在第二项之后,不能覆盖或删除第一项
真正难的不是搭建,而是每次新包上线后,导出机要重新跑 satis build、重新打包、重新摆渡——这个过程无法自动化,必须人工核对版本号和 ZIP SHA256 校验值,否则内网环境就失去了确定性。










