56 KiB
Firewall System Fix + Legacy Styling Cleanup
TL;DR
Quick Summary: Fix broken nftables firewall rule system (rules from UI never actually filter traffic), remove dead LinkedDevices component, and modernize ALL remaining legacy-styled views/components to use the Ng* design system with proper @theme tokens.
Deliverables:
- Working nftables firewall rules (UI rules actually filter kernel traffic)
- fwd_estab duplicate bug fixed
- RemoveForwardRule multi-CIDR fix
- @theme design tokens defined in main.css
- 11 files restyled with Ng* components + design tokens
- All 19 alert() → useToast(), all 6 confirm() → useConfirm()
- LinkedDevices.vue deleted
- Backend go tests for firewall rule CRUD
Estimated Effort: Large Parallel Execution: YES - 3 waves Critical Path: Task 1 (@theme) → Wave 2 frontend tasks → Final verification
Context
Original Request
User identified LinkedDevices.vue ("Perangkat Tertaut") as broken/redundant and asked:
- What is it for? → Answered: legacy component showing device's own data, not actual linked devices
- Firewall needs fixing — conflicts with AllowedIPs and nftables → Investigated: found critical backend bugs
- Fix with recommendations + check all legacy styling across all menus
Interview Summary
Key Discussions:
- LinkedDevices.vue: DELETE — broken, redundant, uses emoji/alert/confirm
- FirewallEditor.vue: Redesign with Ng* design system
- Backend bugs: fwd_estab 3x duplicate, syncRuleToFirewall completely broken (rules never actually filter), RemoveForwardRule single-handle bug
- AllowedIPs vs nftables: Separate concerns (routing vs filtering) — UI already separates them correctly
- Styling: ALL 7 views + 4 components need cleanup (19 alert, 6 confirm, hardcoded colors, emoji)
Research Findings:
- Ng* components use @theme tokens (
bg-bg-surface,text-text-primary, etc.) but main.css has NO @theme block — tokens undefined syncRuleToFirewall()callsAddRangeRule()which creates orphaned nftables SETs with no chain RULE referencing them — UI firewall rules are 100% non-functionalAddRangeRuleinterface inadequate: no protocol, no action, CIDR parsing wrong- 3 callers of AddRangeRule: rules.go:133, devices.go:144, peers.go:128
- useToast() and useConfirm() composables already exist and ready to use
- All 16 Ng* components created during redesign, ready to use
Metis Review
Identified Gaps (addressed):
- @theme tokens missing — added as Wave 0 prerequisite task
- NetManager interface needs new method for proper firewall rules — planned
- devices.go:144 and peers.go:128 SSH auto-provisioning also use broken AddRangeRule — included in scope
- RemoveForwardRule multi-CIDR awk bug — included in scope
- Existing orphaned nftables sets in production — noted, InitNetwork will clean
- alert/confirm count correction: 21/7 raw → 19/6 after LinkedDevices deletion — confirmed
Work Objectives
Core Objective
Fix the completely non-functional firewall rule system so UI-created rules actually filter traffic in the Linux kernel, and modernize all remaining legacy UI to the Ng* design system.
Concrete Deliverables
apps/server-core/internal/firewall/manager.go— newAddFirewallRule/RemoveFirewallRulemethodsapps/server-core/internal/firewall/nftables_linux.go— fwd_estab fix, new methods, RemoveForwardRule fixapps/server-core/internal/firewall/nftables_stub.go— matching stub methodsapps/server-core/api/rules.go— rewrittensyncRuleToFirewall+ delete cleanupapps/server-core/api/devices.go+peers.go— updated SSH auto-provisioning callersapps/dashboard-ui/src/assets/main.css— @theme design tokensapps/dashboard-ui/src/components/LinkedDevices.vue— DELETEDapps/dashboard-ui/src/components/FirewallEditor.vue— redesigned with Ng*- 9 more .vue files restyled (see TODOs)
Definition of Done
cd apps/server-core && go build ./...passescd apps/server-core && go test ./... -tags dev -count=1all passcd apps/dashboard-ui && npm run buildpasses with 0 errorsgrep -r "alert(" --include="*.vue" src/returns 0 matchesgrep -r "LinkedDevices" --include="*.vue" --include="*.ts" src/returns 0 matchesgrep -r "confirm(" --include="*.vue" src/ | grep -v useConfirm | grep -v handleConfirm | grep -v ConfirmModal | grep -v "\/\/"returns 0 matches
Must Have
- Firewall rules created via UI must produce actual nft rules in FORWARD chain
- All alert()/confirm() replaced with useToast()/useConfirm()
- LinkedDevices.vue deleted with all references
- @theme tokens defined so Ng* components render correctly
- Port range support (e.g., 80-443)
- Protocol selection (tcp/udp/both) honored in nft rules
- Action (accept/drop) honored in nft rules
Must NOT Have (Guardrails)
- NEVER use
nft flush table— destroys all peer isolation - NEVER touch
AddForwardRule,AddInputRule,RemoveInputRule,AddUserIsolation— they work correctly - NEVER introduce new Ng* components — use existing 16 only
- NEVER touch Login.vue — already redesigned
- NEVER touch style.css — dead scaffold code
- NEVER add logging/audit trails to firewall backend
- NEVER refactor admin-only checks (known debt, out of scope)
- NEVER add sorting/filtering/pagination to FirewallEditor rule table
- Port format: single port or dash-range ONLY (e.g.,
80or8000-9000). No comma-separated.
Verification Strategy
ZERO HUMAN INTERVENTION - ALL verification is agent-executed. No exceptions.
Test Decision
- Infrastructure exists: YES (Go:
go test, Frontend:npm run build/ vue-tsc) - Automated tests: YES (backend tests after implementation)
- Framework: Go
testingpackage for backend; no frontend unit tests - Test approach: Tests-after for backend; agent QA for frontend
QA Policy
Every task MUST include agent-executed QA scenarios.
Evidence saved to .sisyphus/evidence/task-{N}-{scenario-slug}.{ext}.
- Backend: Use Bash (
go test,go build) — compile, run tests, verify output - Frontend/UI: Use Bash (
npm run build,grep) — type-check, verify no legacy patterns remain
Execution Strategy
Parallel Execution Waves
Wave 0 (Prerequisite — must complete first):
└── Task 1: Define @theme design tokens in main.css [quick]
Wave 1 (Backend firewall — all parallel):
├── Task 2: Fix InitNetwork fwd_estab duplicates [quick]
├── Task 3: Add AddFirewallRule/RemoveFirewallRule to NetManager interface [quick]
├── Task 4: Rewrite syncRuleToFirewall + fix rule delete cleanup [quick]
├── Task 5: Fix RemoveForwardRule multi-CIDR handling [quick]
├── Task 6: Update SSH auto-provisioning callers [quick]
└── Task 7: Add backend tests for firewall rule CRUD [quick]
Wave 2 (Frontend — all parallel, depends on Task 1):
├── Task 8: Delete LinkedDevices.vue + clean references [quick]
├── Task 9: Redesign FirewallEditor.vue with Ng* design system [quick]
├── Task 10: Restyle DeviceDetail.vue [quick]
├── Task 11: Restyle Devices.vue [quick]
├── Task 12: Restyle Dashboard.vue [quick]
├── Task 13: Restyle Servers.vue modals/forms [quick]
├── Task 14: Restyle Users.vue [quick]
├── Task 15: Restyle AddPeerModal.vue + PeerConfigModal.vue [quick]
├── Task 16: Restyle ShareConfig.vue [quick]
└── Task 17: Restyle TrafficChart.vue SVG colors [quick]
Wave FINAL (After ALL tasks — 4 parallel reviews):
├── Task F1: Plan compliance audit (oracle)
├── Task F2: Code quality review (unspecified-high)
├── Task F3: Real manual QA (unspecified-high)
└── Task F4: Scope fidelity check (deep)
-> Present results -> Get explicit user okay
Dependency Matrix
| Task | Depends On | Blocks | Wave |
|---|---|---|---|
| 1 | - | 8-17 | 0 |
| 2 | - | 7 | 1 |
| 3 | - | 4, 5, 6, 7 | 1 |
| 4 | 3 | 7 | 1 |
| 5 | - | 7 | 1 |
| 6 | 3 | 7 | 1 |
| 7 | 2, 3, 4, 5, 6 | FINAL | 1 |
| 8 | 1 | FINAL | 2 |
| 9 | 1 | FINAL | 2 |
| 10 | 1, 8 | FINAL | 2 |
| 11-17 | 1 | FINAL | 2 |
| F1-F4 | ALL | - | FINAL |
Agent Dispatch Summary
- Wave 0: 1 — T1 →
quick - Wave 1: 6 — T2-T6 →
quick, T7 →quick - Wave 2: 10 — T8-T17 →
quick - FINAL: 4 — F1 →
oracle, F2 →unspecified-high, F3 →unspecified-high, F4 →deep
TODOs
-
1. Define @theme design tokens in main.css
What to do:
- Add
@themeblock toapps/dashboard-ui/src/assets/main.cssafter@import "tailwindcss"; - Define ALL color tokens used by existing Ng* components (found by grepping
src/components/ui/*.vue):- Background:
--color-bg-base,--color-bg-surface,--color-bg-elevated,--color-bg-overlay - Text:
--color-text-primary,--color-text-secondary,--color-text-muted - Border:
--color-border-subtle,--color-border-default,--color-border-strong - Accent:
--color-accent,--color-accent-hover - Semantic:
--color-danger,--color-success,--color-warning - Shadow:
--shadow-glow
- Background:
- Color palette: dark glassmorphism theme —
bg-base~#0a0a14,bg-surface~#12121e,bg-elevated~#1a1a2e, accent = cyan (#06b6d4), danger = red (#ef4444) - Verify existing Ng* components render correctly after tokens are defined
Must NOT do:
- Do NOT modify any Ng* component files
- Do NOT touch
style.css(dead code) - Do NOT add custom utility classes beyond what @theme provides
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: NO (prerequisite for all frontend tasks)
- Parallel Group: Wave 0
- Blocks: Tasks 8-17
- Blocked By: None
References:
apps/dashboard-ui/src/assets/main.css— Current file (17 lines: @import + fadeInUp animation only)apps/dashboard-ui/src/components/ui/NgCard.vue:14-17— Usesbg-bg-surface,bg-bg-elevated,border-border-subtle,border-border-default,shadow-glowapps/dashboard-ui/src/components/ui/NgButton.vue:18-22— Usesbg-accent,bg-accent-hover,bg-bg-elevated,bg-bg-surface,text-text-primary,text-text-secondary,bg-danger,ring-accent,ring-offset-bg-baseapps/dashboard-ui/src/components/ui/NgInput.vue:49-51— Usesbg-bg-base,text-text-primary,placeholder-text-muted,border-border-default,border-border-strong,ring-accent,border-danger,ring-dangerapps/dashboard-ui/src/components/ui/NgModal.vue:46,63,70,77— Usesbg-bg-overlay,from-bg-surface,to-bg-elevated,border-border-subtle,text-text-primary,text-text-muted,text-text-secondaryapps/dashboard-ui/vite.config.ts— Vite 8 +@tailwindcss/viteplugin (TailwindCSS v4 syntax,@themedirective)- TailwindCSS v4 docs:
@themedefines custom design tokens as CSS custom properties that generate utility classes
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: @theme tokens compile and generate utilities Tool: Bash Preconditions: apps/dashboard-ui has node_modules installed Steps: 1. Run: cd apps/dashboard-ui && npm run build 2. Check exit code is 0 3. Run: grep -c "@theme" src/assets/main.css Expected Result: Build succeeds. grep returns 1 (one @theme block exists) Evidence: .sisyphus/evidence/task-1-theme-build.txt Scenario: All required token names defined Tool: Bash Steps: 1. Run: grep -E "bg-base|bg-surface|bg-elevated|bg-overlay|text-primary|text-secondary|text-muted|border-subtle|border-default|border-strong|accent|accent-hover|danger|success|warning|shadow-glow" apps/dashboard-ui/src/assets/main.css | wc -l Expected Result: At least 15 matches (all tokens present) Evidence: .sisyphus/evidence/task-1-token-audit.txtCommit: YES (Commit A)
- Message:
feat(dashboard): add @theme design tokens to main.css - Files:
src/assets/main.css - Pre-commit:
npm run build
- Add
-
2. Fix InitNetwork fwd_estab duplicates in nftables_linux.go
What to do:
- DELETE lines 67, 75, 83 in
nftables_linux.go— these are copy-paste duplicates that insertfwd_estabinto FORWARD chain from inside the INPUT chain section - Keep ONLY line 43 (the correct one inside the FORWARD
wg_isolationguard) - Verify the idempotency guard: line 43 is inside
if exec.Command("sh", "-c", checkWgDrop).Run() != nil— this meansfwd_estabis only added whenwg_isolationdoesn't exist yet. This is correct behavior.
Must NOT do:
- Do NOT touch the INPUT chain rules (lines 48-84 excluding the fwd_estab duplicates)
- Do NOT change
wg_isolationrule - Do NOT use
nft flush table - Do NOT modify
AddForwardRule,AddInputRule, or any method outsideInitNetwork
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 3, 4, 5, 6)
- Blocks: Task 7
- Blocked By: None
References:
apps/server-core/internal/firewall/nftables_linux.go:25-87— FullInitNetwork()function- Line 43: Correct
fwd_estabinsert (inside FORWARD wg_isolation guard) - Line 67: DUPLICATE — inside INPUT section, inserts into FORWARD
- Line 75: DUPLICATE — inside INPUT section, inserts into FORWARD
- Line 83: DUPLICATE — inside INPUT section, inserts into FORWARD
- All 3 duplicates lack idempotency guards (no grep check), so every
InitNetwork()call creates new handles
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: fwd_estab appears exactly once in InitNetwork Tool: Bash Steps: 1. Run: grep -c "fwd_estab" apps/server-core/internal/firewall/nftables_linux.go Expected Result: Exactly 1 match (line 43 area only) Evidence: .sisyphus/evidence/task-2-fwd-estab-count.txt Scenario: Build compiles successfully Tool: Bash Steps: 1. Run: cd apps/server-core && go build ./... Expected Result: Exit code 0, no errors Evidence: .sisyphus/evidence/task-2-go-build.txtCommit: NO (groups with Commit B)
- DELETE lines 67, 75, 83 in
-
3. Add AddFirewallRule/RemoveFirewallRule to NetManager interface
What to do:
- Add new method to
manager.gointerface:AddFirewallRule(ruleName string, sourceIP net.IP, destCIDR string, portRange string, protocol string, action string) error RemoveFirewallRule(ruleName string) error - Implement in
nftables_linux.go:AddFirewallRule: Generate propernft insert rule ip nexusguard forward ip saddr {sourceIP} ip daddr {destCIDR} {protocol} dport {portRange} {action} comment "fwrule_{ruleName}"- Handle protocol:
tcp,udp, or omit forboth - Handle portRange: empty = all ports (no dport match), single port
80, range80-443 - Handle action:
acceptordrop - Handle destCIDR: single IP auto-appended
/32, CIDR passed as-is RemoveFirewallRule: Find and delete ALL handles matchingcomment "fwrule_{ruleName}"(loop over awk output)
- Implement stubs in
nftables_stub.go(return nil) - Do NOT remove old
AddRangeRule/RemoveRangeRuleyet — keep for backward compatibility until callers are updated
Must NOT do:
- Do NOT remove
AddRangeRule/RemoveRangeRulefrom interface (callers still reference them until Task 4/6) - Do NOT touch
AddForwardRule,AddInputRule, or any other existing methods - Do NOT use
nft flush table
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 2, 5)
- Blocks: Tasks 4, 6, 7
- Blocked By: None
References:
apps/server-core/internal/firewall/manager.go— Full interface (17 lines). New methods follow existing pattern.apps/server-core/internal/firewall/nftables_linux.go:181-200—AddForwardRuleas PATTERN to follow: usesnft insert rule, handles comma-separated CIDRs, uses comment for identification, usesexec.Commandapps/server-core/internal/firewall/nftables_linux.go:202-211—RemoveForwardRuleas PATTERN but with KNOWN BUG (single handle). NewRemoveFirewallRulemust loop ALL handles.apps/server-core/internal/firewall/nftables_stub.go— All methods return nil, one-liner pattern
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: Interface compiles with new methods Tool: Bash Steps: 1. Run: cd apps/server-core && go build ./... Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-3-go-build.txt Scenario: New methods exist in all 3 files Tool: Bash Steps: 1. Run: grep -c "AddFirewallRule" apps/server-core/internal/firewall/manager.go apps/server-core/internal/firewall/nftables_linux.go apps/server-core/internal/firewall/nftables_stub.go Expected Result: 1 match per file (3 total) Evidence: .sisyphus/evidence/task-3-method-check.txtCommit: NO (groups with Commit B)
- Add new method to
-
4. Rewrite syncRuleToFirewall + fix rule delete in rules.go
What to do:
- Rewrite
syncRuleToFirewall()inrules.goto use newAddFirewallRule():func (h *RulesHandler) syncRuleToFirewall(rule models.FirewallRule, deviceName string) { // Get device's internal IP for source matching var device models.Device if err := h.db.Where("id = ?", rule.DeviceID).First(&device).Error; err != nil { return } sourceIP := net.ParseIP(device.InternalIP) if sourceIP == nil { return } destCIDR := rule.DestIPRange // Single IP → append /32 if !strings.Contains(destCIDR, "/") { destCIDR += "/32" } h.fw.AddFirewallRule( rule.ID.String(), sourceIP, destCIDR, rule.DestPortRange, // "" = all ports, "80" = single, "80-443" = range rule.Protocol, // "tcp", "udp", "both" rule.Action, // "accept", "drop" ) } - Update
Delete()handler to useRemoveFirewallRule()instead ofRemoveRangeRule():h.fw.RemoveFirewallRule(rule.ID.String()) - Add input validation in
Create(): validateDestPortRangeformat (empty, single number, orN-Nrange) - Add input validation: validate
Actionisacceptordroponly - Add input validation: validate
Protocolistcp,udp, orbothonly
Must NOT do:
- Do NOT change the API request/response shape (keep
CreateRuleRequeststruct) - Do NOT add new fields to the model
- Do NOT add logging or audit trails
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES (after Task 3)
- Parallel Group: Wave 1
- Blocks: Task 7
- Blocked By: Task 3
References:
apps/server-core/api/rules.go— Full file (135 lines).syncRuleToFirewallat line 114-135,Deleteat line 83-112,Createat line 49-81apps/server-core/api/helpers.go— HasvalidateAllowedIPs()pattern to follow for validationapps/server-core/internal/models/models.go:95-112—FirewallRulemodel: ID, DeviceID, DestIPRange, DestPortRange, Protocol, Actionapps/server-core/api/rules.go:109— Current delete usesRemoveRangeRule("rule_" + rule.ID.String())→ change toRemoveFirewallRule(rule.ID.String())
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: syncRuleToFirewall uses new AddFirewallRule Tool: Bash Steps: 1. Run: grep "AddFirewallRule" apps/server-core/api/rules.go 2. Run: grep "AddRangeRule" apps/server-core/api/rules.go Expected Result: AddFirewallRule found, AddRangeRule NOT found Evidence: .sisyphus/evidence/task-4-sync-rewrite.txt Scenario: Delete uses RemoveFirewallRule Tool: Bash Steps: 1. Run: grep "RemoveFirewallRule" apps/server-core/api/rules.go 2. Run: grep "RemoveRangeRule" apps/server-core/api/rules.go Expected Result: RemoveFirewallRule found, RemoveRangeRule NOT found Evidence: .sisyphus/evidence/task-4-delete-fix.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/server-core && go build ./... Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-4-build.txtCommit: NO (groups with Commit B)
- Rewrite
-
5. Fix RemoveForwardRule multi-CIDR handling
What to do:
- Fix
RemoveForwardRule()innftables_linux.goto handle multiple handles (multi-CIDR peers) - Current bug:
awk '/comment \\"peer_xxx\\"/ {print $NF}'returns only the LAST match when piped to a single string. Only one handle is deleted. - Fix: Loop over ALL matching handles. Use approach:
func (m *LinuxManager) RemoveForwardRule(peerName string) error { // Match both exact "peer_xxx" and suffixed "peer_xxx_0", "peer_xxx_1" patterns cmdStr := fmt.Sprintf(`nft -a list chain ip nexusguard forward | grep -E 'comment "peer_%s(_[0-9]+)?"' | awk '{print $NF}'`, peerName) out, err := exec.Command("sh", "-c", cmdStr).Output() if err != nil || len(out) == 0 { return nil } for _, handle := range strings.Split(strings.TrimSpace(string(out)), "\n") { handle = strings.TrimSpace(handle) if handle == "" { continue } exec.Command("sh", "-c", fmt.Sprintf("nft delete rule ip nexusguard forward handle %s", handle)).Run() } return nil } - Note:
AddForwardRulecreates rules with commentspeer_xxx(single CIDR) orpeer_xxx_0,peer_xxx_1(multi-CIDR). The grep pattern must match all variants.
Must NOT do:
- Do NOT modify
AddForwardRule— it works correctly - Do NOT use
nft flush table
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 2, 3)
- Blocks: Task 7
- Blocked By: None
References:
apps/server-core/internal/firewall/nftables_linux.go:202-211— Current buggyRemoveForwardRule(single handle)apps/server-core/internal/firewall/nftables_linux.go:181-200—AddForwardRulecreates commentspeer_{name}andpeer_{name}_{i}for multi-CIDR- Line 190-192: Suffix logic —
suffix = fmt.Sprintf("_%d", i)for multi-CIDR, empty string for single CIDR
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: RemoveForwardRule loops over all handles Tool: Bash Steps: 1. Run: grep -A 15 "func.*RemoveForwardRule" apps/server-core/internal/firewall/nftables_linux.go 2. Verify the output contains a loop (for/range or while) over handles 3. Verify grep pattern matches both "peer_xxx" and "peer_xxx_N" suffixes Expected Result: Function contains loop + regex pattern for suffixed comments Evidence: .sisyphus/evidence/task-5-remove-forward.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/server-core && go build ./... Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-5-build.txtCommit: NO (groups with Commit B)
- Fix
-
6. Update SSH auto-provisioning callers (devices.go + peers.go)
What to do:
- Update
apps/server-core/api/devices.go:144— replaceAddRangeRulecall withAddFirewallRule:Where// Before: _ = h.fw.AddRangeRule("rule_"+rule.ID.String(), ip, ip, 22, 22) // After: _ = h.fw.AddFirewallRule(rule.ID.String(), sourceIP, rule.DestIPRange, "22", "tcp", "accept")sourceIPis the device's InternalIP (already available in the handler context) - Update
apps/server-core/api/peers.go:128— same pattern as devices.go - Read surrounding code context to get the correct
sourceIPvariable name (the device's internal IP is available in the handler) - After updating both callers,
AddRangeRuleandRemoveRangeRulecan be removed from the interface + all implementations (cleanup)
Must NOT do:
- Do NOT change the SSH auto-provisioning logic (still creates port 22 TCP accept rule)
- Do NOT change the FirewallRule model or DB operations
- Do NOT remove
AddRangeRule/RemoveRangeRuleuntil AFTER both callers are updated
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES (after Task 3)
- Parallel Group: Wave 1
- Blocks: Task 7
- Blocked By: Task 3
References:
apps/server-core/api/devices.go:144— Current:_ = h.fw.AddRangeRule("rule_"+rule.ID.String(), ip, ip, 22, 22). Read lines 130-150 for full context to find the sourceIP variable.apps/server-core/api/peers.go:128— Current:_ = h.fw.AddRangeRule("rule_"+rule.ID.String(), ip, ip, 22, 22). Read lines 115-135 for full context.apps/server-core/internal/firewall/manager.go— After cleanup, removeAddRangeRule/RemoveRangeRulefrom interfaceapps/server-core/internal/firewall/nftables_linux.go:154-177— RemoveAddRangeRule/RemoveRangeRuleimplementationsapps/server-core/internal/firewall/nftables_stub.go:22-23— Remove stub methodsapps/server-core/internal/firewall/nftables_test.go:40-52— Remove or updateTestRangeRule
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No more AddRangeRule callers in API Tool: Bash Steps: 1. Run: grep -r "AddRangeRule" apps/server-core/ Expected Result: 0 matches (removed from interface, impls, callers, and tests) Evidence: .sisyphus/evidence/task-6-no-rangerule.txt Scenario: AddFirewallRule used in devices.go and peers.go Tool: Bash Steps: 1. Run: grep "AddFirewallRule" apps/server-core/api/devices.go apps/server-core/api/peers.go Expected Result: 1 match per file (2 total) Evidence: .sisyphus/evidence/task-6-new-callers.txt Scenario: Build succeeds after cleanup Tool: Bash Steps: 1. Run: cd apps/server-core && go build ./... Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-6-build.txtCommit: NO (groups with Commit B)
- Update
-
7. Add backend tests for firewall rule CRUD
What to do:
- Add/update tests in
apps/server-core/api/rules_test.go(if exists) or create it:- Test
Createhandler: valid rule with IP/CIDR, port range, protocol, action → 201 Created - Test
Createhandler: invalid port format → 400 - Test
Createhandler: invalid action → 400 - Test
Createhandler: invalid protocol → 400 - Test
Deletehandler: existing rule → 200, verify rule removed from DB - Test
Listhandler: returns rules for a device
- Test
- Update
apps/server-core/internal/firewall/nftables_test.go:- Replace
TestRangeRulewithTestFirewallRuletesting newAddFirewallRule/RemoveFirewallRulevia stub
- Replace
- Run all tests:
go test ./... -tags dev -count=1
Must NOT do:
- Do NOT write tests that require a running Linux nftables kernel — use stub manager
- Do NOT mock the database — use GORM in-memory SQLite (pattern from existing tests)
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: NO (depends on all Wave 1 tasks)
- Parallel Group: Wave 1 (sequential after Tasks 2-6)
- Blocks: Final verification
- Blocked By: Tasks 2, 3, 4, 5, 6
References:
apps/server-core/internal/firewall/nftables_test.go— Existing test file (52 lines), hasTestRangeRuleto replaceapps/server-core/internal/models/models_test.go— Pattern for GORM in-memory SQLite test setup (lines 1-30)apps/server-core/api/rules.go— Handler code to test- Check if
apps/server-core/api/rules_test.goexists — if so, read it for existing patterns
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: All tests pass Tool: Bash Steps: 1. Run: cd apps/server-core && go test ./... -tags dev -count=1 -v 2>&1 | tail -30 Expected Result: All tests PASS, exit code 0 Evidence: .sisyphus/evidence/task-7-test-results.txt Scenario: New firewall tests exist Tool: Bash Steps: 1. Run: grep -c "TestFirewallRule\|TestCreate.*Rule\|TestDelete.*Rule\|TestList.*Rule" apps/server-core/internal/firewall/nftables_test.go apps/server-core/api/rules_test.go 2>/dev/null || echo "no test files" Expected Result: At least 3 test functions found Evidence: .sisyphus/evidence/task-7-test-count.txtCommit: YES (Commit B)
- Message:
fix(firewall): rewrite nftables rule system + fix fwd_estab duplicates - Files:
internal/firewall/manager.go,internal/firewall/nftables_linux.go,internal/firewall/nftables_stub.go,internal/firewall/nftables_test.go,api/rules.go,api/rules_test.go,api/devices.go,api/peers.go - Pre-commit:
go test ./... -tags dev -count=1
- Add/update tests in
-
8. Delete LinkedDevices.vue + clean all references
What to do:
- Delete
apps/dashboard-ui/src/components/LinkedDevices.vue - Remove import and template usage from
DeviceDetail.vue:- Line 175:
<LinkedDevices v-if="device.InternalIP" :device-id="device.ID" />→ DELETE - Line 220:
import LinkedDevices from '../components/LinkedDevices.vue'→ DELETE
- Line 175:
- Remove
src/components/LinkedDevices.vuefrom any tsconfig references if applicable - Verify no other files reference LinkedDevices
Must NOT do:
- Do NOT modify any other component files
- Do NOT remove the FirewallEditor section from DeviceDetail.vue (it stays)
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 9-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/components/LinkedDevices.vue— 84 lines, DELETE entirelyapps/dashboard-ui/src/views/DeviceDetail.vue:175— Template reference:<LinkedDevices v-if="device.InternalIP" :device-id="device.ID" />apps/dashboard-ui/src/views/DeviceDetail.vue:220— Import:import LinkedDevices from '../components/LinkedDevices.vue'
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: LinkedDevices.vue deleted Tool: Bash Steps: 1. Run: test -f apps/dashboard-ui/src/components/LinkedDevices.vue && echo "EXISTS" || echo "DELETED" Expected Result: DELETED Evidence: .sisyphus/evidence/task-8-deleted.txt Scenario: No references to LinkedDevices remain Tool: Bash Steps: 1. Run: grep -r "LinkedDevices" --include="*.vue" --include="*.ts" apps/dashboard-ui/src/ Expected Result: 0 matches Evidence: .sisyphus/evidence/task-8-no-refs.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-8-build.txtCommit: YES (Commit C — group with Task 9)
- Message:
refactor(dashboard): delete LinkedDevices + redesign FirewallEditor - Files:
src/components/LinkedDevices.vue(delete),src/views/DeviceDetail.vue
- Delete
-
9. Redesign FirewallEditor.vue with Ng* design system
What to do:
- Complete rewrite of
apps/dashboard-ui/src/components/FirewallEditor.vue:- Replace raw
<table>with NgTable or keep structured table but use NgCard wrapper - Replace all hardcoded
text-cyan-400,bg-gray-900/50,border-white/10etc. with design tokens - Replace
alert()(if any) withuseToast() - Use NgButton for Add Rule button
- Use NgInput for form fields
- Add NgBadge for Action (accept=success, drop=danger)
- Keep the component functional — same CRUD operations, same API calls
- Add success toast after rule creation/deletion
- Add proper error display (NgBadge or inline error, not
alert()) - Validate port format: empty, single number, or
N-Nrange
- Replace raw
Must NOT do:
- Do NOT change the API contract (same fetchRules/createRule/deleteRule calls)
- Do NOT add sorting/filtering/pagination to the rule table
- Do NOT introduce new Ng* components beyond what already exists
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8, 10-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/components/FirewallEditor.vue— Current file (127 lines). Has raw table, hardcoded colors, no error toasts.apps/dashboard-ui/src/components/ui/NgCard.vue— Use as wrapperapps/dashboard-ui/src/components/ui/NgInput.vue— Use for IP, port inputsapps/dashboard-ui/src/components/ui/NgSelect.vue— Use for protocol, action dropdownsapps/dashboard-ui/src/components/ui/NgButton.vue— Use for Add Rule buttonapps/dashboard-ui/src/components/ui/NgBadge.vue— Use for Action badges (accept/drop)apps/dashboard-ui/src/components/ui/NgEmptyState.vue— Use for empty rule listapps/dashboard-ui/src/composables/useToast.ts— For success/error toastsapps/dashboard-ui/src/api/rules.ts— API contract (unchanged): fetchRules, createRule, deleteRule
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: FirewallEditor uses Ng* components Tool: Bash Steps: 1. Run: grep -c "NgCard\|NgInput\|NgSelect\|NgButton\|NgBadge\|NgEmptyState\|useToast" apps/dashboard-ui/src/components/FirewallEditor.vue Expected Result: At least 3 matches (NgCard + useToast + at least one other) Evidence: .sisyphus/evidence/task-9-ng-components.txt Scenario: No hardcoded colors remain in FirewallEditor Tool: Bash Steps: 1. Run: grep -E "text-cyan-|text-green-|text-red-|bg-gray-|bg-black/|border-white/" apps/dashboard-ui/src/components/FirewallEditor.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-9-no-hardcoded.txt Scenario: No alert() in FirewallEditor Tool: Bash Steps: 1. Run: grep "alert(" apps/dashboard-ui/src/components/FirewallEditor.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-9-no-alert.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-9-build.txtCommit: YES (Commit C — group with Task 8)
- Message:
refactor(dashboard): delete LinkedDevices + redesign FirewallEditor - Files:
src/components/FirewallEditor.vue - Pre-commit:
npm run build
- Complete rewrite of
-
10. Restyle DeviceDetail.vue
What to do:
- Replace ALL
alert()calls withuseToast():alert('Failed to regenerate token')→toast.error('Failed to regenerate token')alert('Failed to save advanced settings')→toast.error('Failed to save advanced settings')alert('Failed to regenerate keys')→toast.error('Failed to regenerate keys')alert('Failed to delete device')→toast.error('Failed to delete device')alert('Failed to toggle suspension')→toast.error('Failed to toggle suspension')alert('Token copied to clipboard!')→toast.success('Token copied to clipboard!')
- Replace ALL
confirm()withuseConfirm():confirm('This will invalidate...')→await confirm('This will invalidate...')confirm('Are you absolutely sure...')→await confirm('Are you absolutely sure...')confirm('This will regenerate...')→await confirm('This will regenerate...')
- Replace emoji
⚙️(line 66) with@iconify/vueicon (e.g.,heroicons:cog-6-tooth) - Replace all hardcoded Tailwind colors with design tokens (
text-cyan-400→text-accent,bg-gray-900/90→bg-surface, etc.) - Use NgCard for sections (keys display, advanced settings, connection status)
- Use NgInput for advanced settings form fields
- Use NgToggle for Allow Internet, Suspend, Disable PSK toggles (if NgToggle exists, else keep custom toggle with token colors)
Must NOT do:
- Do NOT change the API calls or data flow
- Do NOT change the component's functionality
- Do NOT remove the FirewallEditor or PeerConfigModal sections
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8, 9, 11-17)
- Blocks: Final verification
- Blocked By: Task 1, Task 8 (must delete LinkedDevices first since it was in DeviceDetail)
References:
apps/dashboard-ui/src/views/DeviceDetail.vue— Full file (392 lines). 6 alert(), 3 confirm(), emoji ⚙️, extensive hardcoded colors.apps/dashboard-ui/src/composables/useToast.ts—toast.success(msg),toast.error(msg)apps/dashboard-ui/src/composables/useConfirm.ts—const confirmed = await confirm('message')returns booleanapps/dashboard-ui/src/components/ui/NgCard.vue— Use for sectionsapps/dashboard-ui/src/components/ui/NgInput.vue— Use for form fieldsapps/dashboard-ui/src/components/ui/NgToggle.vue— Use for toggle switches@iconify/vue— Use<Icon icon="heroicons:cog-6-tooth" />instead of ⚙️ emoji
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert() in DeviceDetail Tool: Bash Steps: 1. Run: grep "alert(" apps/dashboard-ui/src/views/DeviceDetail.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-10-no-alert.txt Scenario: No confirm() in DeviceDetail Tool: Bash Steps: 1. Run: grep "confirm(" apps/dashboard-ui/src/views/DeviceDetail.vue Expected Result: 0 matches (excluding useConfirm import) Evidence: .sisyphus/evidence/task-10-no-confirm.txt Scenario: No emoji in DeviceDetail Tool: Bash Steps: 1. Run: grep -P "[\x{1F300}-\x{1F9FF}]|⚙️" apps/dashboard-ui/src/views/DeviceDetail.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-10-no-emoji.txt Scenario: No hardcoded colors Tool: Bash Steps: 1. Run: grep -cE "text-cyan-|text-green-|text-red-|bg-gray-|bg-black/|border-white/|text-amber-" apps/dashboard-ui/src/views/DeviceDetail.vue Expected Result: 0 or minimal (allow some if unavoidable) Evidence: .sisyphus/evidence/task-10-hardcoded-count.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-10-build.txtCommit: YES (Commit D — batch with Tasks 11-17)
- Message:
refactor(dashboard): replace alert/confirm + restyle all views - Files:
src/views/DeviceDetail.vue
- Replace ALL
-
11. Restyle Devices.vue
What to do:
- Replace
confirm()(line 111) withuseConfirm():const confirmed = await confirm(`Delete device "${name}"? This will destroy its tunnel and all firewall rules.`) if (!confirmed) return - Replace
alert()(line 116) withtoast.error(err.response?.data?.error || 'Failed to delete device') - Replace hardcoded colors in table rows with design tokens
- Replace raw firewall modal with NgModal:
- Current: manual
<div class="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center..."> - Replace with:
<NgModal :open="showFirewallModal" @close="showFirewallModal = false" title="Firewall Rules" size="lg">
- Current: manual
- Use NgButton for "+ Add Peer" button
- Use NgBadge for Online/Offline status
Must NOT do:
- Do NOT change the API calls or data flow
- Do NOT add sorting/filtering to the device table
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-10, 12-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/views/Devices.vue— Full file (138 lines). 1 confirm, 1 alert, raw table, raw modal.apps/dashboard-ui/src/components/ui/NgModal.vue— Use for firewall modal wrapperapps/dashboard-ui/src/components/ui/NgButton.vue— Use for action buttonsapps/dashboard-ui/src/components/ui/NgBadge.vue— Use for status badgesapps/dashboard-ui/src/composables/useToast.ts— For error toastsapps/dashboard-ui/src/composables/useConfirm.ts— For delete confirmation
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert()/confirm() in Devices Tool: Bash Steps: 1. Run: grep -E "alert\(|confirm\(" apps/dashboard-ui/src/views/Devices.vue Expected Result: 0 matches (excluding useConfirm import) Evidence: .sisyphus/evidence/task-11-no-dialogs.txt Scenario: Firewall modal uses NgModal Tool: Bash Steps: 1. Run: grep "NgModal" apps/dashboard-ui/src/views/Devices.vue Expected Result: 1+ match Evidence: .sisyphus/evidence/task-11-ngmodal.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-11-build.txtCommit: YES (Commit D)
- Files:
src/views/Devices.vue
- Replace
-
12. Restyle Dashboard.vue
What to do:
- Replace stat cards with NgCard (variant="stat"):
<NgCard variant="stat"> <p class="text-text-muted text-sm font-semibold uppercase tracking-wider mb-2">Total Devices</p> <p class="text-4xl font-bold">{{ store.devices.length }}</p> </NgCard> - Replace hardcoded
text-green-400/text-red-400withtext-success/text-danger - Replace hardcoded
bg-gray-900/50withbg-surfaceor use NgCard - Replace hardcoded
text-cyan-400withtext-accent - Replace hardcoded
border-white/10withborder-border-subtle - Keep the device grid but use design tokens for card backgrounds
Must NOT do:
- Do NOT change the API calls or polling logic
- Do NOT add new dashboard features
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-11, 13-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/views/Dashboard.vue— Full file (99 lines). Hardcoded stat cards, no NgCard usage.apps/dashboard-ui/src/components/ui/NgCard.vue— Use variant="stat" for stat cardsapps/dashboard-ui/src/assets/main.css— @theme tokens (from Task 1)
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: Dashboard uses NgCard Tool: Bash Steps: 1. Run: grep "NgCard" apps/dashboard-ui/src/views/Dashboard.vue Expected Result: 1+ match (stat cards) Evidence: .sisyphus/evidence/task-12-ngcard.txt Scenario: No hardcoded stat card colors Tool: Bash Steps: 1. Run: grep -c "bg-gradient-to-br from-gray-900" apps/dashboard-ui/src/views/Dashboard.vue Expected Result: 0 matches (all replaced with NgCard) Evidence: .sisyphus/evidence/task-12-no-hardcoded.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-12-build.txtCommit: YES (Commit D)
- Files:
src/views/Dashboard.vue
- Replace stat cards with NgCard (variant="stat"):
-
13. Restyle Servers.vue modals/forms
What to do:
- Replace
confirm()(line 576) withuseConfirm() - Replace
alert('Delete failed')(line 582) withtoast.error('Delete failed') - Replace hardcoded colors in add/edit modals with design tokens
- Use NgInput for form fields in Register Node and Edit Node modals
- Use NgButton for Register/Save/Cancel buttons
- Use NgSelect for Table dropdown (auto/off)
- Replace hardcoded
text-cyan-400withtext-accent,bg-gray-900/90withbg-surface - Servers list already uses NgCard — no change needed there
Must NOT do:
- Do NOT change the IP parsing logic or form data structure
- Do NOT change the API calls
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-12, 14-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/views/Servers.vue— Full file (599 lines). 1 confirm, 1 alert, modals with hardcoded colors.apps/dashboard-ui/src/components/ui/NgInput.vue— Use for form fieldsapps/dashboard-ui/src/components/ui/NgSelect.vue— Use for dropdownsapps/dashboard-ui/src/components/ui/NgButton.vue— Use for action buttonsapps/dashboard-ui/src/composables/useConfirm.ts— For delete confirmationapps/dashboard-ui/src/composables/useToast.ts— For error toasts
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert()/confirm() in Servers Tool: Bash Steps: 1. Run: grep -E "alert\(|confirm\(" apps/dashboard-ui/src/views/Servers.vue Expected Result: 0 matches (excluding useConfirm import) Evidence: .sisyphus/evidence/task-13-no-dialogs.txt Scenario: Modals use NgInput/NgButton Tool: Bash Steps: 1. Run: grep -c "NgInput\|NgButton\|NgSelect" apps/dashboard-ui/src/views/Servers.vue Expected Result: 3+ matches Evidence: .sisyphus/evidence/task-13-ng-components.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-13-build.txtCommit: YES (Commit D)
- Files:
src/views/Servers.vue
- Replace
-
14. Restyle Users.vue
What to do:
- Replace
confirm()(line 110) withuseConfirm() - Replace
alert('Delete failed')(line 117) withtoast.error('Delete failed') - Replace hardcoded colors with design tokens
- Use NgInput for username/password fields
- Use NgButton for Add User button
- Use NgBadge for username display
- Keep raw table but use design tokens for styling
Must NOT do:
- Do NOT change the API calls or user management logic
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-13, 15-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/views/Users.vue— Full file (124 lines). 1 confirm, 1 alert, hardcoded colors.apps/dashboard-ui/src/components/ui/NgInput.vue— Use for form fieldsapps/dashboard-ui/src/components/ui/NgButton.vue— Use for action buttonsapps/dashboard-ui/src/composables/useConfirm.ts— For delete confirmationapps/dashboard-ui/src/composables/useToast.ts— For error toasts
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert()/confirm() in Users Tool: Bash Steps: 1. Run: grep -E "alert\(|confirm\(" apps/dashboard-ui/src/views/Users.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-14-no-dialogs.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-14-build.txtCommit: YES (Commit D)
- Files:
src/views/Users.vue
- Replace
-
15. Restyle AddPeerModal.vue + PeerConfigModal.vue
What to do:
- AddPeerModal.vue:
- Replace 3x
alert()withtoast.success()/toast.error() - Replace hardcoded colors with design tokens
- Use NgModal for the modal wrapper (currently manual
<div class="fixed inset-0...">) - Use NgInput for peer name field
- Use NgSelect for target node dropdown
- Use NgToggle for "Allow Internet" and "Disable PSK" toggles
- Use NgButton for Create/Download/Copy/Cancel buttons
- Replace 3x
- PeerConfigModal.vue:
- Replace 5x
alert()withtoast.success()/toast.error() - Replace hardcoded colors with design tokens
- Use NgModal for the modal wrapper
- Use NgButton for Save/Cancel/Copy/Download buttons
- Keep QR code and config textarea (textarea is specific, no Ng equivalent)
- Replace 5x
Must NOT do:
- Do NOT change the API calls or provisioning logic
- Do NOT change the QR code generation
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-14, 16-17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/components/AddPeerModal.vue— Full file (171 lines). 3 alert(), hardcoded colors, manual modal.apps/dashboard-ui/src/components/PeerConfigModal.vue— Full file (186 lines). 5 alert(), hardcoded colors, manual modal.apps/dashboard-ui/src/components/ui/NgModal.vue— Use for modal wrapperapps/dashboard-ui/src/components/ui/NgInput.vue— Use for form fieldsapps/dashboard-ui/src/components/ui/NgSelect.vue— Use for dropdownsapps/dashboard-ui/src/components/ui/NgToggle.vue— Use for togglesapps/dashboard-ui/src/components/ui/NgButton.vue— Use for action buttonsapps/dashboard-ui/src/composables/useToast.ts— For success/error toasts
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert() in AddPeerModal Tool: Bash Steps: 1. Run: grep "alert(" apps/dashboard-ui/src/components/AddPeerModal.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-15-addpeer-no-alert.txt Scenario: No alert() in PeerConfigModal Tool: Bash Steps: 1. Run: grep "alert(" apps/dashboard-ui/src/components/PeerConfigModal.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-15-peerconfig-no-alert.txt Scenario: Both use NgModal Tool: Bash Steps: 1. Run: grep -c "NgModal" apps/dashboard-ui/src/components/AddPeerModal.vue apps/dashboard-ui/src/components/PeerConfigModal.vue Expected Result: 1 match per file Evidence: .sisyphus/evidence/task-15-ngmodal.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-15-build.txtCommit: YES (Commit D)
- Files:
src/components/AddPeerModal.vue,src/components/PeerConfigModal.vue
- AddPeerModal.vue:
-
16. Restyle ShareConfig.vue
What to do:
- Replace 2x
alert()withtoast.success()/toast.error() - Replace hardcoded colors with design tokens (
text-cyan-400→text-accent,bg-gradient-to-br from-gray-900 to-gray-800→bg-gradient-to-br from-bg-surface to-bg-elevated) - Use NgButton for Download/Copy buttons
- Use NgCard for the config container
Must NOT do:
- Do NOT change the fetch logic or config display
- Do NOT add new features
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-15, 17)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/views/ShareConfig.vue— Full file (80 lines). 2 alert(), hardcoded colors.apps/dashboard-ui/src/components/ui/NgButton.vue— Use for action buttonsapps/dashboard-ui/src/components/ui/NgCard.vue— Use for config containerapps/dashboard-ui/src/composables/useToast.ts— For success/error toasts
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No alert() in ShareConfig Tool: Bash Steps: 1. Run: grep "alert(" apps/dashboard-ui/src/views/ShareConfig.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-16-no-alert.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-16-build.txtCommit: YES (Commit D)
- Files:
src/views/ShareConfig.vue
- Replace 2x
-
17. Restyle TrafficChart.vue SVG colors
What to do:
- Replace hardcoded SVG hex colors with CSS custom properties:
stroke="#00ffff"→stroke="var(--color-accent)"or usecurrentColorwhere appropriatefill="#00ffff"→fill="var(--color-accent)"fill="#3b82f6"→fill="var(--color-info)"(define --color-info in @theme if needed, or use existing blue token)
- Replace hardcoded gradient stop colors with CSS variables
- Replace inline SVG spinner with NgSkeleton or keep as-is (spinner is functional)
- Replace hardcoded
text-gray-400,text-gray-500withtext-text-secondary,text-text-muted
Must NOT do:
- Do NOT change the chart rendering logic
- Do NOT change the data transformation or formatting functions
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 2 (with Tasks 8-16)
- Blocks: Final verification
- Blocked By: Task 1
References:
apps/dashboard-ui/src/components/TrafficChart.vue— Full file (299 lines). Hardcoded SVG colors (#00ffff, #3b82f6), inline spinner.apps/dashboard-ui/src/assets/main.css— @theme tokens (from Task 1)
Acceptance Criteria:
QA Scenarios (MANDATORY):
Scenario: No hardcoded hex colors in TrafficChart Tool: Bash Steps: 1. Run: grep -E "#[0-9a-fA-F]{3,8}" apps/dashboard-ui/src/components/TrafficChart.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-17-no-hex.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-17-build.txtCommit: YES (Commit D)
- Files:
src/components/TrafficChart.vue - Pre-commit:
npm run build
- Replace hardcoded SVG hex colors with CSS custom properties:
Final Verification Wave
- F1. Plan Compliance Audit —
oracle - F2. Code Quality Review —
unspecified-high - F3. Real Manual QA —
unspecified-high - F4. Scope Fidelity Check —
deepFor each task: read "What to do", read actual diff. Verify 1:1 — everything in spec was built, nothing beyond spec was built. Check "Must NOT do" compliance. Detect cross-task contamination. Flag unaccounted changes. Specifically verify: Login.vue untouched, style.css untouched, AddForwardRule untouched, no nft flush table. Output:Tasks [N/N compliant] | Contamination [CLEAN/N issues] | VERDICT
Commit Strategy
- Commit A (Wave 0):
feat(dashboard): add @theme design tokens to main.css— main.css - Commit B (Wave 1):
fix(firewall): rewrite nftables rule system + fix fwd_estab duplicates— all backend firewall files - Commit C (Wave 2 batch 1):
refactor(dashboard): delete LinkedDevices + redesign FirewallEditor— LinkedDevices.vue (delete), FirewallEditor.vue, DeviceDetail.vue - Commit D (Wave 2 batch 2):
refactor(dashboard): replace alert/confirm + restyle all views— remaining 8 .vue files
Success Criteria
Verification Commands
# Backend
cd apps/server-core && go build ./... # Expected: no errors
cd apps/server-core && go test ./... -tags dev # Expected: all PASS
# Frontend
cd apps/dashboard-ui && npm run build # Expected: no errors
# Legacy pattern audit
grep -r "alert(" --include="*.vue" apps/dashboard-ui/src/ # Expected: 0 matches
grep -r "LinkedDevices" --include="*.vue" --include="*.ts" apps/dashboard-ui/src/ # Expected: 0 matches
Final Checklist
- All "Must Have" present
- All "Must NOT Have" absent
- All go tests pass
- Frontend builds without errors
- Zero alert()/confirm() in .vue files
- Zero LinkedDevices references