Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a36485b
feat(signal): add generic certification controller
chokevin Oct 20, 2026
11432b0
feat(signal): support grouped node certification
chokevin Oct 20, 2026
aec313e
signal: add participant backend adapter
chokevin Oct 20, 2026
c3e1d17
signal: fix adapter formatting
chokevin Oct 21, 2026
53abeee
signal: satisfy pinned adapter formatting
chokevin Oct 21, 2026
2f55243
signal: add typed workload execution policy
chokevin Oct 21, 2026
82dad3f
signal: support participant host networking
chokevin Oct 21, 2026
93886b4
fix(signal): align script digest contract
chokevin Oct 22, 2026
366ea2f
fix(signal): request Kueue topology
chokevin Oct 22, 2026
5ffa1af
fix(signal): preserve diagnostic results
chokevin Oct 22, 2026
da72b22
feat(signal): add runtime execution contract
chokevin Oct 22, 2026
96b2eb8
fix(signal): group deadline parameters
chokevin Oct 22, 2026
a8b50dd
fix(signal): guard optional immutable fields
chokevin Oct 22, 2026
6acf82b
fix(signal): preserve backend failure through cleanup
chokevin Oct 22, 2026
ecffc97
fix(signal): fence MPI worker readiness
chokevin Oct 22, 2026
ab56f86
fix(signal): probe MPI workers over SSH
chokevin Oct 22, 2026
f53f7f6
fix(signal): harden MPI launcher recovery
chokevin Oct 22, 2026
409d4a8
fix(signal): preserve pre-cleanup backend evidence
chokevin Oct 22, 2026
cafcce5
docs(signal): define evidence crash boundary
chokevin Oct 22, 2026
c08ebd2
fix(signal): recover canonical workflow outputs
chokevin Oct 22, 2026
b94817a
feat(signal): group certification checks in one run
chokevin Oct 22, 2026
4a196c3
fix(signal): resolve grouped workflow template inputs
chokevin Oct 22, 2026
69cb1b0
fix(signal): use valid terminal DAG dependencies
chokevin Oct 22, 2026
e25d6aa
test(signal): satisfy grouped workflow lint
chokevin Oct 22, 2026
e810801
fix(signal): run grouped tasks with adapter service account
chokevin Oct 22, 2026
b7bebf2
refactor(signal): trim certification to production path
chokevin Oct 23, 2026
3d85dde
fix(signal): fence certification cleanup and validate execution evidence
chokevin Oct 23, 2026
9100fa3
fix(signal): default backend references to the run namespace
chokevin Oct 23, 2026
18f1b69
fix(signal): preserve standalone certification semantics
chokevin Oct 23, 2026
1eaa761
fix(signal): guard one-time certification invariants
chokevin Oct 23, 2026
2d34a6e
fix(signal): preserve certification audit state
chokevin Oct 23, 2026
9a2a1b7
refactor(signal): narrow certification contract
chokevin Oct 24, 2026
a6cc036
fix(signal): harden one-time certification lifecycle
chokevin Oct 24, 2026
f33e89b
fix(signal): satisfy lifecycle lint checks
chokevin Oct 24, 2026
dae1bbb
fix(signal): align lint spacing
chokevin Oct 24, 2026
76e30eb
fix(signal): separate cancellation evidence steps
chokevin Oct 24, 2026
076cdc1
refactor(signal): snapshot current script contents
chokevin Oct 26, 2026
74a2609
refactor(signal): certify one participant group
chokevin Oct 26, 2026
fb23ece
fix(signal): separate participant encoding
chokevin Oct 26, 2026
d7faf3e
refactor(signal): minimize certification check contract
chokevin Oct 26, 2026
6d79e31
feat(signal): add baseline runner image
chokevin Oct 26, 2026
6b18dbf
ci(signal): validate runner image
chokevin Oct 26, 2026
d5c188e
fix(signal): remove stale JSON decoder
chokevin Oct 26, 2026
221da73
style(signal): format adapter cleanup
chokevin Oct 26, 2026
feee8be
refactor(signal): split runner image into separate PR
chokevin Oct 26, 2026
21cc18d
fix(signal): enforce executable participant limit
chokevin Oct 26, 2026
5406064
refactor(signal): make runner deployment-owned
chokevin Oct 26, 2026
0fe2b0c
fix(signal): coordinate MPI checks across participants
chokevin Oct 26, 2026
9bc13e5
style(signal): satisfy coordinated MPI lint
chokevin Oct 26, 2026
12a1455
style(signal): separate result expansion loop
chokevin Oct 26, 2026
8d013d3
fix(signal): harden deployment contracts
chokevin Oct 27, 2026
957c9c7
refactor(signal): minimize certification contracts
chokevin Oct 27, 2026
7629ebc
style(signal): satisfy contract cleanup lint
chokevin Oct 27, 2026
ec57145
feat(signal): pin immutable script bundles
chokevin Oct 27, 2026
18b6d17
style(signal): satisfy bundle digest lint
chokevin Oct 27, 2026
b9fe9eb
fix(signal): mount adapter service account token
chokevin Oct 28, 2026
0d5413b
fix(signal): quiesce retained MPI workers
chokevin Oct 28, 2026
e9eb9d1
style(signal): satisfy MPI cleanup lint
chokevin Oct 28, 2026
7741ef0
fix(signal): render valid multi-node MPI affinity
chokevin Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ __debug*
tmp/
.tmp/
dist/
deploy/signal/rendered/

