Skip to content

feat: read-only web dashboard (/ui) for developers and DevOps engineers #2192

Description

@iamroseyoung

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, and policy set across 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 message field when a destination endpoint is present, so an operator sees NET:FAIL [LOW] pypi.org:443 with 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_ui module serving a single-file SPA at /ui plus a small JSON API under /ui/api/*:
    • GET /ui/api/overview — gateway version, sandbox counts
    • GET /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 YAML
    • GET /ui/api/sandboxes/{name}/logs — initial log tail
    • GET /ui/api/sandboxes/{name}/stream — SSE live stream bridged from the in-memory TracingLogBus
  • 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=1 is 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} and grpc::policy::{handle_get_sandbox_policy_status, handle_list_sandbox_policies, handle_get_sandbox_logs} (visibility widened to pub; the grpc module itself remains crate-private).
  • SSE bridging from TracingLogBus::subscribe works 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 warnings and cargo fmt clean; no new dependencies added to the workspace.

Activity

  1. drew commented on Jul 10, 2026

    @drew
    Collaborator

    📋 triage-agent

    Triage Assessment

    Classification: needs-investigation

    Summary

    The observability need is real, and existing server handlers plus TracingLogBus make a read-only dashboard feasible. The proposed authorization boundary is not safe as written: an environment flag controls availability, but it does not make routes local-only or protect fleet metadata, policies, and logs.

    Investigation

    Sandbox, policy-history, and log handlers already expose the underlying data in process, and TracingLogBus provides both tail and subscription APIs (crates/openshell-server/src/tracing_bus.rs:74-97). However, gRPC requests are protected by AuthGrpcRouter, while the Axum HTTP router is a separate service without equivalent bearer/RBAC enforcement (crates/openshell-server/src/multiplex.rs:155-185; http.rs:180-188).

    Gateway deployments commonly bind to non-loopback addresses. Therefore OPENSHELL_WEB_UI=1 does not make unauthenticated /ui/api/* routes dev/local-only. Those routes would disclose sandbox inventory, full policy material, and recent/live logs that currently require read scopes. The decision board also needs a structured event contract; parsing shorthand display strings is not a durable OCSF API.

    No exact duplicate exists. #1055 is the broader enterprise-observability roadmap, while #1922 and #1722 are related durability and tenancy/access-control work.

    Recommendation

    Run a focused spike before implementation. Define HTTP authentication and RBAC equivalence, listener exposure, browser token/session handling, CSRF/origin behavior, and a structured event stream. A strictly loopback-only listener could be an alternative, but “disabled by default” alone must not be the security boundary.

  2. added
    area:gatewayGateway server and control-plane work
    topic:observabilityLogging, metrics, and observability work
    and removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Jul 10, 2026
  3. added theissue type on Jul 10, 2026
  4. github-actions commented on Jul 25, 2026

    @github-actions

    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.

  5. johntmyers commented on Aug 20, 2026

    @johntmyers
    Collaborator

    This is being developed out of this repo already by Red Hat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:gatewayGateway server and control-plane workspikestate:staleInactive item at risk of automatic closure.topic:observabilityLogging, metrics, and observability work

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions