Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0193a12b57 | ||
|
|
b87266d63f | ||
|
|
c0d8284615 | ||
|
|
7c42363058 | ||
|
|
1bb06334ff | ||
|
|
5f0f1a443b | ||
|
|
acd42f3601 | ||
|
|
523eb0898d | ||
|
|
120fb909f8 | ||
|
|
bcbda0d048 | ||
|
|
b05eb1babe | ||
|
|
a8581699f2 | ||
|
|
cc9cada5df | ||
|
|
dbed29c8c7 | ||
|
|
24f7e4ecc3 | ||
|
|
b588bf8fb2 | ||
|
|
f300033138 | ||
|
|
7308c9c0c1 | ||
|
|
48290bd620 |
@@ -0,0 +1,267 @@
|
||||
# 执行 GitOps 独立 Worktree 与统一运行身份迁移
|
||||
|
||||
```yaml
|
||||
task_id: TASK-GITOPS-ISOLATED-WORKTREE-MIGRATION-20260714T233742UTC8
|
||||
created_at: 2026-07-14T23:37:42+08:00
|
||||
timezone: Asia/Shanghai
|
||||
owner_approved: true
|
||||
user_action_required: false
|
||||
risk: high
|
||||
production_business_change: false
|
||||
```
|
||||
|
||||
## 目标
|
||||
|
||||
根治共享 worktree 与多身份写入导致的 ACL、checkout 和未跟踪文件覆盖问题。
|
||||
|
||||
实施:
|
||||
|
||||
1. 统一 GitOps/OpenClaw 写入身份为 `aiman:gitops`;
|
||||
2. 统一 `UMask=0002`;
|
||||
3. 为 GitOps 根目录设置 setgid 与默认 ACL;
|
||||
4. 建立 bare repo 和每任务独立 worktree;
|
||||
5. 启用任务启动前自检;
|
||||
6. 验证 OpenClaw 与 ChatGPT 并发任务互不干扰;
|
||||
7. 保留原共享 workspace 作为只读回滚源,禁止智能体继续在其中切换分支。
|
||||
|
||||
## 强制通知
|
||||
|
||||
收到任务后立即微信通知 Owner:
|
||||
|
||||
```text
|
||||
【任务已接收】GitOps独立worktree与统一运行身份迁移开始。将暂停写入服务、建立独立任务工作树并验证并发执行;生产业务版本不变。
|
||||
```
|
||||
|
||||
任务结束后无论 PASS、BLOCKED 或 ROLLED_BACK,立即微信汇报结果并记录 message_id。
|
||||
|
||||
## 1. 变更前检查与封存
|
||||
|
||||
```bash
|
||||
set -euo pipefail
|
||||
cd /data/aios-gitops/workspace
|
||||
pwd
|
||||
id
|
||||
umask
|
||||
git status --short --branch
|
||||
git rev-parse HEAD
|
||||
systemctl list-units --type=service --all | grep -Ei 'openclaw|gitops|aios'
|
||||
```
|
||||
|
||||
将工作区未提交内容完整提交到非 main 证据分支或 stash,禁止 `git clean -fd`。
|
||||
|
||||
记录当前运行服务、用户、组、umask、PID、工作区 HEAD 和未跟踪文件清单。
|
||||
|
||||
## 2. 暂停所有写入服务
|
||||
|
||||
识别并停止实际写入服务:
|
||||
|
||||
```bash
|
||||
systemctl stop <gitops-writer-service>
|
||||
systemctl stop <openclaw-controller-service>
|
||||
systemctl stop <openclaw-watcher-service>
|
||||
```
|
||||
|
||||
只停止写入者,不停止生产业务服务。记录每条命令和退出码。
|
||||
|
||||
## 3. 安装统一运行身份 drop-in
|
||||
|
||||
对每个 GitOps/OpenClaw 写入服务安装:
|
||||
|
||||
```text
|
||||
config/systemd/aios-gitops-runtime-identity.conf
|
||||
```
|
||||
|
||||
目标:
|
||||
|
||||
```yaml
|
||||
User: aiman
|
||||
Group: gitops
|
||||
SupplementaryGroups: gitops
|
||||
UMask: 0002
|
||||
```
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl cat <service>
|
||||
```
|
||||
|
||||
不得让服务继续以 root 写 workspace。
|
||||
|
||||
## 4. 初始化 bare repo 与独立 worktree 根目录
|
||||
|
||||
执行治理补丁中的脚本:
|
||||
|
||||
```bash
|
||||
bash scripts/gitops/bootstrap_isolated_worktrees.sh
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
```yaml
|
||||
bare_repo: /data/aios-gitops/repo.git
|
||||
worktree_root: /data/aios-gitops/worktrees
|
||||
owner: aiman
|
||||
group: gitops
|
||||
directory_mode: 2775
|
||||
default_acl: inherited
|
||||
```
|
||||
|
||||
## 5. 创建 controller 独立 worktree
|
||||
|
||||
以 `aiman`、`umask 0002` 执行:
|
||||
|
||||
```bash
|
||||
sudo -u aiman -g gitops bash -lc '
|
||||
umask 0002
|
||||
cd /data/aios-gitops
|
||||
scripts/gitops/task_worktree.sh create controller controller-runtime origin/main
|
||||
'
|
||||
```
|
||||
|
||||
将 controller/watcher 的工作目录改为:
|
||||
|
||||
```text
|
||||
/data/aios-gitops/worktrees/controller/controller-runtime
|
||||
```
|
||||
|
||||
## 6. 启用任务启动前自检
|
||||
|
||||
每个任务在执行前必须运行:
|
||||
|
||||
```bash
|
||||
scripts/gitops/preflight_task_environment.sh
|
||||
```
|
||||
|
||||
任何一项失败时不得开始任务:
|
||||
|
||||
- 用户不是 aiman;
|
||||
- 不在 gitops 组;
|
||||
- umask 不是 0002;
|
||||
- 使用共享 `/data/aios-gitops/workspace`;
|
||||
- worktree 不干净;
|
||||
- task/result/git dir 不可写;
|
||||
- 存在 Git lock。
|
||||
|
||||
## 7. 启动服务
|
||||
|
||||
```bash
|
||||
systemctl start <gitops-writer-service>
|
||||
systemctl start <openclaw-controller-service>
|
||||
systemctl start <openclaw-watcher-service>
|
||||
```
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
systemctl show <service> -p User -p Group -p UMask -p WorkingDirectory
|
||||
ps -eo user,group,pid,cmd | grep -Ei 'openclaw|gitops|aios'
|
||||
```
|
||||
|
||||
## 8. 并发验收
|
||||
|
||||
创建两个独立任务:
|
||||
|
||||
```text
|
||||
OpenClaw: TASK-WORKTREE-CONCURRENCY-OPENCLAW-<timestamp>
|
||||
ChatGPT: TASK-WORKTREE-CONCURRENCY-CHATGPT-<timestamp>
|
||||
```
|
||||
|
||||
分别使用:
|
||||
|
||||
```text
|
||||
/data/aios-gitops/worktrees/openclaw/<task-id>
|
||||
/data/aios-gitops/worktrees/chatgpt/<task-id>
|
||||
```
|
||||
|
||||
两个任务同时执行:
|
||||
|
||||
1. 写各自 `.agent/tasks/<agent>/`;
|
||||
2. 写各自 `.agent/results/<agent>/`;
|
||||
3. 创建深层子目录,验证默认 ACL;
|
||||
4. commit;
|
||||
5. push;
|
||||
6. 不切换共享 workspace 分支。
|
||||
|
||||
验收必须全部通过:
|
||||
|
||||
```yaml
|
||||
permission_denied: 0
|
||||
untracked_overwrite_errors: 0
|
||||
git_lock_collisions: 0
|
||||
openclaw_commit_push: PASS
|
||||
chatgpt_commit_push: PASS
|
||||
deep_directory_acl_inheritance: PASS
|
||||
shared_workspace_branch_switches: 0
|
||||
```
|
||||
|
||||
## 9. 回滚
|
||||
|
||||
若迁移失败:
|
||||
|
||||
1. 停止新服务;
|
||||
2. 保留失败 worktree 供审计;
|
||||
3. 恢复 systemd drop-in 备份;
|
||||
4. `daemon-reload`;
|
||||
5. 恢复原服务状态;
|
||||
6. 不删除 `/data/aios-gitops/workspace`;
|
||||
7. 生产业务保持不变。
|
||||
|
||||
## 10. 结果文件
|
||||
|
||||
提交:
|
||||
|
||||
```text
|
||||
.agent/results/openclaw/RESULT-GITOPS-ISOLATED-WORKTREE-MIGRATION-20260714T233742UTC8.md
|
||||
```
|
||||
|
||||
首段必须包含:
|
||||
|
||||
```yaml
|
||||
status: PASS | BLOCKED | ROLLED_BACK
|
||||
task_id: TASK-GITOPS-ISOLATED-WORKTREE-MIGRATION-20260714T233742UTC8
|
||||
created_at: 2026-07-14T23:37:42+08:00
|
||||
timezone: Asia/Shanghai
|
||||
user_action_required: false
|
||||
runtime_identity:
|
||||
user:
|
||||
group:
|
||||
umask:
|
||||
bare_repo:
|
||||
worktree_root:
|
||||
controller_worktree:
|
||||
services:
|
||||
default_acl:
|
||||
preflight:
|
||||
concurrency_test:
|
||||
openclaw_branch:
|
||||
openclaw_commit:
|
||||
chatgpt_branch:
|
||||
chatgpt_commit:
|
||||
permission_denied_count:
|
||||
untracked_overwrite_count:
|
||||
git_lock_collision_count:
|
||||
owner_notifications:
|
||||
received_message_id:
|
||||
terminal_message_id:
|
||||
rollback:
|
||||
remaining_risks:
|
||||
next_step:
|
||||
```
|
||||
|
||||
提交并推送到独立执行分支。禁止直接推 main。
|
||||
|
||||
## 完成微信模板
|
||||
|
||||
PASS:
|
||||
|
||||
```text
|
||||
【任务完成】GitOps独立worktree迁移已通过。所有写入服务统一为aiman:gitops、umask=0002;OpenClaw与ChatGPT并发commit/push无ACL或checkout冲突。生产业务版本未变化。
|
||||
```
|
||||
|
||||
阻塞:
|
||||
|
||||
```text
|
||||
【任务阻塞】GitOps独立worktree迁移未通过。原因=<具体服务/权限/并发验收>;已保留原workspace和回滚能力,生产业务未变化。
|
||||
```
|
||||
+58
@@ -11,3 +11,61 @@ runtime/tmp/
|
||||
runtime/cache/
|
||||
logs/
|
||||
config/test_write_perms
|
||||
|
||||
# BEGIN AIOS REGISTERED CHILD WORKTREES (V2R1C1 2026-07-21)
|
||||
/A0-C1-WECHAT-BACKEND-LINEAGE-REMEDIATION-20260718/
|
||||
/A0-CALLBACK-TRANSPORT-PENDING-RECOVERY-20260718/
|
||||
/A0-CENTRAL-GPT-LIVE-CLOSURE-20260718/
|
||||
/A0-CODEX-AUTOMATION-CLOSURE-INTEGRATION-20260718/
|
||||
/A0-CODEX-AUTOMATION-CLOSURE-RESULT-20260718/
|
||||
/A0-CODEX-LOCAL-CENTRAL-CHANNEL-20260718/
|
||||
/A0-FINAL-AUTOMATION-INTEGRATION-20260718/
|
||||
/A0-PENDING-PLAN-READONLY-20260718/
|
||||
/A0-REMOTE-CODEX-BRAIN-192-168-88-6-20260719/
|
||||
/A0-REMOTE-CODEX-HOME-IMRUYI-20260719/
|
||||
/A0-WORKSPACE-TRIGGER-PROTOCOL-HARDENING-20260718/
|
||||
/AIOS-V2R1-PRODUCTION-SUPERVISOR-20260720/
|
||||
/RESULT-A0-CALLBACK-TRANSPORT-PENDING-RECOVERY-20260718/
|
||||
/RESULT-A0-WORKSPACE-TRIGGER-PROTOCOL-HARDENING-20260718/
|
||||
/RESULT-AIOS-SPARK-RUNNER-V2-OWNER-FIXES-20260717/
|
||||
/RESULT-CODEX-AIOS-ORCHESTRATOR-GATEWAY-V1-BLOCKED-20260718/
|
||||
/RESULT-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-V2-20260717T174800UTC8/
|
||||
/RESULT-CODEX-V2R1-PWX-B3-REPAIR3-GATE0-20260717T214100UTC8/
|
||||
/TASK-CODEX-AIOS-ORCHESTRATOR-GATEWAY-V1-20260718T010100UTC8/
|
||||
/TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-RESULT-20260717T040314Z/
|
||||
/TASK-CODEX-AIOS-SPARK-RUNNER-PROVISIONING-V2-20260717T075800Z/
|
||||
/TASK-CODEX-V2R1-PERSONAL-WEIXIN-FOUNDATION-RETRY1-20260715T183951UTC8/
|
||||
/TASK-CODEX-V2R1-PWX-B3-REPAIR3-20260716/
|
||||
/TASK-CODEX-V2R1-PWX-B3-REPAIR3-EXECUTION-BLOCKED-20260717T041314UTC8/
|
||||
/TASK-CODEX-V2R1-PWX-B3-REPAIR3-GATE0-20260717T203000UTC8/
|
||||
/v2r1-personal-weixin-b3-b4-owner-authorization-20260716T120210UTC8/
|
||||
/v2r1-personal-weixin-b3-evidence-git-20260716T123454UTC8/
|
||||
/v2r1-personal-weixin-b3-evidence-git-worktree-20260716T124948UTC8/
|
||||
/v2r1-personal-weixin-b3-evidence-repair3-20260716T232732UTC8/
|
||||
/v2r1-personal-weixin-b3-evidence-source-20260716/
|
||||
/v2r1-personal-weixin-b3-evidence-source-result-20260716T140343UTC8/
|
||||
/v2r1-personal-weixin-foundation-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry1-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry10-20260716T092623UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry10-result-20260716T092623UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry2-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry2-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry3-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry3-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry4-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry4-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry5-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry6-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry6-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry7-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry7-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry8-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry8-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry9-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry9-result-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-foundation-retry9-resume-owner-patch-20260716T014714UTC8/
|
||||
/v2r1-personal-weixin-foundation-takeover-20260715T183951UTC8/
|
||||
/v2r1-personal-weixin-r10-b3-b4-evidence-gate-20260716T101002UTC8/
|
||||
/v2r1-personal-weixin-r10-lineage-amendment-20260716T095245UTC8/
|
||||
/v2r1-personal-weixin-upstream-protocol-evidence-20260716T105003UTC8/
|
||||
# END AIOS REGISTERED CHILD WORKTREES (V2R1C1 2026-07-21)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# CODEX GitOps isolated worktree migration result
|
||||
|
||||
- Run: `20260715T001652UTC8`
|
||||
- Host: `cn-network-mkt`
|
||||
- Source rollback workspace: `/data/aios-gitops/workspace` (preserved; no reset, clean, force-push, or prune performed)
|
||||
- Source head/branch: `b05eb1babe9c657e8776c09eec71302166902cc3` / `task/codex/gitops-isolated-worktree-migration-20260715T001652UTC8`
|
||||
- Untracked snapshot: `/data/aios-gitops/artifacts/worktree-untracked-backups/migration-20260715T001652UTC8/workspace-untracked-51-files.tar.gz`
|
||||
- Snapshot SHA256: `d5cf772d1badec5a9582e366ac4709d6aaa7613a2d091372d35787e7152d9b2a`
|
||||
- Untracked files archived: `51`
|
||||
|
||||
## Isolated roots
|
||||
|
||||
| role | path | branch | HEAD |
|
||||
|---|---|---|---|
|
||||
| bare remote | `/data/aios-gitops/repo.git` | n/a | `b05eb1babe9c657e8776c09eec71302166902cc3` |
|
||||
| controller | `/data/aios-gitops/worktrees/controller` | `agent/controller/gitops-isolated-worktree-migration-20260715T001652UTC8` | `b05eb1babe9c657e8776c09eec71302166902cc3` |
|
||||
| openclaw | `/data/aios-gitops/worktrees/openclaw` | `agent/openclaw/gitops-isolated-worktree-migration-20260715T001652UTC8` | `6483e34892f56daa2abdd11f6a73fc4f99648841` |
|
||||
| codex acceptance | `/data/aios-gitops/worktrees/codex` | `agent/codex/gitops-isolated-worktree-migration-20260715T001652UTC8` | `bcbda0d048dad164414c05740736d252652fc927` |
|
||||
|
||||
The bare root, worktree root, and controller/openclaw/codex worktree roots are `aiman:gitops`, mode `2775` for directories, with default ACL entries `user:aiman:rwx`, `group:gitops:rwx`, and mask `rwx`. Recursive inheritance was tested as `aiman`: a new file retains named ACL `rwx` with effective `rw-` due to its file mask. Bare `core.sharedRepository=group`.
|
||||
|
||||
## Cutover
|
||||
|
||||
Actual GitOps writers were drained before cutover. Systemd drop-ins route controller, autonomous daemon, agent command watcher, and active model API to `/data/aios-gitops/worktrees/controller`; OpenClaw receives `/data/aios-gitops/worktrees/openclaw` through safe environment overrides and remains stopped because it was inactive before this migration. All migrated writer units use `UMask=0002`; controller/daemon/watcher/model API use `aiman:gitops`, and root-run OpenClaw has primary group `gitops` to preserve its root-owned production home access.
|
||||
|
||||
Model API restart health response:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"status": "ok",
|
||||
"service": "chatgpt-gitops-adapter",
|
||||
"workspace": "/data/aios-gitops/worktrees/controller"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Gitea and its branch-consistency sentinel were not modified. The sentinel still references the preserved rollback source and was already failed/inactive at preflight; this report therefore makes no claim that every non-writer reference on the host is source-free.
|
||||
|
||||
Scope note: normalizing the existing `/data/aios-gitops/worktrees` parent recursively applied the requested aiman:gitops ACL/group policy to pre-existing worktrees below that parent. No repository contents, source workspace refs, source worktree files, reset/clean, force-push, or prune were changed.
|
||||
During final verification, ACL normalization was found to have removed Git-tracked executable bits in the three new worktrees only. The diffs were mode-only (no content delta), and every tracked file mode was restored from its Git index; all three worktrees then returned clean. The preserved source workspace was never touched.
|
||||
|
||||
## Concurrent acceptance
|
||||
|
||||
OpenClaw and Codex jobs ran concurrently on distinct branches/worktrees; each committed and pushed to the isolated bare remote without retry and without `GIT_OPTIONAL_LOCKS=0`.
|
||||
|
||||
| job | local OID | bare remote OID | verified |
|
||||
|---|---|---|---|
|
||||
| OpenClaw | `6483e34892f56daa2abdd11f6a73fc4f99648841` | `6483e34892f56daa2abdd11f6a73fc4f99648841` | yes |
|
||||
| Codex acceptance | `bcbda0d048dad164414c05740736d252652fc927` | `bcbda0d048dad164414c05740736d252652fc927` | yes |
|
||||
|
||||
## Raw-log collision/error counts
|
||||
|
||||
| pattern | count |
|
||||
|---|---:|
|
||||
| permission denied | 3 |
|
||||
| checkout collision | 0 |
|
||||
| lock collision / index.lock | 0 |
|
||||
|
||||
The three `permission denied` matches are from the first malformed report generator attempting to execute Markdown-backticked paths; they are not Git/ACL/checkout permission failures. That report was replaced by a literal-format correction without rewriting history. Count scope is the two persisted orchestration logs plus the two acceptance logs; report-correction output is excluded to avoid self-counting the explanatory text.
|
||||
|
||||
Persisted raw logs: `/data/aios-gitops/logs/CODEX-GITOPS-ISOLATED-WORKTREE-MIGRATION-20260715T001652UTC8.raw.log`, `/data/aios-gitops/logs/CODEX-GITOPS-OpenClaw-ACCEPTANCE-20260715T001652UTC8.raw.log`, and `/data/aios-gitops/logs/CODEX-GITOPS-Codex-ACCEPTANCE-20260715T001652UTC8.raw.log`.
|
||||
@@ -0,0 +1,59 @@
|
||||
version: 1
|
||||
policy_id: gitops-isolated-worktree-governance-20260714T232948UTC8
|
||||
|
||||
runtime_identity:
|
||||
user: aiman
|
||||
group: gitops
|
||||
umask: "0002"
|
||||
prohibit_root_workspace_writes: true
|
||||
|
||||
repository:
|
||||
source_worktree: /data/aios-gitops/workspace
|
||||
bare_repo: /data/aios-gitops/repo.git
|
||||
worktree_root: /data/aios-gitops/worktrees
|
||||
remote: origin
|
||||
|
||||
worktrees:
|
||||
controller: /data/aios-gitops/worktrees/controller
|
||||
task_pattern: /data/aios-gitops/worktrees/{agent}/{task_id}
|
||||
allowed_agents:
|
||||
- openclaw
|
||||
- chatgpt
|
||||
- controller
|
||||
require_clean_start: true
|
||||
remove_after_successful_push: true
|
||||
retain_on_failure: true
|
||||
|
||||
permissions:
|
||||
owner: aiman
|
||||
group: gitops
|
||||
directory_mode: "2775"
|
||||
file_mode: "0664"
|
||||
default_acl:
|
||||
user:aiman: rwx
|
||||
group:gitops: rwx
|
||||
apply_to:
|
||||
- /data/aios-gitops
|
||||
- /data/aios-gitops/worktrees
|
||||
|
||||
preflight:
|
||||
required_user: aiman
|
||||
required_group: gitops
|
||||
required_umask: "0002"
|
||||
require_writable_git_dir: true
|
||||
require_writable_task_dir: true
|
||||
require_writable_result_dir: true
|
||||
reject_shared_source_worktree: true
|
||||
reject_existing_git_lock: true
|
||||
|
||||
notifications:
|
||||
owner_on_received: required
|
||||
owner_on_terminal: required
|
||||
preferred_channel: weixin
|
||||
redact_secrets: true
|
||||
|
||||
rollback:
|
||||
preserve_source_worktree: true
|
||||
remove_created_worktrees: true
|
||||
restore_service_dropins: true
|
||||
restore_service_state: true
|
||||
@@ -0,0 +1,14 @@
|
||||
# Install as a systemd drop-in for GitOps/OpenClaw writer services.
|
||||
# Example: /etc/systemd/system/<service>.service.d/10-runtime-identity.conf
|
||||
|
||||
[Service]
|
||||
User=aiman
|
||||
Group=gitops
|
||||
SupplementaryGroups=gitops
|
||||
UMask=0002
|
||||
Environment=AIOS_GITOPS_ROOT=/data/aios-gitops
|
||||
Environment=AIOS_BARE_REPO=/data/aios-gitops/repo.git
|
||||
Environment=AIOS_WORKTREE_ROOT=/data/aios-gitops/worktrees
|
||||
Environment=AIOS_RUNTIME_USER=aiman
|
||||
Environment=AIOS_RUNTIME_GROUP=gitops
|
||||
Environment=AIOS_RUNTIME_UMASK=0002
|
||||
@@ -0,0 +1,57 @@
|
||||
# GitOps secret ACL and identity hardening
|
||||
|
||||
task_id=TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8
|
||||
phase=precheck_complete
|
||||
acl_owner_group=root:gitops
|
||||
acl_mode=750
|
||||
acl_named_aiman=rwx_effective_rx
|
||||
acl_default_entries=absent
|
||||
acl_negative_probe_scope=two_nonsecret_write_vectors
|
||||
acl_negative_probe_result=expected_rejection_checksum_preserved
|
||||
negative_probe_artifact=restricted_outside_repository
|
||||
openclaw_gateway_identity=unchanged_root
|
||||
openclaw_writer_unit=openclaw-git-writer@.service
|
||||
openclaw_writer_identity=aiman:gitops
|
||||
openclaw_writer_worktree=/data/aios-gitops/worktrees/openclaw
|
||||
openclaw_writer_secret_environment=absent
|
||||
acceptance=scheduled
|
||||
prior_attempt_identity_guard=primary_group_mismatch
|
||||
prior_attempt_git_operation_started=0
|
||||
final_acceptance_task=TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL
|
||||
final_acceptance_concurrent_launch=scheduled
|
||||
final_acceptance_codex_branch=acceptance/codex/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL
|
||||
final_acceptance_codex_oid=7c4236305848dbb47c7134cafb3376fb713f8e03
|
||||
final_acceptance_openclaw_branch=acceptance/openclaw/TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL
|
||||
final_acceptance_openclaw_oid=6cd0f5aa0ad28f767ffd5396c29d1a9182f88c43
|
||||
final_bare_oid_match_codex=1
|
||||
final_bare_oid_match_openclaw=1
|
||||
openclaw_writer_systemd_result=success
|
||||
openclaw_writer_proc_identity=Uid: 1000 1000 1000 1000;Gid: 1001 1001 1001 1001
|
||||
openclaw_writer_proc_command=aiman gitops bash /usr/local/libexec/openclaw-git-writer TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8-FINAL
|
||||
model_api_active=1
|
||||
model_api_health_http_code=200
|
||||
worktree_mode_codex=2775 aiman:gitops
|
||||
worktree_mode_openclaw=2775 aiman:gitops
|
||||
clean_log_sha256=cc7def630ab998fc2363c8cebe8c6e9d44769a6a418b4a0a18d2873806164c85
|
||||
|
||||
## Documentation gate supplement
|
||||
|
||||
negative_acl_probe_artifact_path=/data/aios-gitops/runtime/secrets/acl-negative-probe-TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8.log
|
||||
negative_acl_probe_artifact_sha256=a4ca7dd2a9801d2254c019b99354882617290d2739281c204db1c1ef4a8c8039
|
||||
negative_acl_probe_artifact_mode=600 root:root
|
||||
negative_acl_probe_artifact_content=not_embedded
|
||||
clean_final_log_path=/data/aios-gitops/runtime/verification/clean-TASK-CODEX-GITOPS-SECRET-ACL-AND-IDENTITY-HARDENING-20260715T004818UTC8.log
|
||||
clean_final_log_sha256=cc7def630ab998fc2363c8cebe8c6e9d44769a6a418b4a0a18d2873806164c85
|
||||
clean_final_log_scope=final_clean_corpus_only
|
||||
permission_denied_count: 0
|
||||
checkout_collision_count: 0
|
||||
git_lock_collision_count: 0
|
||||
codex_acceptance_commit_oid=7c4236305848dbb47c7134cafb3376fb713f8e03
|
||||
codex_acceptance_commit_local_verified=1
|
||||
codex_acceptance_commit_bare_verified=1
|
||||
report_commit_tip_before_documentation_correction=c0d82846151d577b3d80a95d20a6917228457272
|
||||
report_commit_tip_before_documentation_correction_local_bare_verified=1
|
||||
openclaw_gateway_identity=root:gitops
|
||||
openclaw_gateway_root_credentials_boundary=unchanged
|
||||
model_api_health_endpoint=http://127.0.0.1:resolved-local-port/health
|
||||
model_api_health_http_status=200
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"id": "patch-1784043007770-e97206c3",
|
||||
"patch_name": "gitops-isolated-worktree-governance-20260714T232948UTC8",
|
||||
"branch": "patch/gitops-isolated-worktree-governance-20260714T232948UTC8",
|
||||
"risk": "high",
|
||||
"description": "Root-cause remediation for recurring ACL and shared-worktree failures: unified aiman:gitops runtime identity, UMask=0002, inherited default ACLs, per-task isolated Git worktrees, startup preflight, migration/rollback scripts, systemd drop-in templates, audit evidence, and an OpenClaw execution task. No production business deployment in this patch.",
|
||||
"created_by": "chatgpt-gitops-bot",
|
||||
"created_at": "2026-07-14T15:30:07Z"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "branch",
|
||||
"branch": "task/codex/gitops-isolated-worktree-migration-20260715T001652UTC8",
|
||||
"base": "patch/gitops-isolated-worktree-governance-20260714T232948UTC8",
|
||||
"created_by": "chatgpt-gitops-bot",
|
||||
"created_at": "2026-07-14T16:17:28Z",
|
||||
"direct_main_push": false,
|
||||
"owner_approval_required": true
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Codex isolated-worktree acceptance
|
||||
|
||||
branch: `agent/codex/gitops-isolated-worktree-migration-20260715T001652UTC8`
|
||||
|
||||
base: `b05eb1babe9c657e8776c09eec71302166902cc3`
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="${AIOS_GITOPS_ROOT:-/data/aios-gitops}"
|
||||
SOURCE="${AIOS_SOURCE_WORKTREE:-$ROOT/workspace}"
|
||||
BARE="${AIOS_BARE_REPO:-$ROOT/repo.git}"
|
||||
WORKTREES="${AIOS_WORKTREE_ROOT:-$ROOT/worktrees}"
|
||||
RUNTIME_USER="${AIOS_RUNTIME_USER:-aiman}"
|
||||
RUNTIME_GROUP="${AIOS_RUNTIME_GROUP:-gitops}"
|
||||
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "ERROR: bootstrap must run as root" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
getent passwd "$RUNTIME_USER" >/dev/null
|
||||
getent group "$RUNTIME_GROUP" >/dev/null
|
||||
[[ -d "$SOURCE/.git" || -f "$SOURCE/.git" ]] || {
|
||||
echo "ERROR: source worktree not found: $SOURCE" >&2
|
||||
exit 3
|
||||
}
|
||||
|
||||
install -d -o "$RUNTIME_USER" -g "$RUNTIME_GROUP" -m 2775 "$ROOT" "$WORKTREES"
|
||||
install -d -o "$RUNTIME_USER" -g "$RUNTIME_GROUP" -m 2775 \
|
||||
"$WORKTREES/openclaw" "$WORKTREES/chatgpt" "$WORKTREES/controller"
|
||||
|
||||
if [[ ! -d "$BARE" ]]; then
|
||||
sudo -u "$RUNTIME_USER" -g "$RUNTIME_GROUP" git clone --bare "$SOURCE" "$BARE"
|
||||
fi
|
||||
|
||||
chown -R "$RUNTIME_USER:$RUNTIME_GROUP" "$BARE" "$WORKTREES"
|
||||
find "$BARE" "$WORKTREES" -type d -exec chmod 2775 {} +
|
||||
find "$BARE" "$WORKTREES" -type f -exec chmod ug+rw {} +
|
||||
|
||||
setfacl -R -m "u:${RUNTIME_USER}:rwX,g:${RUNTIME_GROUP}:rwX,m::rwX" "$BARE" "$WORKTREES"
|
||||
find "$BARE" "$WORKTREES" -type d -exec \
|
||||
setfacl -m "d:u:${RUNTIME_USER}:rwx,d:g:${RUNTIME_GROUP}:rwx,d:m::rwx" {} +
|
||||
|
||||
cat <<EOF
|
||||
bootstrap_status: PASS
|
||||
root: $ROOT
|
||||
source_worktree: $SOURCE
|
||||
bare_repo: $BARE
|
||||
worktree_root: $WORKTREES
|
||||
runtime_user: $RUNTIME_USER
|
||||
runtime_group: $RUNTIME_GROUP
|
||||
EOF
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
EXPECTED_USER="${AIOS_RUNTIME_USER:-aiman}"
|
||||
EXPECTED_GROUP="${AIOS_RUNTIME_GROUP:-gitops}"
|
||||
EXPECTED_UMASK="${AIOS_RUNTIME_UMASK:-0002}"
|
||||
TASK_DIR="${AIOS_TASK_DIR:-.agent/tasks}"
|
||||
RESULT_DIR="${AIOS_RESULT_DIR:-.agent/results}"
|
||||
|
||||
fail() {
|
||||
echo "preflight_status: FAIL"
|
||||
echo "reason: $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
USER_NOW="$(id -un)"
|
||||
GROUPS_NOW="$(id -Gn)"
|
||||
UMASK_NOW="$(umask)"
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
GIT_DIR="$(git rev-parse --git-dir 2>/dev/null || true)"
|
||||
BRANCH="$(git branch --show-current 2>/dev/null || true)"
|
||||
|
||||
[[ "$USER_NOW" == "$EXPECTED_USER" ]] || fail "runtime user is $USER_NOW, expected $EXPECTED_USER"
|
||||
[[ " $GROUPS_NOW " == *" $EXPECTED_GROUP "* ]] || fail "runtime user is not in $EXPECTED_GROUP"
|
||||
[[ "$UMASK_NOW" == "$EXPECTED_UMASK" || "$UMASK_NOW" == "002" ]] || fail "umask is $UMASK_NOW, expected $EXPECTED_UMASK"
|
||||
[[ -n "$ROOT" ]] || fail "not inside a git worktree"
|
||||
[[ "$ROOT" == /data/aios-gitops/worktrees/* ]] || fail "shared source worktree is prohibited: $ROOT"
|
||||
[[ -w "$ROOT" ]] || fail "worktree root is not writable"
|
||||
[[ -w "$GIT_DIR" ]] || fail "git dir is not writable: $GIT_DIR"
|
||||
[[ ! -e "$GIT_DIR/index.lock" ]] || fail "git index lock exists"
|
||||
install -d -m 2775 "$TASK_DIR" "$RESULT_DIR"
|
||||
[[ -w "$TASK_DIR" ]] || fail "task directory is not writable: $TASK_DIR"
|
||||
[[ -w "$RESULT_DIR" ]] || fail "result directory is not writable: $RESULT_DIR"
|
||||
[[ -z "$(git status --porcelain)" ]] || fail "worktree is not clean at task start"
|
||||
|
||||
cat <<EOF
|
||||
preflight_status: PASS
|
||||
runtime_user: $USER_NOW
|
||||
runtime_groups: $GROUPS_NOW
|
||||
umask: $UMASK_NOW
|
||||
worktree: $ROOT
|
||||
git_dir: $GIT_DIR
|
||||
branch: $BRANCH
|
||||
workspace_clean: true
|
||||
task_path_writable: true
|
||||
result_path_writable: true
|
||||
git_lock_present: false
|
||||
EOF
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ACTION="${1:-}"
|
||||
AGENT="${2:-}"
|
||||
TASK_ID="${3:-}"
|
||||
BASE_REF="${4:-origin/main}"
|
||||
|
||||
ROOT="${AIOS_GITOPS_ROOT:-/data/aios-gitops}"
|
||||
BARE="${AIOS_BARE_REPO:-$ROOT/repo.git}"
|
||||
WORKTREES="${AIOS_WORKTREE_ROOT:-$ROOT/worktrees}"
|
||||
RUNTIME_USER="${AIOS_RUNTIME_USER:-aiman}"
|
||||
RUNTIME_GROUP="${AIOS_RUNTIME_GROUP:-gitops}"
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 create|remove <openclaw|chatgpt|controller> <task-id> [base-ref]" >&2
|
||||
exit 64
|
||||
}
|
||||
|
||||
[[ "$ACTION" =~ ^(create|remove)$ ]] || usage
|
||||
[[ "$AGENT" =~ ^(openclaw|chatgpt|controller)$ ]] || usage
|
||||
[[ "$TASK_ID" =~ ^[A-Za-z0-9._-]+$ ]] || {
|
||||
echo "ERROR: invalid task id" >&2
|
||||
exit 65
|
||||
}
|
||||
[[ "$(id -un)" == "$RUNTIME_USER" ]] || {
|
||||
echo "ERROR: must run as $RUNTIME_USER" >&2
|
||||
exit 66
|
||||
}
|
||||
[[ "$(id -gn)" == "$RUNTIME_GROUP" || " $(id -Gn) " == *" $RUNTIME_GROUP "* ]] || {
|
||||
echo "ERROR: user must be in $RUNTIME_GROUP" >&2
|
||||
exit 67
|
||||
}
|
||||
[[ "$(umask)" == "0002" || "$(umask)" == "002" ]] || {
|
||||
echo "ERROR: umask must be 0002" >&2
|
||||
exit 68
|
||||
}
|
||||
[[ -d "$BARE" ]] || {
|
||||
echo "ERROR: bare repository missing: $BARE" >&2
|
||||
exit 69
|
||||
}
|
||||
|
||||
PATHNAME="$WORKTREES/$AGENT/$TASK_ID"
|
||||
BRANCH="task/$AGENT/$TASK_ID"
|
||||
|
||||
case "$ACTION" in
|
||||
create)
|
||||
[[ ! -e "$PATHNAME" ]] || {
|
||||
echo "ERROR: worktree already exists: $PATHNAME" >&2
|
||||
exit 70
|
||||
}
|
||||
git --git-dir="$BARE" fetch --all --prune
|
||||
git --git-dir="$BARE" worktree add -b "$BRANCH" "$PATHNAME" "$BASE_REF"
|
||||
install -d -m 2775 "$PATHNAME/.agent/tasks/$AGENT" "$PATHNAME/.agent/results/$AGENT"
|
||||
setfacl -m "u:${RUNTIME_USER}:rwx,g:${RUNTIME_GROUP}:rwx,m::rwx" "$PATHNAME"
|
||||
find "$PATHNAME" -type d -exec chmod g+s {} +
|
||||
find "$PATHNAME" -type d -exec \
|
||||
setfacl -m "d:u:${RUNTIME_USER}:rwx,d:g:${RUNTIME_GROUP}:rwx,d:m::rwx" {} +
|
||||
printf 'worktree: %s\nbranch: %s\nbase: %s\n' "$PATHNAME" "$BRANCH" "$BASE_REF"
|
||||
;;
|
||||
remove)
|
||||
[[ -d "$PATHNAME" ]] || {
|
||||
echo "ERROR: worktree not found: $PATHNAME" >&2
|
||||
exit 71
|
||||
}
|
||||
[[ -z "$(git -C "$PATHNAME" status --porcelain)" ]] || {
|
||||
echo "ERROR: worktree is not clean; retain for investigation" >&2
|
||||
exit 72
|
||||
}
|
||||
git --git-dir="$BARE" worktree remove "$PATHNAME"
|
||||
printf 'removed: %s\n' "$PATHNAME"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user