picatz / picatz/flowstate

Design: runners, placement, and isolation — where a step runs, what contains it, and why disruption is already survivable

Open
#156 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

deployment design kind/design-record
Dominant language
Go
Stars
9
Forks
0
Avg merge
3h 3m
Merged PRs (30d)
509

Description

CI-shaped workloads need what GHA calls runners: pools of different machines (OS, arch, GPU, network position), steps routed to the right pool, hostile code contained, and the whole thing surviving a machine dying mid-run. Flowstate sits on the one substrate where most of this is already true by construction — the design work is surfacing Temporal's primitives without breaking the file-meaning invariant, plus an isolation ladder the field never built properly.

1. Task queues are the runner-pool primitive — this is #133's row, elevated

Temporal already routes work by task queue: workers subscribe, activities land on whichever subscribed worker is alive. A "runner pool" is a task queue with a name. What's missing is only the spelling:

- id: build-arm
  runs_on: linux-arm64        # a pool name the deployment defines
  container.run: { image: "ghcr.io/acme/builder@sha256:…" }

The invariant question, answered up front: does runs_on: make a file's meaning deployment-dependent? No — the same way plugin tasks don't. The file names a requirement; a deployment either satisfies it (workers on that queue) or the run fails loudly when nothing serves the queue (with a schedule-to-start–shaped diagnostic naming the pool — the run-that-cannot-continue-must-fail invariant, not a silent hang; note today's design deliberately avoids ScheduleToStartTimeout, so "nothing serves this pool" needs its own honest detection). What the step does never varies by placement; only whether this deployment can run it.

Capabilities beat hostnames: a pool names what a step needs (linux-arm64, gpu, in-vpc-prod), never which machine. Deployment maps names to workers; flow validate stays silent about pools (a deployment fact, per the diagnostics rule) while flow run fails closed.

2. The isolation ladder — each rung explicit, none pretended

Rung What When Status
in-process built-in tasks (http, log) trusted, engine-authored today
subprocess plugins — own process, own deps, bounded transport trusted-ish code, operator-vetted (#146 pinning) today
container container.run — image by digest, bounded output, no ambient anything arbitrary-but-yours code: builds, tests, tools design here
microVM / VM Firecracker-class, or a dedicated queue of sacrificial workers genuinely hostile code — PR builds from forks design here, later

The GHA sin to not repeat, precisely: arbitrary code with ambient credentials on a long-lived shared machine. The answers are already house rules — secrets reach only declared inputs (the plugin-secrets design generalizes: a container gets exactly the env/files the step declares, nothing ambient); egress from a containerized step goes through netpolicy's posture (network-namespace design needed — the one genuinely new mechanism here); runners for hostile tiers are ephemeral by policy, one workload per instance, which is also #145's provenance rule enforced physically.

container.run should be a plugin (the docker/podman/k8s families differ; #149's factoring applies — container.* portable core, k8s.* specifics), but the isolation contract it must meet is engine-defined, here.

3. Disruption: mostly already solved — say so, then close the gaps

What Temporal gives by construction, worth stating in docs because CI people don't believe it: a worker dying mid-step is a retry on another worker, not a failed pipeline — history survives, position survives (frames), heartbeats (#143) make the takeover prompt instead of timeout-slow, and a run in flight during a deploy of new workers continues pinned to its version (worker versioning, already enforced). "Self-healing" is not a feature to build; it is the substrate's default that the whole field's CI systems lack.

The honest gaps: (a) a step whose side effects are half-done when the worker dies — that's retry_on_unknown_outcome semantics, per-task, already the house pattern, and containers must adopt it; (b) local scratch dying with the worker — already the rule (#145: one invocation, content results); (c) "no worker will ever come" detection per §1.

4. Spanning machines is the default, not a feature

Steps already run wherever a subscribed worker is; with runs_on: it becomes deliberate: build on linux-amd64, sign on the HSM-attached pool, deploy from in-vpc-prod. Data crosses only as values through history (bounded) or offloaded payloads (#150) — never a shared filesystem, which is what makes the spanning correct rather than accidental. A multi-OS matrix is for_each over pools once runs_on: accepts an expression.

Sequencing

runs_on: (per-step queue) is small and unblocks everything — schema field, options plumbing, the fail-loud detection, docs. The container task rides #149's plugin factoring plus the isolation contract here. MicroVM tier is design-only until someone needs it. The Argo/Tekton findings from the workflow-systems research in flight feed §2 directly.

Related: #133 (task queues row — this is its design), #100/#146/#151 (isolation posture, pinning, remote), #145/#150 (data between machines), #153/#155 (the DX these must not degrade).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the schema and options plumbing for per-step runs_on task queues, then trace flow validate and flow run to understand where deployment capabilities and fail-loud behavior belong. Review the task-queue, retry, payload, and isolation contracts described in #133, #145, #149, and #150. Done means a bounded design for runs_on, no validation-time deployment assumptions, honest missing-worker failure, and explicit container isolation requirements.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
ci-cd, devops, distributed-systems, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.