Commit graph

14 commits

Author SHA1 Message Date
7567271536 checker: cross-checker observation composition via ReportContext v1.1.0
Add the plumbing that lets a checker receive (at evaluation, report
rendering, and metrics extraction) observations produced by other
checkers on DiscoveryEntry records it originally published.

Surface changes:

  - RelatedObservation struct: one downstream observation, tagged with
    the producing CheckerID and the Ref matching the DiscoveryEntry
    it covers.

  - ObservationGetter gains GetRelated(ctx, key), so rules can opt in
    to cross-checker composition. mapObservationGetter (remote
    /evaluate path) returns empty; the host owns lineage resolution.

  - ReportContext interface: Data() + Related(key). Reporters consume
    it instead of a raw json.RawMessage, which collapses the former
    legacy/Ctx duplicate and gives one uniform signature:

        GetHTMLReport(ctx ReportContext) (string, error)
        ExtractMetrics(ctx ReportContext, t time.Time) ([]CheckMetric, error)

  - NewReportContext(data, related) and StaticReportContext(data) build
    fixed-payload contexts for entry points without an ObservationContext.

  - ExternalReportRequest gains a Related map so the host can ship
    pre-composed lineage to a remote checker over /report. The SDK's
    /report handler threads it through to the reporter via
    NewReportContext, closing the wire gap that previously forced
    remote reports to a StaticReportContext with no related data.

Tests cover the Related map round-trip end-to-end via a peeking provider.
2026-04-22 16:50:59 +07:00
087032f6cc checker: add DiscoveryPublisher interface for cross-checker discovery
Introduce a DiscoveryEntry struct and an optional DiscoveryPublisher
interface that providers can co-implement to declare things worth
probing by other checkers (TLS endpoints, HTTP probes, ACME challenges,
DNSSEC keys, ...) without having to re-parse raw observations.

DiscoveryEntry carries an opaque Payload: the SDK does not interpret
it. Producers and consumers agree on the Payload schema through a
separate contract (eg. a small shared Go package imported by
both) identified by the free-form Type string. This keeps the SDK
free of protocol-specific concepts; new entry families can appear
without touching it.

The /collect HTTP handler type-asserts the provider against
DiscoveryPublisher immediately after Collect and forwards the
resulting entries in ExternalCollectResponse.Entries.
2026-04-22 16:50:59 +07:00
6b96ee8c2f server: expose runtime metrics on /health for scheduler routing v1.0.0
Adds HealthResponse carrying inflight count, total requests, 1/5/15-min
EWMA load averages, uptime, and NumCPU so a scheduler can pick the least
busy worker. A background sampler updates the load averages every 5s,
stopped by a new idempotent Close method. Work endpoints (/collect,
/evaluate, /report) are wrapped with a trackWork middleware; /health
and /definition are excluded so polling traffic does not pollute the
signal.
2026-04-16 16:52:00 +07:00
fa5198f78c Revert "checker: reorder Status with negatives for good, JSON as string" v0.5.0
This reverts commit 6be3578c33.
2026-04-16 00:50:23 +07:00
6493589bb4 types: make CheckTarget.String() unambiguous by always emitting all fields v0.4.0
The previous implementation skipped empty fields, which meant targets
differing only in which fields were populated could produce the same
string (e.g. {UserId:"A"} and {DomainId:"A"} both gave "A"). This
caused key collisions when the string was used in storage index keys.
2026-04-15 19:54:17 +07:00
2cd323beed types: drop custom JSON marshalling for Status v0.3.0
Keep Status as a plain integer on the wire so that OpenAPI/swaggo
can generate correct enum definitions without needing to handle
the custom string encoding.  The String() method is preserved for
logging and debugging.
2026-04-11 17:22:06 +07:00
36a72f013a server: document lack of built-in authentication on Server type v0.2.0 2026-04-10 16:43:59 +07:00
2fa44f69a4 server: return 500 status on collect errors instead of 200
Errors from provider.Collect() and json.Marshal were returned with HTTP
200, making failures invisible to monitoring, proxies, and clients that
check status codes. Return 500 Internal Server Error so HTTP-level
tooling can detect failures without parsing the response body.
2026-04-10 16:43:56 +07:00
ef7fffd4b7 tests: add coverage for options, types, and HTTP server
- options_test.go: GetOption, GetFloatOption, GetIntOption, GetBoolOption
  with native types, JSON round-trips, missing keys, and wrong types
- types_test.go: Status JSON marshal/unmarshal (strings, legacy ints,
  round-trip, unknown values), CheckTarget.Scope/String, BuildRulesInfo,
  empty-ID rejection
- server_test.go: /health, /collect (success, error, bad body),
  /definition, /evaluate (all rules, disabled rule), /report (HTML,
  metrics, bad body), missing endpoints without CheckerDefinitionProvider
2026-04-10 16:35:50 +07:00
ec4efcf671 server: limit request body size on POST endpoints
Add io.LimitReader (1 MB cap) to /collect, /evaluate, and /report
handlers to prevent memory exhaustion from oversized requests.
2026-04-10 16:24:41 +07:00
688d32cc9f registry: reject checker registration with empty ID
Prevent silent bugs where a CheckerDefinition with an unset ID
would be registered under the empty string key, becoming unfindable
and potentially colliding with other empty-ID registrations.
2026-04-10 16:24:39 +07:00
6be3578c33 checker: reorder Status with negatives for good, JSON as string
Make StatusUnknown the zero value (0) so an uninitialized CheckState
reads as "no signal yet" rather than as healthy. Push StatusOK and
StatusInfo to negative values so the natural int ordering matches
severity ordering: aggregators can simply take max() to compute the
worst status, and Unknown correctly sits above OK/Info but below Warn.

Status now (un)marshals as its string name ("OK", "WARN", ...) so the
wire format is stable across any future renumbering. UnmarshalJSON
still accepts raw ints for backward compatibility with older snapshots
and clients.
2026-04-08 22:16:41 +07:00
8e2ba83a0d registry: refuse duplicate registrations with a warning
RegisterChecker and RegisterObservationProvider previously overwrote
existing entries silently, which lets a misconfigured plugin shadow a
built-in (or another plugin) without any signal. Refuse the duplicate
and keep the existing entry instead.

RegisterExternalizableChecker now performs the dedup check before
appending the "endpoint" AdminOpt, so a rejected re-registration no
longer mutates the live definition.
2026-04-08 20:42:25 +07:00
8160adcdca Initial commit v0.0.1 v0.1.0 2026-04-08 00:53:15 +07:00