Repository navigation
feat: read-only web dashboard (/ui) for developers and DevOps engineers #2192
Description
Problem Statement
OpenShell currently exposes two operator surfaces: the CLI and the ratatui TUI. Both work well for hands-on-keyboard workflows, but several recurring situations are poorly served:
- Policy iteration is log-archaeology. The core loop (watch denials → understand why → allow → retry) requires juggling
openshell logs --tail,policy get, YAML edits, andpolicy setacross terminals. The "why" of a denial (binary mismatch, resolved symlink, policy rule) is buried in log text. - Failure causes are hidden. The OCSF shorthand formatter suppresses the
messagefield when a destination endpoint is present, so an operator seesNET:FAIL [LOW] pypi.org:443with no reason. In a real debugging session this cost us a full day chasing an IPv6 hypothesis when the actual cause was an upstream TLS verification failure. - Sandbox health is opaque. A sandbox stuck in a supervisor crash-loop (e.g. expired static token after a container restart) shows only "Provisioning" — restart counts and supervisor state are not surfaced anywhere.
- No glanceable fleet view for DevOps engineers operating a gateway: phase, policy version, revision history, and live activity require per-sandbox CLI invocations.
Proposed Design
A read-only web dashboard embedded in the gateway binary, as the first slice of a larger web UI direction (full plan drafted separately; this issue covers only the read-only observability slice).
Backend (openshell-server):
- New
web_uimodule serving a single-file SPA at/uiplus a small JSON API under/ui/api/*:GET /ui/api/overview— gateway version, sandbox countsGET /ui/api/sandboxes— fleet list (phase, image, created, active policy version, conditions)GET /ui/api/sandboxes/{name}— detail incl. policy revision history and current policy YAMLGET /ui/api/sandboxes/{name}/logs— initial log tailGET /ui/api/sandboxes/{name}/stream— SSE live stream bridged from the in-memoryTracingLogBus
- Reuses existing gRPC handler logic in-process (no duplicated data access), keeping the JSON shapes as a thin, stable adapter layer.
- Gated off by default: the entire surface only mounts when
OPENSHELL_WEB_UI=1is set. These routes intentionally bypass gRPC bearer auth, so the gate keeps them dev/local-only until proper authn/z (OIDC session or token) is added in a follow-up.
Frontend (embedded assets/ui.html, no build step, no external dependencies):
- Left rail: gateway status + sandbox list with phase indicators, keyboard navigation (j/k, mirroring the TUI).
- Live tab: a "decision board" that renders network policy decisions parsed from OCSF events as flight-strip rows (ALLOW in green, DENY in amber with the full denial reason available), plus a filterable, auto-following log tail.
- Policy tab: current policy YAML and the revision table (status, hash, load errors, active version).
Non-goals for this slice: any mutating action (policy edit, sandbox lifecycle), multi-gateway views, web terminal, persistent event storage. Those are follow-ups per the broader plan.
Alternatives Considered
- tonic-web/gRPC-web + generated TS client + React build pipeline — the right long-term target (streaming RPCs already exist), but it introduces a frontend toolchain and codegen pipeline that is disproportionate for a first read-only slice. The JSON adapter is deliberately small and can be replaced by gRPC-web without UI redesign.
- Standalone BFF process talking gRPC to the gateway — avoids touching the server crate, but adds a second deployable and a second auth boundary; embedding keeps single-binary distribution.
- Extending the TUI instead — the TUI already covers hands-on workflows; the gap is shareable, glanceable observability (a browser tab on a second monitor / TV dashboard), which a terminal UI cannot serve.
Agent Investigation
Feasibility was validated by implementing a working prototype against a live gateway (Docker driver) running a real agent workload (Discord bot in a policy-constrained sandbox):
- All five endpoints serve live data by delegating to
grpc::sandbox::{handle_get_sandbox, handle_list_sandboxes}andgrpc::policy::{handle_get_sandbox_policy_status, handle_list_sandbox_policies, handle_get_sandbox_logs}(visibility widened topub; thegrpcmodule itself remains crate-private). - SSE bridging from
TracingLogBus::subscribeworks end-to-end: a policy denial triggered inside the sandbox appeared on the board in real time, including the full denial reason (binary-not-allowed with ancestor chain and symlink hint) that the shorthand log format suppresses. cargo clippy --workspace --all-targets -- -D warningsandcargo fmtclean; no new dependencies added to the workspace.
Activity
drew commentedon Jul 10, 2026
github-actions commentedon Jul 25, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.
johntmyers commentedon Aug 20, 2026
This is being developed out of this repo already by Red Hat.
vibexp-agent commentedon Oct 25, 2026
Instead of a
/uicommand, there is a separate web ui application for OpenShell.https://github.com/shaharia-lab/openshell-webui