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/<id>/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 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 22:49:47 +04:00
parent 41532a3efc
commit 2753a8df62
2 changed files with 10 additions and 1 deletions
+8 -1
View File
@@ -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
+2
View File
@@ -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},
} {