🖥️ WebSocket application console (daemon)
📌 Description
The daemon exposes a WebSocket console for a specific application: a terminal and live log stream in the browser (attach to a container or to a native application's journal).
🎯 Scenarios
- 🧑💻 A developer opens the application's "Console" tab — sees live output and types commands into the application's stdin (e.g. a Minecraft server console).
- 🔎 In the "Logs" tab they find the moment of a crash using timestamps and a filter.
🏗️ Technical design
🖥️ Application tabs in the UI
Every application in the platform has two tabs:
- Console — real time: a stream of the Docker application's output or the native application's runtime process; at the bottom — an input line for the application's stdin. This is an interactive session (attach), not just viewing.
- Logs — history: every line carries a timestamp, text search/filtering, sorting by time, range selection; a "show in context" jump from a search result.
🎨 Terminal
The terminal is modern and convenient: xterm.js, full ANSI color support and control sequences, automatic sizing (resize → PTY), copy/paste, buffer search, scrollback history, font/theme selection (inherits the UI theme).
⚙️ Transport
- WebSocket console: a server inside the daemon; sessions
logs(a read-only stream: docker logs -f / journald follow, with timestamps) andattach(the application's PTY/stdin, bidirectional). The protocol is binary frames (stdin/stdout/resize) as in ttyd/gotty. - Access via a temporary token: a WebSocket connection to the console opens only with a temporary token, issued through the daemon API (
AppService.IssueConsoleToken/POST /v1/apps/{id}/console-token): TTL 30 seconds, single-use, bound to the application and the session typelogs/attach. The platform obtains the token automatically when the console tab opens (the backend checks permissions → requests a token from the daemon → hands it to the browser); in standalone mode the CLI issues it (asc app console-token <id>). - Endpoint:
GET /v1/console?token=<token>[&tail=N]— outside the API's bearer authentication (a browser cannot set headers on a WS handshake); the only protection is the single-use token; an invalid/used token → 401 before the upgrade. - MVP implementation (DMN-007): the
logssession — text frames, source by runtime: for Docker — log streaming via the Engine API (follow+ timestamps over the unix socket); for systemd —journalctl -f -o short-iso; for process —tail -F app.log(the subprocess is killed when the client disconnects). Theattachsession — Docker only for now (Engine APIattach, binary stdin/stdout frames); systemd/process need a PTY at launch — that arrives with the ttyd-style binary protocol (resize etc.) during the UI work (FE-006). - Multiple connections to one application (tabs/users):
attachsessions are multi-client following the wings pattern — the daemon creates one shared source session (hub) per application and its output is fanned out to all connections via a broadcast channel; a lagging client loses old chunks but does not slow down the rest. The hub keeps a replay buffer of the last ~128 KiB of output — a new tab immediately sees recent output, and all clients' stdin converges into the container's single pipe. The source (Engine APIattach) closes together with the last client.logssessions are independent by construction: every connection is its own follow stream with its owntail, so multiple tabs work there too. - CLI:
asc attach <id>(synonymasc app attach <id>) — an interactive application console right from the server terminal: the CLI process's stdin/stdout are piped into the application. With a daemon running it goes through the daemon's own console (DMN-043): the CLI asks for a console token over the unix socket and opens the same WebSocket session the browser uses, so a regular user needs neither membership in thedockergroup nor access to the root-owned app tree — the peer uid the daemon reads from the socket is the whole authorization. Without a daemon (standalone install) the CLI goes straight to the Engine API instead. Either way Docker fans the output out to everyone attached (CLI + browser tabs). Docker applications only for now; systemd/process — together with PTY (FE-006). Disconnect — Ctrl+C, the application keeps running. - Routing: browser ↔ nodeservice (wss) ↔ daemon tunnel ↔ console module.
- Permissions:
apps.consolecontrols access to the application console (🔐 access-control).
🔗 Related tasks
DMN-007 and FE-006 in ROADMAP.md.