Skip to content

Simplify Actana Control: two images, one installer, three workflows #6

Description

@qcentic-adm

Destination

A locked set of decisions — a handoff spec an implementation effort can start from with nothing left to argue about — defining a radically simpler Actana Control:

  • Two container images. Panel on a distroless runtime; Core a real daemon container that actually runs agent CLIs.
  • One installer. curl … | sh turns a Linux machine into a Core. No Panel outside Docker.
  • Three CI workflows. ci / release / housekeeping, replacing nine.
  • One vocabulary. Core everywhere; "Harness" retired.
  • A task-shaped docs tree and a thin repo root.
  • Protected main. No direct commits, by anyone.

This map produces decisions, not code. Prototypes are throwaway evidence; the deliverable is written answers.

Notes

Domain. Actana Control is a self-hosted control plane for agentic coding. A Panel (web service, one deployment) drives N Cores (machines running a daemon that spawns PTY sessions running Claude Code / Codex / Cursor CLI against git repos). A machine becomes a Core via the installer; the Panel pairs with it over core-link.

Skills every session should consult. /grilling and /domain-modeling by default; /research for AFK research tickets; /prototype for prototype tickets.

Tracker gotcha — read this before any gh call. No git remote in this clone points at actana/control. origin is AgentSystemLabs/mission-control (the read-only upstream fork parent), so bare gh issue … targets the wrong repo. Always pass --repo actana/control.

Frame locked during charting — settled, do not re-litigate:

  1. Destination is decisions, then hand off. No implementation on this map beyond throwaway prototypes.
  2. Core image = a real daemon container that ships git, Node and agent CLIs. The vulnerability count is fixed by changing the base and how packages arrive — not by stripping the toolchain. The toolchain is the product.

    ⚠️ Amended by Core image: pick the base and what ships inside it #8 — do not cite the struck half as settled. Harnesses (agent CLIs) are not baked: ~1.15 GB of a ~1.4 GB image, ~1 finding, and four vendors' redistribution licences uncleared. They install at runtime into the persistent home, so they survive image upgrades and self-update in place. The rest of the item stands, but state the mechanism correctly: the base did change, and the toolchain did ship — but what moves 1248 → ~38 is the package set. build-essentiallinux-libc-dev is 1200 of 1328 OS findings. (ADR 0016 D6, D9)

  3. Panel runtime = distroless, with a normal full-Node build stage. Build-stage CVEs don't ship.
  4. Full rename Harness → Core, end to end: package dirs, npm names, workflow names, release assets, docs.
  5. CI collapses to three workflowsci / release / housekeeping.
  6. Core is Linux x64 + arm64 only. macOS Core targets are dropped.
  7. Docs: thin root, task-shaped docs/. Root keeps README + the community files GitHub expects; everything else moves under docs/ organised by what the reader is trying to do.
  8. Docker Hub is the primary registry, GHCR a mirror. Per-image descriptions on both.
  9. main is protected with no bypass actors — PR + green CI required, binding on admins. Every implementation session after this map must work through PRs.
  10. .scratch/, docs/specs/, docs/tickets/ get deleted; git history is the archive. Anything load-bearing is promoted first.

⚠️ The struck premise is false and nothing may be deleted on the strength of it. actana/control is one squashed commitc2b8d0d8 chore: initial commit. There is no history on it. The archive is this working clone and the azure remote. The sharpest case is repo-template/: untracked, present only in this clone, in no git history anywhere — deleting it destroys it, so it moves out first (#57). The promote-first half of the item stands and is enforced by #58's dependency on #53#57. (ADR 0016 D46)

Standing tension to respect. Distroless has no shell — fine for the Panel, fatal for a Core, whose whole job is spawning shells. Panel and Core will not share a base image.

Handed off. The locked decisions are ADR 0016 — The 0.1.0 shape. The work they imply is #23 — Build the 0.1.0 shape: 32 tickets in 5 phases, plus 6 filed-but-unscheduled. This map keeps only #16 and #17.

Decisions so far

Cross-check. #8, #9 and #15 landed blind to each other — seven contradictions and three gaps, all resolved in ADR 0016 §"Contradictions and gaps".

Not yet specified

All six closed. The record is ADR 0016 — The 0.1.0 shape; §H is the item-by-item table.

  • The handoff spec itself. Closed — ADR 0016, at docs/adr/0016-the-0-1-0-shape.md. An ADR rather than a docs/ page because it is permanent, diffable and amended by PR; the tracker keeps the evidence and the clauses link out to it. Structure: locked clauses in eight groups, the contradictions table, the closed-open-items table, what is genuinely still open, and sequencing.
  • Rootless / non-root posture for the Panel image. Closed (D21, D22) — the :nonroot tag is not forced: every distroless variant already ships nonroot:x:65532:65532, so numeric USER 65532:65532 on the default tag drops privilege and keeps the posture stated in our Dockerfile. Numeric is load-bearing — Kubernetes' runAsNonRoot cannot resolve a username. /data ownership comes from COPY --chown, never a staged chown, which is silently discarded.
  • Supply-chain posture. Closed as out of scope for 0.1.0, with Supply chain: sign the release, publish an SBOM and provenance #24 filed and both options priced (D29, §H). Integrity is SHA256SUMS over the same channel as the tarball. GitHub provenance attestations cost ~+30s/job but install.sh cannot verify them; full cosign + SBOM costs ~+2–3 min and takes assets from 3 to 7 — which is itself a breaking change to the installer contract. The deferral is cheap precisely because that contract is three names, so a signature is a fourth asset.
  • Fate of packages/shared. Closed (D3) — it survives as a third package. It holds the Panel↔Core wire contract, so folding it either way would make one image depend on the other and break the two-image split. Fix the asymmetry while renaming: Core imports it by relative path in 45 places today.
  • Upgrade path for existing deployments. Closed, and it is empty (§H). Zero published releases and no Core tarball ever published anywhere, so there is no pinned download URL to break and no Panel holding paired Core records from a published build. The only migration is developer machines: actana setup removes a legacy actana-harness.service, or a re-run leaves two daemons on one socket (actana setup removes a legacy actana-harness.service #36, ~5 lines). Paths, schema and pairing are untouched — re-pairing is not required.
  • Dependency-audit policy. Closed (D37) — three populations, three owners: shipped npm packages via pnpm audit --prod --audit-level high on PR; the dev tree weekly, opening an issue; the OS layer and shipped node_modules via Trivy on the image build, failing on fixable CRITICAL/HIGH only. pnpm audit cannot see the base image and Trivy has no dev/prod notion, so neither substitutes for the other. The allowlist is one .trivyignore entry for linux-libc-dev with its justification beside it, and nothing else is suppressed anywhere.

Out of scope

  • Panel wired to multiple Cores (original item 4) — already implemented. CoreListResponse { cores }, coreId threaded through panel-link/core-link, packages/panel/src/server/__tests__/cores-api.test.ts, per-core fleet view. Verified during charting; no decision to make.
  • Windows Core support. Windows operators run the Panel and host Cores on Linux (or WSL, which is linux-*).
  • Product, UI and visual design work (PRODUCT.md, designs/). This effort is distribution, CI, security and docs.
  • Electron / desktop remnants (dist-electron*, dist-electron-out/, TERMINAL_FOCUS_BUG.md, shift-enter-bug-notes.md). Pre-existing cruft from the pre-Panel era; a separate cleanup, not a decision on this route.

Metadata

Metadata

Assignees

No one assigned

    Labels

    wayfinder:mapWayfinder map: the shared route for an effort

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions