Skip to content

Commit 2d33872

Browse files
itcmsgrclaude
andcommitted
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>
1 parent 840d5d2 commit 2d33872

4 files changed

Lines changed: 118 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.80.1
1+
1.81.0

cli/lib/nftban/core/nftban_fhs_spec.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env bash
22
# =============================================================================
3-
# NFTBan v1.80.1 - FHS Specification (GENERATED)
3+
# NFTBan v1.81.0 - FHS Specification (GENERATED)
44
# =============================================================================
55
# SPDX-License-Identifier: MPL-2.0
66
#
77
# meta:name="nftban_fhs_spec"
88
# meta:type="core"
99
# meta:header="FHS Specification"
10-
# meta:version="1.80.1"
10+
# meta:version="1.81.0"
1111
# meta:owner="Antonios Voulvoulis <contact@nftban.com>"
1212
# meta:homepage="https://nftban.com"
1313
#

internal/validator/module_health.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ func readConfigBool(localPath, basePath, key string) ConfigState {
341341

342342
// readKeyFromFile reads a KEY="value" line from a shell config file.
343343
func readKeyFromFile(path, key string) string {
344-
data, err := os.ReadFile(path)
344+
data, err := os.ReadFile(path) // #nosec G304 — path is constructed from hardcoded ConfigDir + known config filenames, not user input
345345
if err != nil {
346346
return ""
347347
}

0 commit comments

Comments
 (0)