From 803d8ee8f949a157c1f10213b4a9cded837a7953 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Tue, 24 Mar 2026 02:30:56 -0700 Subject: [PATCH] docs: document user_id propagation through MQ path Update security.md with trusted service user_id forwarding. Update MQ protocol diagram to include user_id field on CreateWorkstreamMessage. Update console data flow diagram to show user_id in message and bridge forwarding. --- docs/diagrams/06-mq-protocol.puml | 1 + docs/diagrams/11-console-data-flow.puml | 5 +++-- docs/security.md | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/diagrams/06-mq-protocol.puml b/docs/diagrams/06-mq-protocol.puml index a1ecd1cd..5d188813 100644 --- a/docs/diagrams/06-mq-protocol.puml +++ b/docs/diagrams/06-mq-protocol.puml @@ -61,6 +61,7 @@ package "Inbound Messages (Client → Bridge)" as InPkg #FFF3E0 { + target_node: str = "" + initial_message: str = "" + skill: str = "" + + user_id: str = "" } class CloseWorkstreamMessage { diff --git a/docs/diagrams/11-console-data-flow.puml b/docs/diagrams/11-console-data-flow.puml index 26895b40..06d13901 100644 --- a/docs/diagrams/11-console-data-flow.puml +++ b/docs/diagrams/11-console-data-flow.puml @@ -154,7 +154,7 @@ activate Server #FFECB3 Server -> CC : _pick_best_node() or\nget_node_detail(node_id) CC --> Server : node validated -Server -> Server : Build CreateWorkstreamMessage\n{target_node:"nodeA", name:"new-task"} +Server -> Server : Build CreateWorkstreamMessage\n{target_node:"nodeA", name:"new-task",\nuser_id: from auth_result} Server -> Redis : RPUSH turnstone:inbound:nodeA\n(directed queue) Server --> Browser : {status:"ok", correlation_id:"abc",\ntarget_node:"nodeA"} @@ -163,7 +163,8 @@ deactivate Server note right of Redis Bridge on Node-A picks up the message from its directed queue, - POSTs to /v1/api/workstreams/new, + POSTs to /v1/api/workstreams/new + (forwarding user_id in payload), registers ownership, publishes ws_created to cluster channel. end note diff --git a/docs/security.md b/docs/security.md index 2d4956a1..19f6f93c 100644 --- a/docs/security.md +++ b/docs/security.md @@ -499,6 +499,17 @@ Service tokens use 1-hour expiry with automatic refresh via httpx event hooks to ensure rotated tokens are picked up on SSE reconnects. +### User identity in MQ-dispatched workstreams + +When the console creates a workstream via MQ (the normal path), the +authenticated user's `user_id` is embedded in the +`CreateWorkstreamMessage`. The bridge forwards this `user_id` in the +HTTP payload when calling the server's `POST /v1/api/workstreams/new`. +The server accepts a `user_id` from the request body **only when the +caller is a trusted service** — identified by `token_source` matching +`bridge`, `console-proxy`, or `console`. Regular API callers cannot +override `user_id`; the server always uses their JWT identity. + Note that the channel gateway uses a distinct JWT audience (`turnstone-channel`) from the server (`turnstone-server`) and console (`turnstone-console`). A server-scoped JWT cannot authenticate to the