Skip to content

feat: add microVM execution environments - #580

Merged
JAORMX merged 52 commits into
mainfrom
acc/microvm-execution-environments
Sep 26, 2026
Merged

JAORMX merged 52 commits into
mainfrom
acc/microvm-execution-environments

Conversation

@JAORMX

@JAORMX JAORMX commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds microvm-local, an opt-in execution environment for local Git repositories. The deployment default remains host-local unless trusted operator settings select MicroVM execution. Linux amd64 now has real-KVM source-build and live-model qualification for private lifecycle v4; Darwin arm64/macOS 15+ remains experimental.

  • New sessions and isolated delegated children receive distinct logical Git worktrees inside a repository-scoped VM. Providers, MCP, hooks, memory, identity, and credentials remain on the host.
  • HarnessContext follows merged feat: compose independent harness context sources #1875/[Plan / Interface] Narrow HarnessContext binding for remote execution #1878: context sources are selected independently of execution. Registered repository instruction/command sources require explicit operator selection and separate project admission. They borrow the exact authorized guest workspace without a runner or execution read-ledger evidence. Independent sources remain usable without guest attachment, including no-FS execution.
  • Multiple local mecatui/mecated instances can share a repository without one instance's cleanup revoking another's placement. Private lifecycle v4 ties each acquisition to its authenticated Unix connection. Close/EOF releases only that owner; final cleanup waits for operation pins and preserves durable worktrees and the repository VM. No heartbeat, TTL, durable lease database, new configuration, or single-instance restriction.
  • Cross-instance scheduled fires and command-source readers safely borrow an originating session's exact placement. Independent schedules, successors, and delegation preserve their existing ownership boundaries. Same-conversation writer leases remain unchanged.
  • Exact binding validation precedes runtime mutation. Cancellation, lost replies, rollback, deletion admission, concurrent resolution, and pending teardown preserve other owners and fail closed without host/default fallback.
  • Runtime artifacts and release evidence are verified before boot. Guest workloads retain UID/GID 65532:65532; Linux uses namespace mapping, while Darwin uses go-microvm v0.0.41 ownership preparation and authenticated launch supervision.
  • Guest IPv4 egress remains permissive by default, with optional deny-all/allowlist tightening. External IPv6 is unsupported. Status/doctor/delete retain their existing local operator surfaces.

Integration and approved amendment

Rebased onto main 17700f1e7a5190141bbf4a784d8990fece338428, including #1875 and #1878, and followed the updated MicroVM HarnessContext handoff. The full final-tree preservation audit included historical merge resolutions, not just patch equivalence. The incremental rebase's tree exactly matched the expected direct merge; environment/microvm and internal/adapter/microvm remain byte-for-byte identical to the previously qualified candidate d5491b7237f5b2c43d23c06e2034e0c706591ab1.

The rebase preserved lineage/read-lock behavior and both placement wiring paths. A separately reviewed persisted-approval correction retains the factory GovernanceRoot for permission/memory context and root-session causality in ordinary and scoped paths. MicroVM ADR 0364 was renumbered to 0367 to avoid main's new native-Kubernetes ADR 0364; the native-Kubernetes decision is unchanged.

The directing human explicitly approved the narrow same-PR connection-owned acquisition amendment after reviewing normal multi-terminal and cross-instance scheduling use cases. The amendment was committed separately as 1c3cba775ae75b7bc1d29f17873627fa3efbf694 before implementation. It changes the private lifecycle protocol from v3 to v4, not public RPCs, engine interfaces, source-selection authority, SessionLease, or persistent ownership schemas. Mixed private protocol versions are rejected; a mismatch does not automatically replace a serving daemon.

Interfaces match the approved amended contract: Yes. No distributed fencing framework, inventory RPC migration, cold-child source reconstruction, or cross-process Git transaction system was added. Human merge remains separate.

  • Plan: docs/acceptance/microvm-execution-environments.md
  • MicroVM decision: docs/adr/0367-microvm-execution-environments.md
  • Darwin decision: docs/adr/0365-microvm-darwin-xattr-ownership.md
  • Shared source-authority decision: docs/adr/0359-harness-context-source-authority.md
  • Operator guide: user-docs/building/deployment/microvm-environments.md

Live validation and corrections

The operator subsequently authorized real Linux KVM and OpenRouter openai/gpt-5.6-sol verification. This exposed three production defects that offline fixtures had missed:

  1. Git archive permission normalization made private 0600 source files fail exact capture under microvmd's deterministic 022 umask. Captured regular-file permissions are now restored within the confined worktree; digest verification remains strict.
  2. A fresh harness resuming an existing placement did not call the configured readiness seam when the daemon was stopped. Exact reattachment now uses that existing seam after validation, without creating a replacement placement or touching no-FS/unselected sources.
  3. A fixed 15-second resolve timeout covered artifact validation and cold VM startup, not just lookup. Exact acquisition now honors its caller's cancellation/deadline, like Bind; independent five-second cleanup bounds remain unchanged.

The existing live E2E test was also corrected to finish the run and retire v4 child/binding/Build owners before daemon restart and final deletion. Only the explicit dirty-retention outcome is accepted during child cleanup; other errors remain failures.

Real executed evidence (pre-rebase)

The following live evidence predates this rebase. Runtime/client trees were preserved byte-exact; new-main integration, wiring and the approval-context correction are verified offline, not by a new live-provider or VM run.

  • MECATL_MICROVM_E2E_PREPARED=1 task e2e:microvm passed at 3d55a8cfa600546fd7c03a177dd2293a1b54aa50: real guest filesystem/Shell, isolation, post-boot child merge/conflict, daemon restart, exact reattachment, and cleanup. The provider in this gate is scripted; the VM is real.
  • The separate live-provider journey completed 10 outer runs plus one isolated Subagent execution, with 42 outer tool calls and two child Reads. It used the public HTTP/SSE API, two independent harness processes, real KVM, and GPT-5.6 Sol—not VM/provider fakes or scripted TUI keystrokes.
  • Passed: coding via Read/Edit/Write/Shell; UID 65532/Linux/Wolfi; key-variable absence, unreadable guest-agent configuration, inaccessible host canary and read-only shared Git objects; unchanged host source; distinct guest placements sharing a repository VM; cross-instance read-only scheduled fire; origin continuation after fire cleanup; peer-reader/server shutdown; exact harness resume; linked-host-worktree source isolation.
  • Final cold resume passed with host 9a1c2cd05abca6f101a2b29e467baeda3babdb80 against the unchanged compatible, verified v4 runtime bundle from 3d55a8cfa600546fd7c03a177dd2293a1b54aa50. The original EnvironmentRef, guest coding fix, proof file and selected context survived; actual Read/Shell results and end_turn confirmed success. No replacement session or command replay was used.

