# sema-stack 单机 Layer 1 清单(DEPLOY-OVERSEAS-PILOT §3 Layer 1,由 sema-up.sh 驱动,不手工 up)。
# compose profiles 映射部署形态:
#   data-center  → profiles: data(PG+MinIO+建桶)[+ registry][+ git]
#   full-single  → data + agent(sema-server)[+ registry][+ git]
#   local-plus   → agent(数据面在远端,PG_HOST/MINIO_ENDPOINT 指过去)
# 所有参数来自同目录 .env(sema-up.sh 生成;缺 .env 直接 up 会 fail-loud——这是有意的)。
# 📌 镜像引用=修改点(clay 待拍稳定 tag/分发源):PG_IMAGE/MINIO_IMAGE/MC_IMAGE/GITEA_IMAGE
#    有公共默认;SEMA_SERVER_IMAGE/SEMA_REGISTRY_IMAGE 必须显式给(GHCR/Docker Hub 公开仓,匿名可拉;
#    SWR 已弃用 2026-07-13,CN 兜底=内网 Gitea registry)。
name: ${SEMA_PROJECT:?run sema-up.sh, do not compose up by hand}

services:
  # 数据面细分 profile:pg/s3 各自独立(外接第三方 PG/S3 时只起需要的那半;
  # "data" 别名保留=两个都起,旧口径兼容)
  pg:
    image: ${PG_IMAGE:-postgres:17}
    profiles: ["data", "pg"]
    restart: unless-stopped
    environment:
      POSTGRES_USER: sema
      POSTGRES_PASSWORD: ${PG_PASSWORD:-}
      POSTGRES_DB: sema
    # REV-2 §3c 调优:sema-up.sh 按机器规格算出 PG_TUNE_FLAGS(pgtune 公式,SSD 假设)
    command: postgres ${PG_TUNE_FLAGS}
    ports:
      - "${PG_BIND:-127.0.0.1}:${PG_PORT:-5432}:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U sema -d sema"]
      interval: 5s
      timeout: 3s
      retries: 20

  minio:
    image: ${MINIO_IMAGE:-minio/minio:latest}
    profiles: ["data", "s3"]
    restart: unless-stopped
    # 单机=单盘模式(§3c:显式无冗余;集群形态才上 EC)
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: sema
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD:-}
    ports:
      - "${MINIO_BIND:-127.0.0.1}:${MINIO_PORT:-9000}:9000"
      - "${MINIO_BIND:-127.0.0.1}:${MINIO_CONSOLE_PORT:-9001}:9001"
    volumes:
      - miniodata:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 5s
      timeout: 3s
      retries: 20

  # 建桶(workspaces/blobs/backups)由 sema-up.sh 装配后执行(一次性动作不进清单:
  # compose --wait 会把正常退出的 init 容器判失败,且"动作"归 Layer 0、"常驻"归 Layer 1)

  # agent:full-single 时连同栈 pg/minio(默认值);local-plus 时 .env 里 AGENT_PG_HOST 等
  # 指向远端数据面(depends_on 不适用——local-plus 没有本地 pg,该档由 sema-up.sh 只激活 agent
  # profile,compose 对未激活服务的 depends_on 自动忽略)。
  server:
    image: ${SEMA_SERVER_IMAGE:-}
    profiles: ["agent"]
    restart: unless-stopped
    depends_on:
      pg:
        condition: service_healthy
        required: false   # local-plus:pg 服务不在激活面,依赖按缺席放行
    environment:
      PORT: "8090"
      # 1.123.0 收编:显式 DB_BACKEND=pg 即 durable。SESSION_BACKEND=auto 是旧镜像(<1.123)兜底——
      # 老版本 pg 不自动强转 durable 会静默 memory(真机验出 session:"memory");auto 新旧版都落 durable(pg)
      DB_BACKEND: pg
      SESSION_BACKEND: auto
      PG_HOST: ${AGENT_PG_HOST:-pg}
      PG_PORT: ${AGENT_PG_PORT:-5432}
      PG_USER: ${AGENT_PG_USER:-sema}
      PG_PASSWORD: ${PG_PASSWORD:-}
      PG_DATABASE: ${AGENT_PG_DATABASE:-sema}
      MINIO_ENDPOINT: ${AGENT_MINIO_ENDPOINT:-http://minio:9000}
      MINIO_ACCESS_KEY: ${AGENT_MINIO_USER:-sema}
      MINIO_SECRET_KEY: ${MINIO_PASSWORD:-}
      # SendUserFile 公网直链(可选;留空=工具签发时 fail-loud 提示补配,行为不变):
      #   S3_PUBLIC_ENDPOINT=用户浏览器可达的 S3/MinIO 公网 base(反代必须透传 Host,SigV4 绑定 Host);
      #   S3_PUBLIC_BUCKET=永久轨匿名桶(缺省 sema-public)。⚠️ 桶匿名策略必须 GetObject-only
      #   (mc anonymous set-json,Action 只含 s3:GetObject;set download 连带开 ListBucket=key 可枚举)。
      S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-}
      S3_PUBLIC_BUCKET: ${S3_PUBLIC_BUCKET:-}
      SEND_USER_FILE_URL_TTL: ${SEND_USER_FILE_URL_TTL:-}
      # 必填校验在 sema-up.sh(compose 对未激活 profile 也做插值,:? 会误伤 data-center 档)
      SERVICE_AUTH_TOKEN: ${SERVICE_AUTH_TOKEN:-}
      MODEL_GATEWAY_BASEURL: ${MODEL_GATEWAY_BASEURL:-}
      MODEL_ID: ${MODEL_ID:-}
      MODEL_API_KEY: ${MODEL_API_KEY:-}
      REQUIRE_PRINCIPAL: ${REQUIRE_PRINCIPAL:-false}
      SANDBOX_PKG_SOURCE: ${SANDBOX_PKG_SOURCE:-}
      REMOTE_EXEC: ${REMOTE_EXEC:-}
      E2B_API_KEY: ${E2B_API_KEY:-}
    ports:
      - "${SERVER_BIND:-0.0.0.0}:${SERVER_PORT:-8090}:8090"

  # sema-registry 配置中心(MCP/skill/模型配置云同步;管人+云同步一环,可选)。
  # 镜像=该仓 CI 产物,现行来源=GHCR/Hub(迁移中,SWR 已弃用 2026-07-13;公开仓匿名可拉,
  # 无需 pull 凭据;CN 兜底=内网 Gitea registry)
  registry:
    image: ${SEMA_REGISTRY_IMAGE:-}
    profiles: ["registry"]
    restart: unless-stopped
    environment:
      NODE_ENV: production
      CONFIG_STORE: sqlite
      CONFIG_DB_PATH: /app/data/config.db
      # 引导管理员必须在该仓 src/lib/auth/admins.ts 白名单里,默认 zhaoheran
      ADMIN_BOOTSTRAP_USER: ${REGISTRY_ADMIN_USER:-zhaoheran}
      ADMIN_BOOTSTRAP_PASSWORD: ${REGISTRY_ADMIN_PASSWORD:-}
      SESSION_SECRET: ${REGISTRY_SESSION_SECRET:-}
      SERVICE_PULL_TOKEN: ${REGISTRY_PULL_TOKEN:-}
      # git 面(--git=provision/connect 时 sema-up.sh 填;都空=不启用 OAuth 登录,只有表单登录)
      GITEA_BASE_URL: ${REGISTRY_GITEA_BASE_URL:-}
      GITEA_OAUTH_CLIENT_ID: ${REGISTRY_GITEA_OAUTH_ID:-}
      GITEA_OAUTH_CLIENT_SECRET: ${REGISTRY_GITEA_OAUTH_SECRET:-}
    ports:
      - "${REGISTRY_BIND:-0.0.0.0}:${REGISTRY_PORT:-3000}:3000"
    volumes:
      - registrydata:/app/data   # SQLite 落盘,重建不丢配置
    healthcheck:
      # node 20 自带 fetch;镜像里没有 curl/wget。/api/healthz=真就绪端点(店面 / 是静态页,
      # store 挂了也 200——config-center 仓 src/app/api/healthz 才探 store 可达)
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
      interval: 5s
      timeout: 4s
      retries: 30

  # 自建 Gitea(--git=provision;git 仓托管,agent 的代码面)。引导管理员/access-token/
  # OAuth app(接 registry 登录)都是一次性动作,归 Layer 0(sema-up.sh),不进清单。
  git:
    image: ${GITEA_IMAGE:-gitea/gitea:1.24}
    profiles: ["git"]
    restart: unless-stopped
    environment:
      GITEA__security__INSTALL_LOCK: "true"    # 跳过安装向导(无它首访是 setup 页,API 全 405)
      GITEA__database__DB_TYPE: sqlite3
      GITEA__server__DOMAIN: ${GIT_DOMAIN:-localhost}
      GITEA__server__ROOT_URL: http://${GIT_DOMAIN:-localhost}:${GIT_HTTP_PORT:-3300}/
      GITEA__server__SSH_PORT: ${GIT_SSH_PORT:-2222}   # 对外告知口(clone 地址用);容器内 sshd 还是 22
      GITEA__service__DISABLE_REGISTRATION: "true"     # 只有引导管理员建的账号,不开放注册
    ports:
      - "${GIT_BIND:-0.0.0.0}:${GIT_HTTP_PORT:-3300}:3000"
      - "${GIT_BIND:-0.0.0.0}:${GIT_SSH_PORT:-2222}:22"
    volumes:
      - gitdata:/data   # 仓库+SQLite 持久卷,重建不丢仓
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/api/healthz"]
      interval: 5s
      timeout: 4s
      retries: 30

volumes:
  pgdata:
  miniodata:
  registrydata:
  gitdata:
