Skip to content

feat(validator): M81-4 per-module health derivation (Day 1+2)#381

Merged
itcmsgr merged 6 commits intomainfrom
feat/m81-4-health-derivation
Apr 14, 2026
Merged

feat(validator): M81-4 per-module health derivation (Day 1+2)#381
itcmsgr merged 6 commits intomainfrom
feat/m81-4-health-derivation

Conversation

@itcmsgr
Copy link
Copy Markdown
Owner

@itcmsgr itcmsgr commented Apr 14, 2026

Summary

Implements M81-4 (HEALTH_METRIC_DERIVATION_v1.81.md) in the Go validator.
Per-module 4-axis health evaluation: config, structural, runtime, effective.

Day 1 — Module health evaluator

  • module_health.go: evaluateModuleHealth() + per-module evaluators
    (BotGuard, DDoS, Portscan, LoginMon, Blacklist)
  • Config readers with .local override chain
  • BotGuard dual-family structural check (Rule 9)
  • StatusIdle enum + overall status emits idle when all modules idle
  • SchemaVersionCurrent = "1.81.0"
  • ModuleHealthMap, ConfigState, StructuralState, EffectiveState types
  • BlacklistHealth with manual/feeds/geoban sub-states
  • Geoban DB missing = degraded (per M81-3 contract)

Day 2 — Evidence fidelity

  • DDoS reads real kernel named counters → effective axis
    (5 enforcement counters, any > 0 = ENFORCING)
  • Portscan effective = IDLE with documented gap (no dedicated counter, per M81-3)
  • Live verified on lab4: DDoS reports "enforcing" from kernel evidence

Live output (lab4)

{
  "schema_version": "1.81.0",
  "status": "protected",
  "modules": {
    "botguard":  { "config": "disabled" },
    "ddos":      { "config": "enabled", "structural": "present", "effective": "enforcing" },
    "portscan":  { "config": "enabled", "structural": "present", "effective": "idle" },
    "loginmon":  { "config": "enabled", "structural": "present", "runtime": "RUNNING", "effective": "idle" },
    "blacklist": { "manual": {"state":"idle"}, "feeds": {"state":"disabled"}, "geoban": {"state":"degraded"} }
  }
}

Tests

35/35 PASS on lab4. 20 new M81-4 tests + 15 existing B80 tests.

Scope

internal/validator/ only. No shell. No CLI. No runtime.

🤖 Generated with Claude Code

itcmsgr and others added 2 commits April 14, 2026 12:51
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>
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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 2d33872.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

Comment thread internal/validator/module_health.go Fixed
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>
@itcmsgr itcmsgr force-pushed the feat/m81-4-health-derivation branch from b3655f0 to 4f67338 Compare April 14, 2026 10:04
itcmsgr and others added 3 commits April 14, 2026 13:07
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>
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>
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>
@itcmsgr itcmsgr force-pushed the feat/m81-4-health-derivation branch from bf9e46b to 2d33872 Compare April 14, 2026 10:32
@itcmsgr itcmsgr merged commit 908d344 into main Apr 14, 2026
7 checks passed
@itcmsgr itcmsgr deleted the feat/m81-4-health-derivation branch April 14, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants