292 Commits
Author SHA1 Message Date
Alex Holliday a2c978527f bump express to 5, migrate, bump multer to 2 2026-09-25 12:57:35 -07:00
Alex Holliday d8e4c775d5 update tests 2026-09-25 11:51:10 -07:00
Alex Holliday 98a5fa9941 fix names 2026-09-25 11:39:16 -07:00
Alexander Holliday 1a287be323 Merge pull request #3995 from bluewave-labs/fix/decision-refactor
fix: convert decision objecty into a state object
2026-09-25 11:13:45 -07:00
fabiangigler e15a85bd61 Merge latest develop into monitor sorting
# Conflicts:
#	client/src/Features/UI/uiSlice.ts
2026-09-25 11:31:38 +02:00
Alex Holliday 4ae8426766 convert decision obj into a state object 2026-09-24 15:13:10 -07:00
Alex Holliday 5d77b8804f update tests 2026-09-24 14:22:31 -07:00
Alex Holliday 18f38818e2 update tests 2026-09-24 14:03:34 -07:00
Alex Holliday f2c23636fe type only imports, add empty guard for threshold breaches do Docker monitors don't get this 2026-09-24 13:20:03 -07:00
Alex Holliday 11c89d150a update tests 2026-09-24 12:58:28 -07:00
Alex Holliday 7abc74ac98 udpate tests 2026-09-23 13:54:55 -07:00
Alex Holliday 0d75629dcb refactor out rebuilding of status response 2026-09-23 13:38:02 -07:00
Alex Holliday 4a66cf6e11 improve email error handling 2026-09-23 12:13:27 -07:00
Alex Holliday 7fb57d8090 fix incident links with helper 2026-09-23 11:46:58 -07:00
Alex Holliday 6d4e944a58 Add tests 2026-09-23 11:17:01 -07:00
fabiangigler 2a45b4d46f Persist monitor list sorting 2026-09-23 12:21:26 +02:00
Alexander Holliday a5e66318b5 Merge pull request #3975 from nikhilsaxena04/feat/status-page-incident-history
feat: add clickthrough incident history to public status page (#3972)
2026-09-22 10:15:57 -07:00
nikhil2004 55184724d0 fix: resolve failing CI checks 2026-09-22 04:42:32 +05:30
nikhil2004 de4c528d55 fix: address PR feedback 2026-09-22 03:33:27 +05:30
nikhil2004 7f0e577dd2 feat: clickthrough historical data view for status page downtime (#3972)
This adds the ability for users to click on any day block in the historical uptime chart to see the specific incidents that occurred on that day.

Closes #3972
2026-09-22 03:31:14 +05:30
Alex Holliday 4a2429d316 docker capture fixes 2026-09-21 10:22:46 -07:00
Alexander Holliday d7f8bcc9e1 Merge pull request #3973 from fabiangigler/feat/capture-docker-monitors
Support Capture-backed Docker monitors
2026-09-21 10:05:51 -07:00
Alex Holliday 8dd57d45e2 port egress to pipeline 2026-09-20 17:37:42 -07:00
Alex Holliday d4d2735d23 exempt local docker sockets 2026-09-20 16:37:02 -07:00
Alexander Holliday 253642dde9 Merge pull request #3963 from web-engineer/feat/egress-2-check-persistence
Egress self-check (2/4): flag checks taken during degraded egress (#3937)
2026-09-20 16:31:06 -07:00
Craig Lawson 1b3b8e5312 Clear the status window when a degraded spell starts
The window was carried across a spell, so the results either side of it ended up
adjacent: a monitor at [t, t, t, f, f] when egress dropped resumed at
[t, t, f, f, f] on its first real failure afterwards, crossing the threshold on
results taken before the outage.

Cleared in the producer beside the maintenance gate, which already clears the
window on entering a window, and guarded on its length so it is one write per
spell rather than one per degraded check. The monitor still goes down once the
rebuilt window holds enough real failures.

The two integration tests that asserted the old behaviour are rewritten: one
expected the window preserved through a spell, the other expected failures
either side of a spell to combine.

🔧
2026-09-19 14:43:16 +01:00
Craig Lawson b55560e2f1 Report whether the peer answered from every provider that can tell
An egress spell masked a service on the surviving network going down: with
`peerResponded` set only by the DNS and gRPC providers, every other failure fell
back to the HTTP-status heuristic, which answers "nothing replied" for anything
that is not speaking HTTP. A refused connection on the local network was read as
the instance's own loss of egress, so the check was recorded as degraded and the
monitor stayed up.

`peerAnsweredFromError` holds the errno codes that require the target's own stack
to have replied — a refusal, a reset, a TLS handshake that got as far as a
certificate. Http, Port, WebSocket and Docker use it; Http also treats any status
code as an answer, and Docker any status from the daemon. Hardware and PageSpeed
go through Http and inherit it.

`true` is the claim that costs something, since it blames the target, so it is
only set on evidence. Two cases stay conservative and are commented as such: a
ping that draws no echo reply, and a gamedig query that fails, neither of which
can be told apart from the instance losing its egress. Nor can a proxied HTTP
request that fails with no response, where the refusal may be the proxy's.

🔧
2026-09-19 14:43:11 +01:00
fabiangigler 862ff15eef Support Capture-backed Docker monitors 2026-09-18 19:54:33 +02:00
Alexander Holliday da8b230cec Merge pull request #3957 from Asgabani/fix/temp-breach-boundary-inconsistency
fix(notifications): don't report a temperature breach exactly at the threshold
2026-09-18 09:16:23 -07:00
Craig Lawson 13799af721 Read unevaluated checks by pending entry in the exclusion test
findUnevaluatedByMonitorId now takes the job's pendingChecks rather than a
timestamp, so the test builds those entries from the ids createChecks returns.

🔧
2026-09-17 22:08:14 +01:00
Craig Lawson 4fcb13694b Exclude degraded checks from the docker and hardware aggregates
Those aggregations were reverted when docker and hardware could not carry the
flag. Both types are now egress attributable, so their checks can be degraded
and their totals, bucket counts and latest-check reads must exclude them like
every other aggregate.

🔧
2026-09-17 22:06:34 +01:00
Craig Lawson 3dc529733a Address the inline review on the egress check pipeline
Distinguish "the peer answered" from "nothing answered", widen attribution to
every check that leaves the instance, make the maintenance exit explicit, and
fix the recovery poll cadence.

Providers can now say outright whether the peer answered, via peerResponded on
MonitorStatusResponse. Previously the producer inferred it from an HTTP status
code, which two providers do not report:

- GrpcProvider: NOT_SERVING shares NETWORK_ERROR with a connection failure, and
  gRPC codes 0-16 are not HTTP statuses, so PERMISSION_DENIED, UNIMPLEMENTED
  and the rest were all read as transport failures. Only DEADLINE_EXCEEDED and
  UNAVAILABLE, or a failure carrying no gRPC code, now count as unreachable.
- DNSProvider: every resolver error became NETWORK_ERROR, so an authoritative
  NXDOMAIN, SERVFAIL or REFUSED was read as unreachable. The c-ares codes where
  the server replied are now separated from timeout and connection refused.

Left unset, the HTTP-status heuristic still applies, so other providers are
unchanged.

Attribution now follows whether the check leaves the instance, not the monitor
type. A hardware check is an ordinary outbound HTTP request to the Capture
agent, and a Docker daemon can be reached over TCP, so both are attributable;
only a Docker daemon on a unix socket is local and exempt.

Leaving a maintenance window is now explicit in the producer, alongside the
entry it already owned. It was a side effect of evaluating a check, which the
degraded-egress short-circuit skips, so a monitor whose window ended during an
outage kept reporting "maintenance" for the length of it.

The recovery poll interval is fixed at 5 seconds and no longer configurable.
While degraded, checks are flagged from stored state without probing, so the
interval is the window in which a recovered instance still discards its checks;
the only effect of raising it was a longer blind window. egressPollIntervalSeconds
is removed from the settings, the validator and the OpenAPI spec.

🔧
2026-09-17 22:06:34 +01:00
Craig Lawson d865103fb5 Exclude degraded-egress checks from every aggregate
A degraded episode now reads as a gap in monitoring rather than as downtime
or as a dip in response time. The exclusion is applied once per query, in the
top-level $match, so no pipeline can count a degraded check by omission.

- check.query.ts: EXCLUDE_DEGRADED_EGRESS_MATCH is the only helper;
  IS_NOT_DEGRADED_EGRESS_EXPR and the $cond accumulators it fed are gone.
- findUptimeDateRangeChecks: excludes at the match, so the response-time
  series, the up/down series and the averages drop degraded checks too.
- findPageSpeedDateRangeChecks: excludes as well; pagespeed is egress
  attributable and was previously missed.
- getDailyStatusBuckets: excludes at the match; the group shapes and the
  totalChecks > 0 guard revert to their original form.
- check.docker.aggregation.ts, check.hardware.aggregations.ts: reverted.
  Neither type is egress attributable, so their checks never carry the flag
  and the filters were dead code. A test now pins that decision.
- Tests: the gap behaviour, a window that was entirely degraded, pagespeed,
  the team listing and the resolve filter, and an incident that stays open
  when egress drops during it.

🔧
2026-09-17 22:06:16 +01:00
Craig Lawson 4335e6b7b6 Address review of degraded-egress check persistence
CheckProducer consults egress only for a transport failure: the check
failed, no HTTP status came back (isHttpStatusCode from PR 1), and the
monitor type is one whose failure can be a loss of egress
(EgressAttributableTypes; hardware and docker are excluded). A 4xx/5xx
or a content mismatch is evaluated as usual while the instance is
degraded. A rejected assessment is logged and treated as unknown so the
check is still buffered.

EXCLUDE_DEGRADED_EGRESS_MATCH moves out of the model into
check.query.ts, with an expression form for $group accumulators.
Daily status buckets and docker stats buckets exclude degraded checks
from their up/total counts while keeping them in the response-time
average, matching the uptime facet. toEntity maps egressStatus like its
siblings.

Adds heartbeatJobDegradedEgress integration tests, extends the
repository exclusion tests, and makes the harness egress stub settable.

🔧
2026-09-17 22:06:16 +01:00
Craig Lawson 4f5dba560d Add integration test for degraded egress uptime exclusions
Runs the summary, daily bucket, date-range, hardware and docker aggregations
against a live mongod with a mix of degraded and ordinary checks, and covers
the egressStatus round trip through createChecks and findUnevaluatedByMonitorId.

🔧
2026-09-17 22:06:16 +01:00
Craig Lawson f52acb3f68 Flag checks taken during degraded egress and exclude them from uptime (#3937)
CheckProducer asks EgressService to assess the instance's own connectivity
after a failing check and stamps the result on the check as egressStatus.
CheckEvaluator short-circuits degraded checks so no incident opens and no
spurious recovery fires later. Uptime and down-count aggregations exclude
degraded checks; the response-time series keeps them.

🔧
2026-09-17 22:06:16 +01:00
Alex Holliday f299098548 bug fixes 2026-09-17 12:30:56 -07:00
Alex Holliday fa0c0abcac Fix: Use Check IDs instead of timestamp to process checks 2026-09-17 11:57:19 -07:00
Alex Holliday 011b93ecc1 update tests 2026-09-16 11:40:42 -07:00
Alex Holliday 50d0721822 cache DB settings, used cached db settings in ProxyResolver, PageSpeedProvider, and EgressService 2026-09-16 11:37:29 -07:00
Alex Holliday 44edf320a0 reset job state on success 2026-09-15 10:13:20 -07:00
Alexander Holliday fea2ef4dbe Merge pull request #3959 from web-engineer/feat/egress-1-check-service
Egress self-check (1/4): egress state and probe service (#3937)
2026-09-15 09:51:01 -07:00
Craig Lawson b68367b04f Harden the egress recovery job after a second review pass (#3937)
- Schedule the recovery job before recording the degraded transition,
  so a crash between the two leaves a stray row (removed on the job's
  first run) rather than a degraded state nothing is polling
- Count any HTTP response, including 4xx/5xx, as reachable for http
  targets; only a transport failure counts against egress
- Cascade a deleted notification out of egressNotifications with an
  atomic $pull in the settings repository, after the team-scoped delete
- Handle two processes racing the first egress state insert
- Address global job rows by one key (deleteGlobalJob on _id) and
  restore the deleteByIdAndType signature
- Inject net into PingProvider like PortProvider
- Integration tests for MongoEgressStateRepository and the global-row
  delete methods on the jobs repository

🔧
2026-09-15 08:59:13 +01:00
Craig Lawson 08f104e283 Address review on the egress recovery job (#3937)
- The egress job runs through WorkerHelper.getEgressRecoveryJob(), like
  the cleanup jobs, instead of a direct service call from the queue
- Toggling egressCheckEnabled resets the egress state and removes any
  pending recovery job
- Probes resolve and pass the proxy, so http targets honour the global
  proxy as monitors do
- PingProvider keeps IPv6 addresses intact instead of truncating at the
  first colon
- Egress targets are parsed by one function shared by the validator and
  the probe; malformed addresses such as unbalanced brackets are rejected
- Notification id pattern drops the /i flag, which produced an invalid
  OpenAPI pattern
- EgressState findSingleton reads before it writes, so a read no longer
  bumps updatedAt
- "disabled" is a distinct assessment result and is cached like any other
- Concurrent failures share an in-flight assessment for as long as it
  runs, then reuse the settled result for 5 s
- The recovery job row is only removed if nobody rescheduled it since
  this run claimed it, so a concurrent new episode keeps its job
- Deleting a notification removes it from egressNotifications

🔧
2026-09-15 08:42:09 +01:00
Craig Lawson 46eb0b4063 Replace the egress recovery timer with a scheduled job (#3937)
A failed check still calls assessAfterFailure, which now shares one
in-flight assessment between concurrent failures (same cache shape as
ProxyResolver). When egress degrades it upserts a global "egress" job;
DBQueueWorker runs checkRecovery for that type at the configured
interval, and the job deletes its own row once any target answers.
The unref'd timer, startRecoveryPolling, stop and init are gone: a
degraded episode survives a restart through the jobs collection.
deleteByIdAndType accepts a null refId for global rows.

🔧
2026-09-14 20:41:58 +01:00
Craig Lawson ec01a5dfe4 Add egress state and probe service (#3937)
Adds the EgressService that probes a configurable set of reliability targets
(ping, TCP or HTTP) when asked, persists a single instance-wide egress state
document, and runs a recovery poll loop while egress is degraded. Adds the four
egress settings fields with validation. The worker constructs and initialises
the service but nothing calls assessAfterFailure yet, so behaviour is unchanged.

🔧
2026-09-14 10:34:12 +01:00
Ankit Gabani b2ace1bec5 fix(notifications): don't report a temperature breach exactly at the threshold
extractThresholdBreaches used maxTemp >= threshold, while
StatusService.computeHardwareStatus (which drives the actual
"breached" status transition and incident) uses a strict
maxTemp > threshold for the same metric. cpu, memory and disk already
agree on > in both files; temp was the one metric where they diverged.

Fixes #3956.
2026-09-14 01:49:54 +05:30
fabiangigler 4bb366f5ab Add custom monitor interval options 2026-09-13 20:28:27 +02:00
Jan Kahmen 406518e0a5 validate invite token type on registration 2026-09-12 12:08:33 +00:00
Alex Holliday 912f1f960d add missing timeout 2026-09-11 11:50:26 -07:00