From 65cc6fd959c364869efbdf61fc0d42972ed3b056 Mon Sep 17 00:00:00 2001 From: calvinleng-science Date: Wed, 14 Oct 2026 15:26:23 -0700 Subject: [PATCH] fix(auth): send the pairing token on GetLogs and TailLogs OPEN_METHODS still listed GetLogs and TailLogs from when the device left logs open, so the interceptor sent them without a token. The device gates them, so every log read from a paired client was refused as "not paired" (synapsectl logs; etester's inference checks, which poll GetLogs). The set is now exactly the device's open list, Info and RequestAuth, pinned by a test. Version 2.8.1. --- setup.py | 2 +- synapse/client/auth.py | 12 ++++++------ synapse/tests/client/test_auth.py | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index bd004bc..bc39a34 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ def stamp_api_version(): setup( name="science-synapse", - version="2.8.0", + version="2.8.1", description="Client library and CLI for the Synapse API", author="Science Team", author_email="team@science.xyz", diff --git a/synapse/client/auth.py b/synapse/client/auth.py index e5078a0..52936c4 100644 --- a/synapse/client/auth.py +++ b/synapse/client/auth.py @@ -134,13 +134,13 @@ def _write(tokens: Dict[str, Tuple[str, str]]) -> None: METADATA_KEY = "authorization" METADATA_SCHEME = "Bearer " -# Mirrors scifi-server's open set (src/auth/rpc_auth_policy.cpp). These need no -# token, so the interceptor never triggers serial resolution for them. +# Mirrors scifi-server's open set exactly (src/auth/rpc_auth_policy.cpp, +# kOpenMethods). These are sent without a token, so the interceptor never triggers +# serial resolution for them; every other RPC is refused without one. GetLogs and +# TailLogs were open at first and are gated now. OPEN_METHODS = frozenset( { "/synapse.SynapseDevice/Info", - "/synapse.SynapseDevice/GetLogs", - "/synapse.SynapseDevice/TailLogs", "/synapse.SynapseDevice/RequestAuth", } ) @@ -170,8 +170,8 @@ class AuthInterceptor( Resolution is lazy and happens at most once per channel: the first call to a token-gated method learns the device serial, looks the token up, and caches - it. Open methods never trigger it, so discovery and log tailing cost nothing - extra, and neither does an unpaired user with no env file. + it. Open methods never trigger it, so discovery costs nothing extra, and + neither does an unpaired user with no env file. """ def __init__(self, channel=None, serial_resolver=None): diff --git a/synapse/tests/client/test_auth.py b/synapse/tests/client/test_auth.py index 75b929c..4b13509 100644 --- a/synapse/tests/client/test_auth.py +++ b/synapse/tests/client/test_auth.py @@ -128,13 +128,18 @@ def test_env_override_is_honored(tmp_path, monkeypatch): assert custom.exists() -def test_open_methods_are_not_gated(): - for name in ("Info", "GetLogs", "TailLogs", "RequestAuth"): - assert f"/synapse.SynapseDevice/{name}" in auth.OPEN_METHODS - - -def test_closed_methods_are_not_in_the_open_set(): - for name in ("Configure", "Start", "Stop", "Query", "StreamQuery"): +def test_open_methods_match_the_device(): + # Exactly the device's open list (headstage src/auth/rpc_auth_policy.cpp). A + # method listed here is sent without the token, so an extra entry is a call the + # device refuses as "not paired" -- as GetLogs/TailLogs were in 2.8.0. + assert auth.OPEN_METHODS == { + "/synapse.SynapseDevice/Info", + "/synapse.SynapseDevice/RequestAuth", + } + + +def test_logs_carry_the_token(): + for name in ("GetLogs", "TailLogs", "Configure", "Start", "Stop", "Query", "StreamQuery"): assert f"/synapse.SynapseDevice/{name}" not in auth.OPEN_METHODS