Make revoking an agent and locking a dashboard actually revoke and lock

An MCP access token is a stateless JWT good until it expires, so deleting
the client row revoked nothing already handed out — on the MCP endpoint or
on the REST API, which takes the same token directly. Both doors now look
the client up by the `client_id` the token has always carried, so tokens
already in circulation are held to it too.

A dashboard's `locked` setting stopped the client drawing a control and
nothing else; the server took a publish from a panel showing it anyway. It
now bounds the panel's write scope, resolved live where a flow drives the
flag, exactly as the client resolves it. Reads are untouched — read-only is
not blind — and so is a querying chart's request, which is how that tile
reads rather than something anyone touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CL9zvnnvcp1mvA8o7impxk
This commit is contained in:
2026-09-06 15:24:53 +02:00
co-authored by Claude Opus 5
parent 15c3dd5838
commit 8cb843eb25
8 changed files with 331 additions and 43 deletions
+5 -3
View File
@@ -555,9 +555,11 @@ def revoke_client(client_id: uuid.UUID, session: SessionDep) -> Any:
"""Withdraw one agent's access, leaving every other agent alone.
Deleting the client cascades to its codes and refresh tokens, so it can
get nothing new and cannot come back without registering again. An access
token already in its hands keeps working until it expires
(``MCP_TOKEN_EXPIRE_MINUTES``) — those are stateless by design.
get nothing new and cannot come back without registering again. The access
token already in its hands stops working too, even though it is a
stateless JWT nobody can reach into: every door that takes one looks the
client up first (``api.deps.oauth_client_lives``), so the row deleted here
is the whole of the revocation.
"""
client = session.get(OAuthClient, client_id)
if client is None: