---
id: w5/m6/t003
title: Live-tail via EventSource against /v1/logs/subscribe (SSE)
worker: worker5
status: done
estimate: 75m
depends_on: [w5/m6/t002]
---

## Objective

Add live tail: subscribe to bex-api's `GET /v1/logs/subscribe` SSE stream (authenticated by the Kratos session), append new lines to the panel in real time, with pause/resume, autoscroll, and a capped in-memory buffer. Document the deliberate SSE-vs-WebSocket divergence from Render in the data-layer code.

## Context

- bex-api SSE endpoint (`docs/ADR006-bex-api.md`, `docs/ADR010-observability.md`): `GET /v1/logs/subscribe?resource=<id>` streams new lines over Server-Sent Events — bex's deliberate choice where Render upgrades to a WebSocket; same "stream new lines live" contract.
- Auth: the same Kratos session the dashboard already uses; SSE requests must carry it.
- Buffer: cap the retained lines to avoid unbounded memory on a long-lived tail.

## Steps

1. Add a `useLiveLogs` hook wrapping `EventSource` (or a fetch-based SSE reader if credentials require it) against `/v1/logs/subscribe`.
2. Merge streamed lines into the panel above the historical query results; dedupe against the last historical page.
3. Implement pause/resume (close/reopen the stream), autoscroll-on-new-line (disabled when the user scrolls up), and a capped ring buffer.
4. Add a code comment documenting the SSE-vs-WebSocket divergence and why (bex's no-dependency choice).

## Files

- `src/features/logs/hooks/use-live-logs.ts` (new)
- `src/features/logs/components/` (live toggle, autoscroll)
- `src/features/logs/locales/{en,zh}.ts`

## Acceptance criteria

- [ ] With live-tail on, new lines from the SSE stream append in real time.
- [ ] Pause/resume, autoscroll, and a capped buffer all work.
- [ ] The SSE-vs-WebSocket divergence is documented in the data-layer code.

## Out of scope

- Tab wiring + live verification (t004).