# Docker
.dockerignore
Expand Down
73 changes: 67 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ MACHINE_OPS_CONTROLLER_NAME ?= machine-ops-controller
MACHINE_OPS_PROVIDER ?=
MACHINE_OPS_SITE ?=

PROJECT_SIGNAL_CONTROLLER_BIN=bin/project-signal-controller
PROJECT_SIGNAL_CONTROLLER_CMD=./cmd/project-signal-controller
SIGNAL_CONTROLLER_IMAGE ?= example.invalid/project-signal-controller@sha256:0000000000000000000000000000000000000000000000000000000000000000
PROJECT_SIGNAL_ADAPTER_BIN=bin/project-signal-adapter
PROJECT_SIGNAL_ADAPTER_CMD=./cmd/project-signal-adapter
SIGNAL_ADAPTER_IMAGE ?= example.invalid/project-signal-adapter@sha256:0000000000000000000000000000000000000000000000000000000000000000
SIGNAL_ADAPTER_BUILD_IMAGE ?= example.invalid/project-signal-adapter:replace-me
SIGNAL_CERTIFICATION_RUNNER_IMAGE ?= example.invalid/project-signal-runner@sha256:0000000000000000000000000000000000000000000000000000000000000000
SIGNAL_NAMESPACE ?= project-signal
SIGNAL_MANIFEST_TEMPLATES_DIR := deploy/signal
SIGNAL_MANIFEST_RENDERED_DIR := deploy/signal/rendered

METALMAN_BIN=bin/metalman
METALMAN_CMD=./cmd/metalman
NETBOOT_IMAGE ?= $(CONTAINER_REGISTRY)/netboot:$(VERSION_TAG)
Expand Down Expand Up @@ -233,14 +245,14 @@ NET_FRONTEND_CACHE_FILE := $(NET_FRONTEND_DIST_DIR)/.frontend-build-key
# Frontend build toggle (dev builds produce unminified output with sourcemaps).
REACT_DEV ?= false

.PHONY: all help fmt lint lint-actions test build vulncheck check-deps kubectl-unbounded kubectl-unbounded-build install-tools install-protoc install-helm generate kubectl-unbounded forge relctl relctl-build agent-artifacts-builder agent-artifacts-builder-build orcadev unbounded-agent machina machina-build machina-oci machina-oci-push machina-manifests machine-ops-controller machine-ops-controller-build machine-ops-controller-oci machine-ops-controller-oci-push machine-ops-manifests metalman metalman-build metalman-oci metalman-oci-push unbounded-operator unbounded-operator-build unbounded-operator-manifests playpen-manifests e2e-gantry e2e-playpen e2e-racer gomod docs-serve unbounded-net-controller unbounded-net-controller-build unbounded-net-node unbounded-net-node-build unbounded-net-routeplan-debug unping unping-build unroute unroute-build license-check notice notice-check gantry gantry-build gantry-manifests inventory-manifests
.PHONY: all help fmt lint lint-actions test build vulncheck check-deps kubectl-unbounded kubectl-unbounded-build install-tools install-protoc install-helm generate kubectl-unbounded forge relctl relctl-build agent-artifacts-builder agent-artifacts-builder-build orcadev unbounded-agent machina machina-build machina-oci machina-oci-push machina-manifests machine-ops-controller machine-ops-controller-build machine-ops-controller-oci machine-ops-controller-oci-push machine-ops-manifests project-signal-controller project-signal-controller-build project-signal-adapter project-signal-adapter-build signal-manifests image-project-signal-controller-local image-project-signal-adapter-local metalman metalman-build metalman-oci metalman-oci-push unbounded-operator unbounded-operator-build unbounded-operator-manifests playpen-manifests e2e-gantry e2e-playpen e2e-racer gomod docs-serve unbounded-net-controller unbounded-net-controller-build unbounded-net-node unbounded-net-node-build unbounded-net-routeplan-debug unping unping-build unroute unroute-build license-check notice notice-check gantry gantry-build gantry-manifests inventory-manifests
.PHONY: net-frontend net-frontend-clean net-ebpf-build net-ebpf-generate net-ebpf-verify net-manifests gantry-chart-lint gantry-chart-package release-bom release-manifests unbounded-operator-release-manifest
.PHONY: image-machina-local image-token-refresher-local image-machine-ops-controller-local image-metalman-local image-unbounded-operator-local image-unbounded-operator-push image-playpen-local image-net-controller-local image-net-node-local image-gantry-local image-gantry-push images-local
.PHONY: image-net-controller-push image-net-node-push images-net-all images-net-all-push

