# ERR-20260516-004 — backend ssh 调 ffoa-deploy 不传 INTERNAL_APP_PUBLIC_DOMAIN，部署到 test 用了生产域名

## 现象

L3 PoC 走通后，自动生成的 Caddy site 文件 `/srv/caddy/sites/itadmin-birthday-reminder.caddy` 写的是：

```caddy
http://itadmin-birthday-reminder.apps.ffworkspace.faradayfuture.com {
    reverse_proxy ffoa-app-itadmin-birthday-reminder:3000
}
```

**生产域名** `apps.ffworkspace.faradayfuture.com`，不是 test 该用的 `apps.ffworkspace.test.faradayfuturecn.com`。nginx 的 server_name `*.apps.ffworkspace.test.faradayfuturecn.com` 因此匹配不上，公网 URL 完全访问不到。

## 根因

backend `container-host.service.ts` `buildCommand()` 拼 SSH 命令：

```ts
ssh -o BatchMode=yes ... ubuntu@127.0.0.1 -- /usr/local/bin/ffoa-deploy --employee-slug ... --app-slug ... --repo-url ... --runtime ... --branch ...
```

**没传任何 env**。SSH 走 non-interactive 模式不会加载 `~/.bashrc` / `~/.profile` / `/etc/environment`（除非 sshd `PermitUserEnvironment=yes` + `~/.ssh/environment` 存在），所以远端 `ffoa-deploy` 读到的 `INTERNAL_APP_PUBLIC_DOMAIN` 是空，触发脚本里的兜底：

```bash
# scripts/internal-app-platform/deploy-container.sh:26
APPS_DOMAIN="${INTERNAL_APP_PUBLIC_DOMAIN:-apps.ffworkspace.faradayfuture.com}"
```

backend `.env.test` 里这个变量明明配的是 test 域名，但只在 backend 进程内可见，没传到 SSH 子进程。

## 临时绕道（PoC 现场用过的）

手工改 Caddy site 文件域名 + reload：

```bash
sudo sed -i "s|apps.ffworkspace.faradayfuture.com|apps.ffworkspace.test.faradayfuturecn.com|g" \
  /srv/caddy/sites/<emp>-<app>.caddy
docker exec ffoa-caddy caddy reload --config /etc/caddy/Caddyfile
```

每次 `deploy` 都要重做，**不可持续**。

## 推荐永久 fix（按工作量从小到大）

### A. 改 backend `buildCommand()` 透传 env（推荐）

```ts
private buildCommand(remoteCmd: string): string {
  const envPropagation: string[] = [];
  for (const key of ['INTERNAL_APP_PUBLIC_DOMAIN', 'INTERNAL_APP_GITEA_BASE_URL', 'INTERNAL_APP_CADDY_CONTAINER']) {
    const v = process.env[key];
    if (v) envPropagation.push(`${key}=${this.shellEscape(v)}`);
  }
  const envPrefix = envPropagation.length ? `env ${envPropagation.join(' ')} ` : '';

  if (!this.sshHost) return `${envPrefix}${remoteCmd}`;
  // ...
  return `ssh ... ${this.sshUser}@${this.sshHost} -- ${envPrefix}${remoteCmd}`;
}
```

`ssh -- env KEY=value /usr/local/bin/ffoa-deploy ...` 形式确保远端 ffoa-deploy 读到值。

### B. 加 `--apps-domain` 显式 flag

更显式但要改两侧：
- deploy-container.sh 加 `--apps-domain` 选项解析
- container-host.service.ts `runDeployScript` 加 `args.appsDomain`

### C. 远端 systemd-style env 文件

在 test 服务器 `/etc/default/ffoa-deploy` 写：

```
INTERNAL_APP_PUBLIC_DOMAIN=apps.ffworkspace.test.faradayfuturecn.com
```

然后 `ffoa-deploy` 头部 `[ -f /etc/default/ffoa-deploy ] && . /etc/default/ffoa-deploy`。

跨环境差异收敛到该文件，backend 不用关心，但**逻辑分散**——env 既可能从 backend 来也可能从远端 default 文件来。

## 复用建议

- **SSH 远端命令默认不带宿主 env**——任何 NestJS `exec(ssh ...)` 模式都要主动 `env KEY=val` 透传，否则远端拿到的是空环境（PATH 例外，sshd 注 default 几条）。
- **跨域名/跨环境的"默认值"风险特别高**：deploy-container.sh 的 `:-apps.ffworkspace.faradayfuture.com` 兜底写的是**生产**域名 —— 任何 env 没传都会"静默命中生产"，没有红字告警。
  - 建议：把这种兜底改成 `${INTERNAL_APP_PUBLIC_DOMAIN:?missing INTERNAL_APP_PUBLIC_DOMAIN}` 让它 fail-fast，比静默漂移好。
- **end-to-end PoC 必须 curl 真实域名**，不能只 curl 容器+Host header——`itadmin-birthday-reminder.apps.ffworkspace.test.faradayfuturecn.com` 走 nginx 才能命中这类 server_name 不匹配 bug。

## 关联

- 触发改动：[`scripts/internal-app-platform/deploy-container.sh`](../../scripts/internal-app-platform/deploy-container.sh) `APPS_DOMAIN` 默认值
- 推荐改动：[`backend/src/modules/internal-app-platform/services/container-host.service.ts`](../../backend/src/modules/internal-app-platform/services/container-host.service.ts) `buildCommand()`
- 同事故链：ERR-20260516-001 (env 没配) / 002 (PoC infra 没装) / 003 (ufw 阻塞主站) —— 都是"PR 合并后没把目标服务器拉到可用状态"系列
