design(runtime): host runner tier + living tier spec (RIG-3070) - #1002
design(runtime): host runner tier + living tier spec (RIG-3070)#1002rigel-mintaka wants to merge 6 commits into
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
74353c7 to
f1c2e19
Compare
…sport (RIG-3512) Fold the three high review findings on the host-runtime-tier record. - The AsUser rejection rule would have errored on every provision exec: the fleet agent uid is the baked constant 1000, so the host tier now derives Workspace.UID from the Runner os.Geteuid(). The strict rejection stays. - The egress fail-loud triggered on a non-empty allowlist, which rejected a looser policy and silently discarded the strictest one (an empty host set is pure default-deny). It now triggers on the policy presence. - The plan omitted the agent transport: the socket and config paths are frozen in the compass-agent package and delivered by bind-mount, which a host process does not have. Adds the host provision leg and the path override.
…shape (RIG-3512) Matt ruled the host tier is needed wherever a user runs an agent on their own machine, not only in a single-tenant deployment. The record had scoped it as a "self-host single-tenant onboarding tier", which contradicts DL-325 itself: that row says the boundary follows the trust model, NOT the deployment shape. A user running one agent on their own box puts exactly one trust domain on that host whatever topology their Server sits in. Re-scopes the tier as single-trust-domain, forbidden for untrusted work and for isolating mutually-distrusting principals — properties of the trust domain, not the product. Also rules the second open question: host capability (real session bus, display, device access) is a PERMANENT case, not onboarding scaffolding a user graduates off. That work has to run where the hardware and the session are. Withdraws the weaker "supported consequence, not a designed-for surface" framing; narrows the remaining question to the device/session-bus surface. Touches the placement prose, the constraint, the tier table and when-to-use in the living spec, and the proposed ledger row. markdownlint 0 issues/209 files; design-ledger-gate:ci and orion-ref-gate:check clean. Refs RIG-3512
…orkload* (RIG-3553) The host tier makes ContainerRuntime span a third non-container backend, after MicroVMRuntime already made it span a second. SelectBackend's own comment says the container path goes away entirely, leaving the interface with no container implementation. Rule the successor name (Workload*), reject Session* (already the user-facing conversational stream, and one environment outlives many sessions) and Sandbox (asserts isolation the host tier does not provide). AgentRuntime is not renamed; it is the per-agent lifecycle facade and that name is accurate. The ~365-reference migration is tracked in RIG-3553, sequenced after the microVM default flip.
The runtime interface was named for one of its backends, and it now has four: podman containers, microVM guests, Apple container on macOS, and the direct host processes the host tier adds. SelectBackend already says the podman path eventually goes away, which would leave an interface named ContainerRuntime with no container implementation. ContainerRuntime -> WorkloadRuntime (85), ContainerID -> WorkloadID (198), ContainerSpec -> WorkloadSpec (58), InContainerError -> InWorkloadError (8), plus the comment and design-record prose describing the seam. Genuine containers keep the old vocabulary: ContainerController (podman-only stack supervisor), ContainerRef (a message container), the testcontainer specs, and the container_name wire field, which is a compatibility boundary. AgentRuntime keeps its name; it is the per-agent lifecycle facade. The S1 freeze reserves the method set, not the identifier. No signature, method set, or behaviour changed.
30e6f3b to
5caad8b
Compare
…ry (RIG-3553) The type rename left five comments describing backend-agnostic constructs as container-specific: the seam file header, requireSuccess, AgentRuntime, BackendConfig, and SelectBackend. Comments on the podman implementation itself are unchanged - those genuinely describe podman containers - as are the two references to "the container path" in SelectBackend, which name the podman backend specifically. Comment-only; no code change. Co-authored-by: Matt Wilkinson <matt@rigel.build>
Re-requesting review — your approval predates most of this PRYour approval is from What landed after you approved:
The rename is the one you ruled on ("we're going to be supporting 3 runtimes, we can't keep calling the interface ContainerRuntime… let's do workload"). Mapping: Worth your attention while reading:
Verification on I should not have pushed onto an approved PR — |
Designs a third runner tier that runs an agent as an ordinary process on the
user's own machine, plus the agent-driven config-import review that makes it
worth something on day one. Both halves exist to shorten one path: getting a
new user's existing coding-agent setup working under Compass.
Design-only. No Go changes; no behaviour moves in this PR.
What the measurement changed
The original framing was "we probably need a file/directory upload, or tell
users to put their config in a git repo". Neither is needed — the transport is
already built.
compass agent-config push --dir <path>tars+gzips a localdirectory and
PutAgentConfigs it (go/cmd/compass/agent_config.go:30-51),and the bundle grammar is already the shape a migrating user has on disk:
skills/,extensions/,mcp/,settings/,rules/,agents/,prompts/,profiles/plusAGENTS.mdandmodels.yml(
go/cmd/compass/bundle.go:23-90).So the real gap is not ingress. It is that a user who imports their corpus
cannot tell what overlaps or is superseded by Compass's built-ins, and a
silently shadowed skill looks exactly like a successful import.
Secrets moved the same way:
keyring://is already an accepted SecretSpecprovider (
WithProvider,go/internal/secrets/resolver.go:83-85) andproduction pins nothing today (
go/server/serve.go:528), so the host tier'ssecret story is one option at one callsite rather than a subsystem.
The tier sits on an axis that already exists
DL-325 already rules that the security boundary follows the trust model, not
the deployment (
docs/designs/DECISIONS.md:158): microVM is required foruntrusted multi-tenant, podman is a permanent supported self-host tier. A host
tier is a third point on that axis, not a new doctrine — so this AMENDS
DL-325 rather than superseding it.
/dev/kvmHost mode is not the preferred steady state, and that stays guidance
rather than a gate. The counterfactual for a host-tier user is not a contained
Compass user — it is someone still on their previous CLI agent at the same
exposure, learning nothing about Compass.
Three things the review loop corrected
The first draft was internally inconsistent in ways that would have shipped a
tier that could not launch:
AsUserrule brokeLaunchon the tier's own target machine. Thefleet agent uid is the baked constant
1000(
go/internal/agentuid/agentuid.go:13, intoSpecDefaultsatgo/cmd/compass-runner/main.go:153) and every provision exec passes it(
go/internal/runtime/agent.go:203-204,249-250,343-344,359-360). Now thehost tier derives
Workspace.UIDfromos.Geteuid(); the strict rejectionof any other uid stays.
allowlist, but an empty
EgressPolicyis pure default-deny — thestrictest posture (
go/internal/runtime/egress.go:29-31) — and--egress-allowdefaults to"". It would have rejected a looser policyand silently discarded the tightest. It now triggers on the policy's
presence.
/run/compass/agent.sockand/run/compass/agent-config(
packages/compass-agent/src/cli.ts:91,config-reader.ts:53), bothdelivered by bind-mount (
go/internal/runner/host.go:198,214) and bothpinned by contract tests whose comment reads "A drift is a silent
unconfigured boot". A host process has no bind mounts. Adds the host
provision leg and the path override.
Egress never reuses
EgressArmedInGuest()(
go/internal/runtime/agent.go:298-306) — that marker means the backend armedegress itself, so claiming it would be false.
Import review is an agent task, not a gate
Filename and collision checks are the shallow half. Real overlap is semantic:
a hand-written skill that does what a built-in already does, differently,
sharing no filename. No deterministic gate finds that, so an agent reads both
corpora and reports redundant / conflicting / safe-to-drop. The agent
proposes and the user disposes — it never rewrites the user's config.
The host tier is what makes this land: the agent is already sitting where the
user's config lives, so there is no push-then-inspect round trip.
Structure
docs/specs/runtime/runner-tiers.md(new) — the living tier strategy,following the existing
docs/specs/convention ("Living source-of-truth …how it currently behaves"). The host tier is marked unbuilt throughout.
docs/designs/infra/runtime/compass-host-runtime-tier/design.md(new) — thepoint-in-time record: the why, the 9-method mapping with degenerate cases
named, the plan.
compass-runner-adoption-strategy/design.md— strategy prose moves to thespec; the ruling, alternatives, plan and every DL citation stay.
ContainerRuntimestays frozen — the host tier implements it, it does notamend it. Ledger rows are proposed in each record's Ledger delta for the
coordinator to mint at freeze; this PR does not touch
DECISIONS.md.Verification
markdownlint-cli20 issues across 209 files;moon run design-ledger-gate:ciOK (308 ledger rows, 130 record headers valid);rollupgreen.
Open question for review
Onboarding-convenience and permanent host-capability (an agent that needs the
user's real session bus — window management, host tooling) are two motivations
in one feature. They may have different lifecycle and trust requirements, and
folding them could distort the onboarding tier. Recorded as an Open Question
with a recommendation to accept the onboarding framing here and revisit
host-capability in its own record.
Refs RIG-3512