##@ General

all: kubectl-unbounded forge relctl machina machine-ops-controller token-refresher unbounded-operator unbounded-net-controller unbounded-net-node unbounded-ipam unbounded-net-routeplan-debug unping unroute gantry ## Build all binaries (default)
all: kubectl-unbounded forge relctl machina machine-ops-controller project-signal-controller project-signal-adapter token-refresher unbounded-operator unbounded-net-controller unbounded-net-node unbounded-ipam unbounded-net-routeplan-debug unping unroute gantry ## Build all binaries (default)

help: ## Show this help
@echo ""
Expand Down Expand Up @@ -292,6 +304,9 @@ help: ## Show this help
@echo " unbounded-agent Build unbounded-agent (linux)"
@echo " machina | machina-build Build machina controller (with/without lint/test)"
@echo " machine-ops-controller Build machine-ops-controller"
@echo " project-signal-controller Build Project Signal controller"
@echo " project-signal-adapter Build Project Signal participant adapter"
@echo " signal-manifests Render Project Signal manifests"
@echo " metalman | metalman-build Build metalman controller (with/without lint/test)"
@echo " unbounded-operator | unbounded-operator-build Build the top-level Site operator"
@echo " unbounded-net-controller Build net controller"
Expand All @@ -313,6 +328,8 @@ help: ## Show this help
@echo " image-machina-local Build machina image with \$$(CONTAINER_ENGINE)"
@echo " image-token-refresher-local Build token-refresher image"
@echo " image-machine-ops-controller-local Build machine-ops-controller image"
@echo " image-project-signal-controller-local Build Project Signal controller image"
@echo " image-project-signal-adapter-local Build Project Signal adapter image"
@echo " image-metalman-local Build metalman image"
@echo " image-unbounded-operator-local Build unbounded-operator image"
@echo " image-unbounded-operator-push Build and push unbounded-operator image"
Expand Down Expand Up @@ -547,13 +564,13 @@ lint-actions: ## Run actionlint over .github/workflows
ifdef CI
# In CI each job is independent; skip chained prerequisites.

test: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests with race detector
test: machina-manifests token-refresher-manifests machine-ops-manifests signal-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests with race detector
$(GOTEST) -race ./...

else
# Locally, chain test -> lint for convenience.

test: lint machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests (implies lint)
test: lint machina-manifests token-refresher-manifests machine-ops-manifests signal-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run all tests (implies lint)
$(GOTEST) ./...

endif
Expand Down Expand Up @@ -661,13 +678,13 @@ image-racer-object-local: ## Build the racer-object container image locally (sin
-f ./images/racer-object/Containerfile .
$(call trivy-maybe,$(RACER_OBJECT_IMAGE))

build: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Build all Go packages
build: machina-manifests token-refresher-manifests machine-ops-manifests signal-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Build all Go packages
$(GOBUILD) ./...

generate: install-protoc ## Run go generate for API types (deepcopy, CRDs) and protobuf
PATH="$(PROTOC_DIR)/bin:$$PATH" $(GOCMD) generate $(GO_PACKAGES)

vulncheck: machina-manifests token-refresher-manifests machine-ops-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run govulncheck; fails only on reachable vulnerabilities that have a final-release fix
vulncheck: machina-manifests token-refresher-manifests machine-ops-manifests signal-manifests playpen-manifests net-manifests unbounded-operator-manifests gantry-manifests ## Run govulncheck; fails only on reachable vulnerabilities that have a final-release fix
@# The JSON stream is the documented programmatic interface. The gate owns
@# the verdict, so govulncheck is not asked for one: in JSON mode it exits 0
@# whether or not it found anything, and a non-zero exit here means the scan
Expand Down Expand Up @@ -1085,6 +1102,36 @@ machine-ops-controller-oci: image-machine-ops-controller-local ## Alias for imag
machine-ops-controller-oci-push: machine-ops-controller-oci ## Build and push the machine-ops-controller image
$(CONTAINER_ENGINE) push $(MACHINE_OPS_CONTROLLER_IMAGE)

project-signal-controller-build: signal-manifests ## Build Project Signal controller without lint/test
@mkdir -p bin
$(GOBUILD) -trimpath -ldflags '$(STAMP_LDFLAGS)' -o $(PROJECT_SIGNAL_CONTROLLER_BIN) $(PROJECT_SIGNAL_CONTROLLER_CMD)

project-signal-controller: test project-signal-controller-build ## Build Project Signal controller (implies test)

project-signal-adapter-build: signal-manifests ## Build Project Signal adapter without lint/test
@mkdir -p bin
$(GOBUILD) -trimpath -ldflags '$(STAMP_LDFLAGS)' -o $(PROJECT_SIGNAL_ADAPTER_BIN) $(PROJECT_SIGNAL_ADAPTER_CMD)

project-signal-adapter: test project-signal-adapter-build ## Build Project Signal adapter (implies test)

image-project-signal-controller-local: ## Build Project Signal controller image locally
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t project-signal-controller:$(VERSION_TAG) -t $(SIGNAL_CONTROLLER_IMAGE) \
-f ./images/project-signal-controller/Containerfile .
$(call trivy-maybe,$(SIGNAL_CONTROLLER_IMAGE))

image-project-signal-adapter-local: ## Build Project Signal adapter image locally
$(CONTAINER_ENGINE) build \
--build-arg VERSION=$(VERSION) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t project-signal-adapter:$(VERSION_TAG) -t $(SIGNAL_ADAPTER_BUILD_IMAGE) \
-f ./images/project-signal-adapter/Containerfile .
$(call trivy-maybe,$(SIGNAL_ADAPTER_BUILD_IMAGE))

MACHINA_NAMESPACE ?= $(UNBOUNDED_NAMESPACE)
MACHINA_API_SERVER_ENDPOINT ?=
MACHINA_MANIFEST_TEMPLATES_DIR := deploy/machina
Expand Down Expand Up @@ -1157,6 +1204,20 @@ machine-ops-manifests: ## Render machine-ops-controller manifests into deploy/ma
--set APIServerEndpoint=$(MACHINE_OPS_API_SERVER_ENDPOINT)
@echo "Rendered machine-ops manifests into $(MACHINE_OPS_MANIFEST_RENDERED_DIR) (image: $(MACHINE_OPS_CONTROLLER_IMAGE))"

