@@ -11,6 +11,120 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212---
1313
14+ ## [ 1.81.0] - 2026-04-14
15+
16+ ** Metrics alignment and health semantics implementation.** Module-aware
17+ health output with frozen JSON schema, vocabulary-aligned states, and
18+ CLI/JSON truth discipline. Portscan classic detection bug fixed and
19+ verified live.
20+
21+ ### Added
22+
23+ - ** M81-4** Per-module health evaluation in Go validator. Each module
24+ evaluated on 4 axes: config, structural, runtime, effective. Truth
25+ tables from ` HEALTH_METRIC_DERIVATION_v1.81.md ` implemented in code.
26+ Modules: BotGuard, DDoS, Portscan, LoginMon, Blacklist (unified:
27+ manual + feeds + geoban). (PR #381 )
28+ - ** M81-6** Frozen JSON schema via mapper layer. ` MapToHealthOutput() `
29+ is the single projection point — internal state never serialized
30+ directly. Schema version ` 1.81.0 ` . No nulls. Vocabulary-approved
31+ values only. (PR #381 )
32+ - DDoS effective axis reads real kernel named counters (` input_ct_ssh_drop ` ,
33+ ` input_ct_http_drop ` , ` input_ct_mail_drop ` , ` input_syn_rate_exceeded ` ,
34+ ` input_syn_prefix_drop ` ). Any > 0 = ENFORCING. (PR #381 )
35+ - ` StatusIdle ` enum: overall status distinguishes PROTECTED (at least one
36+ module active) from IDLE (all modules valid but no enforcement). Both
37+ exit 0. (PR #381 )
38+ - BotGuard dual-family structural evaluation per Rule 9 (per-family
39+ aggregation). IPv6 checked only if ip6 nftban table exists. (PR #381 )
40+ - Consistency block stub in JSON output (` kernel_vs_validator: "ok" ` ).
41+ Full consistency checking is v1.82 scope. (PR #381 )
42+ - ` VAL-GEOBAN-001 ` finding emitted when geoip database missing/empty
43+ and geoban is enabled. (PR #381 )
44+ - ` test_banned_phrases.sh ` : M81-5 regression scanner detecting 7 banned
45+ phrase patterns across CLI files. Informational for v1.81. (PR #381 )
46+
47+ ### Fixed
48+
49+ - ** Portscan classic log-path collision** (CRITICAL). ` PORTSCAN_CLASSIC_LOG_FILE `
50+ was defined twice in ` classic.conf ` — line 32 (kernel input source) and
51+ line 172 (module output log). The detector was grepping its own output
52+ log and finding nothing. Renamed output variable to
53+ ` PORTSCAN_CLASSIC_MODULE_LOG ` . Detection now verified live: lab2=160/4,
54+ lab4=349/6, monitor=59/1 IPs tracked/blocked. Both background timer and
55+ manual ` nftban portscan check ` fixed. (PR #377 )
56+ - ** CF-1** ` service_state.nftband ` now emits uppercase (` RUNNING ` |` STOPPED ` |
57+ ` ERROR ` ) matching JSON schema spec. Module runtime fields remain
58+ lowercase. (PR #381 )
59+ - ** CF-2** Geoban DB missing emits ` "stale" ` (in allowed enum) instead of
60+ ` "degraded" ` (not in enum). Emits ` VAL-GEOBAN-001 ` finding. (PR #381 )
61+ - ** M81-5** CLI banned phrases: ` "healthy" ` replaced with ` "protected" ` in
62+ health word mappings. ` "threats_blocked_24h" ` renamed to
63+ ` "enforcement_events_24h" ` in status JSON. (PR #381 )
64+
65+ ### Changed
66+
67+ - ` ToJSON() ` now uses ` MapToHealthOutput() ` (frozen schema). Legacy
68+ consumers use ` ToJSONLegacy() ` for backward compat. (PR #381 )
69+ - ` ExitCode() ` returns 0 for both PROTECTED and IDLE. (PR #381 )
70+
71+ ### Schema
72+
73+ ``` json
74+ {
75+ "schema_version" : " 1.81.0" ,
76+ "status" : " protected|idle|degraded|down" ,
77+ "service_state" : { "nftband" : " RUNNING|STOPPED|ERROR" },
78+ "modules" : {
79+ "botguard" : { " config" , " structural" , " runtime" , " effective" },
80+ "ddos" : { " config" , " structural" , " effective" },
81+ "portscan" : { " config" , " structural" , " effective" },
82+ "loginmon" : { " config" , " structural" , " runtime" , " effective" },
83+ "blacklist" : { " manual" , " feeds" , " geoban" }
84+ },
85+ "consistency" : { "kernel_vs_validator" : " ok|mismatch" },
86+ "findings" : [... ],
87+ "chain_counts" : {... },
88+ "summary" : {... }
89+ }
90+ ```
91+
92+ ### Specs produced (M81-1 through M81-8)
93+
94+ | Spec | Document |
95+ | ---| ---|
96+ | M81-1 Vocabulary | ` NFTBAN_VOCABULARY_REFERENCE_v1.81.md ` (v1.1) |
97+ | M81-2 Counter inventory | ` METRICS_CATALOG_v1.81.md ` |
98+ | M81-3 Module contracts | 5 module evidence contracts |
99+ | M81-4 Health derivation | ` HEALTH_METRIC_DERIVATION_v1.81.md ` |
100+ | M81-5 CLI output | ` CLI_OUTPUT_SPEC_v1.81.md ` |
101+ | M81-6 JSON schema | ` JSON_SCHEMA_SPEC_v1.81.md ` |
102+ | M81-7 Shadowing detection | ` SHADOWING_DETECTION_SPEC_v1.81.md ` |
103+ | M81-8 Glossary | ` METRICS_GLOSSARY_AND_TROUBLESHOOTING_v1.81.md ` |
104+
105+ ### Known limitations
106+
107+ - ** Set-element counting not implemented.** ` countSetElements() ` returns 0.
108+ BotGuard ENFORCING/OBSERVING and blacklist PRIMED states are unreachable
109+ from the validator. Fix target: v1.82 per-set queries.
110+ - ** Portscan effective evidence is structural-only/idle.** No dedicated
111+ kernel counter. Real enforcement evidence requires kernel log parsing.
112+ - ** LoginMon effective evidence not yet integrated.** Journal query outside
113+ validator's point-in-time snapshot model. Reports idle by default.
114+ - ** Consistency axis is a stub** (` "ok" ` always). Full cross-source
115+ checking is v1.82 scope.
116+ - ** Legacy shell CLI contains 24 banned-phrase instances** in health
117+ subsystem files. Full CLI vocabulary enforcement is v1.82 scope.
118+
119+ ### PRs
120+
121+ | PR | Title |
122+ | ---| ---|
123+ | #377 | Portscan classic log-path collision fix |
124+ | #381 | M81-4/5/6 health derivation + JSON schema + CLI cleanup + CF fixes |
125+
126+ ---
127+
14128## [ 1.80.1] - 2026-04-13
15129
16130** Hotfix.** Fixes validator semantic issue from v1.80.0: module-scoped helper
0 commit comments