# Openclaw dependency matrix Every openclaw-provided capability that the current gitea-webhooks pipeline uses, and what Caret's replacement does with it. Source citations: R01 = RESEARCH-01, R02 = RESEARCH-02, R03 = RESEARCH-03, PLAN = PLAN.md. Categories: - **REMOVE** — Caret's replacement doesn't need this at all - **REPLACE** — Caret builds a standalone equivalent - **KEEP** — Caret continues depending on openclaw for this (and why that's safe) - **BLOCKING** — Caret cannot move forward without resolving this first (Rooh action required) ## By capability ### 1. HTTP webhook ingress (port 18789, `/hooks/agent`) **Category:** REPLACE **What Caret does:** Stand up a bun HTTP listener at a Caret-owned port (e.g. 18790 or unix socket), with its own path. Mirrors openclaw's `/hooks/agent` shape so existing scripts can be reused. [R01 §Ingress path, R02 §HTTP endpoints] **Risk if kept:** Hard-couples Caret's lifecycle to the openclaw gateway container. Defeats the entire migration. **Effort:** 1 day. Stateless request/response, well-understood. [R02 difficulty matrix: Easy] ### 2. HMAC verification (currently NOT done by openclaw) **Category:** REPLACE (net-new — openclaw never had it) **What Caret does:** Real HMAC-SHA256 of the raw body using `X-Gitea-Signature` header, timing-safe compare, raw body preserved before JSON parse. [R01 §HMAC recipe, R03 confirms all 4 known webhooks have `Secret: NOT SET`] **Risk if kept:** No HMAC at all today; protection is bearer + nginx ACL only. Caret should do better. **Effort:** 0.5 day. ~30 lines. **Depends on BLOCKING #1 (webhook secret provisioning).** ### 3. Bearer token authentication (`OPENCLAW_HOOKS_TOKEN`) **Category:** REPLACE **What Caret does:** Own bearer token (`CARET_HOOKS_TOKEN`) stored in Caret's credentials dir, validated on each request as a second factor alongside HMAC. [R01 §Ingress path layer 3] **Risk if kept:** Sharing the openclaw token cross-couples secret rotation. **Effort:** Trivial. ### 4. nginx TLS termination and path rewriting **Category:** KEEP (with new location block) **What Caret does:** Adds a new nginx `location /hooks/caret` block that proxies to the Caret listener, same TLS cert. Reuses existing Let's Encrypt setup. [R01 §Ingress path] **Risk if kept:** None — nginx is host-level infra, not openclaw-specific. **Effort:** 1 hour, but requires host root access. **See BLOCKING #2.** ### 5. Event dedup cache (`X-Gitea-Delivery` 24h) **Category:** REPLACE **What Caret does:** Own JSON-on-disk dedup cache at `/host/root/.caret/state/dedup-cache.json`, identical 24h TTL semantics. [R01 §Ingress path layer 4, R01 gotcha 4] **Risk if kept:** Sharing openclaw's cache file is fragile and couples crash recovery. **Effort:** 0.5 day. ### 6. Rate limiting (5 concurrent per agent) **Category:** REPLACE **What Caret does:** Per-route concurrency limiter in the listener. Caret's routing is simpler (one "agent") so this is mostly a global semaphore. [R01 gotcha 7] **Risk if kept:** N/A. **Effort:** Trivial. ### 7. Session lock manager (`hooks/locks/`) **Category:** REPLACE **What Caret does:** Own lock dir at `/host/root/.caret/state/locks/`, same 2h TTL, same file naming `{owner}-{repo}-{issue}`, same IS_DONE 5min grace. [R01 §Session lock] **Risk if kept:** Two writers to one lock dir = race. **Effort:** 0.5 day. ### 8. Queue daemon (`queue-inbox/` + `queue-daemon.js`) **Category:** REPLACE **What Caret does:** In-process queue inside the listener (or a sidecar bun script reading from `/host/root/.caret/state/queue/`). Re-verifies spawn signatures off the hot path. [R01 gotcha 12, R03 §queue-daemon.js] **Risk if kept:** Couples Caret's spawn pipeline to openclaw container restart. **Effort:** 1 day. ### 9. `sessions_spawn` agent orchestration primitive **Category:** REPLACE (with Claude Code Channels plugin) **What Caret does:** Build a Channels plugin at `/host/root/.caret/channels/gitea-judgment/` that receives an HTTP POST and starts a Claude Code session with the payload as initial prompt. [R02 §Caret's conclusion, PLAN §Phase 3 J3.1] **Risk if kept:** Openclaw's `sessions_spawn` is the deepest coupling — see R02 difficulty matrix "Hard" rating. Keeping it means Caret can never fully cut over. **Effort:** 2-3 days for the minimum viable plugin. Full parity (wakeMode, thinking, model selection) is more like 1 week. ### 10. Tool allowlist enforcement per agent **Category:** KEEP (judgment path runs inside Claude Code, which has its own tool config) **What Caret does:** Caret's deterministic path runs scripts directly with no tools concept. The judgment path is a Claude Code session whose tool allowlist is configured in Claude Code's settings.json (not openclaw's). [R02 §Tool policy resolution] **Risk if kept:** None — Caret never touches openclaw's tool policy resolver. **Effort:** 0 (already separated). ### 11. Plugin SDK (`src/plugin-sdk/`) **Category:** REMOVE **What Caret does:** Doesn't load openclaw plugins. Caret's listener is a plain bun script with no plugin loader. Channels plugin uses Claude Code's plugin mechanism, not openclaw's. [R02 §Don't reinvent #2] **Risk if kept:** N/A. **Effort:** 0. ### 12. Delivery system (Telegram, Mattermost, Discord) **Category:** REPLACE (use tg-stream + Mattermost direct calls) **What Caret does:** Calls tg-stream HTTP API for Telegram alerts and Mattermost webhook URL directly for incident posts. No multi-channel abstraction layer. [R02 §Don't reinvent #4 warns it's tightly coupled — we don't replicate, we use simpler primitives] **Risk if kept:** Hard coupling to openclaw outbound system. **Effort:** 0.5 day (just curl calls). ### 13. Session storage (JSONL transcripts at `~/.openclaw/sessions/`) **Category:** KEEP (judgment path only — Claude Code's own session store) **What Caret does:** Deterministic path has no sessions. Judgment path uses Claude Code's native session JSONL under Claude Code's config dir, NOT openclaw's. [R02 §Don't reinvent #1] **Risk if kept:** None — Caret never reads/writes openclaw's session files. **Effort:** 0. ### 14. Heartbeat scheduler (`heartbeat-runner.ts`) **Category:** REPLACE (use Claude Code `schedule` skill / cron) **What Caret does:** Use systemd timer in the Caret container OR Claude Code's schedule mechanism for the 6h policy sweep and 15min webhook audit. No 28-item checklist — Caret's heartbeat is much smaller. [R02 §Heartbeat loop, R03 §Heartbeat checklists] **Risk if kept:** Couples to openclaw gateway uptime, which R03 shows is currently degraded. **Effort:** 0.5 day for the timer setup. ### 15. Cron service (`CronService` in gateway) **Category:** REPLACE (same as #14 — single mechanism) **What Caret does:** Same as heartbeat — systemd timers or Claude Code schedule. [R02 §Cron service, R03 §Active cron jobs shows 8 of 12 currently failing] **Risk if kept:** R03 shows openclaw cron is currently degraded; depending on it inherits the breakage. **Effort:** Combined with #14. ### 16. Gitea API integration (token + curl wrappers) **Category:** KEEP **What Caret does:** Continue using the same `GITEA_TOKEN` and stateless curl calls. Wrapper scripts ported into `/host/root/.caret/tools/`. [R01 §Tools fan-out, PLAN B2.3] **Risk if kept:** None — Gitea API is a third-party service, openclaw is not in the path. **Effort:** Path-strip and copy, 1 hour. **Depends on BLOCKING #3 (admin scope).** ### 17. Workspace directory (`/root/.openclaw/workspace/`) **Category:** KEEP (155 projects stay owned by openclaw/Xen) **What Caret does:** Caret's migration scope is the Gitea-facing slice ONLY. The 155-project workspace, the PROJ-XXX-* hierarchy, and Xen's project ownership stay as-is. Caret has its own `/host/root/.caret/workspace/` for migration artifacts only. [R03 §What the migration actually has to replace, PLAN §Goal] **Risk if kept:** None for the in-scope migration. **Effort:** 0. ### 18. Project registry (`projects/registry.json`) **Category:** KEEP **What Caret does:** Read-only access if needed (e.g., to look up which manager owns a project for a given issue). No writes. [R03 §Shared state] **Risk if kept:** Stale reads possible but acceptable. **Effort:** 0. ### 19. Memory system (`memory/`) **Category:** REMOVE (from migration scope) **What Caret does:** Caret's own memory is in `/root/.claude/projects/-app/memory/`. Doesn't touch openclaw's memory dir. [R03 §Shared state] **Risk if kept:** N/A. **Effort:** 0. ### 20. Credentials store (`credentials/`) **Category:** KEEP (read-only for shared tokens like GITEA_TOKEN) **What Caret does:** Read GITEA_TOKEN from openclaw credentials OR copy to Caret credentials store. Caret-owned secrets (HMAC webhook secret, CARET_HOOKS_TOKEN) live in `/host/root/.caret/credentials/`. [R03 §Shared state credentials] **Risk if kept:** Cross-coupling on token rotation. Acceptable short-term. **Effort:** Trivial. **See BLOCKING #4 (secret rotation story).** ### 21. `post-repo-audit.sh` **Category:** REPLACE (port the script verbatim) **What Caret does:** Copy to `/host/root/.caret/tools/post-repo-audit.sh`, strip openclaw path prefixes, ship as-is. Pure script, zero tokens. [R01 §Tools fan-out, PLAN B2.3] **Risk if kept:** Couples to openclaw tools dir lifecycle. **Effort:** 1 hour. ### 22. `audit-repo-policies.sh` **Category:** REPLACE (port verbatim) **What Caret does:** Copy to Caret tools dir, point at sol/repo-policies template repo, run with `--fix` from Caret's heartbeat. [R01 §Tools fan-out] **Risk if kept:** Same as #21. **Effort:** 1 hour. ### 23. `spawn-manager.sh` **Category:** REPLACE (port verbatim, but only after #9 is solved) **What Caret does:** Copy script. Generates Manager spawn JSON from issue body, creates project workspace. Caret's listener calls it via execSync with same 30s timeout pattern. [R01 §Tools fan-out, R01 gotcha 9] **Risk if kept:** Tightly coupled to openclaw workspace path conventions — depends on whether Caret keeps openclaw's PROJ-XXX scheme (R01 advises yes). **Effort:** 0.5 day. ### 24. `create-implement-issue.sh` **Category:** REPLACE (port verbatim) **What Caret does:** Copy script. Creates signed `[IMPLEMENT]` issue with HMAC spawn signature using `/host/root/.caret/credentials/spawn-secret`. [R01 §Tools fan-out] **Risk if kept:** Spawn secret coupling. **Effort:** 1 hour. **Depends on BLOCKING #5 (spawn secret ownership).** ### 25. `secret-scan.sh` **Category:** REPLACE (port verbatim) **What Caret does:** Copy script, used by both CI (`make check`) inside repos and Caret's push handler. [R01 §Tools fan-out] **Risk if kept:** N/A. **Effort:** 1 hour. ### 26. `check-implement-orphans.sh` **Category:** REPLACE (port verbatim) **What Caret does:** Copy script, run from Caret's 15min heartbeat to detect stale pending spawns and orphaned managers. [R01 §Tools fan-out] **Risk if kept:** N/A. **Effort:** 1 hour. ### 27. `auditLog → logs/audit.jsonl` **Category:** REPLACE **What Caret does:** Caret's listener writes to `/host/root/.caret/log/audit.jsonl`, same JSONL line schema. Rotation by line count. [R01 §Logging, PLAN B2.5] **Risk if kept:** Two writers to one file = corruption. **Effort:** 0.5 day. ### 28. Incident flagging (`logs/incidents.jsonl`) **Category:** REPLACE **What Caret does:** Caret writes to `/host/root/.caret/log/incidents.jsonl`. Critical incidents also fan out to Telegram via tg-stream. [R01 §Logging, T2.23] **Risk if kept:** Same as #27. **Effort:** 0.5 day (combined with #27). ## Summary by category | Category | Count | |---|---| | REMOVE | 2 (#11, #19) | | REPLACE | 19 (#1, #2, #3, #5, #6, #7, #8, #9, #12, #14, #15, #21, #22, #23, #24, #25, #26, #27, #28) | | KEEP | 6 (#4, #10, #13, #16, #17, #18, #20) — note #20 partially | | BLOCKING | 5 (see below) | ## BLOCKING items — Rooh action required These five items must be resolved by Rooh before Caret can write the production code. They are not engineering decisions; they are policy / access decisions only Rooh can make. ### BLOCKING #1 — Webhook secret provisioning and storage location **Question:** Where is the new HMAC webhook secret stored? `/host/root/.caret/credentials/webhook-secret`? A vault entry? Reused from an existing openclaw secret? **Why it blocks:** Cannot ship HMAC verification (T1.06–T1.11) without the secret being authoritative somewhere. Cannot register webhooks on sol/* repos without knowing what secret to set on the Gitea side. **Reference:** R01 §HMAC recipe, R03 confirms all current webhooks have `Secret: NOT SET`. ### BLOCKING #2 — nginx config write access for `/hooks/caret` location **Question:** Does Caret have permission to edit `/host/etc/nginx/...` and reload nginx, or does Rooh do that one-time setup? **Why it blocks:** Without an nginx route, Gitea cannot reach the Caret listener via HTTPS. Direct port exposure is the only alternative and that needs firewall changes. **Reference:** R01 §Ingress path nginx termination, dependency #4. ### BLOCKING #3 — Gitea token admin scope OR manual system-webhook registration **Question:** Will Rooh elevate the sol token to include `read:admin` + `write:admin`, or will Rooh manually register the system-level webhook one time? **Why it blocks:** Without admin scope Caret cannot list system-level webhooks (R03 confirmed) and cannot create them. The sol/* per-repo webhooks can be registered with the existing token, but for new repo bootstrap to register Caret's webhook automatically (T1.02), the admin scope or manual setup is required. **Reference:** R03 §Registered Gitea webhooks, PLAN §Dependencies token scope. ### BLOCKING #4 — Secret rotation story across openclaw + Caret **Question:** When `GITEA_TOKEN` or the webhook secret rotates, what's the rollout? Does Caret read from openclaw's credentials dir live, or get its own copy that needs separate rotation? Who is responsible for rotation drills? **Why it blocks:** PLAN §Risks #1 explicitly calls this out as a first-class deliverable. Without a documented rotation procedure, T3.13 cannot pass and the migration leaves a security debt. **Reference:** PLAN §Risks #1, dependency #20. ### BLOCKING #5 — Spawn signature secret ownership (`/root/.openclaw/hooks/spawn-secret`) **Question:** Does Caret get its own spawn secret (and `create-implement-issue.sh` is updated to use it), or does Caret read openclaw's existing secret? If the latter, what happens when openclaw rotates it? **Why it blocks:** T1.15–T1.17 (spawn signature verification) require Caret and `create-implement-issue.sh` to share a secret. Cross-system sharing is the cleaner short-term answer but locks Caret to openclaw's lifecycle until cut-over completes. **Reference:** R01 §Ingress path "spawn signatures", dependency #24.