Skip to content

Add probaci serve, a local web UI - #46

Merged
imanimanyara merged 6 commits into
mainfrom
feat/web-ui
Nov 8, 2026
Merged

imanimanyara merged 6 commits into
mainfrom
feat/web-ui

Conversation

@imanimanyara

@imanimanyara imanimanyara commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds probaci serve, a web UI on this machine, as an option beside run and tui. A bare probaci still prints help.

  • Overview: the repositories with their detected languages, whether containers can run, and a run form with every enabled stage ticked (plus "repositories at once" when there are several).
  • Run page: each repository's stages with result, summary, time and output, and the streamed output, all updated live over server-sent events. A run can be canceled, and the results download as JSON.
  • Runs: every run, newest first. Finished runs are kept as JSON in $XDG_STATE_HOME/probaci/runs and survive a restart; a run the server never finished comes back as canceled.
  • Settings and third-party licenses pages.

It is built the way icon-sets-sync's and authama's web UIs are:

  • Sign-in and request checks: a one-time token is exchanged for a SameSite=Strict session cookie. The server answers only its own Host header, and changes need a same-origin request carrying the session's CSRF token.
  • Assets: a strict CSP with nothing inline. Tabler is embedded and fingerprinted, copied byte for byte from icon-sets-sync's vendored set with its MANIFEST.sha256.
  • Favicon: an SVG logo mark (a #066fd1 square with rx=7 on 32x32, white glyph) linked on every page, and also served at /favicon.ico.
  • Server modes: --read-only does not register the routes that start or cancel a run. /healthz needs the token in a header. The port falls back to the next free one unless --port pins it, and a non-loopback --addr needs --allow-remote.

Each run builds a fresh probaci.App, so it rereads the configuration and writes its own log file, the same as probaci run. One run goes at a time.

third-party-notices.md carries the embedded licenses and ships in the release archives. The commit hook requires lowercase Markdown names, so the name differs from icon-sets-sync's THIRD_PARTY_NOTICES.md.

Tests

  • What the new tests cover:
    • every page links the SVG favicon, and the linked asset answers 200 image/svg+xml, including on the 401 page;
    • sign-in, the Host check, same-origin and CSRF refusal;
    • stage order and filtering, recording a run, one run at a time, canceling, the event stream, the JSON report, history across a restart, and no run routes under --read-only;
    • run IDs that cannot form a path;
    • the vendored files against their manifest, and the notices against the embedded licenses.
  • Proved red: removing the favicon link fails the favicon tests on 6 pages, and taking the same-origin check out of the chain fails the CSRF test.
  • Local gates: go vet, go test -race ./..., golangci-lint (0 issues), gosec with CI's exclusions (0 issues), gitleaks (no leaks) and make check-build.
  • In the browser: I ran it against this repository. A four-stage run streamed live and passed. It was then listed after a restart, worked in the dark theme, and caused no horizontal scroll at 375px. There were no console errors.

Toolchain: Go 1.26

The first CI run failed govulncheck and gosec, so two follow-up commits are on this branch:

  • govulncheck found ten standard-library vulnerabilities in Go 1.25.14 that the new net/http server reaches, each fixed only in 1.26.x, plus one in golang.org/x/net v0.55.0. x/net moves to v0.60.0, which itself needs Go 1.26. So the module's go directive is now 1.26.0, and every workflow's go-version is "1.26". gosec moves to v2.30.0, which the CI comment said to do with the Go bump. Anyone building probaci now needs Go 1.26.
  • gosec flagged the session cookie (not Secure) and the sign-in redirect. Both now carry #nosec with the reason: the cookie is Secure whenever the request comes over TLS, and the redirect target has already passed isLocalURL.

All 10 checks are green on b5f0d71.

The CSS, scripts and icon webfont are icon-sets-sync's vendored set,
copied byte for byte with its MANIFEST.sha256, so the two UIs share one
pinned Tabler. third-party-notices.md carries the licences and goes into
every release archive.
A localhost UI over the stage engine, built the way the other Go web UIs
are: a one-time token exchanged for a session cookie, a Host allowlist,
same-origin and CSRF checks, a strict CSP, fingerprinted embedded assets
and an SVG favicon on every page. One run at a time, streamed over
server-sent events, cancellable, and kept as JSON in XDG state. Under
--read-only the routes that act are not registered.
Opens the web UI on 127.0.0.1:7844, taking the next free port when that
one is busy. Every run builds a fresh App, so it rereads the
configuration and writes its own log file, as probaci run does.
Copilot AI balanced review requested due to automatic review settings November 8, 2026 10:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

govulncheck found ten standard-library vulnerabilities in Go 1.25.14 that
the web UI's net/http server reaches, each fixed only in 1.26.x, and one
in x/net v0.55.0. x/net v0.60.0 itself needs Go 1.26, so the module's go
directive moves with it. gosec moves to v2.30.0, which needed 1.26.
The cookie is Secure whenever the request came over TLS, and the default
loopback server is plain HTTP, where a Secure cookie is never sent back.
The redirect target has already passed isLocalURL.
@imanimanyara
imanimanyara merged commit c928f07 into main Nov 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants