Skip to content

P0-5: Governed ACP connector for embedded coding engines (kernel verdicts, allowlisted fs, signed+receipted provisioning)#22

Open
peycheff-com wants to merge 2 commits into
mainfrom
acp-connector
Open

P0-5: Governed ACP connector for embedded coding engines (kernel verdicts, allowlisted fs, signed+receipted provisioning)#22
peycheff-com wants to merge 2 commits into
mainfrom
acp-connector

Conversation

@peycheff-com

Copy link
Copy Markdown
Contributor

P0-5 — Governed ACP connector for embedded coding engines

New package packages/js/acp-connector (@mindburn/helm-acp-connector), zero runtime dependencies, TypeScript/ESM + node --test matching the helm-tool-wrapper stack. Drives Claude Code / Codex over ACP (Zed-originated JSON-RPC) behind the HELM boundary. Design mechanisms adapted from Rowboat (Apache-2.0) with per-module attribution comments; all code original; master-report §7 do-not-copy list respected.

What ships

  • (a) ACP client connector (client.ts, jsonrpc.ts) — session lifecycle over a self-contained ndJSON JSON-RPC peer; 60 s startup deadline on handshake phases only (HELM_ACP_STARTUP_TIMEOUT_MS override); session/cancel as protocol notification; stderr-tail (4 KB ring) + exit-code error enrichment; setHandlers for warm-connection reuse. terminal capability is never advertised — shell execution must flow through kernel-gated tool calls.
  • (b) Kernel-gated permission broker (permission.ts, kernel-evaluator.ts) — every session/request_permission routed through a Kernel verdict (POST /api/v1/evaluate shape replicated from helm-tool-wrapper, contract note in header; no build-time dep on unmerged PRs). Fail-closed: DENY / ESCALATE / transport error / timeout all reject. Deliberately no yolo. Rowboat's tiered model adapted as a LOW-RISK TIER beneath the approval ceremony: auto-approve-reads only classifies read/search/fetch/think as low-risk — the kernel still issues the verdict. Sticky per-session allows are recorded as receipts (decisionId + receiptId). Option-family fallback mapping; reject with no reject option offered answers cancelled, never an allow.
  • (c) Allowlisted fs handlers (fs-guard.ts) — the counter-position to Rowboat's Add TinyFish governed web integration samples #1 hole (acp/client.ts:341-348 raw fs on any path). Declarative {path, read, write} roots; fail-closed construction; one canonical isPathInside ("a divergent copy is a permission-bypass risk"); realpath walk-up canonicalization defeats .. and symlink escapes at final component and ancestors, for reads and writes.
  • (d) Managed engine provisioning (provisioning.ts) — lockfile-pinned versions, sha512 (npm SRI) verification, temp-dir extract, atomic rename, .meta ledger, prune-old. HELM outperform: Ed25519-signed manifest enforcement (fail-closed before any download; canonical-bytes signing surface) + ProvisioningReceipt carrying the installed binary's sha512 + manifest digest, persisted per install; cache hits re-verify the ledger hash and reprovision on tamper. Contract toward helm-desktop / platform-actions documented in module header + README.
  • (e) Tests — 35 tests, all green, against fixtures/fake-acp-agent.mjs speaking the real wire protocol: lifecycle, startup deadline, cancel→grace→force-kill (graceful and ignoring cancel), warm reuse vs grace-0 respawn (pid-embedded session ids), kernel verdict round-trips incl. end-to-end DENY, allowlist enforcement incl. symlink escape attempts, provisioning hash verification, signed-manifest enforcement, tamper-triggered reprovisioning.

Validation

  • npm test in packages/js/acp-connector: 35/35 pass
  • Full repo make validate (both JS packages, Python, samples): green
  • Makefile test-js/clean now cover the new package

Deliberately NOT adopted (Rowboat anti-patterns)

Open fs handlers (full user FS reach) · yolo permission policy · unsigned manifests · terminal client capability · LLM classifier as an authorization boundary.

Follow-ups (deferred, not blockers)

  • Wire AcpSessionManager into helm-desktop's agent runtime (separate Wave item: helm-desktop#21)
  • platform-actions CI: lockfile→manifest generator + release-key Ed25519 signing job
  • Optional LLM borderline classifier as a sub-tier below the kernel tier (convenience only)
  • Login-shell PATH graft if helm-desktop spawns adapters from Electron

Implementation notes: .tmp-research/findings/impl-acp-connector.md

New package @mindburn/helm-acp-connector (packages/js/acp-connector),
zero runtime deps, driving Claude Code / Codex over ACP behind the HELM
boundary:

- ACP client connector: session lifecycle over a self-contained ndJSON
  JSON-RPC peer, 60s startup deadline (HELM_ACP_STARTUP_TIMEOUT_MS),
  stderr-tail/exit-code error enrichment, handler swapping for warm
  connection reuse; terminal capability never advertised
- Kernel-gated permission broker: every session/request_permission routed
  through a Kernel verdict (evaluate contract replicated from
  helm-tool-wrapper with contract note); fail-closed on DENY/ESCALATE/
  transport error; no yolo; auto-approve-reads as a low-risk tier that
  classifies only — kernel still decides; sticky session allows recorded
  with decisionId/receiptId
- FsGuard: declarative read/write allowlist roots, single canonical
  isPathInside, realpath walk-up canonicalization so symlink escapes
  (final component and ancestors) are denied for reads and writes —
  the counter-position to Rowboat's open fs handlers
- Engine provisioning client: lockfile-pinned versions, sha512 SRI
  verification, atomic temp->rename install, .meta ledger, prune-old;
  plus HELM additions: Ed25519-signed manifest enforcement (fail-closed
  before download) and ProvisioningReceipt with installed-binary sha512
  + manifest digest; tampered cache reprovisions
- Session manager: warm-connection reuse with unref'd dispose grace,
  cancel -> grace -> force-kill, per-run session persistence with
  stale-session fallback

Mechanisms adapted from Rowboat (Apache-2.0) with attribution comments;
all code original. Deliberately not adopted: open fs handlers, yolo
policy, unsigned manifests, terminal capability.

35 tests against a fake ACP agent speaking the real wire protocol:
lifecycle, startup deadline, cancel/grace/kill, warm reuse, kernel
verdict round-trips incl. end-to-end DENY, allowlist enforcement incl.
symlink escape attempts, provisioning hash verification, signed-manifest
enforcement, tamper-triggered reprovisioning.
@strix-security

Copy link
Copy Markdown

Strix is installed on this repository, but we couldn't run this PR security review because this workspace's trial has ended. Add a card to resume code reviews here.

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.

2 participants