`formatRelativeTime` is past-only: a time ahead of now reads "just now"
(compact) or "0s ago". Three Patrol lines handed it future times, so
each read wrong for its whole duration:
- Attention detail, suppressed issue: "Returns to active attention just
now." (also for an issue marked reviewed, which expiry hands back to
Reviewed, not active attention)
- Findings, will-fix-later: "Reminding just now"
- Findings, snoozed: "snoozed until just now"
Supersedes #2613, rebuilt on main now that #2598 has landed (#2613 was
stacked on two early #2598 commits whose contract wording main has since
replaced).
## Change
- `formatTimeUntil` in `utils/format.ts`, beside `formatRelativeTime`:
counts down ("in 3h", "in 1d"), rounds to the nearest unit (a 24-hour
suppression set a moment ago reads "in 1d", not "in 23h"), and returns
`dueText` (default "now") once the time arrives. `formatRelativeTime`
stays past-only and its doc points future times at the new helper.
- The three lines use it with the shared relative-time clock
(`useRelativeTimeNow`), since none of those times change while the panel
stays open, and keep the exact time on hover.
- Suppression line: "Returns to active attention in 1d." for an open
issue, "Suppression ends in 1d. It stays reviewed." for one marked
reviewed, "Suppression has ended." once due. It takes its words from
`getAttentionSuppressionEndPresentation` (added on main by 844e1dbb97
for the early-end button and duration field), so the countdown, the
button and the duration label always share one reviewed/unreviewed
split. An unparseable expiry renders no line.
- Findings: "Reminding in 7d", then "Reminder overdue". The label, the
Overdue commitments chip and its filter all read one overdue id list
sampled on the shared clock, so they agree even for a row that mounts
between ticks (it reads "Reminder due" until the next tick), and a tick
that moves no deadline past now does not rebuild open rows. The reminder
hover no longer promises the finding returns only "if it is still
tripping"; the hourly reminder sweep brings it back regardless.
- Snooze: "snoozed, returns in 49m", then "snooze ended" until the next
findings read drops the snoozed status (the findings API sends no
status; the frontend store derives `snoozed` from `snoozed_until` on
each read).
- Audit of every `formatRelativeTime` /
`formatPlatformTableRelativeTimeValue` caller: no other future
timestamps. The autopilot and alert snooze expiries already show
absolute times.
The existing countdowns were not a fit: `CountdownTimer` runs its own
one-second interval with no day unit (a 7-day suppression would read
"167h 59m"), and the replication Next sync helper is private to that
table.
Contract notes sit mid-file: the frontend-primitives note follows main's
wall-clock sentence and precedes the read-time age exception (now opened
as "The relative-age rule has one deliberate exception"), away from the
paragraph #2600 adds after that exception.
## Verification
- Unit and component: `format.test.ts`, `FindingsPanel.test.ts`,
`FindingsPanel.links.test.tsx`, `PatrolAttentionWorkbench.test.tsx`. The
countdown tests fail against main's components; the new between-ticks
test fails against the first draft of this rebuild (the row said
"Reminder overdue" with no chip). Patrol, AI, alerts, utils and pages
suites pass apart from `agentInstallCommand.test.ts`, which needs Linux
`stat`; `tsc --noEmit` is clean.
- Live, on a private mock stack built from this tree at 1440 and 390,
with the websocket held (server frames dropped, heartbeat answered),
every GET replayed from cache and the page clock stepped one tick at a
time: an unreviewed and a reviewed suppressed issue counted down 1d →
20h → 29m → "Suppression has ended." with the matching end button; the
reminder went "Reminding in 7d" → "Reminder overdue" with the Overdue
chip on the same tick and findings reads still pending; set 5 seconds
past the deadline with no tick it read "Reminder due" with no chip, and
both moved together on the next tick; the snooze went "in 49m" → "in 9m"
→ "snooze ended", and the next findings read moved it to Active. With
main's components swapped back under the same Vite, the lines read "just
now" and stayed frozen. Receipt:
`frontend-modern/browser-verification/82459c28d2633b61.json`.
- Two second-model review rounds; the first found the between-ticks
disagreement and four wording nits, the second confirmed the fix on
every reminder path and found two nits, both applied.
Pulse documentation
Start here for installation, platform setup, security, operations, and Pulse Intelligence. Commands, configuration keys, image names, API fields, and product identifiers remain untranslated in localized guides.
Start here
- Install Pulse — signed Proxmox/Linux installation, Docker, Docker Compose, Kubernetes, and first-run setup.
- Production deployment and security — least-privilege Proxmox setup, root-agent boundaries, discovery, supply-chain verification, plan limits, scale evidence, and a rollout checklist.
- Upgrade from Pulse v5 — migration prerequisites, rollback, agent continuity, and post-upgrade checks.
- Releases and update channels: choosing Stable or Preview, beta and RC expectations, and how releases are tested and promoted.
- Configure Pulse — authentication, notifications, discovery, retention, and system settings.
- Deployment models — data locations, lifecycle, and differences between supported deployment paths.
- Troubleshooting and FAQ — common failures, diagnostics, and operator questions.
Localized getting started guides: Deutsch · Español
Platforms and agents
- Proxmox Backup Server
- Proxmox Mail Gateway
- Docker and Podman
- Kubernetes and Helm
- TrueNAS SCALE and CORE
- Unified Agent
- Agent security
- VM disk monitoring
- ZFS monitoring
- Temperature monitoring
VMware vSphere support is early access. Current builds expose dedicated vSphere inventory and recovery context, but operators should validate the integration against their own vCenter before production use.
Monitoring and operations
- Metrics history
- Recovery data
- Webhooks
- Automatic updates
- Centralized agent management (Pro)
- Operational trust model
- Current product screenshots
Pulse Intelligence
- Assistant, Patrol, and external-agent overview
- Patrol modes and safety
- Assistant safety model
- External agent HTTP and MCP substrate
Patrol watch-only analysis is available on Community with a local model or the operator's own provider. Investigation and governed fixes require the relevant Pulse Pro capabilities.
Security, privacy, and access
- Production deployment and security
- Security review scope
- Authentication and credential storage review packet
- Security guide
- Privacy and telemetry disclosure
- OIDC and SSO
- Proxy authentication
- Role-based access control (Pro)
- Audit logging (Pro)
- Reverse proxy configuration
- Code-signing policy
Plans and managed access
- Community, Relay, and Pro capabilities
- Relay and Pulse Mobile handoff
- Multi-tenant organizations (Enterprise/custom)
- Provider-hosted MSP operations (request-assisted)
Pulse Cloud is not generally available. Ordinary self-hosted Pulse remains the primary installation path; MSP and Enterprise access are explicit commercial paths rather than defaults in self-hosted setup.
Development and reference
Detailed design notes and dated migration specifications may remain in this directory for maintainers, but they are not operator setup guides unless they are linked from the sections above.
Previous versions and migrations
- Upgrade from v4 to v5
- Retired unified-navigation migration — historical context only; current Pulse uses platform-shaped navigation.
- Move a Pulse installation
Found a bug? Use the issue forms. For setup questions, use GitHub Discussions.