# NexusGuard — AI Agent Reference: Completed Work History > **Purpose**: Compact record of all completed implementation work. Use this to understand what's been built, key decisions, and files modified. Avoid re-implementing or regressing completed features. > **Generated**: 2026-05-24 > **Format**: One section per completed phase/plan. Minimal token overhead. --- ## 1. Full System Build (nxg-nexusguard-full-build) **Goal**: Build complete NexusGuard SD-WAN from scratch across 3 submodules. **Stack**: Go 1.25 (Gin/GORM) + Vue 3 (Vite/Pinia/Tailwind) + Device Agent (wireguard-go memory-only) **Key Decisions**: - Zero-Trust: nftables default DROP. Per-user sets for isolation. - Stealth Agent: wireguard-go via `device.IpcSet()` — no config files on disk. - Hardware Binding: SHA256(HWID + Salt) for AES key derivation. - Config Encryption: AES-256-GCM server↔agent. Key never transmitted. - JWT for Dashboard↔Server. X-Token-Auth for Agent↔Server (one-time reg token). - Dev: GORM AutoMigrate. Prod: goose versioned migrations. - No STUN/P2P, no WebSocket (polling only), no kernel WireGuard, no agent disk writes. **Guardrails**: - NEVER `nft flush table` — element-level ops only - NEVER GORM AutoMigrate in production - NEVER write WG config to `/etc/wireguard/` --- ## 2. Phase 4.5: Multi-Node & Production Readiness **Goal**: Single-node → Multi-Server architecture + DB startup hardening. | Task | Files | What | |------|-------|------| | Multi-Server DB & API | `models.go`, `001_init.sql`, `api/servers.go`, `main.go` | `WgServerID` FK on Device. CRUD `/api/v1/servers`. | | Provisioning Multi-Node | `api/provisioning.go`, `provisioning_test.go` | Remove `127.0.0.1:51820` fallback. Return true Endpoint. | | Dashboard Nodes | `Servers.vue`, `Devices.vue`, `servers.ts`, `App.vue` | Nodes page, device→Node dropdown, sidebar. | | Install Detection | `main.go` | DB schema check on boot. `-migrate-prod` flag via goose. | --- ## 3. Phase 4.9: WGDashboard Parity **Goal**: Multi-user, global device view, WG interface status, node health, Endpoint/ListenAddress split. | Task | Files | What | |------|-------|------| | User CRUD API | `api/users.go`, `main.go` | `GET/POST/DELETE /api/v1/users`. Admin-only. | | Admin Global Devices | `api/devices.go` | Admin sees ALL devices. `?user_id=` filter. | | Users Page UI | `Users.vue`, `users.ts`, `Devices.vue`, `App.vue`, `router/` | Table, create/delete. Owner column for admin. User filter. | | WG Local Manager | `internal/wgmanager/manager.go` | `GetStatus()`, `SetConfig()`, `Down()`. Stub for Win. | | WG API | `main.go` routes | `/wg/status`, `/wg/up`, `/wg/down`. | | WG Status Widget | `WgStatusCard.vue` on Dashboard | ON/OFF toggle, Rx/Tx, peer count. | | Node Health Check | `main.go` goroutine + `api/servers.go` | UDP ping every 30s. `GET /servers/status`. | | Endpoint/ListenAddress | `models.go`, `servers.go`, `001_init.sql`, provisioning | `PublicEndpoint` ≠ `ListenAddress`. Both editable. | --- ## 4. Phase 5.0: Peer Management + QR/Conf **Goal**: Create peers directly from UI, QR codes, .conf download, share links. **Key Decisions**: - Route: `/api/v1/devices/:id/config` (not `/peers`) - PresharedKey via `wgtypes.GeneratePrivateKey()` for every direct peer - `RegTokenHash=""` for direct peers (never provisionable via token) - Share links: Redis with `SHARE_LINK_TTL` env (default 24h) - AllowedIPs: `AllowInternet=true` → `0.0.0.0/0`, else → `/32` - MUST NOT push peer config to kernel interface (deferred) | Task | Files | What | |------|-------|------| | PresharedKey in Provision | `api/provisioning.go` | Generate PSK after device key. Include in ConfigPayload. | | Direct Peer API | `api/peers.go`, `main.go` | `POST /api/v1/peers`. Generate keys, alloc IP, create device. | | Config Export | `api/peers.go` | `GET /devices/:id/config`. WireGuard `[Interface]`+`[Peer]` text. | | QR Code | `api/peers.go` (dep: go-qrcode) | `GET /devices/:id/qr`. PNG 256x256. | | Share Links | `api/share.go` | `POST /devices/:id/share` + `GET /share/:token` (public). Redis-backed. | | Add Peer Modal | `AddPeerModal.vue`, `Devices.vue`, `api/peers.ts` | Form: Name, Server, AllowInternet. Success: QR + download + copy. | | Config Modal | `PeerConfigModal.vue`, `Devices.vue`, `DeviceDetail.vue` | QR, config text, download, share link, copy. | | Share Page | `ShareConfig.vue`, `router/index.ts` | Public route `/share/:token`. Shows expired for invalid. | | Linked Devices | `LinkedDevices.vue`, `DeviceDetail.vue` | Table: AllowedIPs, Online/Offline, LastHandshake, Disconnect. | | Firewall Sync | `api/peers.go` | Auto-create nftables rules for SSH (22) on peer create. Graceful on failure. | --- ## 5. Phase 5.5: WGDashboard Config Parity **Goal**: Feature parity for WG config editing + peer settings + firewall sync. **DB Model Updates**: - `WgServer`: Added MTU (1420), DNS (1.1.1.1) - `Device`: Added EndpointAllowedIPs, DNS, MTU, PersistentKeepalive (25), Notes, IsSuspended, RxBytes, TxBytes **Backend API**: - `PUT /servers/:id` — pointer fields for partial updates, firewall sync on port change - `PUT /devices/:id` — pointer fields for new Device fields - `POST /devices/:id/suspend`, `/unsuspend` — toggle + firewall rule - Config generation: DNS/MTU/Keepalive cascade (device > server > default) - Firewall sync on CreatePeer, Update (AllowedIPs change), Delete (cleanup) **Firewall Interface** (existing): - `AddForwardRule`, `RemoveForwardRule`, `AddInputRule`, `RemoveInputRule` - Linux: via `nft` CLI. Stub: no-op for non-Linux. **Frontend**: - Node Edit Modal — `PUT /servers/:id` with MTU, DNS, ListenPort - Advanced Peer Settings in DeviceDetail — accordion: AllowedIPs, DNS, MTU, Keepalive, Notes, Suspend toggle - API client: `updateServer()`, `suspendDevice()`, `unsuspendDevice()` --- ## 6. WireGuard VPN Functional Fix (wg-vpn-fix) **Goal**: Fix 4 blocking gaps — server wg0 IP, peer sync, NAT/masquerade, device-agent TUN IP. **Scope**: 10 files across server-core + device-agent. | Fix | Files | What | |-----|-------|------| | Interface types | `internal/wgmanager/manager.go` | Expanded interface + `PeerConfig`, `PeerStatus` types | | Linux impl | `wgmanager_linux.go` | `ip addr add`, `SyncPeers()` replace-all, NAT masquerade | | Stub | `wgmanager_stub.go` | Match new interface (no-op) | | Handler wiring | `api/wg.go`, `api/devices.go`, `api/peers.go`, `api/provisioning.go` | Sync peers on create/delete/suspend/provision | | main.go | `main.go` | Inject wgMgr to new handlers | | Agent TUN IP | `device-agent/.../wireguard.go`, `device-agent/main.go` | Assign `InternalIP` to TUN device | | Tests | `wgmanager/*_test.go`, `api/devices_test.go`, `api/wg_test.go` | Unit + integration | **Unchecked** (environment blockers, not implementation): - Dashboard build (needs submodule init) - Full E2E on Linux (needs WireGuard kernel module) - No duplicate nftables rules after restart toggle (needs Linux) **Key**: NAT interface auto-detected via `ip route show default`, override via `WG_NAT_INTERFACE` env var. --- ## 7. Fix Gaps (nxg-fix-gaps) **Goal**: Fix 6 gaps — stale artifacts, Dockerfiles, CI/CD, frontend tests, nftables Drop, crypto debt. | Gap | What was done | |-----|---------------| | Stale artifacts | Deleted `connect_remote.txt` (JWT+SSH creds), `temp_section*.txt`. Confirmed never in git history. | | Dockerfiles | Created `device-agent/Dockerfile` (Go multi-stage). Pinned `nginx:alpine` → `nginx:1.27-alpine`. | | Root CI/CD | Created `.gitea/workflows/ci.yml` — 3 jobs: server-core test, device-agent test, dashboard-ui build. | | Frontend tests | Installed Vitest + vue-test-utils + happy-dom. Config + 2 test files (auth store 5 tests, modal 3 tests). 9/9 pass. | | nftables Drop | Added conntrack rule (established/related accept). Changed forward policy Drop. Startup recovery loop (re-apply rules from DB). Auto input rule for WG port. | | Crypto debt | Documented in `server-core/AGENTS.md`. 69 lines stdlib, 2 callers — keep duplicated. | **Files changed**: - Deleted: `connect_remote.txt`, `temp_section1.txt`, `temp_section2.txt` - Modified: `package.json`, `index.html`, `dashboard-ui/Dockerfile`, `nftables_linux.go`, `main.go`, `models.go`, `AGENTS.md` - Created: `device-agent/Dockerfile`, `.gitea/workflows/ci.yml`, `vitest.config.ts`, `hello.test.ts`, `add-peer-modal.spec.ts`, `auth-store.spec.ts` --- ## 8. Fix Peers 500 + Delete Device **Goal**: Fix HWID unique constraint violation (500 on POST `/peers`). Add delete button to Devices list. **Root Cause**: `Device.HWID string` with `uniqueIndex`. Empty string `""` collides → SQLSTATE 23505. **Fix**: `string` → `*string`. NULLs are distinct in PostgreSQL unique constraints. | Task | Files | |------|-------| | Model change | `internal/models/models.go:63` — HWID `*string` | | CreatePeer fix | `api/peers.go` — remove `HWID: ""` (nil is correct) | | Delete button | `src/views/Devices.vue` — add Delete to Actions column | | SQL migration | SSH: `UPDATE devices SET hwid = NULL WHERE hwid = ''` | --- ## 9. Server-Core Multi-Stage Docker Build **Goal**: Single-stage → multi-stage Dockerfile. Clean up orphan binaries. | Task | Files | |------|-------| | Dockerfile rewrite | `apps/server-core/Dockerfile` — builder (golang:1.25) + prod (alpine:3.20) | | .dockerignore | `apps/server-core/.dockerignore` — exclude build artifacts | | .gitignore update | `apps/server-core/.gitignore` — `server-core*` glob | | Untrack binary | `git rm --cached server-core-linux` | | Delete binaries | 3 files deleted: `server-core`, `server-core-linux`, `bin/server-core` | **Key**: Dev compose uses `target: builder` with volume mount at `/app` for `air` hot-reload. --- ## 10. Deployment Tools (nxg-deployment-tools) **Goal**: Makefile + HWID migration on live server. | Task | What | |------|------| | Makefile | `up`, `down`, `reset-db`, `logs`, `dev`, `migrate` targets. | | HWID migration | SSH to `172.20.8.191`: SQL `UPDATE...SET hwid=NULL WHERE hwid=''`. Restart container. | --- ## 11. AGENTS.md Generation (agents-deep-init) **Goal**: Generate hierarchical AGENTS.md for root + 3 submodules. | File | Lines | |------|-------| | `./AGENTS.md` | Root — 109 lines: project overview, structure, code map, conventions, anti-patterns | | `apps/server-core/AGENTS.md` | 61 lines: Go backend reference | | `apps/dashboard-ui/AGENTS.md` | 58 lines: Vue 3 frontend reference | | `apps/device-agent/AGENTS.md` | 50 lines: Go stealth daemon reference | --- ## Key Anti-Patterns (Project-wide "Must NOT do") - **NEVER** `nft flush table` — only atomic add/remove - **NEVER** log plaintext or encryption keys - **NEVER** reopen completed phases/commits — fix forward only - **NEVER** rebuild `shared/crypto/encryptor.go` — copy identical file (known debt) - **NEVER** commit build artifacts (binaries, `dist/`) - **NEVER** force push - **NEVER** create cross-phase workarounds - **NEVER** change nftables input chain default (WG UDP listener needs Accept) - **NEVER** write WireGuard config to `/etc/wireguard/` (stealth invariant) - **NEVER** use `docker-compose` (v1) — always `docker compose` (v2) --- ## Files Modified (Master List) ### server-core/ (Go/Gin backend) `main.go`, `models.go`, `api/servers.go`, `api/devices.go`, `api/peers.go`, `api/provisioning.go`, `api/wg.go`, `api/users.go`, `api/share.go`, `api/auth.go`, `internal/firewall/nftables_linux.go`, `internal/firewall/nftables_stub.go`, `internal/firewall/manager.go`, `internal/wgmanager/manager.go`, `internal/wgmanager/wgmanager_linux.go`, `internal/wgmanager/wgmanager_stub.go`, `internal/ipam/manager.go`, `internal/models/models.go`, `migrations/001_init.sql`, `AGENTS.md`, `Dockerfile`, `.dockerignore`, `.gitignore` ### dashboard-ui/ (Vue 3 frontend) `package.json`, `index.html`, `vitest.config.ts`, `Dockerfile`, `src/views/Servers.vue`, `src/views/Devices.vue`, `src/views/Users.vue`, `src/views/DeviceDetail.vue`, `src/views/ShareConfig.vue`, `src/views/Dashboard.vue`, `src/components/AddPeerModal.vue`, `src/components/PeerConfigModal.vue`, `src/components/LinkedDevices.vue`, `src/components/WgStatusCard.vue`, `src/api/peers.ts`, `src/api/servers.ts`, `src/api/users.ts`, `src/api/devices.ts`, `src/stores/auth.ts`, `src/router/index.ts`, `src/App.vue` ### device-agent/ (Go stealth daemon) `main.go`, `Dockerfile`, `internal/tunnel/wireguard.go` ### Root `Makefile`, `.gitea/workflows/ci.yml`, `AGENTS.md` ### Deleted (stale artifacts) `connect_remote.txt`, `temp_section1.txt`, `temp_section2.txt`, `apps/server-core/server-core`, `apps/server-core/server-core-linux`, `apps/server-core/bin/server-core`