Docmost 对应源码交付与重建

本改造保留根 LICENSE 的 AGPL 第3版许可全文、上游署名和第三方声明。
界面使用 AGPL-3.0 作为显示名称；实际授权以随包许可文件为准。
上游仓库、标签和完整 SHA 见 docs/portfolio/upstream.json。
新增交付模板及评审发布功能见前两批说明；第三批加入公开源码入口。

构建一个新的版本
1. 使用 Node 26.12.0、pnpm 11.25.0；执行 pnpm install --frozen-lockfile。
2. 提交所有源码修改，确认 git status --short 没有输出。
3. 执行 pnpm build。此命令先校验源码清单及开源核心边界，再禁用 Nx 缓存
   构建 editor-ext、server、client，最后打包并验证对应源码。
4. 将这次生成的 apps/server/dist 和 apps/client/dist 一起交付；不要只替换
   ZIP、前端资源或服务端文件。重启服务后，它会在注册公开下载路由之前
   重新计算两份构建产物和 ZIP 的 SHA-256。
5. 公开入口 /open-source；元信息 /source/manifest.json；下载路径由该
   元信息的 archive.url 指定。读取、下载不要求登录、工作区或付费权限。

包内内容与版本对应
ZIP 包含干净提交中的源码、LICENSE、pnpm-lock.yaml、构建脚本、运行说明
及生成的 source-export.json。清单记录本次改造的完整 sourceCommit、上游
仓库/标签/SHA、每个文件的路径、权限和 SHA-256，以及整份源码清单摘要。
它不包含 .git、真实 .env、node_modules、dist、企业目录、上传用户数据、
数据库备份或本地日志；只保留已提交的 .env.example 配置示例。
源码路径拒绝绝对路径、点路径、反斜杠、控制字符、符号链接和子模块。
受版本管理的私密/运行文件会使构建失败，不能被悄悄打包。
未被清单列出的源码（即使被 Git 忽略）也会使构建失败。

source-export.json 由构建生成，不提交 Git。它记录已经完成的改造提交，
因此无需把“包含自己提交号的文件”反复提交。每次升级必须先提交再构建。
服务端 dist/source-release/receipt.json 同时绑定 sourceCommit、上游信息、
源码清单摘要、ZIP 摘要和 server/client 构建文件树摘要；构建标识由这些
摘要计算，空提交也会得到不同的源码下载地址。它是完整性校验，不是签名。
同一份无修改导出源码再次打包会生成相同 ZIP。不同平台、公开客户端配置
或工具链产生的应用二进制未承诺字节相同；buildId 会跟随实际产物变化。

没有 .git 的下载包如何重建
1. 将 ZIP 解压到一个新的空目录；保留 source-export.json 和文件结构。
2. 执行 pnpm install --frozen-lockfile，再执行 pnpm build。
   构建会逐个校验导出清单中的文件与摘要，重新验证源码边界并产生新的
   构建产物、对应源码 ZIP 和交付清单。不需要安装 Git 或访问原改造仓库。
3. 本机依赖、随机私密配置及启动方法见 docs/portfolio/run-local.txt。
   node scripts/portfolio/init-local.mjs 只创建尚不存在的 .env。
4. 修改导出源码后，需要 git init、git add 和 git commit 建立自己的新版本，
   再执行 pnpm build。旧 source-export.json 已被 .gitignore 排除，构建器
   会从新提交重新生成准确的清单，不会冒用下载包原来的提交号。

Dockerfile 会将 source-release 随 server/dist 一起复制进运行镜像。
如需自行构建镜像，使用解压后的源码包作为 Docker 上下文，因为 .dockerignore
会排除 .git；导出清单提供无 .git 构建所需的来源信息。本批未验证完整应用镜像。

资源不完整时
缺失、破损或错配的 ZIP/清单/构建产物不会成为可用下载。服务继续可用，但
/source/manifest.json 返回 503 和 SOURCE_RELEASE_UNAVAILABLE；界面禁用
下载并提示重试。未知 /source 路径始终返回 JSON 404，不会退回 SPA HTML。
服务启动后保留已经完整校验的 ZIP 字节，因此即使磁盘文件随后意外删除，
访客仍能下载准确的源码；重启时磁盘不完整则返回 503。请修复完整产物再重启。
元信息响应禁止缓存；ZIP 地址随构建变化，可长期缓存。GET/HEAD 均公开可用；
Range 请求也返回完整 200 文件，本版本没有断点续传或条件下载协议。

开发与验证
pnpm test:source-release：源码归档、无 .git 重建校验、脏工作区、路径/符号链接、
忽略源码注入、错配、缺失、元信息重标、原始 index 模板、匿名 GET/HEAD、404。
pnpm --filter server exec tsc --noEmit --incremental false：服务端类型检查。
client:dev 的 /source 请求代理到 APP_URL 指向的后端；后端必须运行对应完整
构建才会公布有效下载。单独 server:build/client:build 仅用于开发，不会生成
新的完整交付清单，不能代替 pnpm build。