Evidence caveats: the original failed cold request was cut off after 15.008 seconds. The successful corrected run returned HTTP 200 after 12.986 seconds and completed in 19.708 seconds. Its driver returned nonzero solely because an extra greater-than-15-second timing assertion was unmet; functional checks passed. Successful live acquisition beyond 15 seconds was not observed. Deterministic virtual-time regressions prove the old timeout fails a legitimate slow acquisition and the correction preserves caller cancellation/deadlines. One scheduled PresentPlan attempt was correctly denied in headless mode; the read-only fire recovered and completed. The child token was included in its delegated prompt, so token echo alone is not claimed as independent live proof of automatic child-context ingestion; that remains covered by the factory tests.

Provider credentials were supplied only through the provider-process environment, not displayed, placed in prompts, or mounted into the guest. Qualification-owned harnesses and daemon were gracefully stopped; diagnostic fixtures/logs were retained locally, not uploaded. No unrelated local instances were targeted.

Offline verification and review

Rebased candidate: a012760af3df81aea360a6711cc224777ffdc41f on main 17700f1e7a5190141bbf4a784d8990fece338428.

Completed again on this assembled post-rebase head, all exit 0 (run serially):

  • task test
  • task lint
  • task test:race
  • task docs
  • task site:build
  • task api:check
  • task ac-trace-strict
  • go run ./cmd/mecademo — Read, Write permission ask/approval, result, Team, and background Subagent

task test and task test:race cover the root, engine, authn/oidc, MicroVM and all provider modules, plus their standalone/build-tag proofs through the unchanged Taskfile. task lint passed with the pinned binary and existing configuration across all eight module scopes; no timeout override or rule disabling was needed. Strict docs checked 582 documents and 5,667 references with zero broken links/anchors, ambiguous targets, or orphan/unreachable pages.

Global strict trace exited 0. The owning MicroVM plan resolves all 35 criteria (34 named-test criteria and one inspection method) with zero missing references; shared HarnessContext resolves all 25 criteria. Other proposed/in-progress plans still produce report-only diagnostics. Resolution is not itself live execution evidence.

Focused semantic regressions, mutation checks, race tests and the previously completed independent reviews cover ownership, authority, source isolation, file-mode capture, readiness and cold-acquisition cancellation. Spec, Standards, Test adequacy, Security and Architecture/reuse reviews found no remaining issues; the preceding full integration panel was also clean. The subsequent final-tree preservation audit and persisted-approval correction were independently reviewed clean. No new external review panel is claimed for this final gate run. Generated checksum churn was excluded; there were no tracked site-generation changes. CI must independently verify this pushed head; local gates are not a CI-green claim. This PR is not merged.

Remaining qualification limits

This is Linux source-build qualification using locally verified development artifacts, not qualification of a published signed release. Physical Apple Silicon/HVF and Darwin release qualification remain pending; native unit tests and cross-compilation are not presented as live Darwin proof. Actual TUI keyboard interaction and a naturally slow successful live acquisition exceeding 15 seconds were not tested. The overall acceptance plan remains in-progress for the outstanding platform/release work.

This is a local, single-operator feature supporting multiple harness processes. Linux arm64 live operation, remote/multi-user placement, non-Git sources, repository-VM deletion, and advanced orphan recovery remain outside this slice.

Closes #526
Closes #527
Closes #528
Closes #529
Closes #530
Closes #531
Closes #532
Closes #533
Closes #534
Closes #535

PANEL: ship_blockers=0 important=0 advisory=0 reviewer_failures=0

@jhrozek jhrozek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel review — the consequential subset

Adversarial review of this PR across three orthogonal axes — Spec (does it implement what was asked), Standards (does it follow this repo's documented conventions), and Domain (what specialist reviewers say) — run as three panels over a 202-file diff, with 19 agents total.

Note on scope of this post: the full review produced 84 inline comments. GitHub's secondary rate limiter refuses a review that creates that much content at once, so this posts the 23 consequential ones: every ship-blocker and every hard standards violation. Four further findings are folded into the comment they pair with. The remaining ~57 — mechanical fixes, judgement calls, polish, and the reuse findings (net: -1,100 lines in the module, -162/-194 in host wiring) — are summarised below and available in full on request.

Findings are not merged across axes: Spec, Standards and Domain are orthogonal by design, so each comment is tagged with the panel and axis it came from. cross-confirmed marks a finding two or more independent reviewers reached separately — highest confidence. Where two reviewers disagreed, both views are preserved rather than resolved.

The seven I would fix before merge

  1. internal/app/build.go:2254 — prepareSessionDiscovery re-roots cfg.Workspace before the project-trust gate is evaluated, so a session-selected root inherits a trust decision made for a different repo. Because the re-root happens first, even projectIngestionAdmittedForRoot — the guard written specifically to prevent this — returns true for an arbitrary root. This fires on any CreateSession with a non-launch-root workspace, so it is a live regression on the existing served path, not a microVM-only issue.
  2. Three daemon concurrency defects, none with a test — environment/microvm/runtime.go:350 (unlocked struct fields behind a locked map lookup), registry.go:94 (a flock giving no intra-process exclusion on the durable registry), control/multiplex.go:255 (an uncancellable write that can pin a Bash tool call forever).
  3. .github/workflows/release.yml:211 — the release is created non-draft before any artifact exists, on a fail-fast: false matrix where two of three cells are guaranteed to fail (cross-arch docker run with no QEMU, package-microvm-release.sh:81). The first v* tag after this merges produces a public, partial, half-signed release — the exact outcome this workflow's own comments say it is designed to prevent.
  4. internal/adapter/server/service.go:1897 — DeleteSession removes the session record without destroying the VM, and the verb that could destroy it has no wire surface, so it becomes permanently unreachable for that id. The docs describe the deletion sequence as working behaviour.
  5. internal/adapter/server/environment_profile.go:59 — the ADR-0224 §5 egress disclosure crosses an independently-versioned module boundary as an English sentence, re-derived by prefix/suffix parsing, with a mismatch failing session creation. It is already inconsistent in-tree (client_profile_test.go:36,61 asserts strings this Service rejects), and the parse verifies grammar rather than policy.
  6. .github/workflows/microvm-e2e.yml:38 — id-token: write on a pull_request job that runs PR-authored code and never signs keylessly.
  7. .github/scripts/install-microvm-release.sh:45 — the installer verifies no signature; the digest it checks comes from the same unsigned manifest, which is itself absent from SHA256SUMS. Two docs claim it verifies.

Cheapest high-value fixes

Four comments below carry one-click suggestion blocks: CombinedOutput() → Output() in gitexec.run() (a git warning currently corrupts a captured tar), both ADR 0108 → 0224 corrections, and the ADR status flip to Accepted. Also cheap: delete three id-token: write lines, and the two-line VersionValid guard at client.go:448.

Not posted inline — the remaining ~57

Spec (Panel 3): docs/usage/http-sse-api.md:125 says the microVM backend is unwired when build.go:1574-1624 wires it · docs/usage/microvm-environments.md:207 documents a deletion sequence with no operator surface · AC8.1's three-platform live matrix is one platform in practice (microvm-e2e.yml:123 — the arm64 and macOS live cells are workflow_dispatch-gated and default to false) · the release builds and signs a mecatl-owned execution image the ADR does not authorise · go-microvm version skew (ADR says v0.0.39, go.mod says v0.0.40, and internal/apicheck/microvm_module_contract_test.go:19 pins the string from a root-module test that the next dependabot bump will break).

Standards: no depguard rule for environment/microvm in .golangci.yml, so the allowlist ADR 0093 says "travels with the modules" does not exist for this one · two missing ADR-0027 List-1 resource rows (Service.sessionEnvironmentInfo, microVMClients.byEndpoint — the latter never closed) · Config.EnvironmentSessionResolver is undocumented and contradicts ADR 0224 §1 · prepareSessionDiscovery is an unsanctioned per-session catalog delta · AGENTS.md not updated for the new environment/ module tier.

Domain, mechanical: dead + lossy LifecycleWorkspace/LifecycleExec proxy arms (~117 lines, and the copy collapses three distinct error codes into "internal") · proxyWorkspace replace mutates before validating · QuotaKind is a parallel vocabulary for admission.Resource with 9 of 11 values never emitted · Stat returns success on a malformed response · no deadline on teardown paths (CloseSession uses bare context.Background()) · Build mutates the caller's maps · unbounded daemon error text reaching API clients · five hand-written cleanup marks · reply-frame echo copied at 7 sites · configgen documents five config keys the strict decoder deliberately rejects.

Domain, judgement calls: egress allowlist is hostname-only with no post-resolution IP denylist (SSRF; flagged because it is the claimed control failing open, not the documented deferral) · two live IdentityAllocators, one hardcoding Generation: 1 · the neutral server layer now speaks microVM vocabulary, and those names have reached the proto · the host-side wire Workspace is the one ADR-0208 implementation with no conformance coverage · Taskfile.yml:232 selects live coverage by a hardcoded 19-name regex, and go test -run '<no match>' exits 0 · the installer's archive preflight has no negative test on any of its ten controls · a third frame-codec copy that re-types the wire shapes as map[string]any, hiding renames.

Checked and deliberately not flagged

Recorded so it is not re-litigated: the environment/ module tier is justified (authn/oidc is the precedent) · the "three-profile fan-out" does not exist — tool profile and placement alias are orthogonal axes pinned by a test — and should not be unified · the mirrored wire structs in internal/adapter/microvm are essential module-graph isolation · guest errorResponse vs host remoteError are deliberate inverses across a process boundary · the +37-line append to frozen ADR-0027 is legitimate (AGENTS.md names that file explicitly) · engine/adapter/fsconformance's additions are the right shape and placement · the 1,739-line e2e suite is real fault injection, not mock theatre · forker.KindRouter is a real abstraction introduced when its second implementation arrived · ~8,000 lines of per-scenario test setup should stay explicit · no host credential crosses into the guest, and the codec framing, artifact supply chain, peer-credential fail-closed behaviour, and operator-tier config gating were all verified sound.


Each axis is orthogonal — verify each independently before shipping.


go 1.26.6

replace github.com/stacklok/mecatl/engine => ../../engine

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 1 · Standards · HARD VIOLATION] Committed replace directive in a published module.

ADR 0093 (Decision): "replace directives live in go.work (dev) and the root go.mod (the monorepo consumer), NEVER in a published provider go.mod — a committed replace breaks downstream go get."

