Show egress/ingress levels in the registry; freeze checks at the verdict
Registry: the "last result" column now also shows, per level (egress,
ingress), how many of the recorded checks of the latest cycle succeeded, split
by check family (tcp-22 and tcp-443 are both "tcp"). One grouped query per
chunk of addresses; new fields last_cycle_id, egress, ingress in
GET /admin/registry; the dashboard renders them under the verdict.
Verdict integrity (migration 0010):
- the prober is handed an address once per site and attempt, not on every
poll, so results are no longer overwritten by later probe rounds;
- UpsertCheckIfOpen refuses writes once the address is aggregating or has its
verdict, or for an older attempt; senders get {"ok":true,"ignored":N} and a
result_dropped event is recorded;
- the checking window counts from checking_started_at, not from assigned_at;
- checks.recorded_at (server clock) and checks.after_verdict (flag for rows
written after the verdict in existing data);
- the verdict rule is a pure function (computeVerdict) and the aggregated
event carries the egress/ingress check counts.
Rebuilt bin/control-api and bin/admin-dashboard to match. Plans and summaries
are in docs/changes; README, API, USAGE, DASHBOARD and DIAGRAMS are updated.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db73409e8f
commit
864208238f
34 files changed
+1570
-72
No files matched your search
+45
-8
@@ -1,6 +1,9 @@
|
||||
package db
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Validator and IP lifecycle states. Kept as typed string constants rather
|
||||
// than a Go enum type so they round-trip through SQLite TEXT columns and
|
||||
@@ -71,6 +74,37 @@ func InboundSource(siteIndex int) string {
|
||||
return "inbound-site-" + itoa(siteIndex)
|
||||
}
|
||||
|
||||
// Check levels: the two directions a check can run in, derived from
|
||||
// checks.source (there is no separate direction column).
|
||||
const (
|
||||
LevelEgress = "egress"
|
||||
LevelIngress = "ingress"
|
||||
)
|
||||
|
||||
// CheckLevel maps a checks.source value to its level: "egress" for the
|
||||
// validator's own outbound checks, "ingress" for any prober site
|
||||
// ("inbound-site-N"). Any other source yields "".
|
||||
func CheckLevel(source string) string {
|
||||
switch {
|
||||
case source == SourceEgress:
|
||||
return LevelEgress
|
||||
case strings.HasPrefix(source, "inbound-site-"):
|
||||
return LevelIngress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CheckFamily maps a checks.check_type value to its family for grouping:
|
||||
// the part before the first "-", so tcp-22 and tcp-443 are both "tcp" while
|
||||
// https, icmp, ssh and tls-443 ("tls") stay distinct. A check type added in
|
||||
// the future is grouped by its own name without code changes.
|
||||
func CheckFamily(checkType string) string {
|
||||
if i := strings.IndexByte(checkType, '-'); i > 0 {
|
||||
return checkType[:i]
|
||||
}
|
||||
return checkType
|
||||
}
|
||||
|
||||
func itoa(n int) string {
|
||||
if n == 0 {
|
||||
return "0"
|
||||
@@ -118,13 +152,16 @@ type IPQueueItem struct {
|
||||
EgressComplete bool
|
||||
OverallResult string
|
||||
AssignedAt *time.Time
|
||||
FIPAssociatedAt *time.Time
|
||||
AggregatedAt *time.Time
|
||||
FIPReleasedAt *time.Time
|
||||
RegistryID int64
|
||||
CycleID int
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
// CheckingStartedAt is when the address entered the checking state; the
|
||||
// aggregation window counts from it. nil on rows that predate it.
|
||||
CheckingStartedAt *time.Time
|
||||
FIPAssociatedAt *time.Time
|
||||
AggregatedAt *time.Time
|
||||
FIPReleasedAt *time.Time
|
||||
RegistryID int64
|
||||
CycleID int
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type Check struct {
|
||||
|
||||
Reference in new issue
Block a user