feat(host_agent): how far each host's clock is from Steward's (milestone 490)
CI / lint (push) Successful in 5s
CI / integration (push) Successful in 1m8s
CI / unit (push) Successful in 1m34s
CI / publish (push) Successful in 2m27s

- Agent 1.12.0 stamps each post with sent_at; ingest compares it with the
  moment the post arrived: clock_offset_s / clock_checked_at on the
  registration (host_agent_003), and a clock_off_secs metric (either way)
  that alert rules can watch. Replaces the old skew log, which measured the
  newest sample and was off by up to an interval.
- Settings -> Thresholds: Clock drift, 30 s warn / 5 min critical.
- Host page: 'clock 0.3s ahead' in the vitals line, toned past the warning.
- Hosts list: names hosts whose clock is off; when every reporting host is
  off by about the same amount, says Steward's own clock is the likely one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 08:00:33 -04:00
co-authored by Claude Opus 5.5
parent 766eef04c5
commit 96c1ea9407
15 changed files with 217 additions and 11 deletions
+4 -1
View File
@@ -92,7 +92,10 @@ async def _list_page(add: dict | None = None):
ctx = dict(hosts=shown, total=len(hosts), host_status=host_status, uptime=uptime, agent=agent,
groups=groups, group_names=group_names, f=f, filter_url=filter_url, sort_url=sort_url,
states=host_svc.HOST_STATES, no_group=host_svc.NO_GROUP, add=add)
states=host_svc.HOST_STATES, no_group=host_svc.NO_GROUP, add=add,
clock=host_svc.clock_trouble(
agent, (current_app.config.get("THRESHOLDS", {}).get("clock") or {"warn": 30})["warn"]),
host_ids={h.name: h.id for h in hosts})
# The filter bar's swap gets the table alone. A history restore (Back to a
# pushed URL) is an HTMX request too but wants the page, so key on the
# target; Vary keeps the browser from caching one as the other.