Files
姚章浩 910c0fcd76
build / build (push) Failing after 16s
feat(library): 页面发起封存的最小权限主机侧执行边界
- libraryagent:0700 目录内 0600 unix socket + SO_PEERCRED 同 uid,
  固定 binary/归档/日志/项目/写入者声明,请求只能选范围与作业号
- API:POST/GET /api/v1/library/captures(入队 library.capture + 读队列状态)
- worker:library.capture handler 走 socket;启动先 recover;不挂 Docker socket、
  不持有来源凭据
- web:整库版本页显示真实作业状态并可发起封存(未接线/无权限给出原因)
- scripts/library-agent-install.sh:构建、安装 systemd user 服务并读回权限
2026-10-28 12:11:08 +08:00

259 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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