# test-memind Portal 无源码迁移说明

## 目标

把 `test-memind` 的 Portal 先迁到“`103` 仅运行 runtime artifact，不再依赖源码树”的模式，同时严格保护这些持久数据：

- `MindSpace/`
- `data/`
- `users/`
- `public/plaza-covers/`
- `.env`
- `.tailscale/`
- `logs/`

## 已确认的生产差异

这些差异不应该再体现在源码目录里，而应该全部通过运行时配置保留：

1. 本地默认可以连本地数据库；生产实际使用 RDS。
2. 本地 Goose 可以是单实例；生产 `103` 是双实例：
   - `TKMIND_API_TARGET=https://127.0.0.1:18006`
   - `TKMIND_API_TARGET_1=https://127.0.0.1:18007`
3. 本地端口和外网入口可以不同；生产实际是：
   - Portal `127.0.0.1:8081`
   - Plaza `127.0.0.1:3001`
   - `105` 只做入口/转发，不承载主 Portal 源码运行
4. 生产用户公开入口是 `https://g2.tkmind.cn`，不是裸 IP，也不是本地 localhost。
5. 运维、发包、健康检查统一走固定公网地址，不再依赖 `10.10.*` 局域网：
   - `105`：`120.26.184.105`
   - `103 / Studio`：`58.38.22.103`

## 迁移原则

1. 构建发生在本机，不发生在 `103`。
2. `103` 不再 `npm install`，不再 `npm run build`。
3. `103` 只接收运行产物、继承持久目录、启动服务。
4. 生产差异通过 `.env` 和启动环境注入，不通过“线上源码和本地源码不同”来兜底。
5. 迁移前必须先打整包备份，且单独备份持久目录。
6. 后续所有发布脚本默认都应优先直连 `58.38.22.103`，只有用户明确要求时才允许覆盖 `STUDIO_HOST`。

## Portal 运行产物

新增本地构建脚本：

```bash
node scripts/build-portal-runtime.mjs
```

产物目录：

```text
.runtime/portal
```

当前产物包含：

1. `dist/` 前端静态资源。
2. `public/` 静态资源。
3. `server.mjs` 后端单文件 runtime bundle。
4. `mindspace-sandbox-mcp.mjs` sandbox-fs 扩展子进程入口（必须与 `server.mjs` 同目录）。
5. `node_modules/` 运行依赖。
6. `package.json`、`scripts/run-memind-portal-prod.sh` 与运行说明。

## 为什么本地/生产差异不会阻止无源码部署

因为这些差异本质上都属于运行时配置：

1. 数据库差异：
   本地和生产只是 `.env` 里的 `DATABASE_URL` / `MYSQL_*` 不同，artifact 不应该内置数据库地址。
2. Goose 单例/双例差异：
   只是 `TKMIND_API_TARGET` / `TKMIND_API_TARGET_1` 的值不同，artifact 不应该写死实例数量。
3. 105 转发到 103：
   这是入口层拓扑，不是 Portal 源码结构。只要 `103` 上的 Portal 监听端口不变，入口层可以保持不变。
4. 端口差异：
   只是 `H5_PORT`、`PLAZA_PORT`、反代配置不同，不是源码必须驻留在 `103` 的理由。

## 生产发布入口

Portal 在 `103` 上必须只运行 runtime artifact，唯一合法发布命令：

```bash
bash scripts/release-portal-runtime-prod.sh --dry-run
bash scripts/release-portal-runtime-prod.sh
```

`bash scripts/release-prod.sh` 已停用，不得再用于 Portal。
