diff --git a/README.md b/README.md index 75915f2..6e7c166 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,11 @@ Use this as a template when you create your own checker. - [Step 6: Wire It Up (main.go)](#step-6-wire-it-up-maingo) - [Step 7: Create the Plugin Entrypoint](#step-7-create-the-plugin-entrypoint) 5. [Optional: Standalone Human UI (`CheckerInteractive`)](#optional-standalone-human-ui-checkerinteractive) -6. [Optional: Declaring rule prerequisites (`RulePrecheck`)](#optional-declaring-rule-prerequisites-ruleprecheck) -7. [Running the Checker](#running-the-checker) -8. [Testing with curl](#testing-with-curl) -9. [Deploying to happyDomain](#deploying-to-happydomain) -10. [License & happyDomain compatibility](#license--happydomain-compatibility) -11. [Going Further](#going-further) +6. [Running the Checker](#running-the-checker) +7. [Testing with curl](#testing-with-curl) +8. [Deploying to happyDomain](#deploying-to-happydomain) +9. [License & happyDomain compatibility](#license--happydomain-compatibility) +10. [Going Further](#going-further) --- @@ -134,6 +133,7 @@ const ObservationKeyDummy = "dummy" type DummyData struct { Message string `json:"message"` Score float64 `json:"score"` + CollectedAt time.Time `json:"collected_at"` } ``` @@ -197,6 +197,7 @@ func (p *dummyProvider) Collect(ctx context.Context, opts CheckerOptions) (any, return &DummyData{ Message: message, Score: score, + CollectedAt: time.Now(), }, nil } ``` @@ -471,59 +472,6 @@ Returning an error from `ParseForm` re-renders the form with the error message d --- -## Optional: Declaring rule prerequisites (`RulePrecheck`) - -Some rules cannot run unless the operator has supplied a prerequisite, typically an API key or auth token in an admin option. Without a way to advertise that, the UI shows the rule as if it were ready and the user only discovers the problem after a check returns an error. The SDK exposes an optional per-rule hook so the host can tell ahead of time which rules are usable with the current options. - -```go -type RulePrecheck interface { - CheckRule - Precheck(ctx context.Context, opts CheckerOptions) error -} -``` - -Return `nil` when the rule is ready to run; return an error describing the missing prerequisite otherwise. The error message is shown verbatim to the operator, so name the option they need to set (`"VirusTotal API key is not configured"` is much more useful than `"missing key"`). - -### Minimal implementation - -```go -func (r *vtRule) Precheck(ctx context.Context, opts sdk.CheckerOptions) error { - if v, _ := opts["virustotal_api_key"].(string); v == "" { - return fmt.Errorf("VirusTotal API key is not configured") - } - return nil -} -``` - -### How the host consumes it - -`GET /definition` is unchanged: it returns the static checker definition with no per-rule availability information. Whenever the host has a concrete set of options at hand (rendering the rule list with the user's admin/user settings, validating an edit, …) it issues a `POST /definition` carrying those options: - -```bash -curl -X POST http://localhost:8080/definition \ - -H "Content-Type: application/json" \ - -d '{"options": {}}' -``` - -Response: - -```json -{ - "precheck_failures": { - "virustotal": "VirusTotal API key is not configured" - } -} -``` - -Rules that pass precheck, or that do not implement `RulePrecheck`, are absent from the map. The UI uses this to grey out unconfigured rules and surface the message returned by `Precheck`. - -### Important: Precheck is advisory only - -The SDK never calls `Precheck` from `Collect` or `Evaluate`. If the user enables a rule whose precheck fails and the host runs `/collect` anyway, your `Collect` (or the rule itself) still has to short-circuit and surface its own error: precheck is purely a UI hint, not an admission gate. - ---- - - ## Running the Checker ### Build and run locally @@ -577,6 +525,7 @@ Response: "data": { "message": "Testing my checker!", "score": 73.2, + "collected_at": "2026-01-15T10:30:00Z" } } ``` @@ -588,7 +537,7 @@ curl -X POST http://localhost:8080/evaluate \ -H "Content-Type: application/json" \ -d '{ "observations": { - "dummy": "{\"message\":\"test\",\"score\":42.5}" + "dummy": "{\"message\":\"test\",\"score\":42.5,\"collected_at\":\"2026-01-15T10:30:00Z\"}" }, "options": { "warningThreshold": 50, @@ -621,7 +570,7 @@ Status codes: `1` = OK, `3` = Warning, `4` = Critical. curl -X POST http://localhost:8080/report \ -H "Content-Type: application/json" \ -d '{ - "data": "{\"message\":\"test\",\"score\":73.2}" + "data": "{\"message\":\"test\",\"score\":73.2,\"collected_at\":\"2026-01-15T10:30:00Z\"}" }' ```