release: v1.97.0 — lifecycle Wave 1 (types + engine + authority + CLI dry-run)#457
Merged
release: v1.97.0 — lifecycle Wave 1 (types + engine + authority + CLI dry-run)#457
Conversation
Add internal/lifecycle package with orchestration types: - Stage enum: DETECT, PLAN, APPLY, VERIFY, FINAL - Outcome enum: SUCCESS, FAILED, FAILED_RECOVERED (high-level orchestration; detailed failure in v1.96 FailureClass) - Mode enum: install, update, uninstall, maintenance - AuthorityOwner enum: NFTBAN, EXTERNAL, NONE - LifecycleHealth enum: PROTECTED, IDLE, DEGRADED, DOWN - Action enum: NOOP, TAKE_AUTHORITY, PRESERVE_AUTHORITY, ABORT, NEEDS_RECOVERY - AuthorityState struct with Owner+Health independence (INV-LC-004) - NormalizeNone(): Owner=NONE normalizes health to DOWN - Detection struct: conflicting_firewall, kernel_valid, validator_consistent, ssh_safe - LastOperation struct: embeds v1.96 result/failure_class/recovery_pending (INV-LC-003) - Plan struct with HasAbort()/HasRecovery() helpers - Snapshot (engine input) and RunResult (engine output) - JSON schema version 1.0 15 unit tests covering: mode validation, authority ownership + health independence, DEGRADED ownership preservation, NONE normalization, plan helpers, two-truths model (FAILED_RECOVERED + PROTECTED), JSON roundtrip for Snapshot and RunResult. Contract: V197_LIFECYCLE_CONTRACT.md No behavior change — types only. INV-LC-001 enforced. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add lifecycle engine: pure function, no side effects (INV-LC-001). Run(Snapshot) → RunResult: - DETECT stage: checks conflicting firewall, recovery pending, mode validity - PLAN stage: resolves action per mode (install/update/uninstall/maintenance) - FINAL stage: determines outcome and resulting authority State machine rules: - ConflictingFirewall + desired NFTBAN → ABORT - RecoveryPending → NEEDS_RECOVERY (must resolve v1.96 recovery first) - EXTERNAL authority → ABORT (no unauthorized takeover) - NONE + DOWN → TAKE_AUTHORITY (safe for fresh install) - NFTBAN + DOWN + update → NEEDS_RECOVERY - NFTBAN + DEGRADED + update → PRESERVE with caution warning - FAILED_RECOVERED + PROTECTED → SUCCESS with warning (INV-LC-008) Per-mode resolvers: - install: NONE→TAKE, EXTERNAL→ABORT, NFTBAN→PRESERVE - update: requires NFTBAN owner, DOWN→NEEDS_RECOVERY - uninstall: NONE→NOOP, otherwise release authority - maintenance: requires NFTBAN owner, DOWN→NEEDS_RECOVERY 13 test cases covering all decision paths. No side effects. No system mutation. Plan-only. Contract: V197_LIFECYCLE_CONTRACT.md §6 PR-02 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add authority resolution: prior → desired → resulting with health dimension. ResolveAuthority() rules: - NFTBAN + PROTECTED/IDLE → PRESERVE_AUTHORITY - NFTBAN + DEGRADED → PRESERVE_AUTHORITY (still owned, INV-LC-004) - NFTBAN + DOWN → NEEDS_RECOVERY - EXTERNAL + desired NFTBAN → ABORT (unless --force) - EXTERNAL + force → TAKE_AUTHORITY (explicit takeover) - NONE + desired NFTBAN → TAKE_AUTHORITY (fresh install) - ConflictingFirewall → ABORT - Uninstall (NFTBAN → NONE) → release authority Helpers: - CanProceed(action) — true for TAKE/PRESERVE/NOOP, false for ABORT/RECOVERY - TransitionDescription() — human-readable transition logging - NormalizeNone() applied on entry (NONE+PROTECTED → NONE+DOWN) 11 test cases covering all authority transitions. No side effects. No system mutation. Contract: V197_LIFECYCLE_CONTRACT.md §6 PR-03 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add stable lifecycle output rendering: - RenderJSON(): versioned JSON output (schema_version always set) - RenderText(): human-readable summary with authority, detection, plan - ParseRunResult(): deserialize from JSON - Field stability test: 25 required JSON field names snapshot-tested v1.96 truth preservation (INV-LC-003, INV-LC-008): - last_operation.result, failure_class, recovery_pending, last_rebuild_failed all present in JSON output - FAILED_RECOVERED + PROTECTED renders correctly in both formats - Schema version 1.0, additive-only (INV-LC-006) 8 test cases: JSON render, v1.96 truth preservation, text render, text with recovery, parse roundtrip, invalid parse, schema version auto-set, field stability snapshot. Contract: V197_LIFECYCLE_CONTRACT.md §4, §6 PR-04 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add structured lifecycle event logging: - Logger with detect/plan/result event emission - JSONL output format (one JSON object per line) - Events: detect (authority + detection observations), plan (actions + warnings + v1.96 last_operation), result (outcome + stage + resulting authority) - Dry-run logs say "plan_completed", not "execution_completed" (no misleading success claims, INV-LC-001) - Reusable by v1.98+ installer/update execution 5 tests: detect event, plan with v1.96 truth, dry-run result label, execution result label, JSONL format validation (3 events = 3 lines). Contract: V197_LIFECYCLE_CONTRACT.md §6 PR-06 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add nftban lifecycle run --mode=MODE [--dry-run] [--json]: - Reads real system state: authority (validator), recovery marker (v1.96), detection (conflicting firewalls, kernel validity, SSH safety) - Runs lifecycle engine (pure, no side effects) - Renders output as text or JSON - Logs evidence (detect/plan/result) to stderr - REFUSES --no-dry-run (INV-LC-007: no execution in v1.97) - Exit 0 for valid plan, exit 1 for failures/aborts System detection: - Authority: checks nftban-validate status for health - Recovery: reads v1.96 rebuild.RecoveryMarker - Conflicts: checks firewalld/ufw service state - Kernel: checks nftban table existence via nft list table No side effects. No nftables mutation. Plan-only. Contract: V197_LIFECYCLE_CONTRACT.md §6 PR-05 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Policy gate requires //lint:ignore, not //nolint:. Simpler fix: use _, _ = to discard write errors (best-effort log writes). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements v1.97 Lifecycle Wave 1 — the lifecycle foundation for v1.98+ installer/update canonization.
Side-effect free by contract (INV-LC-001). No nftables mutation, no installer replacement, no truth drift.
Core additions (6 PRs)
Run(Snapshot) → RunResult, deterministic, no side effectsnftban lifecycle run --mode=X [--json], refuses--no-dry-runKey architectural decisions
--no-dry-runprints "not available in v1.97" and exits 1 (INV-LC-007)Contract
V197_LIFECYCLE_CONTRACT.md(locked 2026-04-17)8 invariants (INV-LC-001 through INV-LC-008):
Lab4 Validation
Unit tests (50 PASS)
CLI tests (5 PASS on lab4)
Build
Files Changed
New files (8):
internal/lifecycle/types.go— enums + structsinternal/lifecycle/types_test.gointernal/lifecycle/engine.go— pure state machineinternal/lifecycle/engine_test.gointernal/lifecycle/authority.go— authority resolutioninternal/lifecycle/authority_test.gointernal/lifecycle/output.go— JSON + text renderinginternal/lifecycle/output_test.gointernal/lifecycle/logger.go— evidence logginginternal/lifecycle/logger_test.gocmd/nftban-core/cmd_lifecycle.go— CLI commandModified files (1):
cmd/nftban-core/main.go— addedlifecyclecommand dispatchTest plan
🤖 Generated with Claude Code