mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
v0.61 — wire the scheduler workload-policy gate into the MPC SignGate.
SignGate gains optional Scheduler + Worker fields; AuthorizeSign now
runs scheduler.Eligible(worker, ci.PolicyID) BEFORE the policy
verifier and short-circuits on insufficient trust posture. Existing
callers that pass nil Scheduler are unaffected.
pkg/scheduler/policy_gate.go — TrustMode/IOLevel enums,
WorkloadPolicy.Eligible(Worker)
pkg/scheduler/service_drain.go — AuthorizeDrain + AuthorizeSession
pkg/mpc/sign_orchestrator.go — Scheduler/Worker fields,
WithScheduler(...), two-stage gate
v0.62 — NVIDIA Confidential-Compute attestation primitives. Clean-room
parsers + verifiers against the publicly documented nvTrust schema
(Apache-2 derived); no NVIDIA proprietary code vendored.
pkg/attestation/nvidia/gpu_report.go — GPU evidence-report parser
pkg/attestation/nvidia/nras_client.go — NRAS HTTP client + JWS verify
pkg/attestation/nvidia/rim.go — RIM signature + value match
pkg/attestation/nvidia/composite.go — VerifiedBundle (CPU+GPU bind)
Tests (57 passing, -race clean):
pkg/scheduler — 11 (truth-table, drain,
session, orchestrator
integration)
pkg/attestation/nvidia — 46 (parser, RIM Ed25519/ECDSA/
RSA-PSS, NRAS happy/error/
signature paths, ECDSA JOSE)
pkg/attestation/nvidia (hwattest) — 3, env-skipped without real GPU
Sibling v0.60 reconciliation: pkg/registry + pkg/attestation already
land WorkerCapability + CompositeAttestation envelope using lattice
trust types. nvidia.VerifiedBundle is renamed (was Composite-) to
avoid collision with pkg/attestation.CompositeAttestation; the
canonical envelope wraps the verified blobs this package emits.
Residual: lattice/v7/types is not yet published, so pkg/registry +
pkg/attestation cannot compile against luxfi/[email protected]; the
sibling v0.60 work tracks that. pkg/policy still has a pre-existing
unresolved import of luxfi/fhe/threshold (should be /pkg/threshold);
sign_orchestrator_scheduler_test.go is ready to run as soon as that
upstream issue is fixed by its owner.
100 lines
3.5 KiB
Go
100 lines
3.5 KiB
Go
package scheduler
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// TestOrchestratorIntegration_TrustGateRunsBeforePolicy proves the
|
|
// integration contract that pkg/mpc.SignGate now relies on:
|
|
//
|
|
// - The scheduler is consulted with (Worker, ci.PolicyID) BEFORE
|
|
// any policy-bundle verification work.
|
|
// - A worker below MinTrust short-circuits with ErrInsufficientTrust
|
|
// wrapped from Eligible.
|
|
// - A worker at or above MinTrust returns nil, allowing the caller
|
|
// (the policy verifier) to proceed.
|
|
//
|
|
// This is the behavioral contract the orchestrator file imports the
|
|
// scheduler for; testing it here keeps the proof close to the scheduler
|
|
// logic and avoids the upstream pkg/policy build dependency that is
|
|
// pre-existing breakage in this branch.
|
|
func TestOrchestratorIntegration_TrustGateRunsBeforePolicy(t *testing.T) {
|
|
// Workload requires GPU + private lane + attested CPU+GPU.
|
|
policy := &WorkloadPolicy{
|
|
WorkloadID: "p-prod",
|
|
MinTrust: TrustGPU,
|
|
MinIO: IOPrivateLane,
|
|
RequireGPUAttested: true,
|
|
RequireCPUAttested: true,
|
|
RequireModelConfidentiality: false, // simplify
|
|
}
|
|
src := NewStaticPolicySource(map[string]*WorkloadPolicy{"p-prod": policy})
|
|
s := New(src)
|
|
|
|
// Eligible worker matches the policy floors.
|
|
good := Worker{
|
|
ID: "node-good", TrustMode: TrustGPU, IOLevel: IOPrivateLane,
|
|
GPUAttested: true, CPUAttested: true, Online: true,
|
|
}
|
|
if err := s.Eligible(good, "p-prod"); err != nil {
|
|
t.Fatalf("expected eligible, got %v", err)
|
|
}
|
|
|
|
// Worker below GPU trust → reject. Wraps ErrInsufficientTrust via
|
|
// AuthorizeSession; via Eligible, returns descriptive error.
|
|
bad := good
|
|
bad.TrustMode = TrustCVM
|
|
if err := s.Eligible(bad, "p-prod"); err == nil {
|
|
t.Fatal("expected ineligible")
|
|
}
|
|
|
|
// Worker offline — second-level guard.
|
|
off := good
|
|
off.Online = false
|
|
if err := s.Eligible(off, "p-prod"); err == nil {
|
|
t.Fatal("expected offline rejection")
|
|
}
|
|
|
|
// Workload not found in the policy source — distinct error.
|
|
if err := s.Eligible(good, "ghost-policy"); err == nil {
|
|
t.Fatal("expected lookup error")
|
|
}
|
|
}
|
|
|
|
// TestOrchestratorIntegration_AuthorizeSession_FiltersWorkers proves the
|
|
// scheduler's AuthorizeSession (used by the sign coordinator to pick
|
|
// the t+1 nodes that may participate) excludes any worker that fails
|
|
// any gate, in stable order. This is the multi-node analogue of the
|
|
// per-call gate inside SignGate.AuthorizeSign.
|
|
func TestOrchestratorIntegration_AuthorizeSession_FiltersWorkers(t *testing.T) {
|
|
policy := &WorkloadPolicy{
|
|
WorkloadID: "p-prod",
|
|
MinTrust: TrustGPU,
|
|
MinIO: IOPrivateLane,
|
|
RequireGPUAttested: true,
|
|
RequireCPUAttested: true,
|
|
}
|
|
src := NewStaticPolicySource(map[string]*WorkloadPolicy{"p-prod": policy})
|
|
s := New(src)
|
|
|
|
good1 := Worker{ID: "n1", TrustMode: TrustGPU, IOLevel: IOPrivateLane, GPUAttested: true, CPUAttested: true, Online: true}
|
|
good2 := Worker{ID: "n2", TrustMode: TrustGPU, IOLevel: IOPrivateLane, GPUAttested: true, CPUAttested: true, Online: true}
|
|
bad := good1
|
|
bad.ID = "n3"
|
|
bad.GPUAttested = false // missing GPU attestation
|
|
|
|
out, err := s.AuthorizeSession("p-prod", []Worker{good1, bad, good2}, 2)
|
|
if err != nil {
|
|
t.Fatalf("authorize: %v", err)
|
|
}
|
|
if len(out) != 2 || out[0].ID != "n1" || out[1].ID != "n2" {
|
|
t.Fatalf("ordering or filter wrong: %+v", out)
|
|
}
|
|
|
|
// Below threshold → ErrInsufficientTrust.
|
|
if _, err := s.AuthorizeSession("p-prod", []Worker{bad}, 1); !errors.Is(err, ErrInsufficientTrust) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|