Authenticate sockets by header or one-time ticket instead of ?token=

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GAeg4Jo6xjZzjwy9QDWMQ
This commit is contained in:
2026-10-07 08:58:22 +02:00
co-authored by Claude Opus 5.5
parent eb45fb2763
commit 5259b8db0a
21 changed files with 336 additions and 44 deletions
+10 -6
View File
@@ -233,20 +233,24 @@ the process is dead.
| `GET` | `/workers/tokens` | the worker credentials minted, without the tokens (superuser) |
| `POST` | `/workers/tokens/{jti}/revoke` | refuse one from now on and drop a worker attached with it (superuser) |
| `GET` | `/workers/runtime` | the node runner's source, for a host without pip |
| `WS` | `/workers/attach` | where a worker dials in |
| `WS` | `/workers/attach` | where a worker dials in, with `Authorization: Bearer <worker token>` |
See [Remote workers](workers.md).
## Live events
```text
ws://127.0.0.1:8000/api/v1/flows/ws?token=<access token>
ws://127.0.0.1:8000/api/v1/flows/ws
```
The websocket authenticates from its query string. It sends a snapshot on
connect and then every engine event: node executions, values published, health
changes, run started and finished. This is what the canvas and the dashboards
draw from.
A client that can set headers sends `Authorization: Bearer <access token>` on
the handshake. A browser cannot, so it first calls `POST /flows/ws-ticket` and
opens `/flows/ws?ticket=<ticket>`: a ticket is good for one connect, within 30
seconds, so the URL that access logs keep is worth nothing afterwards.
`?token=` is still accepted for one more release. The socket sends a snapshot
on connect and then every engine event: node executions, values published,
health changes, run started and finished. This is what the canvas and the
dashboards draw from.
## Errors