Skip to content

Feat/2026.14.4 - #31

Merged
ConnorHaggerty merged 13 commits into
mainfrom
feat/2026.14.4
Oct 12, 2026
Merged

ConnorHaggerty merged 13 commits into
mainfrom
feat/2026.14.4

Conversation

@ConnorHaggerty

@ConnorHaggerty ConnorHaggerty commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Containers: the Images, Volumes and Networks tables fit the page without scrolling sideways. Long names wrap; IDs, sizes and actions stay on one line.
  • Store:
    • Search ignores capitals and ranks matches in the database, so "pycharm" returns PyCharm first.
    • Sync errors are shown, with a Sync now button, so a failed winget sync no longer leaves the Store silently empty.
    • "Save as task" saves an install as a task and opens it in the editor.
  • Apps, By update: a "Policy..." action on each row and in the bulk bar creates a policy from the update.
  • Playbook inputs:
    • Playbooks can declare inputs and map them onto their steps.
    • Tasks, automations and rollouts that call a playbook can fill those inputs.
    • A deploy's raw inputs can no longer override a step's own inputs.
  • Task templates:
    • The editor's templates are YAML files on the server, served from /api/v1/tasks/editor-templates/.
    • Each one has comments that explain it, and a new Learn 1-9 series teaches the task language.
    • Install-trivy and the other templates people use are kept.
  • Template playbooks: five playbooks in a new Templates library, plus six built-in tasks they use.
  • Topology:
    • The agent now reports each host's addresses, listening ports and connections.
    • A new /api/v1/hosts/topology/ endpoint returns hosts, addresses, ports, firewalls, stacks and links.
    • A new Topology page draws this as a map grouped by site, with boxes around tags that you can show or hide, a detail panel for each host, and an optional Internet node and container rows.

Notes

  • Topology only has full data from hosts that run the 14.4 agent. Until they do, the page shows a banner and those hosts appear with their known IP only.
  • On the topology map, links can pass behind cards, and the map does not refit when the window is resized. The Fit button refits it.
  • This PR has no release commit; the agent version is not bumped yet.

Test plan

  • Server suite: 2534 tests pass (1 skipped), on SQLite
  • Agent suite: 817 tests pass
  • Browser pass against a dev server:
    • Store search, Save as task, Policy from an update
    • Containers tables at 1024px
    • Playbook input dialog, task templates, template playbooks
    • Topology in dark, light and mobile layouts
  • CI against TimescaleDB

Long image tags, mount paths and lists of containers made the resource
tables wider than the page. They now use fixed column widths and wrap
inside each column.
… as task, policy from an update

- Store search matches every word in any order and case, and treats
  dashes, dots and underscores as spaces. It ranks in the database
  (the app's own name first, then ids, then summaries) instead of
  sorting the first 500 matches by name, which could drop the app
  you typed when many summaries matched.
- The Store says when a package list failed to sync, or has not been
  downloaded because no machine uses it, instead of an empty tab. An
  admin can press Sync now (POST /api/v1/catalog/sync/).
- An app's page has Save as task beside Install..., which saves the
  task and opens it in the task editor.
- Apps > By update has a Policy... button on each row and for the
  selected rows. A Linux package becomes an app rule, and a Windows
  update turns on patching for its classification.
…llouts

A playbook can declare inputs in the same shape as a task's inputs:.
Each step maps them onto its task's inputs with ${{ inputs.x }}
references, and a task input with the same id as a playbook input gets
its value without a mapping.

Values come from whoever runs the playbook:
- an automation (Automation.inputs, for a task or a playbook);
- a rollout (inputs in the start request, for a task or a playbook);
- a task that calls it inline with type: playbook and params.inputs,
  whose values may refer to the calling task's own inputs.

Auto-enrolment has nobody to fill inputs in, so a playbook can only
auto-enrol when every required text input has a default. The inputs
are validated when the playbook is saved, and also when it is
imported or exported as community YAML.
…'s inputs

Resolving an inline playbook's steps passed the deploy request's raw
inputs dict as the values to substitute. A key named like a step's task
input, even one the calling task does not declare, replaced the value
the playbook had resolved and validated for that step. Only the
playbook's resolved values are substituted now. The calling task's own
inputs are already substituted into the playbook reference, and checked
against its declarations, before expansion.
…hem in rollouts and automations

QA-28 gave the server playbook inputs, per-step mappings, automation.inputs and
rollout inputs; nothing in the UI could produce any of them, so the feature was
reachable only from a shell.

The declared-inputs form the deploy dialog already had is now shared:
inputsFormMount/inputsFormRead in vigil-pickers.js take a container and starting
values, which is what the rollout start dialog and the automation's Inputs
dialog both need. The playbook editor keeps its declarations in _blInputs and
posts them; the step inspector writes a task's declared inputs onto the step
under the key the API parses; the automation editor stores values for the picked
target's declared inputs and keeps the old per-action override editor as
'Advanced: override action params'.

The editor's Inputs note had to be wrapped in {% verbatim %}: the literal
${{ inputs.<id> }} is Django template syntax to the parser, and the render test
in test_inputs_ui.py is what keeps it that way.
The topology view (QA-31/QA-32) needs what a host has never told us: its
interface addresses, what it listens on, and which machines it talks to.
Only Host.ip_address, container ports and the firewall existed before.

The agent collects a network block every 5 minutes and rides it on the
check-in under the same absent-means-keep-the-last-snapshot contract as the
other optional collectors. Socket enumeration needs root on macOS, so an
AccessDenied ships the interfaces it did get with partial: true rather than
dropping the whole block. Connections are aggregated on the host they talk
to, counting on the local port for inbound sessions — an inbound aggregate
keyed on the ephemeral remote port would never count past one.

The server keeps one HostNetwork row per host, clipped and validated on the
way in (addresses, ports, protocol and direction whitelists, list caps) so a
broken or hostile agent cannot fill the database through these lists.
…s and links

GET /api/v1/hosts/topology/ gives the topology page everything it draws,
for the hosts the user can see:
- sites and tags;
- IPv4 subnets the hosts share;
- per host: its addresses and primary IP, listening ports (marked public
  when they bind more than loopback), a firewall summary, stacks with
  their containers and ports, and its busiest destinations outside the
  fleet;
- unmanaged devices with an IP;
- links between hosts and devices, built from each host's connections.
  The same connection seen from both ends counts once.

The number of queries does not grow with the number of hosts.
…t dialog lays out, Save as task names the task
@ConnorHaggerty
ConnorHaggerty merged commit 9820270 into main Oct 12, 2026
7 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.

1 participant