Remove legacy client-manager platform path

This commit is contained in:
npc0-hue
2026-09-03 16:40:05 +08:00
parent ec2462a312
commit 80cddbf19d
51 changed files with 382 additions and 4271 deletions
+66
View File
@@ -18,6 +18,7 @@ import (
"browser.local/platform/dto"
"browser.local/platform/repo"
"browser.local/platform/service"
"browser.local/platform/validator"
)
func TestAuthorizedRouterEnforcesAdminAndCrossOwnerBoundaries(t *testing.T) {
@@ -165,6 +166,71 @@ func TestRunHTTPEnvelopeRequiresValidSignatureAndRejectsReplay(t *testing.T) {
}
}
func TestSignedRunArtifactChunkUploadUsesHeaderEnvelope(t *testing.T) {
store := repo.NewMemoryStore()
core := service.NewCoreService(store)
if _, err := core.CreateGamePlugin(validGamePluginRequest().ToDomain()); err != nil {
t.Fatalf("create plugin: %v", err)
}
if _, err := core.CreateRunEndpoint(validRunEndpointRequest().ToDomain()); err != nil {
t.Fatalf("create endpoint: %v", err)
}
if _, err := core.CreateServerInstance(domain.ServerInstance{ID: "server-signed-artifact", PluginID: "server.scum", RunEndpointID: "run-local", Name: "Signed Artifact", State: domain.ServerInstanceStateReady, ConfigVersion: 1}); err != nil {
t.Fatalf("create server: %v", err)
}
if _, err := core.CreateJob(domain.Job{ID: "job-signed-artifact", ServerInstanceID: "server-signed-artifact", RunEndpointID: "run-local", Capability: "process.start", IdempotencyKey: "job-signed-artifact"}); err != nil {
t.Fatalf("create job: %v", err)
}
token := "run-artifact-session-secret"
stamp := time.Now().UTC()
hash := sha256.Sum256([]byte(token))
if err := store.RunControlSessions().Create(domain.RunControlSession{RunEndpointID: "run-local", SessionTokenHash: hex.EncodeToString(hash[:]), Status: domain.AuthSessionStatusActive, Generation: 1, CapabilityFingerprint: "cap-v1", HeartbeatIntervalSeconds: 15, CreatedAt: stamp, UpdatedAt: stamp, ExpiresAt: stamp.Add(time.Hour), RequireSignedRequests: true}); err != nil {
t.Fatalf("create Run session: %v", err)
}
payload := []byte("signed raw artifact chunk")
opened, err := core.OpenArtifactTransfer(domain.ArtifactTransferOpen{RunEndpointID: "run-local", SessionToken: token, ArtifactID: "artifact-signed-raw", Direction: domain.ArtifactTransferDirectionUpload, OwnerKind: domain.ArtifactOwnerKindJob, OwnerID: "job-signed-artifact", SizeBytes: int64(len(payload)), ChunkSizeBytes: len(payload), Checksum: validator.BytesChecksum(payload), IdempotencyKey: "signed-raw-artifact"})
if err != nil {
t.Fatalf("open transfer: %v", err)
}
router := NewTestRouterWithCore(core)
unsigned := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-unsigned-raw", stamp, false)
assertErrorResponse(t, unsigned, http.StatusUnauthorized, errorCodeUnauthorized)
signed := rawArtifactChunkRequest(t, router, token, opened.TransferID, payload, "nonce-signed-raw", stamp, true)
assertStatus(t, signed, http.StatusOK)
response := decodeBody[dto.ArtifactChunkUploadResponse](t, signed)
if !response.Accepted || response.TransferID != opened.TransferID || response.NextMissingChunkIndex != 1 {
t.Fatalf("unexpected signed chunk response: %+v", response)
}
}
func rawArtifactChunkRequest(t *testing.T, router http.Handler, token string, transferID string, payload []byte, nonce string, stamp time.Time, signed bool) *httptest.ResponseRecorder {
t.Helper()
path := "/api/v1/run/artifacts/chunks"
timestamp := strconv.FormatInt(stamp.Unix(), 10)
req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("X-Run-Endpoint", "run-local")
req.Header.Set("X-Run-Session-Token", token)
req.Header.Set("X-Run-Timestamp", timestamp)
req.Header.Set("X-Run-Nonce", nonce)
req.Header.Set("X-Artifact-Transfer-Id", transferID)
req.Header.Set("X-Artifact-Id", "artifact-signed-raw")
req.Header.Set("X-Artifact-Chunk-Index", "0")
req.Header.Set("X-Artifact-Offset", "0")
req.Header.Set("X-Artifact-Size", strconv.Itoa(len(payload)))
req.Header.Set("X-Artifact-Checksum", validator.BytesChecksum(payload))
if signed {
bodyHash := sha256.Sum256(payload)
canonical := strings.Join([]string{http.MethodPost, path, timestamp, nonce, hex.EncodeToString(bodyHash[:])}, "\n")
mac := hmac.New(sha256.New, []byte(token))
_, _ = mac.Write([]byte(canonical))
req.Header.Set("X-Run-Signature", hex.EncodeToString(mac.Sum(nil)))
}
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, req)
return recorder
}
func TestAuthorizedRouterAllowsRunLifecycleReportWithoutBearer(t *testing.T) {
store := repo.NewMemoryStore()
core := service.NewCoreService(store)
-12
View File
@@ -17,7 +17,6 @@ type componentLogServerContextKey struct{}
const (
logEventHeartbeatInterval = 15 * time.Second
liveLogSourceClockSkew = 90 * time.Second
managedLogSessionIDPrefix = "log-session:"
)
@@ -27,7 +26,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
writeMethodNotAllowed(w, http.MethodGet)
return
}
liveBoundary := time.Now().UTC().Add(-liveLogSourceClockSkew)
instance, streams, liveEligible, subscription, err := h.openLogEventSubscription(r)
if err != nil {
writeServiceError(w, err)
@@ -150,12 +148,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
return
}
}
if !sourceLogEntryIsLive(event.Entry, liveBoundary) {
if event.Entry.Seq > emittedThrough[event.Stream.ID] {
emittedThrough[event.Stream.ID] = event.Entry.Seq
}
continue
}
if !active.hasStream(event.Stream.ID) {
active.streams = append(active.streams, event.Stream)
if err := writeSSEJSON(w, "stream", "", dto.LogStreamFromDomain(event.Stream)); err != nil {
@@ -179,10 +171,6 @@ func (h *coreHandlers) serverLogEvents(w http.ResponseWriter, r *http.Request) {
}
}
func sourceLogEntryIsLive(entry domain.LogEntry, liveBoundary time.Time) bool {
return !entry.Timestamp.IsZero() && !entry.Timestamp.Before(liveBoundary)
}
type supervisedLogSession struct {
sessionID string
startedAt time.Time
+4 -3
View File
@@ -153,7 +153,7 @@ func TestLogEventsSSELiveOnlyStartsAfterSnapshotTail(t *testing.T) {
assertSSEEvent(t, reader, "log", `"seq":2`)
}
func TestLogEventsSSESkipsBufferedBackfillAfterOpen(t *testing.T) {
func TestLogEventsSSEStreamsBufferedAppendAfterOpen(t *testing.T) {
router := newTestRouter()
hello := createLogIngestAPIFixtures(t, router)
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", validLogBatchRequest(t, hello.SessionToken, 1, 1)), http.StatusOK)
@@ -180,11 +180,12 @@ func TestLogEventsSSESkipsBufferedBackfillAfterOpen(t *testing.T) {
assertSSEEvent(t, reader, "stream", `"id":"log-1"`)
assertSSEEvent(t, reader, "ready", `"streamCount":1`)
stale := validLogBatchRequest(t, hello.SessionToken, 2, 2)
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", stale), http.StatusOK)
buffered := validLogBatchRequest(t, hello.SessionToken, 2, 2)
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", buffered), http.StatusOK)
fresh := validLogBatchRequest(t, hello.SessionToken, 3, 3)
retimestampLogBatchRequest(t, &fresh, time.Now().UTC().Add(time.Second))
assertStatus(t, performJSON(t, router, http.MethodPost, "/api/v1/run/logs/batches", fresh), http.StatusOK)
assertSSEEvent(t, reader, "log", `"seq":2`)
assertSSEEvent(t, reader, "log", `"seq":3`)
}
+4
View File
@@ -944,6 +944,10 @@ func (h *coreHandlers) gamePluginManifestRegistration(w http.ResponseWriter, r *
writeDecodeError(w, err)
return
}
if violations := request.Manifest.RuntimeProfiles.UnsupportedLegacyClientManagerViolations("manifest.runtimeProfiles"); len(violations) > 0 {
writeServiceError(w, validator.ValidationError{Violations: violations})
return
}
plugin, err := h.core.RegisterGamePluginManifest(request.ToDomain())
if err != nil {
writeServiceError(w, err)