## The problem
Right now, looking at a node’s Xray log means SSH-ing into that node. The Panel already knows the node, is already authenticated to it, and already talks to it over an authenticated channel — but for the one thing you need most while debugging (“what is this node actually doing right now?”), it sends you to a terminal.
This gets worse the more nodes you run. Reproducing a user’s problem means: find which node they’re on, find its IP, SSH in, remember where the log went, `tail -f`, grep. Every time.
## The ask
A **Logs** tab on the node detail page. Live tail of that node’s Xray log, with a text filter. That’s it.
```
2026/08/09 16:09:28 from 1.2.3.4:23311 accepted tcp:docs.google.com:443 [my-reality → residence] email: 2
2026/08/09 16:09:38 from 1.2.3.4:23339 accepted udp:rr4—sn-qja5mc-5h.googlevideo.com:443 [my-reality → residence] email: 2
```
Being able to see that line live answers, in seconds, questions that currently take a round trip to a shell:
- Is this user’s traffic actually reaching the node, or dying before it?
- Is my routing doing what I think? (` → ` vs `>>` in `[inbound → outbound]` tells you whether a rule matched or it fell through to the default.)
- Is that new inbound receiving anything at all?
- Why is one user’s traffic going out the wrong outbound?
## What this is *not*
Scope creep is the obvious risk here, so to be explicit — I am **not** asking for:
-
log storage or search history
-
alerting, rules, thresholds, notifications
-
log aggregation across nodes
-
analytics, dashboards, per-user reports
Just an ephemeral stream. Open the tab, see what is happening now, close it, nothing is kept. Think Docker Desktop’s *Logs* tab, not a SIEM.
Everything above already has better tools if someone wants it (Loki/Promtail, and Torrent Blocker already covers the “notify me” case). The gap is specifically the *interactive, right now* view.
## Why it should be small to build
Most of the pieces are already there:
- **Transport** — Panel↔Node already have an authenticated HTTP channel (`SECRET_KEY` / `NODE_PORT`). An SSE endpoint next to the existing ones in `src/modules/xray-core/xray.controller.ts` fits the existing shape.
- **The Node already knows where its logs go.** It hands Xray the full config over the internal socket (`-config @${INTERNAL_SOCKET_PATH}:/internal/get-config`), so it can pick the right source with no user configuration:
- `log.access` empty → read from the s6 pipe (`xray/run` already does `exec 2>&1` into the `xray-log` consumer)
- `log.access` set to a file → tail that file
This matters: people who have already pointed `log.access` at a file (to get persistence and logrotate, since `s6-log -b n0` keeps no archives) would otherwise be locked out of the feature. Handling both means it just works either way.
- **No storage, no queue, no migration.** The Panel side is an `EventSource` and a scrolling buffer.
## Security — opt-in via env
It should be **off by default** and enabled explicitly on the node, e.g.:
```yaml
environment:
- ENABLE_LIVE_LOGS=true
```
and the Node returns 403 on the endpoint unless it is set.
The Node config schema already has exactly this pattern, so it’s a three-line addition (`src/common/config/app-config/config.schema.ts`):
```ts
DISABLE_HASHED_SET_CHECK: z
.string()
.default('false')
.transform((val) => val === 'true'),
```
and `init-env.sh` already conditions behaviour on optional env (`CUSTOM_CORE_URL`), so this doesn’t introduce a new mental model.
To be clear about the reasoning: this is **not** about protecting users from the admin. An admin with SSH on the node can already `tail` the file — this feature doesn’t change what they’re capable of. The reasons for the switch are different:
1. **Defence in depth.** If the Panel is ever compromised (XSS, stolen session, weak password), the attacker should not automatically also get a live traffic feed from every node. SSH is a separate credential and a separate door; a Panel tab is free.
2. **Separation of roles.** Panel admin and node owner aren’t always the same person (resellers, managed nodes, a friend’s spare box). Enabling it by default makes that decision for them.
3. It sidesteps the “should this be on by default?” debate entirely.
The **Create Node** wizard could carry a checkbox that adds the env line to the generated `docker-compose.yml`, so it costs nothing at setup time — consistent with how the wizard already generates the compose file for you.
## Nice to have (not required)
- Filter box (client-side substring match is enough)
- Pause / resume
- Copy visible buffer
- Line cap (e.g. keep last 1–2k lines in the browser)
—
Happy to hear if this overlaps with something already planned, or if the preferred direction is different (e.g. a CLI command rather than a Panel tab). The core need is just: *see a node’s live Xray log without SSH-ing into it.*