Skip to main content
PathMon

Chapter 11 of 26

Patch History and Live Logs

Updated Read the full guide

The Runs & History tab on the Patching page is where every past and pending patch run lives. This page covers how to read the history table, filter and search it, select runs for bulk actions, and work with the live log stream on the Run Detail page.


Getting to Runs & History

From the left sidebar, click Patching and switch to the Runs & History tab. The URL becomes /patching?tab=runs and is shareable.

Deep-link filters are also supported via URL parameters:

  • /patching?tab=runs&status=active: queued + running
  • /patching?tab=runs&status=failed: only failed runs
  • /patching?tab=runs&status=completed: only completed runs
  • /patching?tab=runs&status=pending_approval&type=patch_all: combined filter

These are the same URLs the Patching dashboard cards link to when you click the Total runs / Queued / Completed / Failed tiles at the top of the page.


The Runs & History Table

The table has eight columns on desktop:

Column What it shows
Delete checkbox Selects the row for bulk delete. Only shown for deletable statuses (queued, pending_validation, pending_approval, validated, approved, scheduled).
Approve checkbox Selects the row for bulk approval. Only shown for approvable statuses (validated, pending_validation, pending_approval).
Host Friendly name if set, otherwise hostname, otherwise host UUID. Clickable from the Run Detail page sidebar.
Type Summary of the run type: "Patch all", or a compact list of package names for patch_package (e.g. curl, openssl). Dry-runs render the same way but the status badge tells you it was a validation.
Status The run status badge plus an Extra deps pill when a validated run would install more packages than you requested.
Initiated by The username of the operator who triggered the run. Empty for runs triggered by automation.
Started created_at timestamp for not-yet-started runs, started_at for running / completed runs.
Completed completed_at timestamp if the run has finished, otherwise blank.
Actions Inline action buttons: Retry, Skip & Patch, Approve, View. See Inline row actions below.

On mobile (<768px) the table collapses into per-run cards with the same information stacked vertically. Actions sit at the bottom of each card as full-width buttons.

Pagination and page size

The table is paginated server-side via GET /patching/runs?limit=<N>&offset=<M>. The default is 25 rows per page. You can change the page size to 50, 100, or 200 from the dropdown at the bottom; your choice is remembered in localStorage under patching-runs-limit.

The runs list is sorted by created_at descending by default, with newest runs at the top. The server also accepts sort_by (created_at, started_at, completed_at, status) and sort_dir (asc, desc) but the UI does not expose sort controls today; filter and paginate to narrow the window instead.

Filtering

Two filters are available above the table:

  • Status: All, Active (queued + running), Queued, Pending validation, Pending approval, Validated (awaiting approval), Approved, Scheduled, Running, Completed, Failed, Cancelled.
  • Type: All, Patch all, Patch package.

Filters reset the pagination to page 1. Click Clear filters to remove both. The selected filters are also encoded in the URL, so you can bookmark or share a filtered view.

Empty states

  • If the filters match nothing, the table shows "No runs match your filters" with a prompt to adjust the filter.
  • If there are no runs at all (fresh install), the table shows "No patch runs yet. Patch runs triggered from the Overview tab or from host detail pages will appear here."

Inline Row Actions

The rightmost Actions column shows action buttons specific to the row's current status:

Status Buttons shown
pending_validation Retry (re-queue the dry-run), Skip & Patch (bypass validation and go straight to executing), View
pending_approval Approve, View
validated Approve, View
All others View only

View always opens the Run Detail page at /patching/runs/{id}.

Approve and Skip & Patch both route through the Patch Wizard in approve mode, even for a single row. This is a deliberate consistency choice: every path that turns a validation into a real run uses the same UI, so you get the per-host policy override UI for free (e.g. you can pick "Run immediately" at approval time even if the host normally has a delayed policy).

Retry re-queues the dry-run task without opening the wizard. Use this after bringing an offline host back online. Only available for patch_package runs; patch_all cannot be re-validated because it cannot be dry-run.

Bulk selection and bulk actions

At the top of each row are two optional checkboxes, one for delete and one for approve. Clicking them adds the row to a selection set; the header checkbox selects every eligible row on the current page.

Once at least one row is selected, a bulk action bar appears above the table:

  • Delete N selected: deletes every selected run. This also removes any scheduled asynq task for each run.
  • Approve N selected: opens the Patch Wizard pre-loaded with every selected validation. You get one Timing / Submit sequence for the batch, with per-host policy overrides.

After a bulk approval, the UI shows a summary banner (e.g. "Approved 5, 1 failed"). Failures for individual hosts are surfaced without blocking the other approvals.

You cannot mix a delete selection and an approve selection for the same row. The two checkboxes toggle independently and both sets are tracked. You can clear either selection at any time with the Clear delete or Clear approve buttons.


The Run Detail Page

Click View on any row, or navigate directly to /patching/runs/{id}, to open the Run Detail page. The layout is:

  • Header: back arrow, host name as H1, subtitle with run type / status badge / post-patch pill, and primary action buttons (Approve & Patch, Retry Validation, Skip & Patch, Stop Run) right-aligned.
  • Run summary sidebar (left, on desktop): host, type, initiated by, approved by, started / completed / scheduled-for, link to the linked validation run (when applicable), patch policy in effect, and the full list of packages affected.
  • Primary content (right): state banners for non-terminal statuses (pending_validation, pending_approval, validated), an error panel when error_message is set, and the Shell output terminal.

State banners

