Files
codex-app-server/chv-guest/chv-sync.sh
T
host 81e4eaf4db feat(chv): 对齐 Muse P6 privsep worker + 确定性安全检查层
- cloud/chv/privsep.py(新):Gitea worker 注册表(凭证白名单只含
  GITEA_TOKEN)+ 类型化请求/响应(request_id 绑定、64KB 上限)+
  Unix socket 客户端(超时/崩溃 -> 类型化失败 WorkerRefusal)+
  客户端白名单校验
- cloud/chv/safety.py(新):UntrustedData 来源标记 +
  InjectionScanner 确定性规则(block/flag/allow,中英)+
  redact_secrets + AuditLogger(只记指纹,不记值)+
  audit_inference_request/response
- chv-guest/chv-gitea-worker.py + .service(新):systemd 沙箱化
  worker(SO_PEERCRED 只放 root,仓限 ~/workspace,git 只配 authd
  helper,绝不持 token;输出先扫描再返回)
- chv-guest/chv-sync-worker-client.py(新):agent 侧薄 CLI
- chv-guest/chv-sync.sh:pull/push/status 有 socket 时走 worker,
  否则旧路径 + stderr 警告(过渡兼容)
- cloud/chv/executor.py:_run_guest 前后加推理边界审计钩子
  (best-effort)
- docs/chv-muse-p6-gap.md(新):差距表 + 覆盖边界 + 真机待验证清单
- docs/chv-production-readiness.md:§15
- cloud/tests/test_chv_muse_p6.py(新):43 项

测试:新测试 43/43;全量 cloud 套件 1910 OK(skipped=9);mypy
在新增文件上干净(剩余 4 处为基线旧错 executor.py:1396/1401、
marker_tools.py:142,未动)。
真机验证:未做,排队在 round-11 之后,清单见
docs/chv-muse-p6-gap.md §4。
2026-11-06 22:31:54 +00:00

162 lines
6.9 KiB
Bash
Executable File
Raw 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.
#!/bin/bash
# chv-sync:轻量 VM 里的 Gitea 双向同步(用户工作区口径)。
#
# 主角是用户,不是 agent:用户在 Gitea 上的仓库在 VM 启动时
# clone 到 ~/workspace,用完再同步回 Gitea;VM 是用户的工作区,
# Gitea 是权威远端。同步口径沿用 Git Workspace Platform 已定
# 的规则:分叉或冲突时失败关闭,不强制覆盖、不自动 rebase、
# 不静默丢提交。
#
# 凭证绝不进镜像。启动时由 seed 盘投递 /etc/chv/guest-env
# (0600,root 所有),格式(KEY=VALUE,每行一项):
# GITEA_URL=https://gitea.tose.top
# GITEA_USER=<Gitea 用户名>
# GITEA_TOKEN=<专用 API token,仅此次测试/此次 VM 有效>
# SYNC_REPO=<owner/repo>
# SYNC_BRANCH=<可选,默认分支>
# GITEA_EMAIL=<可选,commit 身份邮箱;不给则留镜像占位>
# boot(root,cloud-init runcmd 调)读 guest-env,把 token 写进
# /home/chv/.git-credentials(0600,chv 所有,git credential
# store 标准机制),再以 chv 身份 clone 到 ~/workspace/<仓名>。
# pull/push/status 以 chv 身份跑,只用已落盘的 credential,不
# 再读 guest-env;token 不会出现在 git remote URL 或镜像里。
#
# 用法:
# chv-sync boot # 启动时 clone(已 clone 则静默退出,不自动 pull)
# chv-sync pull [仓目录] # 脏树拒绝;只接受 fast-forward,分叉即失败
# chv-sync push [仓目录] # 普通 push;无 upstream 时 push -u origin HEAD;
# # 非 fast-forward 即失败,绝不 --force
# chv-sync status [仓目录] # 本地状态 + 最近提交
# 仓目录省略时取 $WORKSPACE 下唯一的 git 仓库;多个时必须显式给。
set -euo pipefail
GUEST_ENV="${CHV_GUEST_ENV:-/etc/chv/guest-env}"
WORKSPACE="${CHV_WORKSPACE:-/home/chv/workspace}"
USER_HOME="${CHV_USER_HOME:-/home/chv}"
SYNC_USER="${CHV_USER:-chv}"
# privsep worker(feat-chv-muse-p6):socket 在时 pull/push/status
# 改走 worker(类型化请求),不在时走旧的直接路径 + 警告(过渡兼容,
# 旧镜像退役后改为无 worker 即 fail-closed)。
WORKER_SOCK="${CHV_GITEA_WORKER_SOCK:-/run/chv-gitea/worker.sock}"
WORKER_CLIENT="${CHV_GITEA_WORKER_CLIENT:-/usr/local/bin/chv-sync-worker-client}"
die() { echo "chv-sync: $*" >&2; exit 1; }
info() { echo "chv-sync: $*"; }
# 试走 worker:返回 42 = worker 不在(调用方走旧路径);其它返回
# 值即 worker 的退出码(调用方直接退出,不再回落重跑)。
try_worker() {
local action="$1"; shift
[ -S "$WORKER_SOCK" ] || return 42
[ -x "$WORKER_CLIENT" ] || return 42
"$WORKER_CLIENT" "$action" "$@"
}
# 解析仓目录:显式给则用它;否则取 WORKSPACE 下唯一的 git 仓库。
resolve_repo() {
if [ -n "${1:-}" ]; then
[ -d "$1/.git" ] || die "不是 git 仓库:$1"
printf '%s' "$1"
return 0
fi
local found=()
local d
for d in "$WORKSPACE"/*/; do
[ -d "${d}.git" ] && found+=("${d%/}")
done
if [ "${#found[@]}" -eq 1 ]; then
printf '%s' "${found[0]}"
return 0
fi
die "WORKSPACE 下 git 仓库不是唯一(${#found[@]} 个),请显式给仓目录"
}
cmd_boot() {
# 无 guest-env = 本次启动不同步任何仓,静默成功(VM 照常用)。
[ -f "$GUEST_ENV" ] || { info "无 $GUEST_ENV,本次启动不同步"; exit 0; }
# shellcheck disable=SC1090
set -a; . "$GUEST_ENV"; set +a
: "${GITEA_URL:?guest-env 缺 GITEA_URL}"
: "${GITEA_USER:?guest-env 缺 GITEA_USER}"
: "${GITEA_TOKEN:?guest-env 缺 GITEA_TOKEN}"
: "${SYNC_REPO:?guest-env 缺 SYNC_REPO}"
[ "$(id -u)" = "0" ] || die "boot 须以 root 运行(cloud-init runcmd)"
local host="${GITEA_URL#https://}"
host="${host#http://}"
host="${host%%/*}"
local name="${SYNC_REPO##*/}"
local dir="$WORKSPACE/$name"
mkdir -p "$WORKSPACE"
chown "$SYNC_USER:$SYNC_USER" "$WORKSPACE"
# token 只落盘进用户家目录的 credential store(0600),
# 不进 git remote URL、不进镜像、不进日志(此处不打印 token)。
local creds="$USER_HOME/.git-credentials"
printf 'https://%s:%s@%s\n' "$GITEA_USER" "$GITEA_TOKEN" "$host" > "$creds"
chmod 0600 "$creds"
chown "$SYNC_USER:$SYNC_USER" "$creds"
if [ -d "$dir/.git" ]; then
info "已 clone:$dir(不自动 pull,改动由用户显式同步)"
exit 0
fi
[ -e "$dir" ] && die "$dir 已存在但不是 git 仓库,请人工处理"
local clone_args=(clone)
[ -n "${SYNC_BRANCH:-}" ] && clone_args+=(--branch "$SYNC_BRANCH")
clone_args+=("$GITEA_URL/$SYNC_REPO.git" "$dir")
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git "${clone_args[@]}"
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" config user.name "$GITEA_USER"
if [ -n "${GITEA_EMAIL:-}" ]; then
runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" config user.email "$GITEA_EMAIL"
fi
info "已 clone $SYNC_REPO 到 $dir,HEAD=$(runuser -u "$SYNC_USER" -- env HOME="$USER_HOME" git -C "$dir" rev-parse --short HEAD)"
}
cmd_pull() {
local rc=0
try_worker pull "${1:-}" || rc=$?
[ "$rc" != "42" ] && exit "$rc"
echo "chv-sync: worker 不在,走旧的直接路径(过渡兼容)" >&2
local dir
dir="$(resolve_repo "${1:-}")"
[ -z "$(git -C "$dir" status --porcelain)" ] \
|| die "工作树有未提交改动,拒绝 pull(先 commit 或 stash)"
git -C "$dir" pull --ff-only \
|| die "pull 不是 fast-forward(本地与远端已分叉),请人工处理,不自动 rebase/merge"
info "已同步:$dir HEAD=$(git -C "$dir" rev-parse --short HEAD)"
}
cmd_push() {
local rc=0
try_worker push "${1:-}" || rc=$?
[ "$rc" != "42" ] && exit "$rc"
echo "chv-sync: worker 不在,走旧的直接路径(过渡兼容)" >&2
local dir
dir="$(resolve_repo "${1:-}")"
if git -C "$dir" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
git -C "$dir" push \
|| die "push 被拒绝(远端已有新提交,先 pull 再 push,不 --force)"
else
git -C "$dir" push -u origin HEAD \
|| die "push 被拒绝(远端已有新提交,先 pull 再 push,不 --force)"
fi
info "已 push:$dir HEAD=$(git -C "$dir" rev-parse --short HEAD)"
}
cmd_status() {
local rc=0
try_worker status "${1:-}" || rc=$?
[ "$rc" != "42" ] && exit "$rc"
echo "chv-sync: worker 不在,走旧的直接路径(过渡兼容)" >&2
local dir
dir="$(resolve_repo "${1:-}")"
git -C "$dir" status -sb
git -C "$dir" log --oneline -5
}
case "${1:-}" in
boot) shift; cmd_boot "$@" ;;
pull) shift; cmd_pull "$@" ;;
push) shift; cmd_push "$@" ;;
status) shift; cmd_status "$@" ;;
*) echo "用法:chv-sync boot|pull|push|status [仓目录]" >&2; exit 2 ;;
esac