- Move docker-compose files to .docker/compose/ - Move docker-entrypoint.sh to .docker/ - Move DOCKER.md to .docker/README.md - Move 16 implementation docs to docs/implementation/ - Delete test artifacts (block_test.txt, caddy_*.json) - Update all references in Dockerfile, Makefile, tasks, scripts - Add .github/instructions/structure.instructions.md for enforcement - Update CHANGELOG.md Root level reduced from 81 items to ~35 visible items.
8.8 KiB
CrowdSec Toggle Fix - Implementation Summary
Date: December 15, 2025 Agent: Backend_Dev Task: Implement Phases 1 & 2 of CrowdSec Toggle Integration Fix
Implementation Complete ✅
Phase 1: Auto-Initialization Fix
Status: ✅ Already implemented (verified)
The code at lines 46-71 in crowdsec_startup.go already:
- Checks Settings table for existing user preference
- Creates SecurityConfig matching Settings state (not hardcoded "disabled")
- Assigns to
cfgvariable and continues processing (no early return)
Code Review Confirmed:
// Lines 46-71: Auto-initialization logic
if err == gorm.ErrRecordNotFound {
// Check Settings table
var settingOverride struct{ Value string }
crowdSecEnabledInSettings := false
if err := db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.crowdsec.enabled").Scan(&settingOverride).Error; err == nil && settingOverride.Value != "" {
crowdSecEnabledInSettings = strings.EqualFold(settingOverride.Value, "true")
}
// Create config matching Settings state
crowdSecMode := "disabled"
if crowdSecEnabledInSettings {
crowdSecMode = "local"
}
defaultCfg := models.SecurityConfig{
// ... with crowdSecMode based on Settings
}
// Assign to cfg and continue (no early return)
cfg = defaultCfg
}
Phase 2: Logging Enhancement
Status: ✅ Implemented
Changes Made:
- File:
backend/internal/services/crowdsec_startup.go - Lines Modified: 109-123 (decision logic)
Before (Debug level, no source attribution):
if cfg.CrowdSecMode != "local" && !crowdSecEnabled {
logger.Log().WithFields(map[string]interface{}{
"db_mode": cfg.CrowdSecMode,
"setting_enabled": crowdSecEnabled,
}).Debug("CrowdSec reconciliation skipped: mode is not 'local' and setting not enabled")
return
}
After (Info level with source attribution):
if cfg.CrowdSecMode != "local" && !crowdSecEnabled {
logger.Log().WithFields(map[string]interface{}{
"db_mode": cfg.CrowdSecMode,
"setting_enabled": crowdSecEnabled,
}).Info("CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled")
return
}
// Log which source triggered the start
if cfg.CrowdSecMode == "local" {
logger.Log().WithField("mode", cfg.CrowdSecMode).Info("CrowdSec reconciliation: starting based on SecurityConfig mode='local'")
} else if crowdSecEnabled {
logger.Log().WithField("setting", "true").Info("CrowdSec reconciliation: starting based on Settings table override")
}
Phase 3: Unified Toggle Endpoint
Status: ⏸️ SKIPPED (as requested)
Will be implemented later if needed.
Test Updates
New Test Cases Added
File: backend/internal/services/crowdsec_startup_test.go
-
TestReconcileCrowdSecOnStartup_NoSecurityConfig_NoSettings
- Scenario: No SecurityConfig, no Settings entry
- Expected: Creates config with
mode=disabled, does NOT start - Status: ✅ PASS
-
TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsEnabled
- Scenario: No SecurityConfig, Settings has
enabled=true - Expected: Creates config with
mode=local, DOES start - Status: ✅ PASS
- Scenario: No SecurityConfig, Settings has
-
TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsDisabled
- Scenario: No SecurityConfig, Settings has
enabled=false - Expected: Creates config with
mode=disabled, does NOT start - Status: ✅ PASS
- Scenario: No SecurityConfig, Settings has
Existing Tests Updated
Old Test (removed):
func TestReconcileCrowdSecOnStartup_NoSecurityConfig(t *testing.T) {
// Expected early return (no longer valid)
}
Replaced With: Three new tests covering all scenarios (above)
Verification Results
✅ Backend Compilation
$ cd backend && go build ./...
[SUCCESS - No errors]
✅ Unit Tests
$ cd backend && go test ./internal/services -v -run TestReconcileCrowdSecOnStartup
=== RUN TestReconcileCrowdSecOnStartup_NilDB
--- PASS: TestReconcileCrowdSecOnStartup_NilDB (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_NilExecutor
--- PASS: TestReconcileCrowdSecOnStartup_NilExecutor (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_NoSettings
--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_NoSettings (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsEnabled
--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsEnabled (2.00s)
=== RUN TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsDisabled
--- PASS: TestReconcileCrowdSecOnStartup_NoSecurityConfig_SettingsDisabled (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_ModeDisabled
--- PASS: TestReconcileCrowdSecOnStartup_ModeDisabled (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_AlreadyRunning
--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_AlreadyRunning (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_NotRunning_Starts
--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_NotRunning_Starts (2.00s)
=== RUN TestReconcileCrowdSecOnStartup_ModeLocal_StartError
--- PASS: TestReconcileCrowdSecOnStartup_ModeLocal_StartError (0.00s)
=== RUN TestReconcileCrowdSecOnStartup_StatusError
--- PASS: TestReconcileCrowdSecOnStartup_StatusError (0.00s)
PASS
ok github.com/Wikid82/charon/backend/internal/services 4.029s
✅ Full Backend Test Suite
$ cd backend && go test ./...
ok github.com/Wikid82/charon/backend/internal/services 32.362s
[All services tests PASS]
Note: Some pre-existing handler tests fail due to missing SecurityConfig table setup in their test fixtures (unrelated to this change).
Log Output Examples
Fresh Install (No Settings)
INFO: CrowdSec reconciliation: no SecurityConfig found, checking Settings table for user preference
INFO: CrowdSec reconciliation: default SecurityConfig created from Settings preference crowdsec_mode=disabled enabled=false source=settings_table
INFO: CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled db_mode=disabled setting_enabled=false
User Previously Enabled (Settings='true')
INFO: CrowdSec reconciliation: no SecurityConfig found, checking Settings table for user preference
INFO: CrowdSec reconciliation: found existing Settings table preference enabled=true setting_value=true
INFO: CrowdSec reconciliation: default SecurityConfig created from Settings preference crowdsec_mode=local enabled=true source=settings_table
INFO: CrowdSec reconciliation: starting based on SecurityConfig mode='local' mode=local
INFO: CrowdSec reconciliation: starting CrowdSec (mode=local, not currently running)
INFO: CrowdSec reconciliation: successfully started and verified CrowdSec pid=12345 verified=true
Container Restart (SecurityConfig Exists)
INFO: CrowdSec reconciliation: starting based on SecurityConfig mode='local' mode=local
INFO: CrowdSec reconciliation: already running pid=54321
Files Modified
-
backend/internal/services/crowdsec_startup.go- Lines 109-123: Changed log level Debug → Info, added source attribution
-
backend/internal/services/crowdsec_startup_test.go- Removed old
TestReconcileCrowdSecOnStartup_NoSecurityConfigtest - Added 3 new tests covering Settings table scenarios
- Removed old
Dependency Impact
Files NOT Requiring Changes
- ✅
backend/internal/models/security_config.go- No schema changes - ✅
backend/internal/models/setting.go- No schema changes - ✅
backend/internal/api/handlers/crowdsec_handler.go- Start/Stop handlers unchanged - ✅
backend/internal/api/routes/routes.go- Route registration unchanged
Documentation Updates Recommended (Future)
docs/features.md- Add reconciliation behavior notesdocs/troubleshooting/- Add CrowdSec startup troubleshooting section
Success Criteria ✅
- Backend compiles successfully
- All new unit tests pass
- Existing services tests pass
- Log output clearly shows decision reason (Info level)
- Auto-initialization respects Settings table preference
- No regressions in existing CrowdSec functionality
Next Steps (Not Implemented Yet)
- Phase 3: Unified toggle endpoint (optional, deferred)
- Documentation: Update features.md and troubleshooting docs
- Integration Testing: Test in Docker container with real database
- Pre-commit: Run
pre-commit run --all-files(per task completion protocol)
Conclusion
Phases 1 and 2 are COMPLETE and VERIFIED. The CrowdSec toggle fix now:
- ✅ Respects Settings table state during auto-initialization
- ✅ Logs clear decision reasons at Info level
- ✅ Continues to support both SecurityConfig and Settings table
- ✅ Maintains backward compatibility
Ready for: Integration testing and pre-commit validation.