The Run Detail page shows a context-specific banner for every non-terminal state so you immediately know what the run is waiting for:

  • Pending validation: "Validation pending. Host may be offline." Explains that you can retry when the host is back, or skip validation.
  • Pending approval: "Awaiting approval." Explains that the run was submitted for approval and needs a second reviewer.
  • Validated: "Validation complete. Approval required." If the run would install more packages than you asked for, the banner includes the dependency count so you know to review the Packages affected panel.

Polling cadence

While the page is open, the Run Detail query refetches on a status-dependent interval:

  • queued, pending_validation: every 3 seconds.
  • running: every 5 seconds if the live WebSocket is open, every 3 seconds otherwise (the faster poll is a safety net if the WebSocket drops).
  • completed and the host is still flagged as "awaiting post-patch inventory report": every 3 seconds so the Awaiting inventory report pill flips to New report received as soon as the agent's next report arrives.
  • Everything else: no automatic refetch.

Live Log Streaming

When a run is running, the Run Detail page opens a WebSocket to /api/v1/patching/runs/{id}/stream. Authentication is the same JWT cookie you use for the rest of the UI; the outer Auth middleware handles the upgrade.

Message types

The stream speaks JSON. Three message types arrive from the server:

// Sent exactly once when the browser connects
{ "type": "snapshot",
  "patch_run_id": "...",
  "stage": "running",
  "shell_output": "Reading package lists...\n...",
  "error_message": "" }

// Sent for each line-buffered stdout/stderr chunk the agent pushes
{ "type": "chunk",
  "patch_run_id": "...",
  "stage": "progress",
  "chunk": "Setting up libssl3 (3.0.2-0ubuntu1.15)...\n" }

// Sent once when the run reaches a terminal stage on the agent
{ "type": "done",
  "patch_run_id": "...",
  "stage": "completed",        // or failed / cancelled / validated / dry_run_completed
  "error_message": "" }

The browser appends every chunk.chunk to its local terminal buffer. When done arrives, the browser closes the socket and invalidates the run query so the page refetches the final persisted state.

Keepalive

The server sends a WebSocket ping frame every 30 seconds to keep the connection alive through proxies and load balancers. Write operations use a 10-second deadline; a stuck client is dropped rather than pinning a goroutine. There is no agent or server-side retry logic for the browser socket. If the page reconnects (for example, after a brief network blip), the snapshot replays the full buffered output, and any missed chunks that were persisted to the database are included in it.

Why you may see "(No output yet)"

There's a small window after clicking Queue & patch where the run is still queued:

  • The asynq worker has not yet dequeued the task.
  • The agent has not yet received the run_patch command.
  • No output has been published.

The terminal shows "(No output yet)" during this window. The status badge tells you Queued; once the agent flips to running, the first chunk arrives within a second or two.

Terminal rendering

The Run Detail page renders output in a GitHub-dark-styled <pre> at ~420px tall (55vh max). It is scrollable and word-wrapping is preserved. Progress-bar \r characters from apt-get and dpkg are converted to \n so each progress update becomes its own line in the scrollback. You lose the animated overwrite but gain readability.

If you scroll up manually (more than ~32px from the bottom) the UI stops auto-scrolling so it doesn't fight you. Scroll back to within 32px of the bottom and auto-scroll resumes.

Copying output

When the run is in any non-running, non-queued state, a Copy output button appears above the terminal. It copies the full shell_output to your clipboard via navigator.clipboard.writeText. Use this to paste into a ticket, email, or post-incident report.


No Built-in Export or Download

There is no server-side export endpoint for runs. You cannot download a run's log as a file, and there is no CSV/JSON export of the Runs & History table. Options if you need one:

  • For a single run: use Copy output on the Run Detail page and paste into a file yourself.
  • For bulk analysis: hit the API directly (GET /api/v1/patching/runs?limit=200&offset=0) and write the JSON to disk. The API is paginated to 200 rows max per request, and authenticates with the same JWT bearer token as the web UI.

Note: The listed GET /patching/runs response includes the run metadata (status, timestamps, host info, package list) but not the full shell output. To get shell output in bulk, iterate over run IDs and fetch each with GET /patching/runs/{id}.


Notifications for Runs

Run lifecycle events emit notifications via the normal notifications pipeline. Destinations like SMTP, webhooks, or ntfy configured in Settings → Notifications see them. The events are:

Event type Emitted when Default severity
patch_run_started Agent reports running stage informational
patch_run_approved An operator approves a validation informational
patch_run_completed Agent reports completed stage (non-dry-run) informational
patch_run_failed Agent reports failed stage error
patch_run_cancelled An operator deletes a not-yet-running run informational

The notification message includes the host name, patch type, package list (truncated to 5 with "... and N more"), effective policy name, and (for failures) the captured error message truncated to 300 characters. Severity is resolved via the per-event alert settings, so you can raise or lower the default by event type in the alerts configuration.


Deleting Runs

Runs in queued, pending_validation, pending_approval, validated, approved, or scheduled state can be deleted. Deletion:

  1. Removes the row from patch_runs.
  2. Removes the asynq task (patch-run-<id> and patch-run-<id>-retry if it exists) from the queue.
  3. Emits a patch_run_cancelled notification event.

Terminal runs (completed, failed, cancelled, dry_run_completed) cannot be deleted from the UI; they are historical audit records. If you need to purge old runs for storage reasons, contact support or write a direct database query targeting patch_runs.created_at.

Running runs cannot be deleted. Use Stop Run (see Running a Patch) instead; that issues a graceful cancel through the agent.


  • Patching Overview: the three core concepts: run, policy, dry-run.
  • Running a Patch: detailed walkthrough of triggering, approving, and stopping a run.
  • Patch Policies and Scheduling: policy model, assignments, and how the schedule is computed.
  • Alerts and Notifications: configure where patch run events get delivered.