The Janitor periodically walks every CheckPlan, loads its executions,
and deletes the ones that the tiered RetentionPolicy says to drop.
Per-user overrides are honoured: if a user's UserQuota.RetentionDays
is set, that horizon replaces the system default for the user's plans.
User lookups are cached per sweep to avoid repeated storage hits.
The janitor is the long-tail counterpart of the (still TODO) cheap
hard cap that will be applied at execution-creation time. It runs
immediately on Start() and then every configured interval (default 6h).
Insert an hourly tier between the full-detail window and the daily
bucket so users still get sub-day resolution for the first week:
0..1 day -> all
1..7 days -> 1 per hour
7..30 -> 2 per day
...
Introduce a pure RetentionPolicy.Decide function that partitions check
executions into keep/drop sets according to a tiered policy:
- 0..7 days -> every execution
- 7..30 days -> 2 per day per (checker, target)
- 30..D/2 -> 1 per week per (checker, target)
- D/2..D days -> 1 per month per (checker, target)
- > D days -> dropped
The function is intentionally storage-agnostic so the upcoming janitor
goroutine can call it on any execution slice and so it can be unit
tested directly. All thresholds are configurable to allow per-user
overrides via UserQuota.
Introduce a UserQuota field on the User model to hold admin-controlled
limits and flags that the user cannot modify. Only checker-related
fields are defined for now (max checks per day, retention days,
inactivity pause days, scheduling kill switch); future paid-plan
attributes will be added here later.
The user-facing API only exposes settings updates and account deletion,
so Quota cannot be written through it. Updates go through the existing
admin user PUT endpoint, with a new editor card in the admin UI under
/users/[uid].
Introduce a transport abstraction so observation providers can run either
locally or be delegated to a remote HTTP endpoint. When an admin sets the
"endpoint" option, the engine substitutes the local provider with an
HTTPObservationProvider that POSTs to {endpoint}/collect.
Instead of rebuilding the entire scheduler queue, incrementally add or
remove jobs when domains are created/deleted or zones are
imported/published. A wake channel interrupts the run loop so new jobs
are picked up immediately. A jobKeys index prevents duplicate entries.
Hook points: domain creation, domain deletion, zone import, and zone
publish (correction apply) all notify the scheduler via the narrow
SchedulerDomainNotifier interface, wired through setter methods to
avoid initialization ordering issues.
Refactor observation data pipeline to serialize once after collection and
keep json.RawMessage throughout storage and API responses. This eliminates
double-serialization and makes DB round-trips lossless.
Prevent more specific scopes from overriding option values locked at a
higher scope (e.g. admin). Includes defense-in-depth stripping on
Set/Add operations, merge-time preservation, and frontend filtering.
Add DomainWithCheckStatus model and GetWorstDomainStatuses usecase to
compute the most critical checker status per domain. The GET /domains
endpoint now returns status alongside each domain. The frontend domain
store, list components, and table row display dynamic status badges
with color and icon instead of a hardcoded "OK".
ZoneList is made generic (T extends HappydnsDomain) so the badges
snippet preserves the caller's concrete type without unsafe casts.
Add a separate section on the domain checks page to display zone and
service-level checkers that can be configured but won't produce results
at the domain scope. Hide the scheduling and rules cards when configuring
a non-domain checker from the domain context.
Add Chart.js-based line chart for checker metrics. The chart appears
on the executions list page (aggregated) and on individual execution
detail pages. Metrics view mode is selectable via the sidebar alongside
HTML report and raw JSON views.
Observation providers can now implement CheckerMetricsReporter to
extract time-series metrics from their stored data. The controller
returns the metrics as a JSON array.
Routes: user-level (/api/checkers/metrics), domain-level, per-checker,
and per-execution.
Introduce CheckerHTMLReporter interface that observation providers can
implement to render rich HTML documents from their data. The Zonemaster
provider implements it with collapsible accordions and severity badges.
Adds API endpoint GET .../observations/:obsKey/report, frontend stores
for view mode switching (HTML/JSON), and wires the sidebar toggle buttons.
Add all checker UI pages and components:
- Checker list, config, schedule, and rules pages
- Execution list, detail, results, and rules pages
- Sidebar components for domain/service checker status
- Run check modal with option overrides and rule selection
- Domain-scoped and service-scoped check routes
- Admin pages for checker configuration and scheduler management
- Header navigation link for checkers section
Add the frontend infrastructure for the checker UI:
- API client with scoped helpers for domain/service-level operations
- Svelte stores for checker state (currentExecution, currentCheckInfo)
- Utility functions for status colors, icons, i18n keys, date formatting
- Shared helpers: withInheritedPlaceholders, downloadBlob, collectAllOptionDocs
- English translations for all checker UI strings
- Zone model and form types extended for checker support
Wire up the checker system to the HTTP layer:
- API controllers for checker operations, options, plans, and results
- Scoped routes at domain and service level
- Admin controllers for checker config and scheduler management
- App initialization: create usecases, start/stop scheduler
- Zone controller updated to include per-service check status
Implement the checker business logic:
- CheckerOptionsUsecase: scope-based option resolution, validation,
auto-fill from execution context (domain, zone, service)
- CheckPlanUsecase: CRUD for user scheduling configurations
- CheckStatusUsecase: aggregated status queries, execution history
- CheckerEngine: full execution pipeline (observe, evaluate, aggregate)
- Scheduler: background job executor with auto-discovery, min-heap
queue, worker pool, and jitter-based scheduling
Add the persistence layer for the checker system:
- Storage interfaces (CheckPlanStorage, CheckerOptionsStorage,
CheckEvaluationStorage, ExecutionStorage, ObservationSnapshotStorage,
SchedulerStateStorage) in the usecase/checker package
- KV-based implementations for LevelDB/Oracle NoSQL/InMemory backends
- Integrate checker storage into the main Storage interface
- Add tidy methods for checker entities (plans, configurations,
evaluations, executions, snapshots, observation cache) and
secondary index cleanup
Add ValidateMapValues() to the forms package for validating
checker option maps against field documentation (required fields,
allowed choices, type checking).
Scan -plugins-directory paths at startup, open each .so via plugin.Open,
look up the NewCheckerPlugin symbol from checker-sdk-go, and register the
returned definition and observation provider in the global checker
registries. A pluginLoader indirection keeps the door open for future
plugin kinds.
When TidyUsers removes an AuthUser with unverified email and no login
after 7 days, the corresponding User record was left orphaned in the
database. Now DeleteUser is called before dropping the AuthUser.
Add click-to-collapse and double-click-to-solo behavior on group
headers in ZoneList, with a chevron indicator. Collapsed state is
persisted in localStorage and cleared on login/logout.
Refresh the overall visual design across the frontend: floating labels
and centered card layout for auth pages, animated sidebar+results view
for the resolver, polished dropdown menus, toasts, and feedback widget,
and cleaner global styles (navbar shadow, border, transitions).
Switch ServiceMeta.Domain, ServiceMeta.Ttl, ServiceMeta.NbResources,
and ZoneMeta.DefaultTTL from binding:"required" to validate:"required".
This keeps them marked as required in the OpenAPI spec (swaggo reads
both tags) without gin rejecting valid zero values (0 for uint32,
"" for root domain).
CreateDomain now takes a DomainCreationInput and returns the created
Domain, so the controller no longer needs to construct an intermediate
Domain struct and the response includes the server-assigned fields.
Fixes: https://github.com/happyDomain/happydomain/issues/44
Avoid potential panics from nil or missing fields when migrating SRV
records by using comma-ok type assertions and guarding against empty
name/proto values.
The InMemoryStorage struct carried per-entity maps and fields that were
never used: all storage goes through the generic KV data map. Strip the
struct down to just mu + data, and update tests to use
inmemory.Instantiate() instead of manually wrapping with kv.NewKVDatabase.
openapi-ts v0.95.0 now requires a valid tsconfig chain to resolve.
Since tsconfig.json extends .svelte-kit/tsconfig.json which is generated,
we need to run svelte-kit sync first to create it.
Replace `any` with precise types (unknown, Record<string, any>, specific
interfaces) in props, function signatures, and index signatures. Introduce
ServiceWithValue to bridge SDK's HappydnsService.Service from unknown to
its runtime shape. Simplify updateDomain to take explicit id + body params.
Replace $state + $effect pattern with $derived for localDomains to properly react to prop changes. Also reformat genGroups signature and sort callback indentation.