Skip to content

Isolated local Stack

The local environment is runnable on macOS with an already-running OrbStack Kubernetes cluster. It starts one disposable namespace containing dedicated runtime and Temporal PostgreSQL storage, Temporal primary/visibility database declarations, MinIO-compatible blob storage, telemetry, and all eight trust-scoped runtime roles: API, orchestration, model, tool, blob, codec, sandbox control, and sandbox host. Each role uses the actual cmd/runtime binary and its one checked operator configuration. Tilt groups their logs and rebuilds stack-scoped copies of the production composition image. This proves binary build, configuration validation, secret-reference injection, readiness, and lifecycle—not the complete public Agent Runtime API, Temporal payload codec behavior, or any example app.

For the explicitly local profile only, the model and tool roles can run the checked-in local-demo-v1 fixture. It has separately named local-only credentials, emits a deterministic research Tool call, and still requires a policy created through the public admin API plus an owner Approval before the tool role can produce its bounded citation artifact. It is topology evidence, not a model-provider integration, Codex subscription path, or production provider claim. CI and production profiles do not mount or configure this fixture.

The local profile explicitly declares the normal ten-minute reset/replay scenario. A second short-expiry scenario exists only for retained local evidence. Both are recorded in private Stack lifecycle state and rejected by CI and production rendering. They do not accept a prompt, Stack-name convention, ambient environment value, or ConfigMap mutation as a selector.

go run ./tools/dev up --stack=workspace-reset-proof --root=. --kubeconfig=/absolute/path/to/kubeconfig --actor=local-operator --fixture-scenario=workspace-approval-reset-v1
go run ./tools/dev up --stack=workspace-expiry-proof --root=. --kubeconfig=/absolute/path/to/kubeconfig --actor=local-operator --fixture-scenario=workspace-approval-expiry-v1

workspace-approval-reset-v1 keeps the normal ten-minute lifetime for a reset/reconnect replay proof. workspace-approval-expiry-v1 uses only the declared short local fixture lifetime for a pending-Approval late-decision proof. Both still emit the same bounded artifact-only fixture response and never execute a workspace service, a Sandbox, or Firecracker.

Main CI invokes the same two-Stack isolation harness against a checksum-pinned, disposable k3d/K3s cluster using the typed ci profile. Its twelve-minute readiness bound includes a clean node’s immutable dependency-image pulls and Temporal startup. The lane must bring both full stacks Ready concurrently, observe three allowed declared-egress connections and three blocked default-deny connections, prove the second stack is unchanged after the first teardown, retain bounded evidence/diagnostics, and delete the cluster. It is Kubernetes-in-container lifecycle and NetworkPolicy evidence, not Linux/KVM or Firecracker evidence.

Each CI attempt owns a generated k3d cluster and registry name derived from the run and attempt IDs. It uses a private kubeconfig and deterministically derived loopback API and registry-port candidates, then availability-checks them before creation. A TOCTOU collision during the subsequent k3d bind fails safely rather than using an OS-selected port or adopting a pre-existing resource. After successful registry creation, it passes the discovered registry addresses to Tilt. The workflow will not reuse or delete a pre-existing resource; cleanup requires the successful-create record and matching immutable Docker container IDs.

just dev-preflight
just dev

The omitted Stack name deterministically derives from the current worktree. To run two worktrees or two explicit stacks at once, use different names:

just dev stack=research-a
just dev stack=research-b

The public Tilt identity is the same in both forms—there is no instance identity:

tilt up -- --stack=research-a

The just wrapper additionally selects orbstack, the explicit ar-<stack> namespace, and an OS-selected dashboard port. It writes private per-worktree state beneath ignored .runtime/dev/; it never changes your Kubernetes current context.

To rerun the containment proof locally against OrbStack and retain evidence:

just two-stack-smoke local orbstack /absolute/path/to/two-stack-evidence.json
just dev-status stack=research-a

The checked-in Stack deliberately contains no runtime-api Service or port-forward command while its pinned image lacks the durable API executable. The source API remains runnable against disposable dependencies, but that is separate from this local Stack and does not imply a deployed public endpoint. Tilt exposes links for declared runtime-role readiness endpoints and these docs.

The checked-in Stack contains only Secret references and their key names. The local materializer enumerates those references from the reviewed local profile, generates random development-only values once per Stack, and stores them in a mode-0600, ignored local file. Each role receives only its declared credential keys. Startup enumerates the known credential-key inventory and refuses a role with any present key that it is not entitled to use. Values never appear in the typed Stack document, logs, health response, or repository. dev-down removes the namespace and those Kubernetes Secrets; remove the ignored local state if you want entirely fresh development credentials next time.

Every runtime role has its own explicit ServiceAccount with token automounting disabled and a default-deny, role-scoped egress NetworkPolicy. The local role images do not call the Kubernetes API. Policies for workloads that resolve a declared Service opt into a narrow DNS rule: UDP and TCP port 53 only to CoreDNS pods in kube-system. It grants no general namespace or Internet egress.

Runtime PostgreSQL, Temporal PostgreSQL, and blob storage each use finite per-Stack persistent claims. Temporal task-queue routing, the Temporal namespace declaration, primary/visibility database declarations, blob prefix, resource labels, Secret names, and image names derive from the same Stack identity.

just dev-reset stack=research-a
just dev-down stack=research-a

dev-reset restarts only the eight declared runtime-role Deployments. Before reset or teardown, the launcher re-reads the target Namespace and requires its UID plus Agent Runtime, Stack, and local-profile labels to match. It refuses a missing, relabelled, or foreign namespace. It never targets default, another Stack, or a cluster-scoped object.

This is local container/Kubernetes infrastructure, not Firecracker evidence or hostile-tenant sandbox isolation. A compile-tested public Go contract and deterministic in-memory Agent kernel now exist, but their HTTP, PostgreSQL, and Temporal production adapters—and all three examples—remain separate implementation work.