Skip to content

fix: agent state follows the service identity (#598) - #600

Merged
chubes4 merged 1 commit into
mainfrom
fix/598-service-identity-ownership
Sep 16, 2026
Merged

chubes4 merged 1 commit into
mainfrom
fix/598-service-identity-ownership

Conversation

@chubes4

@chubes4 chubes4 commented Sep 16, 2026

Copy link
Copy Markdown
Member

Problem

setup.sh defaults to running as root while the agent runs as a dedicated service user. Everything setup wrote for the agent to maintain — /opt/kimaki-config, <site>/.opencode, <site>/.claude, <site>/.wp-coding-agents — stayed root-owned. Every later non-root ./upgrade.sh then degraded into per-file cp:/chmod: permission errors and silently kept stale Kimaki plugin sources, skills, subagent projection and hooks, while printing a sudo command as the fix. #597 patched the first symptom (unreadable profile); this fixes the cause.

Change

New lib/agent-state-ownership.sh

  • agent_state_ownership_roots — the agent-maintained set only (persistent Kimaki config dir, .wp-coding-agents, .opencode, .claude, .codex under the site). Symlinked roots skipped. Privileged host state (units, sudoers, journald) deliberately excluded — that stays behind the systems-capabilities handoff.
  • agent_state_ownership_reconcile (root) — chown -R each root not fully owned by SERVICE_USER; roots under the site keep the site group so www-data retains access. Idempotent; no-op when already owned, non-root, local mode, or root service.
  • agent_state_ownership_audit (non-root) — read-only; lists each unmaintainable root once with its owner, emits one {"status":"root_repair_required","component":"agent_state_ownership","paths":[…],"repair_command":…} record. Maintainability = every entry owned by the current uid (group-writable is not enough: only the owner can chmod +x the hook).
  • agent_state_ownership_can_maintain <path> — lets phases skip cleanly.

Wiring

  • setup.sh: reconcile after all phases → a root-run setup for a non-root service leaves zero root-owned agent state.
  • --migrate-non-root: reconcile after the site reclaim.
  • upgrade.sh: reconcile as root / audit as non-root right after identity resolution; new --reconcile-agent-state-ownership (root-only one-shot, exits after); summary shows the repair command.
  • Kimaki config sync (bridge_sync_config), OpenCode subagent projection, and the Claude Code hook install check maintainability and skip into PENDING_ITEMS with one warning instead of failing per file.
  • Help: --root is the workspace-mode default; owned mode is non-root (matches No model for agent server capability: every install runs an unrestricted root shell, including managed #327 behaviour that the help text contradicted).

Verified

  • tests/agent-state-ownership.sh (new, in CI matrix): root set, symlink skip, clean-tree audit, consolidated single record + owner + repair command, descendant skip, non-root reconcile no-op, wiring assertions; a root-only branch covers the chown/group/idempotency path.
  • installation-profile, opencode-subagents-optional, service-migration, service-identity-defaults, ci-coverage still pass.
  • On the affected VPS (4 root-owned roots, upgrade run as opencode): exit 0, zero cp:/chmod: errors, one [agent-state] block naming the four roots and the one-shot command, three phases skipped into Pending.

Fixes #598. Follows #597.

Authored by Extra Chill Bot (AI agent); not yet human-reviewed.

A root-run setup for a non-root service left every agent-maintained root
(persistent Kimaki config, site runtime config, installation profile)
root-owned, so each later non-root upgrade sprayed cp/chmod permission
errors and silently kept stale plugin sources, skills, and hooks.

- lib/agent-state-ownership.sh: enumerate the agent-maintained roots;
  reconcile() hands them to SERVICE_USER as root (site group preserved);
  audit() reports unmaintainable roots once as non-root with a single
  root_repair_required record and the exact one-shot command.
- setup.sh and --migrate-non-root reconcile at the end, so a root-run
  install leaves no root-owned agent state behind.
- upgrade.sh reconciles as root, audits as non-root, adds
  --reconcile-agent-state-ownership (root-only one-shot), and reports
  in the summary.
- Kimaki config sync, OpenCode subagent projection, and the Claude Code
  hook install skip cleanly into Pending when their root is unmaintainable
  instead of failing file by file.
- Help text: --root is the workspace-mode default; owned mode is non-root.

Fixes #598
@chubes4
chubes4 merged commit 9b521c0 into main Sep 16, 2026
70 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Managed VPS: root-owned install state (/opt/kimaki-config, .opencode, .claude/hooks) makes non-root upgrade permanently partial

1 participant