- TypeScript 81.6%
- Svelte 18.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Status fields card stays for the disabled state, where it explains how to enable the console. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
| backend | ||
| frontend | ||
| CLAUDE.md | ||
| opsdeck.module.json | ||
| README.md | ||
OpsDeck console module
An interactive terminal for OpsDeck: a real shell on the OpsDeck host, in the browser.
Security — read this first
This runs a real pseudo-terminal shell as the OpsDeck server process. That process typically has host mounts and the Docker socket, so this is effectively a root shell on the host. Accordingly it is:
- Off by default. The operator must set
OPSDECK_MOD_CONSOLE_ENABLE=1on the server to turn it on. - Admin-only, enforced server-side on the WebSocket route.
Enable it only where you would hand out SSH root access.
What it is
A genuine PTY (not the old per-command runner): colors, line editing, job
control, and curses apps (top, vim, htop) all work. The server
allocates the pty and bridges it to an xterm.js terminal in the browser
over a WebSocket.
xterm.js (browser) ──ws /api/mod/console/pty──▶ ctx.upgradeWebSocket
▲ │
└──────────── pty master bytes ◀── ctx.terminal.open() ──▶ shell
Two modes
-
Container shell (default): a shell inside the OpsDeck container. That process has the host mounts + docker socket, so it's already powerful, but the environment is the container's (bash, no host dotfiles).
-
Host login shell (
OPSDECK_MOD_CONSOLE_CHROOT_HOST=1):chrootinto the mounted host root and drop to a host user — their own login shell, init files (~/.bashrc/.zshrc),$PATH, home, and supplementary groups (sudo,docker, …). As close tossh user@hostas you get from a container. NeedsCAP_SYS_CHROOT(a docker default — already present) and the container running as root (it does). Works underno-new-privileges(the uid drop is a directsetuidfrom root, no setuid binary).Read-only: the
/host/rootbind isread_only: truein the compose, so the host shell can read and run anything but not write. For a read-write host shell, flip that mount toread_only: false— that lets the container modify the host, so decide deliberately.
Config
| Env | Default | Meaning |
|---|---|---|
OPSDECK_MOD_CONSOLE_ENABLE |
unset | 1 enables the terminal; anything else keeps it off |
OPSDECK_MOD_CONSOLE_CHROOT_HOST |
unset | 1 = host login shell (chroot into the host root as a user); else container shell |
OPSDECK_MOD_CONSOLE_USER |
1000 |
host user (name or uid) for chroot mode |
OPSDECK_MOD_CONSOLE_SHELL |
/bin/bash |
shell for container mode (chroot mode uses the user's passwd shell) |
OPSDECK_MOD_CONSOLE_CWD |
host root mount | start dir for container mode (chroot mode uses the user's home) |
Requirements
OpsDeck core new enough to provide ctx.terminal (with chroot/user) +
ctx.upgradeWebSocket. The pty uses libc + setsid/chroot; the container
runs as root with CAP_SYS_CHROOT (docker default). The terminal is
web-only; non-web clients see a status page via the schema.
Limitations
- One shell per browser session (open the page again for another).
The terminal resizes live with the browser window, and shell-integration
escape sequences (prompt marks, title, cwd) render normally. Transient
prompts (oh-my-posh transient_prompt, powerlevel10k) work correctly —
provided core launches the shell with a UTF-8 ctype locale (see below).
Note: UTF-8 locale is required
A shell drawing Nerd-Font / box-drawing glyphs must run under a UTF-8
LC_CTYPE. Without one, glibc falls back to C/POSIX and wcwidth() counts
each byte of a multibyte glyph (─ and powerline separators are 3 bytes)
as a column, so the shell mis-measures its own prompt width — which makes
transient-prompt redraws over-erase real scrollback (ESC[<n>A overshoots).
Core's pty sets LANG=LC_CTYPE=C.UTF-8 by default, so this is handled; if
you override the pty env, keep a UTF-8 ctype locale.
Install
Production — pinned by commit:
OPSDECK_EXTERNAL_MODULES=https://git.imhof.cloud/OpsDeck/module_console.git#<sha>
# then, to actually enable the shell:
OPSDECK_MOD_CONSOLE_ENABLE=1
Dev loop — see CLAUDE.md.
Reference
Built from the OpsDeck module template. ctx.terminal /
ctx.upgradeWebSocket and the @opsdeck/ui Terminal component are
documented in core/docs/modules.md.