- ws_relay.rs: Add ALPN protocol http/1.1 to rustls ClientConfig to prevent
HTTP/2 negotiation which breaks WebSocket upgrades (Sec-WebSocket-Accept mismatch)
- ws_relay.rs: Add detailed TLS error chain logging for debugging connection failures
- ws_relay.rs: Add HTTP polling fallback when WebSocket connection fails, using
AgentClient to poll /api/v1/jobs/{id} every ws_relay_poll_interval_secs
- config.rs: Add ws_relay_poll_interval_secs field (default: 10 seconds)
- config.example.toml: Add ws_relay_poll_interval_secs documentation
- jobs.rs: Fire pg_notify with event_type job on cancel
- job_executor.rs: Fire pg_notify with event_type job when parent job transitions
- ws_relay.rs: Add event_type field to NotifyPayload (host vs job events)
- Frontend: Add event_type, succeeded_count, failed_count, host_count to JobWsEvent
- Frontend: handleWsEvent distinguishes host vs job events for accurate status updates
Worker was crashing on startup with:
Could not automatically determine the process-level CryptoProvider
Added rustls::crypto:💍:default_provider().install_default()
to pm-worker main.rs, matching the pm-web initialization.
- Frontend: handleWsEvent now distinguishes host vs job events
- Host events only update detail rows + optimistic counters
- Job events (event_type=job) set authoritative status + counts
- Backend ws_relay: NotifyPayload now includes event_type field
- Host events: event_type=host
- update_parent_job_status fires pg_notify with event_type=job
- Backend job_executor: sync_job_status fires pg_notify with event_type=job
- Backend jobs cancel endpoint fires pg_notify with event_type=job
- Fixes jobs appearing stuck because host status was mapped to job status
BUG-15: Empty patch_selection sent to agent as-is, causing apply nothing
instead of apply all available patches per SPEC. When packages is empty,
now query host_patch_data and expand to full package list.
BUG-16: refresh_listener used INSERT instead of UPSERT for
host_patch_data, causing duplicate key constraint errors.
BUG-14: Patch poller was INSERT-ing a new row every poll cycle instead of
UPSERT-ing, creating 51 duplicate rows in host_patch_data for a single host.
Changes:
- patch_poller.rs: Changed INSERT to INSERT...ON CONFLICT (host_id) DO UPDATE
so each host only has one row that gets updated on each poll
- Migration 006: Added UNIQUE constraint on host_id, cleaned up 50 duplicate
rows keeping only the latest polled_at per host
The dashboard showing 174 pending patches and 0% compliance is expected
behavior - the patch data was collected before the job ran and the poller
runs every 30 minutes. The next poll cycle will refresh the data.
The linux_patch_api agent returns "completed" as its terminal success
status, but the worker only recognized "succeeded". This caused the
worker to log "unexpected agent status — ignoring" every 60 seconds
and never mark patch jobs as finished.
Changes:
- job_executor.rs: match "completed" alongside "succeeded" as terminal
success status, mapping both to patch_job_hosts.status = succeeded
- job_executor.rs: match "cancelled" as a terminal failure status,
routing to handle_host_failure with appropriate error message
- pm-agent-client types.rs: updated AgentJobStatus doc comment to
list all valid agent statuses: queued, running, succeeded, completed,
failed, cancelled
BUG-10: PostgreSQL inet type includes CIDR suffix (/32) when cast to text,
causing malformed agent URLs like https://127.0.0.1/32:12443.
Fixed by using host(ip_address)::text in all SQL queries across
pm-worker and pm-web modules, and adding a Rust-side safety
strip of CIDR notation in pm-agent-client.
Files changed:
- crates/pm-agent-client/src/client.rs: strip CIDR suffix from IP
- crates/pm-worker/src/health_poller.rs: host(ip_address)::text
- crates/pm-worker/src/patch_poller.rs: host(ip_address)::text
- crates/pm-worker/src/refresh_listener.rs: host(ip_address)::text
- crates/pm-worker/src/job_executor.rs: host(ip_address)::text (2 places)
- crates/pm-worker/src/ws_relay.rs: host(h.ip_address)::text
- crates/pm-web/src/routes/discovery.rs: host(ip_address)::text (2 places)
- crates/pm-web/src/routes/hosts.rs: host(ip_address)::text (3 places)
- docs/linux_patch_api_research.md: added research notes