chore: cleanup old plan docs, add evidence/references dirs
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
# 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 → `<InternalIP>/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`
|
||||
Reference in New Issue
Block a user