name: knowledge-governance-p1 services: postgres: image: postgres:17-alpine environment: POSTGRES_DB: governance POSTGRES_USER: governance POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password secrets: - postgres_password volumes: - governance-pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U governance -d governance"] interval: 5s timeout: 3s retries: 20 restart: unless-stopped api: build: context: . dockerfile: Dockerfile command: ["/app/api"] user: "${APP_UID:-1000}:${APP_GID:-1000}" environment: <<: &app-environment DATABASE_URL: postgres://governance@postgres:5432/governance?sslmode=disable PGPASSFILE: /run/secrets/pgpass APP_ADDR: :8080 WORKER_POLL_INTERVAL: 1s WORKER_LEASE_DURATION: 30s WORKER_RETRY_DELAY: 5s TEST_WEKNORA_BASE_URL: "${TEST_WEKNORA_BASE_URL:-}" # 验收通过后是否自动批准:off | internal(默认,仅对内;对外仍需人工)| all。 # 只影响"审核"这一步;生产发布的双闸、人工授权与发布前快照门禁不受影响。 TEST_AUTO_REVIEW_POLICY: "${TEST_AUTO_REVIEW_POLICY:-internal}" DINGTALK_LOGIN_SIDECAR_URL: "http://dingtalk-login:8091" MINERU_BASE_URL: "${MINERU_BASE_URL:-}" TEST_INTERNAL_WEKNORA_KB_ID: "${TEST_INTERNAL_WEKNORA_KB_ID:-1b52c140-2e18-4676-afc5-4c7d8d190a8c}" TEST_EXTERNAL_WEKNORA_KB_ID: "${TEST_EXTERNAL_WEKNORA_KB_ID:-fd7db6aa-763a-46f1-84d2-ecc7c952a5ea}" # 「版本记录」页:**匿名只读**远端仓库的提交与 tag(不使用任何凭据)。 # 留空 = 未接线(页面降级、同步入口不可用);来源只允许下面批准的 origin 与固定仓库。 VERSION_HISTORY_GITEA_BASE_URL: "${VERSION_HISTORY_GITEA_BASE_URL:-}" VERSION_HISTORY_REPO: "${VERSION_HISTORY_REPO:-Weknora/knowledge-governance}" VERSION_HISTORY_ALLOWED_ORIGINS: "${VERSION_HISTORY_ALLOWED_ORIGINS:-}" # 页面链接用的 origin(给内网外的浏览器点开);留空 = 与抓取 origin 相同。 VERSION_HISTORY_LINK_ORIGIN: "${VERSION_HISTORY_LINK_ORIGIN:-}" VERSION_HISTORY_HTTP_TIMEOUT: "${VERSION_HISTORY_HTTP_TIMEOUT:-15s}" VERSION_HISTORY_MAX_PAGES: "${VERSION_HISTORY_MAX_PAGES:-200}" # 「整库版本」页:只读已封存的整库候选产物。归档根由 `libraryctl capture` # 在主机上写入(私有 0700),这里只挂只读;留空 = 未接线(页面降级展示原因)。 LIBRARY_ARCHIVE_ROOT: "${LIBRARY_ARCHIVE_ROOT:-/var/lib/knowledge-governance/library}" # 主机侧封存 agent 的 unix socket:api 只用它判断"能不能从页面发起", # worker 用它投递作业。容器里没有 Docker socket,也没有来源凭据。 LIBRARY_AGENT_SOCKET: "${LIBRARY_AGENT_SOCKET:-/run/library-agent/agent.sock}" # Mount this independent master-key file read-only using the deployment overlay. QA_AI_MASTER_KEY_FILE: "${QA_AI_MASTER_KEY_FILE:-}" QA_AI_DNS_OVER_TLS: "${QA_AI_DNS_OVER_TLS:-}" QA_AI_TRUST_TLS_PROXY: "${QA_AI_TRUST_TLS_PROXY:-false}" # P10 readiness probe reads the routing KB with the same scoped key. TEST_WEKNORA_API_KEY_FILE: /run/secrets/test_weknora_api_key TEST_WEKNORA_ALLOW_DOCKER_HTTP: "${TEST_WEKNORA_ALLOW_DOCKER_HTTP:-false}" WORKER_LIVENESS_STALE_AFTER: "${WORKER_LIVENESS_STALE_AFTER:-2m}" PRODUCTION_RELEASE_REQUESTS_ENABLED: ${PRODUCTION_RELEASE_REQUESTS_ENABLED:-false} PRODUCTION_BACKUP_RESTORE_VERIFIED: ${PRODUCTION_BACKUP_RESTORE_VERIFIED:-false} # Read-only probe of the production WeKnora endpoint; publishing keeps # reading its target from RELEASE_CONFIG_FILE. PRODUCTION_WEKNORA_BASE_URL: "${PRODUCTION_WEKNORA_BASE_URL:-}" PRODUCTION_WEKNORA_API_KEY_FILE: "${PRODUCTION_WEKNORA_API_KEY_FILE:-}" PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP: "${PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP:-false}" # 数据源发现(系统设置 → 数据源配置):api 用 dws 只读列举钉钉知识库与钉盘空间。 DWS_BIN: /usr/local/bin/dws DWS_CONFIG_DIR: /data/dws DWS_HOME: /data/home DWS_DISABLE_KEYCHAIN: "1" secrets: - pgpass - test_weknora_api_key volumes: # 整库封存产物:只读挂载,api 只读不写(写入只发生在主机上的 libraryctl)。 - /home/zheromiku/.local/share/knowledge-governance/library:/var/lib/knowledge-governance/library:ro # 与 worker 共用同一份 dws 状态:refresh token 是轮换制,绝不能在两处各留一份。 - /home/zheromiku/.dws:/data/dws # dws 的文件型 keychain 必须有 $HOME/.local/share/dws-cli,故挂到 DWS_HOME 下。 - /home/zheromiku/.local/share/dws-cli:/data/home/.local/share/dws-cli depends_on: postgres: condition: service_healthy healthcheck: # P10: liveness only — a third-party outage must never restart the API. test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/livez"] interval: 5s timeout: 3s retries: 20 read_only: true tmpfs: - /tmp networks: - default - weknora-local restart: unless-stopped worker: build: context: . dockerfile: Dockerfile command: ["/app/worker"] user: "${APP_UID:-1000}:${APP_GID:-1000}" environment: <<: *app-environment WORKER_ID: worker-1 QA_AI_MASTER_KEY_FILE: "${QA_AI_MASTER_KEY_FILE:-}" QA_AI_DNS_OVER_TLS: "${QA_AI_DNS_OVER_TLS:-}" PP_SCAN_ENABLED: "${PP_SCAN_ENABLED:-false}" PP_SCAN_ALLOWED_ROOT: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}" PP_SCAN_LEDGER_PATH: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}/public-weknora-sync/state.jsonl" PP_SCAN_MANIFEST_PATH: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}/public-documents/manifest.json" TEST_WEKNORA_ENABLED: "${TEST_WEKNORA_ENABLED:-false}" TEST_WEKNORA_ALLOW_DOCKER_HTTP: "${TEST_WEKNORA_ALLOW_DOCKER_HTTP:-false}" TEST_WEKNORA_KB_ID: "${TEST_WEKNORA_KB_ID:-}" TEST_WEKNORA_API_KEY_FILE: /run/secrets/test_weknora_api_key GOVERNANCE_SPOOL_ROOT: /var/lib/governance/spool INGESTION_SPOOL_ROOT: /var/lib/governance/ingestion TEST_WEKNORA_HTTP_TIMEOUT: "${TEST_WEKNORA_HTTP_TIMEOUT:-60s}" TEST_WEKNORA_POLL_INITIAL: "${TEST_WEKNORA_POLL_INITIAL:-10s}" TEST_WEKNORA_POLL_MAX: "${TEST_WEKNORA_POLL_MAX:-60s}" TEST_WEKNORA_STALLED_AFTER: "${TEST_WEKNORA_STALLED_AFTER:-30m}" TEST_WEKNORA_MAX_UPLOAD_BYTES: "${TEST_WEKNORA_MAX_UPLOAD_BYTES:-52428800}" TEST_ACCEPTANCE_POLICY_VERSION: "${TEST_ACCEPTANCE_POLICY_VERSION:-structural-v1}" # DingTalk 取件走 dws(默认优先 dws,可重试失败回落老路径):dws|auto|legacy ARTIFACT_DINGTALK_FETCHER: "${ARTIFACT_DINGTALK_FETCHER:-auto}" DWS_BIN: /usr/local/bin/dws DWS_CONFIG_DIR: /data/dws # dws 的文件型 keychain 在 $HOME/.local/share/dws-cli(XDG_DATA_HOME 不被识别), # 仅给 dws 子进程换 HOME,不动整个 worker 的 HOME。 DWS_HOME: /data/home DWS_DISABLE_KEYCHAIN: "1" SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt secrets: - pgpass - test_weknora_api_key volumes: - ./spool:/var/lib/governance/spool:ro - ./spool/ingestion:/var/lib/governance/ingestion # 整库封存 agent 的 socket 目录(由主机侧 systemd user 服务持有)。 # 容器只拿到"投递作业"这一条通道:Docker socket 与来源凭据都不在这里。 - /home/zheromiku/.local/share/knowledge-governance/agent:/run/library-agent - "${PP_SCAN_HOST_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}:${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}:ro" - /home/zheromiku/dingtalk_kb_download:/sources/dingtalk:ro # dws 凭据:与宿主共享同一份 state(refresh token 是轮换制,不能在两处各留一份) - /home/zheromiku/.dws:/data/dws # dws 的文件型 keychain:必须有 $HOME/.local/share/dws-cli,故挂到 DWS_HOME 下 - /home/zheromiku/.local/share/dws-cli:/data/home/.local/share/dws-cli - /home/zheromiku/tmp/alist:/sources/alist:ro - /home/zheromiku/dobot-portal-sync:/sources/pp:ro - ./secrets/dingtalk_capture.json:/run/source-secrets/dingtalk_capture.json:ro - ./secrets/alist_connections.xlsx:/run/source-secrets/alist_connections.xlsx:ro - ./secrets/alist_api_token:/run/source-secrets/alist_api_token:ro - ./secrets/pp_credentials.json:/run/source-secrets/pp_credentials.json:ro networks: - default - weknora-local depends_on: postgres: condition: service_healthy read_only: true tmpfs: - /tmp restart: unless-stopped release-worker: build: context: . dockerfile: Dockerfile command: ["/app/release-worker"] user: "${APP_UID:-1000}:${APP_GID:-1000}" environment: <<: *app-environment WORKER_ID: release-worker-1 RELEASE_WORKER_ENABLED: "false" RELEASE_CONFIG_FILE: "" secrets: - pgpass depends_on: postgres: condition: service_healthy read_only: true tmpfs: - /tmp restart: unless-stopped dingtalk-login: # Chromium profile for DingTalk scan sessions. The bind-mounted secrets # directory is rw ONLY here; the worker keeps reading the capture read-only. build: context: ./services/dingtalk-login image: knowledge-governance-dingtalk-login:local user: "${APP_UID:-1000}:${APP_GID:-1000}" environment: CAPTURE_FILE: /run/source-secrets/dingtalk_capture.json DINGTALK_STATE_FILE: /sources/dingtalk/data/dingtalk-internal/inventory-state.json VERIFY_SPACE: "${DINGTALK_VERIFY_SPACE:-9JOGO6332vlBnm4Q}" QR_TTL_MS: "300000" volumes: - ./secrets:/run/source-secrets - /home/zheromiku/dingtalk_kb_download:/sources/dingtalk - dingtalk-chromium-profile:/profile security_opt: - no-new-privileges:true tmpfs: - /tmp healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:8091/healthz',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"] interval: 10s timeout: 5s retries: 30 start_period: 15s restart: unless-stopped web: build: context: . dockerfile: Dockerfile.web ports: - "8088:8080" depends_on: api: condition: service_healthy read_only: true volumes: # 上一版构建的静态资源(只读,由部署脚本落盘):CDN/浏览器可能还持有旧 index.html, # 让它的旧哈希引用不至于 404 —— 否则页面会空白或"只有文字没样式"。 - ./deploy/legacy-assets:/usr/share/nginx/legacy:ro tmpfs: - /tmp - /var/cache/nginx - /var/run restart: unless-stopped secrets: postgres_password: file: ./secrets/postgres_password pgpass: file: ./secrets/pgpass test_weknora_api_key: file: ./secrets/test_weknora_api_key volumes: governance-pgdata: dingtalk-chromium-profile: networks: weknora-local: external: true name: weknora071full-network