You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(validator): M81-4 per-module health derivation (Day 1+2) (#381)
* feat(validator): M81-4 Day 1 — per-module health evaluator
Implements the module health derivation from HEALTH_METRIC_DERIVATION_v1.81.md.
Each module evaluated on 4 axes: config, structural, runtime, effective.
Added:
- module_health.go: evaluateModuleHealth() + per-module evaluators
(BotGuard, DDoS, Portscan, LoginMon, Blacklist)
- Config readers: readConfigBool() with .local override chain
- BotGuard dual-family structural check (Rule 9 per-family aggregation)
- StatusIdle enum + evaluateOverallStatus emits idle when all modules idle
- SchemaVersionCurrent = "1.81.0" + schema_version in JSON output
- ModuleHealthMap, ConfigState, StructuralState, EffectiveState types
- BlacklistHealth with manual/feeds/geoban sub-states
Gap fixes applied:
- StatusIdle added to overall status enum (was missing)
- Geoban DB missing = degraded (was stale — per M81-3 contract)
- Feeds = loaded when configured (not idle — per shared counter rule)
- BotGuard IPv4+IPv6 dual-family evaluation
Tests: 32/32 PASS on lab4 (17 new + 15 existing, 0 regressions).
Live output verified on lab4 with correct per-module states.
Known gaps (Day 2): DDoS counter evidence, blacklist element counting,
portscan effective axis stub.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(validator): M81-4 Day 2 — DDoS counter evidence + portscan baseline
Day 2 evidence fidelity: effective axis now uses real kernel data.
DDoS:
- GetCounter() method on RulesetDocument reads named counter packets
- evaluateDDoS() checks 5 enforcement counters (ct_ssh/ct_http/ct_mail/
syn_rate/syn_prefix_drop). Any > 0 = ENFORCING. All zero = IDLE.
- Live verified on lab4: DDoS now reports "enforcing" from real kernel
counter evidence (input_syn_rate_exceeded > 0)
Portscan:
- Effective axis explicitly set to IDLE with code documentation:
no dedicated counter exists, effective evidence incomplete per M81-3
portscan contract. Real enforcement evidence requires kernel log
parsing (M81-7 Day 6 scope).
- TestPortscanEffectiveAlwaysIdle pinned as regression guard
Blacklist:
- countSetElements() remains stubbed (nft -j list ruleset does not include
set elements — per-set queries needed, future enhancement)
- Documentation updated in stub explaining the limitation
Overall status:
- Now correctly PROTECTED on lab4 (DDoS enforcing = active module)
- Was IDLE on Day 1 (no active modules detected)
Tests: 35/35 PASS on lab4 (3 new + 32 existing, 0 regressions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(validator): M81-6 Day 3 — frozen JSON schema via mapper layer
Implements the M81-6 JSON_SCHEMA_SPEC_v1.81.md as a separate projection
layer between internal state and JSON output.
Added:
- health_output.go: HealthOutput, ModulesJSON, ModuleJSON, BlacklistJSON,
ConsistencyJSON, FindingJSON — the frozen schema types
- health_mapper.go: MapToHealthOutput() — single mapping point from
ValidationResult to HealthOutput. Enforces:
- disabled modules: config only, no other fields
- kernel-only modules: no runtime field
- daemon-dependent modules: runtime lowercase
- no nulls
- vocabulary-approved values only
- health_mapper_test.go: 10 schema compliance tests including:
- schema version check
- all 4 status values mapped
- disabled omits axes
- kernel-only omits runtime
- daemon-dependent includes runtime
- no-nulls golden test
- blacklist composite mapping
- consistency stub
- findings mapping
Changed:
- cli.go: ToJSON() now uses MapToHealthOutput() (frozen schema).
ToJSONLegacy() preserves raw serialization for rebuild safety code.
StatusString() + ExitCode() handle IDLE (exit 0).
Backward compat: .status and .chain_counts.total_chains still present
in the frozen schema — rebuild safety code in cmd_firewall.sh unaffected.
Tests: 45/45 PASS on lab4 (10 new + 35 existing, 0 regressions).
Live verified: lab4 emits frozen schema with all M81-6 constraints met.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): M81-5 Day 4 — banned phrase cleanup + regression scan
Implements CLI_OUTPUT_SPEC_v1.81.md vocabulary enforcement (partial).
Fixed:
- cmd_status.sh: "healthy" → "protected" in health_word mapping (4 sites)
- cmd_status.sh: "threats_blocked_24h" → "enforcement_events_24h" in JSON
output (per M81-5 banned terms + M81-6 frozen schema alignment)
- cmd_list.sh: "protecting your IPs" → "in the whitelist" (1 site)
Added:
- tests/test_banned_phrases.sh: M81-5 regression scanner
Scans cli/*.sh for 7 banned phrase patterns. Informational for v1.81
(24 remaining findings in health subsystem). Full enforcement is v1.82.
Remaining (v1.82 scope):
- "OK" in health context (19 sites, mostly cmd_health_analysis.sh +
cmd_health_core.sh — requires health subsystem rewrite)
- "healthy" in 2 deeper health paths
- "working" in cmd_queue.sh (queue context, low severity)
Findings: 27 → 24 (3 fixed, 24 deferred to v1.82 CLI phase)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(validator): CF-1..4 spec-conformance audit fixes for v1.81 release
Resolves 4 critical failures from SPEC_CONFORMANCE_AUDIT_v1.81.md.
CF-1: service_state.nftband now emits uppercase ("RUNNING"|"STOPPED"|
"ERROR") matching JSON_SCHEMA_SPEC §5. Module runtime fields remain
lowercase per spec. Fix: removed normalizeRuntime() call for
service_state, using raw RuntimeState enum string directly.
CF-2: geoban DB missing now emits "stale" (in allowed blacklist sub-state
enum) instead of "degraded" (not in enum). Also emits VAL-GEOBAN-001
finding (SeverityWarn) for visibility. New finding code registered in
types.go. Module findings collected via moduleFindings slice and
appended to result.Findings.
CF-3: JSON_SCHEMA_SPEC_v1.81.md updated to mark families and module_truth
as "legacy-only (ToJSONLegacy), not part of M81-6 primary schema."
The M81-6 HealthOutput replaces module_truth with the richer modules
block. No code change — spec alignment only.
CF-4: countSetElements() stub documented as v1.81 known limitation with
explicit consequence description and v1.82 fix target. BotGuard
ENFORCING/OBSERVING and blacklist PRIMED states are unreachable
from the validator until per-set queries are implemented.
All tests pass on lab4. Live output verified: uppercase service_state,
geoban="stale" with VAL-GEOBAN-001 finding, no schema violations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* release: v1.81.0 — metrics alignment and health semantics
Bumps VERSION 1.80.1 -> 1.81.0.
Full CHANGELOG with: M81-4/5/6 implementation, portscan classic fix,
CF-1..4 audit fixes, 12 M81 spec documents, known limitations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 commit comments