Docmost 演示部署与回滚

先在隔离环境执行脚本测试、类型检查、完整构建及实际会话验收。
从干净提交 pnpm build 得到 apps/server/dist/source-release/<buildId>.zip。
将该ZIP完整解包到源码导出目录，再执行：
  docker build --platform linux/amd64 -f Dockerfile.demo -t docmost-demo:<提交短号> .
不要复制个人.env、数据库备份、SSH资料或包含密钥的运行配置到源码归档。
镜像内全量源码与网关/runtime执行代码、应用dist及公开下载包在启动时校验绑定。

部署采用独立 /opt/docmost-demo/releases/<发布标识>、shared、backups 和 Compose 项目名。
shared保存demo-config.json、独立槽应用/初始化密码、compose.env，目录700、文件600。
Linux文件型Compose secrets沿用宿主文件所有权；只设600但仍归ubuntu UID1000，
会使PostgreSQL初始化进程无法读取slot-password，留下“数据库已启动但应用角色未创建”的状态。
首次启动前，先核对本次固定镜像中的postgres UID和GID，不猜测用户名对应的数字：
  docker run --rm --network none --entrypoint id postgres:18-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873 -u postgres
  docker run --rm --network none --entrypoint id postgres:18-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873 -g postgres
该固定Alpine镜像的预期结果均为70；核对相符后，只对compose.env指定的本次槽位密码文件执行：
  sudo chown 70:70 "$DOCMOST_DEMO_SLOT1_PASSWORD_FILE" "$DOCMOST_DEMO_SLOT1_BOOTSTRAP_PASSWORD_FILE"
  sudo chmod 600 "$DOCMOST_DEMO_SLOT1_PASSWORD_FILE" "$DOCMOST_DEMO_SLOT1_BOOTSTRAP_PASSWORD_FILE"
启用第二槽时，对其两个SLOT2密码文件做同样处理。若镜像UID/GID与预期不同，先查明镜像差异。
网关配置文件单独归UID/GID1000:1000，权限600，以匹配网关node用户：
  sudo chown 1000:1000 "$DOCMOST_DEMO_CONFIG_FILE"
  sudo chmod 600 "$DOCMOST_DEMO_CONFIG_FILE"
不递归更改shared目录中的其他文件，不通过chmod644或放开everyone读取权限解决问题。
pg_isready成功仅证明数据库接受连接；还须用专用应用账号确认可登录、数据库名正确且非superuser。
若首次初始化已失败，重启已有PG卷不会自动重跑初始化脚本，须先核查并仅恢复本项目可丢弃演示库。
详细JSON结构和资源限制见 demo-runtime.txt；单槽默认配置用于资源较少的同机部署。
启动前检查端口3009、内存/磁盘，不停止或清理其他项目的容器和卷。
  docker compose --env-file <shared/compose.env> -p docmost-demo -f <release>/scripts/demo/compose.yaml up -d --wait
第二槽需显式profile two-slots、双槽config以及足够内存。发布不使用浮动镜像标签。
网关健康探针使用原生node:http连接回环3009，同时明确发送publicOrigin对应的Host。
Node26的fetch会重写Host，不能用于这个按公网来源校验的回环探针；HTTP非200、连接失败或2秒超时均失败。

DNS A记录必须指向部署主机。新增独立Nginx server_name，不能替换主站配置。
参考 scripts/demo/nginx.conf.example。先建立独立HTTP-01 webroot并验证DNS，再申请证书。
TLS通过后检查匿名 /demo、/open-source、/source/manifest.json/ZIP及其SHA-256；
进行真实start、编辑、评审、角色切换/end与expired-cookie验证后再开放门户入口。
网关仅127.0.0.1:3009；子进程回环；PG/Redis没有公网端口。
同源Cookie为HttpOnly/SameSite=Strict，HTTPS启用Secure。禁止复制或公开内部角色Cookie。

回滚前保存当前镜像ID、源码commit/buildId、Nginx配置摘要、Compose配置文件路径。
只对docmost-demo项目停止服务，再切换回已验证发布目录和镜像；私有密码保持匹配。
不同版本更换会终止已有短期演示，重新启动会清理本项目可回收专库；不会迁移正式业务数据。
不用 docker compose down -v，也不运行全局prune；需要人工检查时先保留专用卷。
Nginx回滚只删除本次独立vhost/include，先nginx -t再reload，不恢复覆盖其他人后续修改的旧主站文件。

演示数据是可丢弃的合成数据，无跨版本恢复承诺。备份对象是对应源码包、镜像标识、
Nginx和私有配置（私有备份应独立加密并限制访问），不是真实用户文档。
手工恢复演示只能在停止该项目容器后进行；重新start会重新生成本槽数据。
不得在正在使用的旧开发库上执行重置脚本。
