Skip to content

feat(web): managed server setup progress & recovery UX (PLO-94)#51

Merged
ismatBabirli merged 5 commits into
mainfrom
feature/plo-94
Jun 27, 2026
Merged

feat(web): managed server setup progress & recovery UX (PLO-94)#51
ismatBabirli merged 5 commits into
mainfrom
feature/plo-94

Conversation

@ismatBabirli

Copy link
Copy Markdown
Contributor

Adds the dashboard UX for dashboard-managed server setup (drives PLO-93's ServerSetupService). Stacked on #50 (PLO-93) — review/merge that first; this PR's base is feature/plo-93.

What

Connect Server now has two paths (features/servers/connect/):

  • Run a command — the existing one-line installer (unchanged).
  • Plorigo sets it up — a managed SSH form (host, port, username, password or private key) that calls StartSetup, then streams progress.

Live progress + recovery (SetupProgress):

  • Step timeline (connecting → detecting OS → checking access → pre-flight → installing Docker/Caddy/agent → provisioning the plorigo user → waiting for heartbeat → ready), polled from GetSetupRun / ListSetupEvents (the same poll-the-events pattern as deployments; stops on a terminal status).
  • Progressive disclosure: plain-English summary first, the server-redacted raw log one click away.
  • Failures show the reason + Retry and Use a command instead; at most one server record is created and reused across retries/tab switches (no orphans).

Server cards distinguish no agent / setting up / ready / degraded / failed setup, and expose Rotate key / Revoke SSH access when an active managed credential exists (RotateManagementKey / RevokeManagementKey, confirmed dialogs).

Security

The one-time bootstrap credential is held in client state only until StartSetup is called, then cleared on success and failure — never re-displayed, logged, or placed in error text. Setup-event messages are redacted server-side; the UI never renders a token/password/key.

Tests

There was no web test harness — this adds vitest + @testing-library/react (jsdom) and component tests covering manual setup, managed success, managed failure, retry, and rotate/revoke (incl. revoked-key gating). Wired into CI as a Web test step; pnpm-lock.yaml updated.

Wiring & docs

setupClient + the ServerSetupService dev proxy entry + useSetupRun/useSetupEvents/useManagementKey hooks + setup status tones; docs/architecture/dashboard.md updated.

Verification

pnpm --dir apps/web typecheck ✓ · lint ✓ (0 errors) · test ✓ (8) · build ✓.
The actual SSH bootstrap is exercised by PLO-93's backend; this PR is UI-only.

…ls (PLO-92)

Dashboard-managed server setup needs a persistent SSH management channel, which
means the control plane must hold a credential that can reach the server. This adds
the credential store and lifecycle that bounds it — the foundation the SSH bootstrap
runner (a follow-up) builds on. It does not open any SSH connection yet.

New `internal/serversetup` module (mirrors `secrets`):
- ssh_management_keys table (migration 00019): server-scoped, one credential per
  server, holding the SHA256 fingerprint, public authorized_keys line, the SEALED
  private key (AES-256-GCM via the existing crypto box + APP_MASTER_KEY), rotation
  state, last-used/rotated/revoked timestamps, and created_by. No raw bootstrap
  credential is ever a column.
- The private key is write-only: no RPC returns it. ServerSetupService exposes only
  GetManagementKey / RotateManagementKey / RevokeManagementKey (metadata + public key).
  Provision, MarkUsed, RecordFailedAuth, and OpenPrivateKey are in-process only, for
  the future SSH runner.
- Workspace-scoped authorization (resolved from the server) before every mutation,
  with the audit record committed in the same transaction. Provision, use, rotation,
  revocation, and failed-auth are each audited; reads are not.
- New authz actions (server_setup.run / key.rotate / key.revoke / key.read): running
  managed setup and the destructive lifecycle ops are owner/admin only; reading
  non-secret metadata is allowed for every role.

New `internal/platform/sshkeys` generates the ed25519 keypair (distinct from the
agent's job-signing key), returning the sealed-at-rest private PEM, the authorized_keys
line, and the fingerprint.

Unit tests cover encryption, redaction, authorize-before-mutate, audit behavior,
rotation, revocation, and cross-workspace denial. Docs (server-management.md,
data-and-api.md) updated to record the shipped storage module and the new table.
Add the dashboard-managed setup path that complements the one-line installer: the user
enters fresh-VPS connection details and Plorigo prepares the server for them over SSH,
as an asynchronous, audited run. Builds on the serversetup credential store (PLO-92).

ServerSetupService gains StartSetup / GetSetupRun / ListSetupEvents:
- StartSetup validates host/port/username + a one-time bootstrap credential (password or
  private key), creates a run (auditing start vs retry), and dispatches the bootstrap on a
  background goroutine carrying the caller's principal. The raw bootstrap credential is held
  only on the stack for the attempt — never written to the DB, never logged.
- The run connects over SSH (TOFU: pins the host key on first connect, refuses a later
  mismatch), then executes ordered steps via an SSHExecutor port: detect OS (Ubuntu
  22.04/24.04 only, else a clear reason), check root/sudo, preflight (apt lock, ports 80/443,
  Docker present/old, UFW), drive scripts/install-agent.sh with a minted registration token,
  provision the non-root `plorigo` user + its authorized_keys + a wildcard-free scoped sudoers
  drop-in (validated with visudo), and wait for the agent's first heartbeat.
- Ordered, redacted status/log lines persist to server_setup_runs / server_setup_events
  (migration 00020) and are polled by the dashboard, mirroring deployment events. The install
  command carries the token but is never emitted; only the installer's own (self-redacted)
  output is streamed.
- TOFU host-key fingerprint is pinned on a new servers.host_key_fingerprint column.

The step runner depends only on an SSHExecutor port, so it is fully unit-tested with a fake
executor across the required scenarios: success, unsupported OS, missing root/sudo, apt lock,
Docker already installed, old Docker, occupied ports, UFW active, agent heartbeat timeout,
transport failure, and a provisioning failure — plus a check that the registration token
never leaks into an event. The real x/crypto/ssh dialer is the thin, manually-verified
boundary. The agents module is reached through an adapter wired in internal/app, so
serversetup never imports it.

The durable job queue isn't built yet, so the run is an in-process goroutine that persists
its steps; a process restart mid-run leaves the run for the user to retry. Docs
(server-management.md, jobs-and-realtime.md, data-and-api.md) updated.
Connect Server now offers two paths: run a one-line command yourself, or let
Plorigo prepare a fresh Ubuntu box over SSH (the PLO-93 ServerSetupService).

- Two-tab ConnectServerDialog (features/servers/connect/): manual command, or a
  managed SSH form collecting host, port, username, and a one-time bootstrap
  credential (password or private key). The credential lives in client state only
  until StartSetup is called, then is cleared on success AND failure — never
  re-displayed, logged, or put in error text.
- SetupProgress: a live step timeline polled from GetSetupRun / ListSetupEvents
  (the deployment-events poll pattern), with the server-redacted raw log one click
  away, plain-English failure summaries, and recovery actions (retry, or fall back
  to the command). At most one server record is created and reused across retries
  and tab switches.
- Server cards distinguish no agent / setting up / ready / degraded / failed setup,
  and expose Rotate key / Revoke SSH access when an active managed credential exists.
- Wiring: setupClient + ServerSetupService dev proxy + useSetupRun/useSetupEvents/
  useManagementKey hooks + setup status tones.
- Adds a vitest + @testing-library/react harness (none existed) and component tests
  covering manual setup, managed success, managed failure, retry, and the
  rotate/revoke affordances; runs in CI (Web test step).
- Updates docs/architecture/dashboard.md.
@linear-code

linear-code Bot commented Jun 16, 2026

Copy link
Copy Markdown

PLO-94

# Conflicts:
#	internal/platform/database/db/models.go
#	internal/policy/service.go
The managed SSH setup form was only reachable from the page-level Connect
server button, so a server first connected with a command — or one whose
password later changed — had no way to (re-)run setup. The backend StartSetup
is already re-runnable (reuses the pinned host key, re-provisions the
management credential), so this exposes it from the server card.

- ConnectServerDialog gains an optional existingServer prop: opens on the SSH
  tab, locks the name, and reuses the existing record instead of creating one.
- ServerCardActions adds a Set up over SSH / Re-run setup action.
- ServersPage drives a second, keyed dialog in existing-server mode, sharing
  the managed-run tracking so the card shows live setup progress.
@ismatBabirli ismatBabirli merged commit cd8fd4e into main Jun 27, 2026
7 checks passed
@ismatBabirli ismatBabirli deleted the feature/plo-94 branch June 27, 2026 12:42
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant