From 0dfd45cafb62c47752ccdcc50cd3458291b365d1 Mon Sep 17 00:00:00 2001 From: Renn F Date: Mon, 6 Jul 2026 13:38:09 +0200 Subject: [PATCH] =?UTF-8?q?[scan]=20secretary=20token=20signs=20over=20rea?= =?UTF-8?q?l=20team=20(board)=20not=20empty=20=E2=80=94=20fixes=20/api/sec?= =?UTF-8?q?retary/*=20401=20(L31-class)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboco/runtime/orchestrator.py | 6 ++++- tests/unit/runtime/test_secretary_spawn.py | 28 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/roboco/runtime/orchestrator.py b/roboco/runtime/orchestrator.py index 5ebe9933..c44b968a 100644 --- a/roboco/runtime/orchestrator.py +++ b/roboco/runtime/orchestrator.py @@ -4413,7 +4413,11 @@ class AgentOrchestrator: cli_model=cli_model, api_url=api_url, agent_uuid=agent_uuid, - agent_token=issue_agent_token(agent_uuid, "secretary", ""), + agent_token=issue_agent_token( + agent_uuid, + "secretary", + AGENTS[SECRETARY_AGENT_ID].team.value, + ), provider_base_url=route.base_url, provider_auth_token=route.auth_token, provider_type=route.provider_type.value, diff --git a/tests/unit/runtime/test_secretary_spawn.py b/tests/unit/runtime/test_secretary_spawn.py index f64b5ef8..02dd5219 100644 --- a/tests/unit/runtime/test_secretary_spawn.py +++ b/tests/unit/runtime/test_secretary_spawn.py @@ -4,6 +4,12 @@ from __future__ import annotations from unittest.mock import MagicMock +from roboco.agents_config import ( + get_agent_team, + issue_agent_token, + verify_agent_token, +) +from roboco.foundation.identity import AGENTS from roboco.runtime.orchestrator import ( SECRETARY_AGENT_ID, AgentOrchestrator, @@ -57,3 +63,25 @@ def test_resolve_secretary_host_paths_has_claude_and_prompt() -> None: assert "claude" in paths assert "prompt" in paths assert SECRETARY_AGENT_ID in str(paths["prompt"]) + + +def test_secretary_token_signs_over_real_team_not_empty(monkeypatch) -> None: + """The secretary token must verify against the (id, role, team) the + secretary driver actually sends. secretary_driver._headers sends + X-Agent-Team = get_agent_team(uuid) = the secretary's real team ("board"), + so the token issued at spawn (orchestrator _spawn_secretary_container) must + be signed over that same team — not "" — or every /api/secretary/* call + 401s with "signature mismatch" under ROBOCO_AGENT_AUTH_REQUIRED. + """ + monkeypatch.setenv("ROBOCO_AGENT_AUTH_SECRET", "x" * 32) + secretary = AGENTS[SECRETARY_AGENT_ID] + agent_uuid = str(secretary.uuid) + team = secretary.team.value + assert team # the bug was signing "" — the secretary IS on a team + + token = issue_agent_token(agent_uuid, "secretary", team) # mirrors spawn line + # secretary_driver._headers sends X-Agent-ID=uuid, role=secretary, + # X-Agent-Team=get_agent_team(uuid). + assert verify_agent_token( + token, agent_uuid, "secretary", get_agent_team(agent_uuid) + )