Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 24 additions & 8 deletions go/internal/board/projection.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ type Projection struct {
sessions map[string]sessionEntry
}

// sessionEntry is the board's per-session record: the latest state and the
// agent account it was attributed to (empty when the hub could not resolve the
// binding — the stated DL-167 residual gap).
// sessionEntry is the board's per-session record: the latest state, the agent
// account it was attributed to (empty when the hub could not resolve the
// binding — the stated DL-167 residual gap), and the owning Runner's runtime
// tier and egress posture (stamped from enrollment, so the snapshot path carries
// them too).
type sessionEntry struct {
state compassv1.AgentSessionState
account string
state compassv1.AgentSessionState
account string
tier compassv1.RuntimeTier
egressPosture compassv1.EgressPosture
}

// NewProjection constructs an empty board over the SubscribeEvents bus it fans
Expand Down Expand Up @@ -95,7 +99,12 @@ func (p *Projection) PublishSessionStatus(status *compassv1.AgentSessionStatus)
}
p.mu.Lock()
defer p.mu.Unlock()
p.sessions[status.GetSessionId()] = sessionEntry{state: status.GetState(), account: status.GetAgentAccountId()}
p.sessions[status.GetSessionId()] = sessionEntry{
state: status.GetState(),
account: status.GetAgentAccountId(),
tier: status.GetRuntimeTier(),
egressPosture: status.GetEgressPosture(),
}

