{# Per-host agent panel — embedded into /hosts/ via HTMX. Self-contained. #} {% set scope = "steward:target:" ~ target.id if target else "" %}

Agent

{% if reporting %} {{ 'stale' if stale else 'reporting' }}{% if reg.agent_version %} · v{{ reg.agent_version }}{% endif %} · last seen {{ reg.last_seen_at|ago }} {% elif reg %} pending — no check-in yet {% endif %}
{% if reporting %} {# Reporting: live vitals are shown by the vitals strip above; this panel is the way to the full metrics, with the lifecycle actions folded under "Manage agent" (#5278). #} Full metrics → {# What the host has waiting, from the agent's hourly check (#5765): a needed reboot first, then the updates. #} {% if reg.updates_checked_at or reg.reboot_required or maint_job %}
{# Steward's own maintenance job on this host (#5767): a running one first. #} {% if maint_job %} {% set running = maint_job.state not in ('done', 'failed', 'timed_out') %}
Maintenance: {{ maint_summary }} · {% if running %}since {{ maint_job.state_at|ago }}{% else %}{{ maint_job.finished_at|ago }}{% endif %}{% if maint_job.state in ('failed', 'timed_out') and maint_job.detail %} · {{ maint_job.detail|truncate(90) }}{% endif %}
{% endif %} {% if reg.reboot_required %}
Reboot required{% if reg.reboot_required_since %} since {{ reg.reboot_required_since|ago }}{% endif %}{% if reg.reboot_packages %} · {{ reg.reboot_packages|truncate(80) }}{% endif %}
{% endif %} {% if reg.updates_checked_at %}
{% if reg.updates_error %}Update check failed: {{ reg.updates_error }} {% elif reg.updates_pending %}{{ reg.updates_pending }} update{{ 's' if reg.updates_pending != 1 }} waiting{% if reg.updates_security %} ({{ reg.updates_security }} security){% endif %} {% else %}Up to date{% endif %} · checked {{ reg.updates_checked_at|ago }}
{% endif %}
{% endif %} {# What the agent said happened while it was away, last 7 days (#5756). #} {% if downtime %}
Downtime, last 7 days
{% for d in downtime %}
{{ d.label }} {{ d.started_at|when }} {{ 'going on, ' if d.ongoing }}{{ d.seconds|duration }}
{% endfor %}
{% endif %} {% if session.user_role == 'admin' %}
Manage agent
{% if ansible_available and target %}
{{ csrf_field() }}
{% endif %}
{{ csrf_field() }}
{{ csrf_field() }}
{% if ansible_available and not target %}

Link an Ansible target (below) to update the agent from here.

{% endif %} {# Update now (#5767): the agent's helper updates the host and reboots it only if the update needs it, draining its swarm node first. #} {% if maint_blocker %}

Can't update from here: {{ maint_blocker }}.

{% else %}
{{ csrf_field() }}
{% endif %} {# The root helper that carries out scheduled updates and reboots (#5766). #}

{% if reg.maint_helper %}Maintenance helper installed: Steward can update and reboot this host. {% else %}No maintenance helper yet: Update agent installs it, so Steward can update and reboot this host.{% endif %}

{% if ansible_available and target and managed_key_set %}
Re-provision (reinstall the steward account + managed key, then the agent)

Use after regenerating the managed key, or if SSH auth as steward breaks. Connects over a one-time bootstrap user + password (not stored).

{{ csrf_field() }}
{% endif %}
{% endif %} {% else %} {# ── Not reporting: pending (token minted, no check-in) or never installed ── #} {% if reg %}
A token was minted for this host but no metrics have arrived yet — the deploy may still be running, or it failed. Open the latest Ansible run to check, then retry below. Live metrics appear here once the agent checks in.
{% else %}

No agent installed. The agent reports CPU, memory, disk, network and more back to Steward.

{% endif %} {% if session.user_role != 'admin' %}

An admin can install the agent here.

{% elif not ansible_available %}

Ansible isn't available, so the agent can't be deployed from here. Install it manually instead (below).

{% elif not managed_key_set %}
⚠ No managed SSH key yet — Steward needs one to log into hosts.
{{ csrf_field() }}
{% elif not target %}

Link or create an Ansible target for this host (in the Ansible section below) first — provisioning needs an SSH connection.

{% else %}

Deploys the agent to {{ target.name }} by running an Ansible playbook. Steward mints the host's API token automatically; you'll watch the run live.

{# Provision: brand-new host (creates steward account + key over a one-time password) #}
{{ csrf_field() }}
Provision (first contact — fresh host):
{# Install: host already has the steward account (managed key works) #}
{{ csrf_field() }} Already provisioned?
{% endif %} {% if not reg and session.user_role == 'admin' %} {# A manual install: no Ansible, no SSH. Issues this host's token and shows the one-line install command on the fleet page, once (#5282). #}
{{ csrf_field() }} Or by hand on the host:
{% endif %} {% if reg and session.user_role == 'admin' %}
{{ csrf_field() }}
{% endif %} {% endif %}