Every sibling opt-in module obeys this: authn/oidc/go.mod and all four provider/*/go.mod require engine at a real version with no replace — the Taskfile even notes "Its go.mod deliberately has no replace directive". This PR already adds use ./environment/microvm to go.work, so the replace is redundant in dev and harmful on publish.

Related (not inline-commentable — .golangci.yml is unchanged): there is no depguard rule for environment/microvm. grep 'environment/microvm' .golangci.yml returns zero hits, while the config carries file-scoped strict rules for **/authn/oidc/**.go and each **/provider/*/**.go. ADR 0093 claims "Each provider shares the root .golangci.yml depguard allowlist … so the layering rule travels with the modules" — with no matching rule, this module has no allowlist at all.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The committed replace and the absent MicroVM-specific depguard allowlist are both still present at this head. The nested module is currently repository-scoped rather than published, so I cannot substantiate the downstream-release consequence, but the standalone-boundary and layering coverage points stand. Leaving this thread open for the module contract decision and matching lint rule.

module github.com/stacklok/mecatl/environment/microvm
go 1.27
replace github.com/stacklok/mecatl/engine => ../../engine
require (
github.com/bmatcuk/doublestar/v4 v4.10.0
github.com/gofrs/flock v0.13.0
github.com/google/go-containerregistry v0.22.1
github.com/stacklok/go-microvm v0.0.41
github.com/stacklok/mecatl/engine v0.10.0
github.com/stacklok/toolhive-core v0.0.46
golang.org/x/sys v0.48.0
)

mecatl/.golangci.yml

Lines 69 to 80 in 20fa604

depguard:
rules:
# ORTHOGONAL to the core-tier layering rules below: this is the house
# deprecation guard (io/ioutil), not part of the clean-core allowlist set.
deprecated:
list-mode: lax
files:
- $all
- '!$test'
deny:
- pkg: io/ioutil
desc: deprecated; use io and os

Comment thread environment/microvm/runtime.go Outdated
if err != nil {
return err
}
if generation.services != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 1 · Domain · HIGH] runtimeGeneration.services is raced.

runtime.go:310-312 writes it under r.mu; Services() (:322), Reattach (:334) and Destroy (:366) read it — and this Detach write clears it — without the lock. composition.go:130 spawns a goroutine per connection, so a LifecycleWorkspace/LifecycleExec proxy can read services while a concurrent LifecycleDetach nils it: nil deref, or use of a closed control.Client. generation() (:386-397) locks only the map lookup; every field access after it is unguarded.

Fix: lock the struct, or make runtimeGeneration immutable after negotiate.

Source: go-architect

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raced runtimeGeneration.services path is no longer the active production composition: the current runtime is repository-scoped and keeps registrations behind its own control mutex rather than exposing the former per-VM services field. That removes this specific race from the shipped path, although it would not justify retaining the old implementation unreviewed. The repository runtime starts from NewRepositoryRuntime; no legacy runtime.go remains at this head.

type repositoryRuntimeGeneration struct {
record RepositoryVMRecord
instance GoMicroVMInstance
issuer *control.CapabilityIssuer
authorityKey []byte
network NetworkHandle
objectSnapshot string
controlMu sync.Mutex
nextSequence uint64
pending *guestagent.RepositoryControlRequest
registrations map[string]*repositoryHostRegistration
}
type repositoryHostRegistration struct {
binding control.Binding
incarnation string
registered bool
tearingDown bool
}
type repositoryRuntimeAttacher interface {
AttachRepository(RepositoryVMRecord, RepositoryBootAuthority) error
}
type repositoryRuntimeAborter interface {
Abort(context.Context, RepositoryVMRecord) error
}
// RepositoryRuntime is both the concrete singleton VM runtime and logical guest
// registrar used by production microvmd composition.
type RepositoryRuntime struct {
backend GoMicroVMBackend
dial GuestDialer
controlDial GuestDialer
unixEndpoint bool
network *NetworkController
guestEgress GuestEgressPolicy
observer *OperationsObserver
launchReconciler RepositoryLaunchReconciler
rollbackTimeout time.Duration
mu sync.Mutex
vms map[string]*repositoryRuntimeGeneration
listeners map[string]*net.UnixListener
}
// NewRepositoryRuntime constructs the production repository adapters. There is
// deliberately no host filesystem or command-runner fallback.
func NewRepositoryRuntime(cfg RepositoryRuntimeConfig) (*RepositoryRuntime, error) {
if cfg.Backend == nil || cfg.Network == nil || (cfg.UnixEndpoint && (cfg.DialGuest != nil || cfg.DialControl != nil)) || (!cfg.UnixEndpoint && (cfg.DialGuest == nil || cfg.DialControl == nil)) {
return nil, errors.New("repository microvm runtime is not fully configured")
}
if backend, ok := cfg.Backend.(*LibkrunBackend); ok && (backend == nil || backend.launchOwnership == nil) {
return nil, ErrLaunchOwnershipUnsupported
}
if cfg.LaunchReconciler == nil {
cfg.LaunchReconciler, _ = cfg.Backend.(RepositoryLaunchReconciler)
}
runtime := &RepositoryRuntime{backend: cfg.Backend, dial: cfg.DialGuest, controlDial: cfg.DialControl, unixEndpoint: cfg.UnixEndpoint, network: cfg.Network, guestEgress: cloneGuestEgressPolicy(cfg.GuestEgress), observer: cfg.Observer, launchReconciler: cfg.LaunchReconciler, rollbackTimeout: repositoryRollbackTimeout, vms: make(map[string]*repositoryRuntimeGeneration), listeners: make(map[string]*net.UnixListener)}
if cfg.UnixEndpoint {
runtime.dial = runtime.acceptGuest
runtime.controlDial = runtime.acceptGuest
}
return runtime, nil

Comment thread environment/microvm/registry.go Outdated
return result, err
}

func (r *FileRegistry) withLock(ctx context.Context, fn func(*registryDocument) error) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 1 · Domain · HIGH] FileRegistry's flock gives no intra-process exclusion — lost updates on the durable registry.

All callers share one *flock.Flock. gofrs/flock@v0.13.0/flock_unix.go:141-147 returns (true, nil) immediately when that instance is already locked, so two goroutines in the same daemon both "acquire", read the same document, and the first defer Unlock() (:108) drops the file lock while the second is still mid-transaction. The type's own doc comment (:17-19) claiming one authoritative set is also wrong for that window. validRecordTransition catches state regressions but not two concurrent create appends.

One sync.Mutex on FileRegistry fixes it.

No test covers this: the concurrent case (lifecycle_reconcile_test.go:185-189) uses an in-memory registry, and the two-FileRegistry case (:197-225) is sequential.

Source: go-architect

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same-instance flock concern was valid for the original FileRegistry, but that registry is no longer part of the repository-scoped production path. Logical environments now go through RepositoryLogicalManager and the singleton repository registry, so there is no current FileRegistry mutation path to fix here. This does not claim an in-process flock would have been sufficient in the removed design.

// RepositoryLogicalManager creates logical worktrees over the task-62 singleton registry.
type RepositoryLogicalManager struct {
registry *RepositoryVMRegistry
preparer *worktree.Preparer
guest RepositoryGuestRegistrar
prepareOwnership repositoryOwnershipPreparer
}
// NewRepositoryLogicalManager constructs the daemon-side logical routing use case.
func NewRepositoryLogicalManager(registry *RepositoryVMRegistry, preparer *worktree.Preparer, guest RepositoryGuestRegistrar) (*RepositoryLogicalManager, error) {
if registry == nil || preparer == nil || guest == nil {
return nil, errors.New("microvm repository logical manager is not fully configured")
}
return &RepositoryLogicalManager{registry: registry, preparer: preparer, guest: guest, prepareOwnership: prepareRepositoryOwnership}, nil
}
// Create reuses one healthy repository VM while allocating a fresh ref, branch,
// index, worktree, and assigned guest root.
func (m *RepositoryLogicalManager) Create(ctx context.Context, request LogicalEnvironmentRequest) (_ *LogicalEnvironment, retErr error) {
result, err := m.registry.Ensure(ctx, RepositoryVMRequest{

}
}

func (c *Client) write(frame multiplexFrame) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 1 · Domain · HIGH] The multiplex client cannot be cancelled if the guest stops reading — this can pin a Bash tool call permanently.

This write has no deadline and no ctx, and Stream holds requestMu across it (:178-194, needed only to satisfy the server's monotonic-ID check at :405). If the guest stops draining, every caller — including the cancel write at :208 — blocks on writeMu indefinitely and ctx is inert.

Worse, :203-252: after sending cancel it sets ctxDone = nil and then waits for an end/error frame forever, so a hung guest handler pins the caller goroutine — and via guestexec/exec.go:174, a Bash tool call — permanently.

Needs a write deadline plus a bounded post-cancel wait.

Sources: go-architect; secure-code-reviewer (CWE-400, on the read-loop half)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed on the current multiplex implementation. Stream still performs synchronous writes while holding the request ordering lock, and after a successful cancel write it disables ctxDone and waits for a terminal frame. A guest that stops reading or never terminates can therefore strand the transport work even though the outer host RPC now closes its UDS connection on cancellation. Leaving this open.

func (c *Client) Stream(ctx context.Context, service ServiceName, method string, request any, receive func(json.RawMessage) error, response any) error { //nolint:gocyclo // explicit request stream/cancel state machine
payload, err := json.Marshal(request)
if err != nil {
return fmt.Errorf("encode microvm multiplex request: %w", err)
}
c.requestMu.Lock()
id := c.nextID.Add(1)
responses := make(chan multiplexFrame, 1)
c.mu.Lock()
if c.err != nil {
err = c.err
c.mu.Unlock()
c.requestMu.Unlock()
return err
}
c.pending[id] = responses
c.mu.Unlock()
writeErr := c.write(multiplexFrame{
Kind: kindRequest, Service: service, Method: method, RequestID: id,
Capability: c.capability, Payload: payload,
})
c.requestMu.Unlock()
defer func() {
c.mu.Lock()
delete(c.pending, id)
c.mu.Unlock()
}()
if writeErr != nil {
return writeErr
}
ctxDone := ctx.Done()
cancelled := false
for {
select {
case <-ctxDone:
if err := c.write(multiplexFrame{Kind: kindCancel, Service: service, Method: method, RequestID: id, Capability: c.capability}); err != nil {
return ctx.Err()
}
cancelled = true
ctxDone = nil
case <-c.done:
if cancelled {
return ctx.Err()
}
c.mu.Lock()
err := c.err
c.mu.Unlock()
if err == nil {
err = io.EOF
}
return err
case frame := <-responses:
if frame.Service != service || frame.Method != method {
return ErrMalformedFrame
}
switch frame.Kind {
case kindStream:
if receive != nil {
if err := receive(frame.Payload); err != nil {
_ = c.write(multiplexFrame{Kind: kindCancel, Service: service, Method: method, RequestID: id, Capability: c.capability})
return err
}
}
case kindEnd:
if cancelled {
return ctx.Err()
}
if response != nil && len(frame.Payload) != 0 {
if err := json.Unmarshal(frame.Payload, response); err != nil {
return fmt.Errorf("decode microvm multiplex response: %w", err)
}
}
return nil
case kindError:
return &RemoteError{Code: frame.ErrorCode}
default:
return ErrMalformedFrame
}
}
}
}
func (c *Client) write(frame multiplexFrame) error {
c.writeMu.Lock()
defer c.writeMu.Unlock()
if err := c.codec.Write(c.stream, frame); err != nil {

return microvm.CreateRequest{Owner: request.Owner, SessionID: request.SessionID, Profile: request.Profile,
Worktree: worktree.Request{Source: request.SourceCheckout, WorktreePath: names.WorktreePath, MetadataPath: names.MetadataPath, Branch: names.Branch},
ArtifactRequests: artifactRequests, Resources: usage,
ProfileStatus: microvm.EnforcedProfileStatus{Profile: request.Profile, GuestEgress: egress.Status(), HostEgress: "not constrained: LLM providers, WebFetch, WebSearch, MCP, hooks, OCI pulls, telemetry"}}, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 1+2 · Domain · HIGH · cross-confirmed] The egress disclosure is a prose sentence used as a load-bearing cross-module protocol constant.

hostServiceEgressStatus (internal/adapter/server/environment_profile.go:59) is a prose literal retyped byte-for-byte here, and network.go:157,159 mints "deny-all (IPv6 disabled)" / "allowlist (%d destinations; IPv6 disabled)" which the host prefix/suffix-parses at environment_profile.go:61-72. A mismatch on either string fails session creation with ErrFailedPrecondition (:141).

The fact is structured — policy mode, destination count, IPv6 state — but crosses an independently-versioned module boundary as an English sentence. Two things a module boundary is supposed to permit (improving a human-facing message; host/daemon version skew) become total loss of the microVM capability.

It is already broken in-tree: internal/adapter/microvm/client_profile_test.go:36,61 asserts a successful Provision with HostEgress: "host services not constrained" and GuestEgress: "daemon enforced deny-all (IPv6 disabled)" — both of which the Service rejects. Nothing in CI ties the two modules together (the daemon's own test asserts only HostEgress != ""; the one default-run cross-layer test uses a hand-rolled fake with the literal hardcoded; the real-daemon suite is behind //go:build microvm_e2e).

It also buys no security: validGuestProcessEgressStatus accepts allowlist (1 destinations; IPv6 disabled) regardless of what was actually configured — the host verifies grammar, not policy.

Recommended: one versioned struct {mode, destinations, ipv6_enabled}; validate the invariant (mode=="allowlist" ⇒ destinations>0) and render prose at the presentation layer. The host-scope sentence is a host-side constant — asking the daemon to echo it back so the host can compare it to itself is a round-trip that can only ever fail.

Sources: software-architect + go-architect (both HIGH)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The load-bearing prose parser has been removed from the host admission path. The placement client now requires only nonempty daemon-provided disclosure fields while validating the exact binding and fixed guest root; it does not parse or compare egress wording. The daemon still renders a human-readable status, which is presentation metadata rather than a cross-module gate.

safeCleanupTarget := response.Binding.Owner == owner && response.Binding.SessionID == placementID &&
strings.HasPrefix(response.Binding.EnvironmentID, "logical-") && canonicalBinding(response.Binding)
primary := errors.New("microvmd create returned incomplete or inconsistent placement metadata")
if response.Created == nil || response.Created.Ref.Kind != string(kindMicroVM) || response.Created.Ref.ID != response.Binding.Ref ||
response.Created.Generation != response.Binding.Generation || response.Created.HostWorktree == "" || response.Created.GuestRoot != publicGuestRoot ||
response.Created.Profile != c.profile || response.Created.GuestEgress == "" || response.Created.HostEgress == "" {
if safeCleanupTarget {
return server.PlacementBinding{}, invalidPlacementResponseError(primary, c.boundedRollbackPlacement(ctx, response.Binding))
}
return server.PlacementBinding{}, unsafePlacementResponseError(primary)
}
if !safeCleanupTarget {
return server.PlacementBinding{}, unsafePlacementResponseError(errors.New("microvmd create binding mismatch"))
}
ref := refForBinding(response.Binding)
closePlacement := func() error { return c.boundedOperate(ctx, "detach", response.Binding) }
rollbackPlacement := func() error { return c.boundedRollbackPlacement(ctx, response.Binding) }
return c.placementBinding(ref, response.Binding, closePlacement, rollbackPlacement), nil
return microvm.RepositoryPlacement{}, errors.New("unknown microvm environment profile")
}
if _, ok := artifactRequests[microvm.ArtifactExecutionImage]; !ok {
return microvm.RepositoryPlacement{}, errors.New("microvm daemon has no execution image")
}
if _, ok := artifactRequests[microvm.ArtifactGuestAgent]; !ok {
return microvm.RepositoryPlacement{}, errors.New("microvm daemon has no independently admitted guest agent")
}
status := microvm.EnforcedProfileStatus{Profile: request.Profile, GuestEgress: cfg.GuestEgress.Status(), HostEgress: "not constrained: LLM providers, WebFetch, WebSearch, MCP, hooks, OCI pulls, telemetry"}
return microvm.RepositoryPlacement{
Request: microvm.LogicalEnvironmentRequest{Owner: request.Owner, Checkout: request.SourceCheckout, Artifacts: repositoryArtifactSnapshot(artifactVerifier, artifactRequests)},
Status: status,

Comment thread .github/workflows/microvm-e2e.yml Outdated
timeout-minutes: 45
permissions:
contents: read
id-token: write

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 3 · Domain · HIGH · cross-confirmed] id-token: write on a pull_request-triggered job that executes PR-authored code and never signs keylessly.

Same unused grant at :128 and :183. The live suite runs cosign in keyed mode with an ephemeral local key — environment/microvm/e2e/prepare.sh:152 does COSIGN_PASSWORD= cosign generate-key-pair — and sign-microvm-release-evidence.sh takes the --key branch, so no step ever reads ACTIONS_ID_TOKEN_REQUEST_URL. (docs/usage/microvm-environments.md claims "live CI cells continue to exercise keyless OIDC"; they do not — this grant looks like a leftover from that intent.)

Impact: PR-authored code (this job runs ./environment/microvm/e2e/prepare.sh and task e2e:microvm verbatim) can mint a Fulcio-backed OIDC token with subject repo:stacklok/mecatl:pull_request, then (a) cosign sign-blob arbitrary bytes with a certificate attributable to this repository in the public Rekor log, and (b) exchange that token against any cloud/vault trust relationship whose subject condition is repo-scoped rather than workflow-and-ref-scoped — the single most common OIDC federation misconfiguration.

Fork PRs are capped read-only by GitHub, so the reachable case is a same-repo head branch — including one pushed by a coding agent running in CI.

Not Critical because the published artifact chain is not forgeable this way: microvmd admission pins an exact --certificate-identity and --certificate-oidc-issuer (environment/microvm/artifact_sigstore.go:86-87), and the documented operator regexp is anchored to release.yml@refs/tags/v*, so certs from this workflow do not satisfy it. That containment is what keeps this HIGH rather than Critical.

Fix: delete the three id-token: write lines (the permissions: blocks then reduce to the inherited contents: read). If a keyless cell is wanted later, put it in a separate job gated if: github.event_name != 'pull_request'.

Verification: add forbid 'id-token: write' "$e2e" to microvm-ci-release_test.sh — it already has a working forbid helper and this is exactly the drift it should catch. Independently, audit every OIDC trust policy naming this repo for a sub condition narrower than repo:stacklok/mecatl:*.

CWE-269 / CWE-250 / CICD-SEC-4 / CICD-SEC-5.

Sources: secure-code-reviewer (HIGH) + devops-expert (M4)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by removing every id-token: write grant from the pull-request-triggered MicroVM E2E workflow; the workflow and live-hypervisor job now have only contents: read (workflow). The release contract test also rejects a future OIDC grant or keyless signing command in that job (guard). I have not treated this as evidence of any external OIDC trust-policy audit.


while IFS=" " read -r kind payload payload_digest reference digest provenance bundle; do
archive="$assets/$payload"
test "sha256:$(sha256_file "$archive")" = "$payload_digest"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 3 · Domain · HIGH · cross-confirmed] The installer verifies no signature — the digest it checks comes from the same unsigned manifest.

This compares the archive against $payload_digest, which was read out of the manifest. The script never invokes cosign verify-blob: it reads the provenance / sigstore_bundle names from the manifest and copies their paths into microvmd-artifacts.json — a projection, not a verification. So the trust chain at install time is self-referential.

Compounding it, SHA256SUMS-$platform (package-microvm-release.sh:102,126) lists the two binaries and the three tarballs but not microvm-release-<platform>.json, so the documented sha256sum --check gives an operator zero integrity on the one file that feeds this installer.

Both .github/workflows/README.md:163-166 and docs/usage/microvm-environments.md claim the installer verifies, and the documented sequence places the install step before the cosign verify-blob snippet.

Fix — verify the statement, then bind it to the manifest, before extractall:

cosign verify-blob --bundle "$assets/$bundle" \
  --certificate-identity "$MICROVM_RELEASE_IDENTITY" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  "$assets/$provenance"
# then assert the statement's subject digest equals the manifest's tree digest

Require the identity to be supplied (env or --identity) and abort if empty — do not default to a permissive value. Also add the manifest and this script to SHA256SUMS-$platform.

Execution stays fail-closed today because microvmd admission re-verifies the signed in-toto subject against the recomputed tree digest under an exact identity/issuer policy — which is why this is not Critical. But attacker-chosen bytes are still materialized into a privileged host path with no cryptographic check having run.

Verification: flip one byte of a payload tarball and assert the installer exits non-zero; swap a bundle and assert rejection.

CWE-347 / CWE-494 / CICD-SEC-3 / SLSA v1 verification requirements.

Sources: secure-code-reviewer + devops-expert (H2)


Folded in — two more defects in this same script:

  • :80 (HIGH) — the shipped installer cannot run standalone. It resolves the digest tool via "$(dirname -- "$0")/../../environment/microvm", but package-microvm-release.sh:41-45 copies this script into dist/<platform>/, where that path does not exist. Under set -e it aborts, so it is fail-closed — but the shipped installer is unusable for every operator following the documented flow, and the predictable response to a broken verifying installer is a manual tar -xzf that skips every control here. The e2e never catches it because MECATL_MICROVM_INSTALLER (Taskfile.yml:240) points at the in-repo copy.
  • :78 (HIGH) — filter="fully_trusted" is a Python 3.12+ kwarg; stock macOS /usr/bin/python3 is 3.9.6 and raises TypeError, breaking the installer on darwin-arm64, a platform this release ships. Drop the kwarg — "fully_trusted" is the legacy default, and the preflight at :56-74 is the real defence.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trust boundary changed since this comment: the installer is executed only from an outer bootstrap bundle whose exact SHA-256 is supplied by the release-stamped host binary and checked before extraction (download verification); artifact signatures are then verified in-process by microvmd, not by this installer. The standalone path issue is fixed by shipping and invoking mecatl-artifact-digest-$platform beside the manifest (installer, digest check). The Python 3.12-only filter="fully_trusted" call remains, however, so that folded portability point is still open; the development fixture is not production release-signature proof.

Comment thread Taskfile.yml Outdated
# authn/oidc is the opt-in caller-identity adapter module (ADR 0206),
# separate from both the dependency-free engine and provider modules.
- cd authn/oidc && go build ./...
# environment/microvm is the opt-in local microVM runtime module (ADR 0108).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 3 · Standards · HARD VIOLATION] Wrong ADR number.

ADR 0108 is Read skill assets on demand by logical name; the microVM ADR is 0224. docs/design/README.md treats a citation as a load-bearing claim, and docs/lint does not scan Taskfile.yml, so nothing catches this.

Suggested change
# environment/microvm is the opt-in local microVM runtime module (ADR 0108).
# environment/microvm is the opt-in local microVM runtime module (ADR 0224).

A grep of ADR 0108|adr/0108|0108- across .github/, Taskfile.yml, docs/ and user-docs/ confirms this and user-docs/deployment/microvm-environments.md:151 are the only two mis-citations — every other 0108 hit is a legitimate reference to the real ADR.

Source: Standards axis (also independently found by the Spec axis)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original 0108 citation was removed, but the current Taskfile now says ADR 0350 while the accepted repository-scoped MicroVM decision is ADR 0352 (Taskfile, ADR 0352). The citation defect therefore still exists after renumbering, and I’m leaving this thread open for the mechanical correction to 0352.


For configuration and recovery details, see the
[operator guide](https://github.com/stacklok/mecatl/blob/main/docs/usage/microvm-environments.md)
and [ADR 0108](https://github.com/stacklok/mecatl/blob/main/docs/adr/0108-microvm-execution-environments.md).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 3 · Standards + Spec · HARD VIOLATION] Dead link to an ADR file that does not and will not exist — and both CI gates miss it.

docs/adr/0108-microvm-execution-environments.md is not a file; 0108 is 0108-on-demand-logical-skill-assets.md. The correct ADR is 0224.

Because this is an absolute external GitHub URL, matlatl check . --strict treats it as external and Docusaurus onBrokenLinks: 'throw' does not resolve it either — so neither task docs:check nor task site:build can catch it. That makes AC8.5's proof ("verify: inspection — task docs and task site:build prove the documented surface is linked") vacuous for the one link an operator follows to reach the trust contract.

Suggested change
and [ADR 0108](https://github.com/stacklok/mecatl/blob/main/docs/adr/0108-microvm-execution-environments.md).
and [ADR 0224](https://github.com/stacklok/mecatl/blob/main/docs/adr/0224-microvm-execution-environments.md).

The neighbouring docs/usage/microvm-environments.md GitHub URL is correct, for reference.

Sources: Standards axis + Spec axis (independently)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed through the documentation relocation rather than by retaining the old link. The canonical operator page is now user-docs/building/deployment/microvm-environments.md, the historical docs/usage page is only a compatibility pointer, and the live architecture references resolve to accepted ADR 0352 (canonical guide, compatibility pointer, ADR).

@@ -0,0 +1,216 @@
# ADR 0224 — Local microVM execution environments

- Status: Proposed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Panel 3 · Standards + Spec · HARD VIOLATION] ADR Status contradicts the shipped state.

This says Proposed (dated 2026-08-14), while docs/acceptance/microvm-execution-environments.md says Status: landed, 2026-08-15, docs/acceptance/README.md says "landed", and the code is merged. Every comparable shipped ADR (0214, 0217, 0218, 0221) is Accepted.

Per AGENTS.md the ADR is the frozen why of shipped behaviour, so shipped work under a Proposed ADR is a documentation-lifecycle contradiction. docs/lint's header gate checks the header exists, not that the value is current, so nothing catches a stale one.

Suggested change
- Status: Proposed
- Status: Accepted

The header is otherwise well-formed (Status / Date / Scope / Supersedes / Superseded by, Context / Decision / Consequences / See also).

Sources: Standards axis + Spec axis (independently)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original status contradiction is resolved: the repository-scoped Linux decision is now ADR 0352 with Status: Accepted (ADR 0352). The Darwin arm64 delta is separately ADR 0351 and deliberately remains Proposed, matching the user documentation’s explicit statement that native Apple Silicon and signed-candidate qualification are still pending (ADR 0351, qualification boundary). The existing acceptance plan was updated in this PR under the user-authorized process waiver rather than creating a second plan.

@JAORMX
JAORMX marked this pull request as draft August 18, 2026 11:46
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch from 9c310d6 to e1365d5 Compare August 19, 2026 20:42
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch 10 times, most recently from 38e2267 to 74253a4 Compare September 3, 2026 07:51
@JAORMX
JAORMX marked this pull request as ready for review September 3, 2026 07:53
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch 5 times, most recently from ab8b47c to 6d65d53 Compare September 10, 2026 07:42
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch 3 times, most recently from df457f1 to 4908e4a Compare September 10, 2026 10:02
@JAORMX JAORMX added the e2e-live Run the live e2e suite on this PR label Sep 11, 2026
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch from 60c673f to 8809053 Compare September 14, 2026 09:35
JAORMX and others added 24 commits September 26, 2026 00:12
Co-Authored-By: mecatl <noreply@stacklok.com>
Co-Authored-By: mecatl <noreply@stacklok.com>
Co-Authored-By: mecatl <noreply@mecatl.dev>
Co-Authored-By: Mecatl <mecatl@users.noreply.github.com>
Co-Authored-By: Mecatl <mecatl@users.noreply.github.com>
Record the approved same-PR lifecycle v4 ownership contract and exact Scenario 5 proofs without implementing it. Update the resource inventory, preserve ADR renumbering, and remove the retired readiness-tracker link.

Co-Authored-By: Mecatl <mecatl@users.noreply.github.com>
Preserve explicit guest-source admission, isolated read evidence, child source lifetimes, and successor ownership. The separately approved daemon acquisition amendment follows in the next implementation step.

Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI Codex <codex@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Validate complete persisted bindings before runtime mutation, reserve exact refs through resolve publication, and unwind abandoned acquisitions without deleting durable worktrees. Keep fork rollback on the originating socket, release cancelled client owners, make authenticated control I/O cancellable, and permit exact pending-delete retries.

Add deterministic authority, publication, cancellation, cleanup, and shutdown regression proofs while preserving standalone module boundaries.

Co-Authored-By: OpenAI <noreply@openai.com>
Retire abandoned resolve reservations under the daemon lifetime context. Start bounded cleanup only after operation pins drain, and propagate cancellation through guest detach without changing public attachment APIs.

Prove automatic unregister after a real pinned workspace call outlives the cleanup timeout using deterministic fake time, including explicit detach and daemon shutdown cancellation.

Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: OpenAI <noreply@openai.com>
Real KVM session creation exposed Git archive permission normalization when a private source checkout uses a different umask from microvmd. Restore the captured regular-file modes without weakening exact-state verification.

Co-Authored-By: OpenAI <noreply@openai.com>
A fresh harness resuming after the compatible local daemon stopped must run the existing readiness path before exact reattachment. Validation and no-filesystem attenuation still precede readiness; no placement or command is replayed.

Co-Authored-By: OpenAI <noreply@openai.com>
Close v4 child, explicit binding, and service owners in lifecycle order, then reacquire after daemon restart. Preserve all real guest assertions and recognize only explicit dirty retention during child cleanup.

Co-Authored-By: OpenAI <noreply@openai.com>
Exact resolve can validate retained artifacts and boot the repository VM. Keep that acquisition cancellable by its caller instead of imposing a 15-second hot-lookup deadline; independent cleanup bounds remain unchanged.

Co-Authored-By: OpenAI <noreply@openai.com>
Distinguish real KVM and live GPT-5.6 Sol functional evidence from deterministic slow-acquisition coverage and unqualified Darwin/release paths.

Co-Authored-By: OpenAI <noreply@openai.com>
Remove obsolete lineage migration and read locking resurrected by merge replay, restore exact discovery acquisition and terminal-title wiring, and retain the original Taskfile wording. Link the MicroVM installation instruction to its canonical page to satisfy the new strict documentation graph gate.

Co-Authored-By: OpenAI <noreply@openai.com>
Co-Authored-By: mecatl <noreply@stacklok.com>
Co-Authored-By: OpenAI <noreply@openai.com>
@JAORMX
JAORMX force-pushed the acc/microvm-execution-environments branch from d5491b7 to a012760 Compare September 25, 2026 21:58
Use private portable socket fixtures, restore captured symlink permissions without following targets, and bind Studio publication to the immutable release-ref gate. Cover umask changes and path substitution, and fix opt-in development lint findings.

Co-Authored-By: OpenAI <noreply@openai.com>
@JAORMX
JAORMX merged commit 178ed3f into main Sep 26, 2026
42 checks passed
@JAORMX
JAORMX deleted the acc/microvm-execution-environments branch September 26, 2026 05:38
This was referenced Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment