From 2753a8df624a6e4259e777e407b4ef5525ce8f81 Mon Sep 17 00:00:00 2001 From: kami Date: Wed, 26 Aug 2026 22:49:47 +0400 Subject: [PATCH] Let a federated worker read the intent endpoint Found live on the first burn-in task. Every federated launch failed with `effective intent: federation: 401 Unauthorized: unauthorized surface`, and the task nacked back to queued. GET /v1/tasks//intent was added so a worker renders its own launch instruction from the reduced intent, but authz.HTTPWithSessions never got the matching worker-path exemption. An unlabelled request defaults to the Web surface, which is session-gated, so the one caller the endpoint exists for could never reach it. Twenty test packages passed throughout, because the authz tests covered the surface capability table and not the worker path list. Only the live owner path established this. Co-Authored-By: Claude Opus 5 --- internal/authz/authz.go | 9 ++++++++- internal/authz/authz_test.go | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/authz/authz.go b/internal/authz/authz.go index e511c00..b16b2dd 100644 --- a/internal/authz/authz.go +++ b/internal/authz/authz.go @@ -225,7 +225,14 @@ func HTTPWithSessions(tokens map[Surface]string, sessions *Sessions, next http.H workerPath := strings.HasPrefix(r.URL.Path, "/v1/federation/") || (r.Method == http.MethodGet && r.URL.Path == "/v1/tasks") || (r.Method == http.MethodPost && r.URL.Path == "/v1/artifacts") || - (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/artifacts/")) + (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/artifacts/")) || + // A federated worker renders its own launch instruction from the + // reduced intent, so this read is as necessary to it as the task + // list. Found live on the first burn-in task: the endpoint was + // added for workers, the exemption was not, and every federated + // launch failed with "effective intent: federation: 401 + // Unauthorized: unauthorized surface". + (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/tasks/") && strings.HasSuffix(r.URL.Path, "/intent")) if federationRegistration || r.URL.Path == HarnessTurnPath || (worker && workerPath) { next.ServeHTTP(w, r) return diff --git a/internal/authz/authz_test.go b/internal/authz/authz_test.go index bd40881..ccebd34 100644 --- a/internal/authz/authz_test.go +++ b/internal/authz/authz_test.go @@ -140,6 +140,8 @@ func TestFederationRequestsUseTheirOwnCredentials(t *testing.T) { {name: "worker task reconciliation reaches worker handler", method: http.MethodGet, path: "/v1/tasks", worker: "workpc-opencode", want: http.StatusNoContent}, {name: "worker artifact read reaches worker handler", method: http.MethodGet, path: "/v1/artifacts/ref", worker: "workpc-opencode", want: http.StatusNoContent}, {name: "worker artifact upload reaches worker handler", method: http.MethodPost, path: "/v1/artifacts", worker: "workpc-opencode", want: http.StatusNoContent}, + {name: "worker intent read reaches worker handler", method: http.MethodGet, path: "/v1/tasks/06G3YR34117MAYT6KEAC9RJHD0/intent", worker: "workpc-opencode", want: http.StatusNoContent}, + {name: "unnamed intent read remains web gated", method: http.MethodGet, path: "/v1/tasks/06G3YR34117MAYT6KEAC9RJHD0/intent", want: http.StatusUnauthorized}, {name: "unnamed worker request remains web gated", method: http.MethodGet, path: "/v1/federation/events", want: http.StatusUnauthorized}, {name: "worker list remains web gated", method: http.MethodGet, path: "/v1/federation/workers", want: http.StatusUnauthorized}, } {