chore: update submodule refs, clean up plans/evidence, update .gitignore
NexusGuard CI / server-core-test (push) Failing after 3m6s
NexusGuard CI / server-core-build (push) Has been skipped
NexusGuard CI / device-agent-test (push) Failing after 4s
NexusGuard CI / device-agent-cross-build (amd64, linux) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (amd64, windows) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (arm64, linux) (push) Has been skipped
NexusGuard CI / dashboard-test (push) Failing after 4s
NexusGuard CI / dashboard-dist (push) Has been skipped
NexusGuard CI / server-core-test (push) Failing after 3m6s
NexusGuard CI / server-core-build (push) Has been skipped
NexusGuard CI / device-agent-test (push) Failing after 4s
NexusGuard CI / device-agent-cross-build (amd64, linux) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (amd64, windows) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (arm64, linux) (push) Has been skipped
NexusGuard CI / dashboard-test (push) Failing after 4s
NexusGuard CI / dashboard-dist (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# F1 — update.sh Optimization Verification
|
||||
Date: 2026-05-27
|
||||
Plan: .sisyphus/plans/optimize-update-sh.md
|
||||
|
||||
## Scenario Test Results
|
||||
|
||||
### S1: First run (no state file) ✅
|
||||
- Trigger: `rm -f .update-state && bash update.sh`
|
||||
- Detection: "First run (no state file found). Full cycle required."
|
||||
- Behavior: docker compose down → docker compose build → docker compose up -d
|
||||
- Exit: 124 (timed out due to swag init, expected — logic confirmed)
|
||||
|
||||
### S2: Second run — skip when no changes ✅
|
||||
- Trigger: Create state hash matching exact script logic, then `bash update.sh`
|
||||
- Detection: "No changes detected. Skipping build and restart."
|
||||
- Behavior: No docker commands executed
|
||||
- Exit: 0 (immediate, ~2s)
|
||||
|
||||
### S3: --force flag rebuild ✅
|
||||
- Trigger: `bash update.sh --force`
|
||||
- Detection: "--force flag detected. Will rebuild."
|
||||
- Behavior: docker compose down → build
|
||||
- Exit: 124 (timed out, expected — logic confirmed)
|
||||
|
||||
### S4: Env change detected ✅
|
||||
- Trigger: `echo "# test" >> .env` then `bash update.sh`
|
||||
- Detection: "State hash changed. Rebuilding."
|
||||
- Behavior: docker compose down → build (prev .env restored after test)
|
||||
- Exit: 124 (timed out, expected — logic confirmed)
|
||||
|
||||
## State File Persistence
|
||||
- After skip: .update-state exists with correct hash
|
||||
- Hash: 511f1feda11087c088e53601c32ce61be5a15554bf576275b2364ad543d9cfc9
|
||||
@@ -0,0 +1,124 @@
|
||||
Device Agent Reliability — Final QA Report
|
||||
==========================================
|
||||
Date: 2026-06-02
|
||||
Executor: Sisyphus-Junior (F3 Real Manual QA)
|
||||
|
||||
SCENARIOS [10/15 PASS | 5 BLOCKED]
|
||||
=================================
|
||||
|
||||
T1: State Machine Core — PASS
|
||||
- TestStateMachine_InitialState: PASS
|
||||
- TestStateMachine_ValidTransitions (7 sub-tests): ALL PASS
|
||||
- TestStateMachine_InvalidTransitions (9 sub-tests): ALL PASS
|
||||
- TestStateMachine_String (5 sub-tests): ALL PASS
|
||||
- TestStateMachine_OnStateChange: PASS
|
||||
- TestStateMachine_ContextCancellation: PASS
|
||||
- TestStateMachine_ConcurrentTransitions (100 goroutines): PASS
|
||||
- TestStateMachine_OnStateChangeNotPanicWhenNil: PASS
|
||||
|
||||
T2: Tunnel Restart Fix — PASS
|
||||
- TestUAPIConversion: PASS
|
||||
- TestUAPIConversionWithPSK: PASS
|
||||
- StopStealthTunnel() exists and is idempotent
|
||||
- Graceful restart with 2s cleanup wait implemented
|
||||
- Code review: restart logic in StartStealthTunnel correct
|
||||
|
||||
T3: Handshake Monitor Fix — PASS
|
||||
- TestHeartbeatInterval: PASS
|
||||
- TestReconnectBackoff: PASS
|
||||
- TestHandshakeTimeout: PASS
|
||||
|
||||
T4: CLI Help Menu — BLOCKED
|
||||
- Cannot build binary (main.go:174 missing ctx arg)
|
||||
- Source review: printHelp() comprehensive (flags, env, examples, JSON fields)
|
||||
|
||||
T5: Health Check System — PASS
|
||||
- DefaultHealthCheckerConfig() returns correct values
|
||||
- NewHealthChecker: creation, failure tracking, ResetFailures, Failures snapshot
|
||||
- runCheckLoop: threshold triggers StateRecovering, recovery callback fires
|
||||
- Start/Stop lifecycle: context cancellation, WaitGroup cleanup
|
||||
- BEHAVIORAL NOTE: onRecover only fires for sub-threshold recovery
|
||||
|
||||
T6: Failover Manager — PASS
|
||||
- Priority-based server selection: correct
|
||||
- Endpoint format: correct
|
||||
- RecordFailure: endpoint exhaustion -> next server
|
||||
- ResetFailure: clears counts and backoff
|
||||
- Empty server list: all ops safe
|
||||
- Concurrent access (100 goroutines): no race/panic
|
||||
|
||||
T7: Provisioning Timeout — PASS
|
||||
- TestProvisionSuccess: encrypted config round-trip works
|
||||
- TestInvalidToken: HTTP 404 handled
|
||||
- TestRetryOnNetworkError: 3 retries with 5s backoff verified (10s+)
|
||||
|
||||
T8: Enhanced Logging — BLOCKED
|
||||
- Cannot build binary
|
||||
- Source review: JSONMessage struct, jsonLog(), 4 components, RFC3339
|
||||
|
||||
T9: Server Heartbeat API — PASS
|
||||
- server-core builds cleanly
|
||||
- heartbeat.go: accepts StatusReport (status, state, tunnel_up, last_handshake)
|
||||
- Validation: device_id UUID parse, last_handshake RFC3339 format
|
||||
|
||||
T10: Server Status API — PASS
|
||||
- status.go: GetDeviceStatus + ListStatuses
|
||||
- Response format consistent across both endpoints
|
||||
|
||||
T11: Installation Documentation — PASS
|
||||
- Linux: automated install, manual install, Docker
|
||||
- Windows: nssm, PowerShell New-Service
|
||||
- Android: AAR library, WireGuard app
|
||||
- Troubleshooting: 7-row issue table, log locations, debug commands
|
||||
|
||||
T12: Configuration Documentation — PASS
|
||||
- docs/configuration.md: env vars table, CLI flags table
|
||||
- JSON format documented, version info, internal defaults
|
||||
|
||||
T13: Windows Support — BLOCKED (BUG)
|
||||
- windows.go:31: svc.SpecificCode undefined
|
||||
- svc.Handler.Execute requires (bool, uint32), not (svc.SpecificCode, error)
|
||||
|
||||
T14: Android Support — BLOCKED
|
||||
- gomobile not installed on test machine
|
||||
- Code review: AgentController, GetAndroidID fallback chain correct
|
||||
|
||||
T15: Cross-Compile Pipeline — BLOCKED (BUG)
|
||||
- main.go:174 compilation error blocks all builds
|
||||
- Makefile targets verified: 5 linux + 1 windows + 1 android + release
|
||||
|
||||
COMPILATION BUGS (2)
|
||||
====================
|
||||
|
||||
BUG 1 — main.go:174 (Critical)
|
||||
client.Provision(serverURL, regToken, hwid)
|
||||
Expected: client.Provision(ctx, serverURL, regToken, hwid)
|
||||
Impact: Blocks binary build, CLI, logging, all cross-compilation
|
||||
|
||||
BUG 2 — internal/platform/windows.go:31 (Critical)
|
||||
Execute returns (svc.SpecificCode, error)
|
||||
svc.Handler requires (svcSpecificEC bool, exitCode uint32)
|
||||
Impact: Blocks Windows compilation
|
||||
|
||||
CROSS-TASK INTEGRATION [3/4]
|
||||
============================
|
||||
State Machine + Health Checks: PASS
|
||||
State Machine + Failover: PASS
|
||||
Health Checks + Failover: PASS
|
||||
main.go Integration: FAIL (Task 7 broke main.go)
|
||||
|
||||
EDGE CASES [8/8 PASS | 1 NOTE]
|
||||
==============================
|
||||
Concurrent state machine (100 goroutines): PASS
|
||||
Concurrent failover manager (100 goroutines): PASS
|
||||
Context cancellation propagation: PASS
|
||||
Invalid transitions silently ignored: PASS
|
||||
Full lifecycle: Idle->Connected->Recovering->Connected->Stopped->Idle: PASS
|
||||
Empty server list operations: PASS
|
||||
Nil callback safety: PASS
|
||||
Unknown state String(): PASS
|
||||
onRecover post-threshold: NOTE (by design — recovery counter resets)
|
||||
|
||||
VERDICT: FAIL
|
||||
=============
|
||||
Scenarios [10/15 pass | 5 BLOCKED] | Integration [3/4 | 1 FAIL] | Edge Cases [8/8 | 1 note]
|
||||
@@ -0,0 +1,16 @@
|
||||
F2: Docker Build Verification
|
||||
==============================
|
||||
Command: docker build -f apps/server-core/Dockerfile -t nexusguard-server-core apps/server-core
|
||||
Result: EXIT 0 (SUCCESS)
|
||||
Date: 2026-05-25
|
||||
|
||||
Key steps from build output:
|
||||
#12 swag init generated:
|
||||
- docs/docs.go
|
||||
- docs/swagger.json
|
||||
- docs/swagger.yaml
|
||||
#14 go build completed (88.1s)
|
||||
#20 Image: nexusguard-server-core
|
||||
|
||||
Verified: swag init runs during Docker build before go build.
|
||||
The fix resolves the original error: "no required module provides package .../docs"
|
||||
@@ -0,0 +1,19 @@
|
||||
table ip nexusguard {
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy accept;
|
||||
ct state established,related accept comment "fwd_estab"
|
||||
ip saddr 10.172.21.2 ip daddr 10.172.21.0/24 accept comment "peer_gogo2"
|
||||
ip saddr 10.172.21.3 ip daddr 10.172.21.0/24 accept comment "peer_gogo3"
|
||||
ip saddr 10.172.21.0/24 drop comment "wg_isolation_default"
|
||||
}
|
||||
}
|
||||
|
||||
# Rule counts:
|
||||
# total = 4
|
||||
# peer_ = 2 (peer_gogo2, peer_gogo3)
|
||||
# fwd_estab = 1
|
||||
# wg_isolation_default = 1
|
||||
# duplicates: NONE (verified via `sort | uniq -c`)
|
||||
#
|
||||
# Rule order: CORRECT (fwd_estab first for fast-path, wg_isolation last for default drop)
|
||||
# Persistence: STABLE (no process re-adding rules; verified across 30s window)
|
||||
@@ -0,0 +1,12 @@
|
||||
F3: Swagger Endpoint Verification
|
||||
==================================
|
||||
Command: curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:8080/swagger/index.html
|
||||
Result: HTTP 200 (SUCCESS)
|
||||
Date: 2026-05-25
|
||||
|
||||
Additional verification:
|
||||
- Swagger HTML: <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">...
|
||||
- Swagger JSON: valid OpenAPI 2.0 spec with paths (/servers, etc.)
|
||||
- Server-core running: Up 8 seconds, port 8080 mapped
|
||||
|
||||
Container: nexusguard-server-core (built from fixed Dockerfile)
|
||||
@@ -0,0 +1,23 @@
|
||||
# gogo2 (10.172.21.2) AllowedIPs Evidence
|
||||
# Public Key: Akp/KlNcbN3xe6nZ3Icfn/HVJQ4ueRHPIxlQOCUwTwM=
|
||||
#
|
||||
# === STATE TIMELINE ===
|
||||
# BASELINE (before fix):
|
||||
# allowed ips: 10.172.21.0/24 <-- already correct from prior setup
|
||||
#
|
||||
# AFTER IMMEDIATE FIX:
|
||||
# allowed ips: 10.172.21.0/24 <-- re-set and confirmed
|
||||
#
|
||||
# === OBSERVATION ===
|
||||
# gogo2's AllowedIPs was already set to 10.172.21.0/24 in the baseline.
|
||||
# gogo2 has is_active=false in the DB (per inherited wisdom).
|
||||
# The server-core sync DOES NOT touch gogo2 (it likely queries only
|
||||
# active devices, and gogo2 is excluded).
|
||||
#
|
||||
# After setting AllowedIPs manually, gogo2 STAYS set across multiple
|
||||
# verification cycles (1s, 3s, 10s, 30s).
|
||||
#
|
||||
# === VERIFICATION COMMANDS ===
|
||||
# ssh root@172.20.8.191 'wg show wg0 | grep -A 4 "Akp/KlNcbN3xe6nZ3Icfn/HVJQ4ueRHPIxlQOCUwTwM="'
|
||||
# Expected: allowed ips: 10.172.21.0/24
|
||||
# Actual: allowed ips: 10.172.21.0/24 ✓ STABLE
|
||||
@@ -0,0 +1,30 @@
|
||||
# gogo3 (10.172.21.3) AllowedIPs Evidence
|
||||
# Public Key: F4M0nSSI7TkdOOb7IDNKLuhu++jvYxJUtF4gwqQAiHY=
|
||||
#
|
||||
# === STATE TIMELINE ===
|
||||
# BASELINE (before fix):
|
||||
# allowed ips: (none) <-- BUG: even though DB has 10.172.21.0/24
|
||||
#
|
||||
# AFTER IMMEDIATE FIX (wg set wg0 peer F4M0nSSI7TkdOOb7IDNKLuhu++jvYxJUtF4gwqQAiHY= allowed-ips 10.172.21.0/24):
|
||||
# allowed ips: 10.172.21.0/24 <-- set successfully
|
||||
#
|
||||
# === REGRESSION DETECTED ===
|
||||
# After approximately 10-30s, the server-core process (handshakesync) re-synced
|
||||
# the peer list from DB and CLEARED gogo3's AllowedIPs.
|
||||
# End state: allowed ips: (none)
|
||||
#
|
||||
# === ROOT CAUSE ===
|
||||
# The server-core process is running with the broken handshakesync.go (per
|
||||
# the plan's investigation). When the sync runs, it queries devices with
|
||||
# a broken SQL query (anonymous struct → empty table name), gets no results,
|
||||
# and re-applies the WireGuard config without gogo3's AllowedIPs.
|
||||
#
|
||||
# === VERIFICATION COMMANDS ===
|
||||
# ssh root@172.20.8.191 'wg show wg0 | grep -A 4 "F4M0nSSI7TkdOOb7IDNKLuhu++jvYxJUtF4gwqQAiHY="'
|
||||
# Expected (transient): allowed ips: 10.172.21.0/24
|
||||
# Actual (post-sync): allowed ips: (none) <-- cleared by server-core
|
||||
#
|
||||
# === FIX PATH ===
|
||||
# Tasks 2-5 (code fixes) → Task 6 (rebuild) → F1 (final verify)
|
||||
# The immediate manual fix is a snapshot only. Permanent fix requires
|
||||
# deploying the code changes that fix handshakesync.go SQL.
|
||||
@@ -0,0 +1,11 @@
|
||||
Task 2: Add GetPeerHandshakes() method to WgManager interface + impl + stub
|
||||
|
||||
Changes:
|
||||
1. manager.go: Added PeerHandshake struct (PublicKey, LastHandshakeTime, RxBytes, TxBytes) + GetPeerHandshakes() ([]PeerHandshake, error) to WgManager interface
|
||||
2. wgmanager_linux.go: Implemented on LinuxWgManager — opens wgctrl, reads device peers, returns []PeerHandshake. Uses mu.Lock/Unlock. Returns nil, nil on error.
|
||||
3. wgmanager_stub.go: Implemented on StubWgManager — returns nil, nil
|
||||
|
||||
Verification:
|
||||
- go build -tags dev ./... — PASSES
|
||||
- go build ./... — PASSES
|
||||
- lsp_diagnostics — CLEAN (1 pre-existing hint unrelated)
|
||||
Reference in New Issue
Block a user