p.bus.Publish(&compassv1.SubscribeEventsResponse{
Payload: &compassv1.SubscribeEventsResponse_AgentSessionStatus{
Expand Down Expand Up @@ -151,7 +160,14 @@ func isTerminal(state compassv1.AgentSessionState) bool {
}

// statusOf builds one board entry from a retained session record, carrying the
// DL-167 agent_account_id alongside the state.
// DL-167 agent_account_id and the owning Runner's runtime tier and egress
// posture alongside the state.
func statusOf(sessionID string, entry sessionEntry) *compassv1.AgentSessionStatus {
return &compassv1.AgentSessionStatus{SessionId: sessionID, State: entry.state, AgentAccountId: entry.account}
return &compassv1.AgentSessionStatus{
SessionId: sessionID,
State: entry.state,
AgentAccountId: entry.account,
RuntimeTier: entry.tier,
EgressPosture: entry.egressPosture,
}
}
200 changes: 115 additions & 85 deletions go/internal/gen/compass/v1/runner.pb.go

Large diffs are not rendered by default.

92 changes: 92 additions & 0 deletions go/internal/runner/enroll_identity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//go:build unix

package runner

// Dial declares the Runner's runtime tier and egress posture ONCE at enrollment,
// derived from the engine it drives. This drives the real Dial (interceptor-
// wrapped client) against a recording RunnerService handler and asserts the
// EnrollRequest carried the right wire enums for a HOST engine (tier HOST, egress
// UNENFORCED) — the two facts the hub stamps onto every session status.

import (
"context"
"net/http"
"net/http/httptest"
"sync"
"testing"

"connectrpc.com/connect"

compassv1 "github.com/RigelBuild/compass/go/gen/compass/v1"
compassv1internal "github.com/RigelBuild/compass/go/internal/gen/compass/v1"
"github.com/RigelBuild/compass/go/internal/gen/compass/v1/compassv1internalconnect"
"github.com/RigelBuild/compass/go/internal/runtime"
)

// recordingEnroll is a RunnerService handler that captures the EnrollRequest it
// received, so a Dial test can assert on the tier + posture the client declared.
type recordingEnroll struct {
compassv1internalconnect.UnimplementedRunnerServiceHandler
mu sync.Mutex
req *compassv1internal.EnrollRequest
}

func (r *recordingEnroll) Enroll(_ context.Context, req *connect.Request[compassv1internal.EnrollRequest]) (*connect.Response[compassv1internal.EnrollResponse], error) {
r.mu.Lock()
defer r.mu.Unlock()
r.req = req.Msg
return connect.NewResponse(&compassv1internal.EnrollResponse{Reattached: false}), nil
}

func (r *recordingEnroll) enrolled() *compassv1internal.EnrollRequest {
r.mu.Lock()
defer r.mu.Unlock()
return r.req
}

// recordingEnrollServer stands up an h2c httptest RunnerService serving rec and
// returns its base URL, torn down via t.Cleanup.
func recordingEnrollServer(t *testing.T, rec *recordingEnroll) string {
t.Helper()
path, handler := compassv1internalconnect.NewRunnerServiceHandler(rec)
mux := http.NewServeMux()
mux.Handle(path, handler)
srv := httptest.NewUnstartedServer(mux)
srv.Config.Protocols = cleartextHTTP2()
srv.Start()
t.Cleanup(srv.Close)
return srv.URL
}

// TestDialDeclaresEngineTierAndPosture pins the enrollment declaration: dialing
// with a HOST engine sends runtime_tier=HOST and egress_posture=UNENFORCED on the
// EnrollRequest, derived from the engine via runtime.TierOf / runtime.PostureOf.
//
// Negative control: dropping the two fields from Dial's EnrollRequest (the
// pre-fix shape, runner_id only) reddens both assertions — observed "runtime_tier
// = RUNTIME_TIER_UNSPECIFIED, want RUNTIME_TIER_HOST".
func TestDialDeclaresEngineTierAndPosture(t *testing.T) {
rec := &recordingEnroll{}
url := recordingEnrollServer(t, rec)

// context.Background() is the test root context.
if _, err := Dial(context.Background(), RunnerConfig{
RunnerID: "r-1",
ServerAddr: url,
Token: "tok",
Engine: runtime.NewHostRuntime(t.TempDir()),
}); err != nil {
t.Fatalf("Dial = %v, want success", err)
}

got := rec.enrolled()
if got == nil {
t.Fatal("handler recorded no EnrollRequest")
}
if got.GetRuntimeTier() != compassv1.RuntimeTier_RUNTIME_TIER_HOST {
t.Errorf("EnrollRequest runtime_tier = %v, want RUNTIME_TIER_HOST", got.GetRuntimeTier())
}
if got.GetEgressPosture() != compassv1.EgressPosture_EGRESS_POSTURE_UNENFORCED {
t.Errorf("EnrollRequest egress_posture = %v, want EGRESS_POSTURE_UNENFORCED", got.GetEgressPosture())
}
}
4 changes: 3 additions & 1 deletion go/internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ func Dial(ctx context.Context, cfg RunnerConfig) (*ServerLink, error) {
connect.WithInterceptors(otelInterceptor, &bearerToken{token: cfg.Token}),
)
resp, err := client.Enroll(ctx, connect.NewRequest(&compassv1internal.EnrollRequest{
RunnerId: cfg.RunnerID,
RunnerId: cfg.RunnerID,
RuntimeTier: runtimeTierProto(runtime.TierOf(cfg.Engine)),
EgressPosture: egressPostureProto(runtime.PostureOf(cfg.Engine)),
}))
if err != nil {
return nil, fmt.Errorf("enrolling runner %q: %w", cfg.RunnerID, err)
Expand Down
31 changes: 16 additions & 15 deletions go/internal/runnerhub/binding_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"sync"
"testing"

compassv1 "github.com/RigelBuild/compass/go/gen/compass/v1"
"github.com/RigelBuild/compass/go/internal/fabric"
"github.com/RigelBuild/compass/go/internal/store"
)
Expand Down Expand Up @@ -228,7 +229,7 @@ func TestRestartResolvesPreRestartBindingBothDirections(t *testing.T) {

// A FIRST enroll on a fresh hub (the restart case): reattached is false, so
// enroll does NOT reap the durable rows — they are still valid.
if reattached := hub.enroll(context.Background(), "runner-1", runnerSubject()); reattached {
if reattached := hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED); reattached {
t.Fatal("first enroll on a fresh hub reported reattached=true, want false (no durable reap)")
}

Expand All @@ -255,7 +256,7 @@ func TestFailClosedStoppedNeverSeenAndPostReconnect(t *testing.T) {
hub := newHubOnly()
bindings := newFakeBindingStore()
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

// Never-seen: no binding anywhere.
if acct, ok := hub.accountForSession(context.Background(), "ghost"); ok {
Expand Down Expand Up @@ -283,7 +284,7 @@ func TestFailClosedStoppedNeverSeenAndPostReconnect(t *testing.T) {
t.Fatalf("accountForSession(sess-pre) before reconnect = (%q, %v), want (%s, true)", acct, ok, testAgentAccount)
}
// Runner reconnects: a re-enroll (reattached=true) durably reaps.
if reattached := hub.enroll(context.Background(), "runner-1", runnerSubject()); !reattached {
if reattached := hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED); !reattached {
t.Fatal("second enroll reported reattached=false, want true (a Runner reconnect)")
}
if acct, ok := hub.accountForSession(context.Background(), "sess-pre"); ok {
Expand Down Expand Up @@ -320,7 +321,7 @@ func TestPeerBindingChangeEvictsOtherInstanceCache(t *testing.T) {
bindingsB.seed("sess-old")
hubB.SetSessionBindingStore(bindingsB)
hubB.SetRoutingFabric(routing)
hubB.enroll(context.Background(), "runner-1", runnerSubject())
hubB.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
hubB.bindContainer("cont-new", testAgentAccount)

// B re-points the account onto sess-new: displaces sess-old, publishes the
Expand Down Expand Up @@ -360,7 +361,7 @@ func TestDisplacedSessionResolvesNowhere(t *testing.T) {
hub := newHubOnly()
bindings := newFakeBindingStore()
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

// Bind the account to sess-old.
hub.bindContainer("cont-old", testAgentAccount)
Expand Down Expand Up @@ -406,7 +407,7 @@ func TestAckPathBindingReadNeverRunsUnderSystemRole(t *testing.T) {
hub.SetDeliveryStore(del)
// Enrolled Runner + empty maps (no bindSession) => the ack's account
// resolve is a cache MISS that falls through to the read-through table.
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

if err := hub.Deliver(context.Background(), RunnerEvent{
RunnerSeq: 1, SessionID: "sess-1", Frame: deliveryAckFrame("m1"),
Expand Down Expand Up @@ -434,7 +435,7 @@ func TestAckPathBindingReadNeverRunsUnderSystemRole(t *testing.T) {
hub.SetSessionBindingStore(bindings)
del := newFakeDeliveryStore()
hub.SetDeliveryStore(del)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

if err := hub.Deliver(context.Background(), RunnerEvent{
RunnerSeq: 1, SessionID: "sess-1", Frame: forgeAckFrame("sub-1"),
Expand Down Expand Up @@ -470,7 +471,7 @@ func TestStoreFaultsFallBackWithoutLosingFailClosed(t *testing.T) {
routing := &fakeRoutingFabric{}
hub.SetSessionBindingStore(bindings)
hub.SetRoutingFabric(routing)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

hub.bindContainer("cont-1", testAgentAccount)
hub.promoteSession(context.Background(), "cont-1", "sess-1")
Expand All @@ -488,13 +489,13 @@ func TestStoreFaultsFallBackWithoutLosingFailClosed(t *testing.T) {
hub := newHubOnly()
bindings := newFakeBindingStore()
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
hub.bindContainer("cont-1", testAgentAccount)
hub.promoteSession(context.Background(), "cont-1", "sess-1")

// The reconnect sweep now faults; the in-RAM snapshot must still drive it.
bindings.deleteForRunnerErr = faultErr
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

if acct, ok := hub.accountForSession(context.Background(), "sess-1"); ok {
t.Fatalf("accountForSession(sess-1) = (%q, true) after a reconnect, want fail-closed: a reap fault must not leave a dead session resolvable", acct)
Expand All @@ -507,7 +508,7 @@ func TestStoreFaultsFallBackWithoutLosingFailClosed(t *testing.T) {
bindings.seed("sess-1")
bindings.resolveErr = faultErr
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

if acct, ok := hub.accountForSession(context.Background(), "sess-1"); ok {
t.Fatalf("accountForSession(sess-1) = (%q, true), want fail-closed: a store fault must never resolve", acct)
Expand All @@ -520,7 +521,7 @@ func TestStoreFaultsFallBackWithoutLosingFailClosed(t *testing.T) {
bindings.seed("sess-1")
bindings.reverseErr = faultErr
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

if sess, ok := hub.SessionForAccount(context.Background(), testAgentAccount); ok {
t.Fatalf("SessionForAccount = (%q, true), want fail-closed: a store fault must never resolve", sess)
Expand All @@ -541,7 +542,7 @@ func TestReusedSessionIDConflictIsSwallowed(t *testing.T) {
hub := newHubOnly()
bindings := newFakeBindingStore()
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)

// A row from before the joint restart, under a DIFFERENT account.
bindings.mu.Lock()
Expand Down Expand Up @@ -580,7 +581,7 @@ func TestConcurrentResolveDuringAFaultingReapCannotResurrect(t *testing.T) {
release: make(chan struct{}),
}
hub.SetSessionBindingStore(bindings)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
hub.bindContainer("cont-1", testAgentAccount)
hub.promoteSession(context.Background(), "cont-1", "sess-1")

Expand All @@ -590,7 +591,7 @@ func TestConcurrentResolveDuringAFaultingReapCannotResurrect(t *testing.T) {
done := make(chan struct{})
go func() {
defer close(done)
hub.enroll(context.Background(), "runner-1", runnerSubject())
hub.enroll(context.Background(), "runner-1", runnerSubject(), compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
}()

<-bindings.entered // maps are cleared; the reap is in flight and will fail
Expand Down
12 changes: 6 additions & 6 deletions go/internal/runnerhub/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func TestStartRelaySurfacesAlreadyRunningAsAlreadyExists(t *testing.T) {
hub := newHubOnly()
// Enroll a Runner and bind a send that answers every command with an
// ALREADY_RUNNING error result correlated by the pushed request id.
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
router, _, err := hub.routerFor("any")
if err != nil {
t.Fatalf("routerFor after enroll = %v, want a router", err)
Expand Down Expand Up @@ -130,7 +130,7 @@ func TestStartRelaySurfacesAlreadyRunningAsAlreadyExists(t *testing.T) {
// exercised.
func TestStartRelayReturnsSessionIdOnSuccess(t *testing.T) {
hub := newHubOnly()
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
router, _, _ := hub.routerFor("any")
router.attach(func(cmd *compassv1internal.SessionsResponse) error {
go router.complete(&compassv1internal.SessionsRequest{
Expand All @@ -156,7 +156,7 @@ func TestStartRelayReturnsSessionIdOnSuccess(t *testing.T) {
// from. A non-zero count means the initial-signal path was re-introduced.
func TestStartEmitsNoInitialSignal(t *testing.T) {
hub := newHubOnly()
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
hub.bindContainer("c1", testAgentAccount)
router, _, _ := hub.routerFor("any")
rec := newRecordingSend()
Expand Down Expand Up @@ -187,7 +187,7 @@ func TestStartEmitsNoInitialSignal(t *testing.T) {
// variant, and the typed result flows back.
func TestRemoveRelayReturnsResponseOnSuccess(t *testing.T) {
hub := newHubOnly()
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
router, _, _ := hub.routerFor("any")
var sawRemove bool
router.attach(func(cmd *compassv1internal.SessionsResponse) error {
Expand Down Expand Up @@ -223,7 +223,7 @@ func TestRemoveRelayReturnsResponseOnSuccess(t *testing.T) {
// true after teardown and reddens this.
func TestRemoveClearsContainerBinding(t *testing.T) {
hub := newHubOnly()
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
hub.bindContainer("c1", testAgentAccount)
if !hub.HasContainerBinding("c1") {
t.Fatal("precondition: container c1 should be bound after bindContainer")
Expand All @@ -250,7 +250,7 @@ func TestRemoveClearsContainerBinding(t *testing.T) {
// the pushed request id — the seam the SessionState fallback tests drive.
func attachStatusResponder(t *testing.T, hub *Hub, statuses []*compassv1.AgentSessionStatus) {
t.Helper()
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"})
hub.enroll(context.Background(), "runner-1", store.Subject{Kind: store.SubjectRunner, ID: "runner-1"}, compassv1.RuntimeTier_RUNTIME_TIER_UNSPECIFIED, compassv1.EgressPosture_EGRESS_POSTURE_UNSPECIFIED)
router, _, err := hub.routerFor("any")
if err != nil {
t.Fatalf("routerFor after enroll = %v, want a router", err)
Expand Down
Loading
Loading