signal-manifests: ## Render Project Signal manifests into deploy/signal/rendered
@mkdir -p $(SIGNAL_MANIFEST_RENDERED_DIR)/crd
@find $(SIGNAL_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
$(GOCMD) run ./hack/cmd/render-manifests \
--templates-dir $(SIGNAL_MANIFEST_TEMPLATES_DIR) \
--output-dir $(SIGNAL_MANIFEST_RENDERED_DIR) \
--set Namespace=$(SIGNAL_NAMESPACE) \
--set ControllerImage=$(SIGNAL_CONTROLLER_IMAGE) \
--set AdapterImage=$(SIGNAL_ADAPTER_IMAGE) \
--set RunnerImage=$(SIGNAL_CERTIFICATION_RUNNER_IMAGE)
@mkdir -p $(SIGNAL_MANIFEST_RENDERED_DIR)/crd
@cp $(SIGNAL_MANIFEST_TEMPLATES_DIR)/crd/*.yaml $(SIGNAL_MANIFEST_RENDERED_DIR)/crd/
@echo "Rendered Project Signal manifests into $(SIGNAL_MANIFEST_RENDERED_DIR) (image: $(SIGNAL_CONTROLLER_IMAGE))"

playpen-manifests: ## Render playpen operator and runner manifests into deploy/playpen/rendered
@mkdir -p $(PLAYPEN_MANIFEST_RENDERED_DIR)
@find $(PLAYPEN_MANIFEST_RENDERED_DIR) -mindepth 1 -not -name .gitignore -delete
Expand Down
105 changes: 105 additions & 0 deletions api/signal/v1alpha1/certificationprofile_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type CheckID string

// +kubebuilder:validation:XValidation:rule="duration(self.backendActiveDeadline) > duration('0s')",message="backendActiveDeadline must be positive"
// +kubebuilder:validation:XValidation:rule="self.backend != 'MPIJob' || (has(self.gpuCount) && self.gpuCount > 0)",message="MPIJob checks require a positive gpuCount"
type CertificationCheck struct {
// ID and Order are derived from the check's position and are never serialized.
ID CheckID `json:"-"`
Order int32 `json:"-"`
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[-._a-zA-Z0-9]+$`
ScriptKey string `json:"scriptKey"`
Backend ExecutionBackend `json:"backend"`
WorkloadPolicy WorkloadPolicy `json:"workloadPolicy,omitempty"`
// +kubebuilder:validation:Minimum=0
GPUCount int32 `json:"gpuCount,omitempty"`
BackendActiveDeadline metav1.Duration `json:"backendActiveDeadline"`
}

// +kubebuilder:validation:Enum=Pod;MPIJob
type ExecutionBackend string

const (
ExecutionBackendPod ExecutionBackend = "Pod"
ExecutionBackendMPIJob ExecutionBackend = "MPIJob"
)

// +kubebuilder:validation:XValidation:rule="(has(self.selector.matchLabels) && size(self.selector.matchLabels) > 0) || (has(self.selector.matchExpressions) && size(self.selector.matchExpressions) > 0)",message="selector must contain at least one matchLabels or matchExpressions term"
type ParticipantPolicy struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=8
Count int32 `json:"count"`

Selector metav1.LabelSelector `json:"selector"`
}

// +kubebuilder:validation:XValidation:rule="self.mountPath.startsWith('/') && !self.mountPath.contains('//') && !self.mountPath.contains('/./') && !self.mountPath.contains('/../') && !self.mountPath.endsWith('/.') && !self.mountPath.endsWith('/..')",message="mountPath must be a clean absolute path"
// +kubebuilder:validation:XValidation:rule="self.mountPath != '/scripts' && self.mountPath != '/results'",message="mountPath must not replace a reserved Project Signal mount"
// +kubebuilder:validation:XValidation:rule="(self.path == '/proc' || self.path.startsWith('/proc/') || self.path == '/sys' || self.path.startsWith('/sys/') || self.path == '/var/lib/project-signal' || self.path.startsWith('/var/lib/project-signal/')) && !self.path.contains('//') && !self.path.contains('/./') && !self.path.contains('/../') && !self.path.endsWith('/.') && !self.path.endsWith('/..')",message="path must be a clean absolute path under /proc, /sys, or /var/lib/project-signal"
type WorkloadHostPath struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=4096
MountPath string `json:"mountPath"`
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=4096
Path string `json:"path"`
}

type WorkloadPolicy struct {
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
Privileged bool `json:"privileged,omitempty"`
IPCLock bool `json:"ipcLock,omitempty"`
HostNetwork bool `json:"hostNetwork,omitempty"`
// +kubebuilder:validation:MaxItems=16
// +listType=map
// +listMapKey=mountPath
HostPaths []WorkloadHostPath `json:"hostPaths,omitempty"`
}

type ScriptBundleSource struct {
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
Name string `json:"name"`
// Digest is the aggregate algorithm-qualified SHA-256 digest derived from
// the selected script bytes in check order.
// +kubebuilder:validation:Pattern=`^sha256:[0-9a-f]{64}$`
Digest string `json:"digest"`
}

type CertificationProfileSpec struct {
Participants ParticipantPolicy `json:"participants"`
ScriptBundle ScriptBundleSource `json:"scriptBundle"`
// +kubebuilder:validation:MinItems=15
// +kubebuilder:validation:MaxItems=15
Checks []CertificationCheck `json:"checks"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,shortName=certprofile
// +kubebuilder:printcolumn:name="Checks",type=integer,JSONPath=`.spec.checks.size()`
// +kubebuilder:printcolumn:name="Participants",type=integer,JSONPath=`.spec.participants.count`
type CertificationProfile struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec CertificationProfileSpec `json:"spec"`
}

// +kubebuilder:object:root=true
type CertificationProfileList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CertificationProfile `json:"items"`
}
Loading
Loading