Compare commits

...

66 Commits

Author SHA1 Message Date
datadunia f98eedbfbe chore: update device-agent submodule
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 10m19s
CI / build-device-agent (push) Successful in 6m20s
CI / build-dashboard (push) Successful in 4m30s
CI / build-docs (push) Successful in 4m6s
CI / release (push) Successful in 29s
2026-06-21 13:28:02 +07:00
datadunia ea6fbe83b5 chore: update dashboard-ui submodule ref 2026-06-21 13:24:44 +07:00
datadunia 91ed1adcda chore: update dashboard-ui submodule ref 2026-06-21 13:20:24 +07:00
datadunia 95ec25b80e chore: update dashboard-ui submodule ref 2026-06-21 13:06:14 +07:00
datadunia 2738e3c7d1 chore: update dashboard-ui submodule ref 2026-06-21 05:44:21 +07:00
datadunia 80c97bbc22 chore: update dashboard-ui submodule ref 2026-06-21 05:32:13 +07:00
datadunia 077384c433 chore: update dashboard-ui submodule ref 2026-06-21 05:29:17 +07:00
datadunia fc204bba17 chore: update device-agent submodule to b1c1576 2026-06-21 01:24:52 +07:00
datadunia e1e35318de chore: update submodules — agent 65714b5, server decb2e4
agent: console X survives, UTF-8 encoding, debug-gated heartbeat
server: reject heartbeat for suspended devices (403)
2026-06-21 01:16:10 +07:00
datadunia bd79bb0043 chore: update device-agent submodule to 05b5e5c 2026-06-21 00:52:50 +07:00
datadunia 248ad123bb chore: update device-agent submodule to 1ab8304 (console X fix) 2026-06-21 00:14:25 +07:00
datadunia 855fb2dbee chore: update device-agent submodule to 71a2be5 (crash fix + config redaction) 2026-06-21 00:05:17 +07:00
datadunia 45f95a957a chore: update device-agent submodule to e93ca27 (log console + WG redaction) 2026-06-20 23:32:04 +07:00
datadunia e50c1900ad chore: update device-agent submodule to 9cfd127 2026-06-20 19:43:11 +07:00
datadunia 957cc8d5cb chore: update device-agent submodule 2026-06-20 18:33:42 +07:00
datadunia da94cc3f6b chore: update AGENTS.md + device-agent submodule 2026-06-20 18:32:11 +07:00
datadunia 465b1f7388 chore: update submodule refs 2026-06-20 18:16:22 +07:00
datadunia e9e4db261d chore: update device-agent submodule 2026-06-20 17:56:16 +07:00
datadunia 6f6b44b8a2 chore: update submodule refs 2026-06-20 17:48:10 +07:00
datadunia 1a1f8a5a8a chore: update submodule refs 2026-06-20 17:14:47 +07:00
datadunia 61a5936224 chore: update submodule refs 2026-06-20 14:38:15 +07:00
datadunia f03686ac3d chore: update device-agent submodule (assign_ip_other fix) 2026-06-20 10:39:46 +07:00
datadunia e1cbdb66c8 fix: update submodule pointers (port forward push + debug logging) 2026-06-20 10:30:35 +07:00
datadunia a89fdaf435 chore: update submodule refs 2026-06-20 09:36:23 +07:00
datadunia 633e06f556 chore: update submodule pointers (docs: AGENTS.md updates) 2026-06-20 07:56:18 +07:00
datadunia b447ad9757 docs(root): comprehensive AGENTS.md with signaling architecture, topology, lifecycle 2026-06-20 07:46:38 +07:00
datadunia d7bbbbdefd chore: update submodule refs 2026-06-20 06:53:20 +07:00
datadunia b1458d3a99 chore: update device-agent submodule (debug logging)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-20 05:06:04 +07:00
datadunia 2c5a3c7fa5 chore: update device-agent submodule (heartbeat primary)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-20 04:53:03 +07:00
datadunia 5a6675c70a chore: update submodules (gRPC keepalive + 3-level fallback)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 21:01:52 +07:00
datadunia 4528493647 chore: update device-agent submodule (transport memory)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 20:22:06 +07:00
datadunia 9f1e37fc57 chore: update device-agent submodule (gRPC fallback)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 20:18:24 +07:00
datadunia 5e8b72efdc chore: update device-agent submodule (insecure gRPC fix)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 20:06:12 +07:00
datadunia 08b74ce45b chore: update server-core submodule ref 2026-06-19 19:48:17 +07:00
datadunia 7c5604d510 chore: update submodule refs (gRPC multiplex on port 8080)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 18:08:46 +07:00
datadunia 26e65006b5 chore: update submodule refs (gRPC port fix + server_wg_ip)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-19 17:50:52 +07:00
datadunia c011215deb chore: update submodule refs (gRPC signaling added) 2026-06-19 12:25:47 +07:00
datadunia 6f5ffb8b1d chore: update dashboard-ui submodule ref 2026-06-19 09:08:44 +07:00
datadunia 934d4a03d7 chore: update dashboard-ui submodule ref 2026-06-19 08:49:30 +07:00
datadunia 27c003864e chore: update submodule refs 2026-06-19 08:03:41 +07:00
datadunia 014309c390 fix(ci): strip debug symbols from device-agent release build
- CI build: add -ldflags='-s -w' to reduce binary size ~30%
- Update device-agent submodule reference (heartbeat fixes)
2026-06-18 20:07:13 +07:00
datadunia d5b1f4428a chore: update submodule refs 2026-06-18 18:36:42 +07:00
datadunia 6c5a762407 feat: EndpointAllowedIPs in provisioning/heartbeat, agent uses server AllowedIPs 2026-06-18 16:08:47 +07:00
datadunia 9c8a3c0751 fix: heartbeat config change detection, device_id, DNS, tunnel rebuild timing 2026-06-18 14:44:14 +07:00
datadunia 272d744e12 chore: update submodule refs 2026-06-18 14:24:51 +07:00
datadunia 5304bd11ca chore: update device-agent + server-core submodule refs 2026-06-18 13:48:42 +07:00
datadunia 359a23c079 chore: update dashboard-ui + device-agent submodule refs 2026-06-18 12:45:18 +07:00
datadunia facd8bd7d5 chore: update all submodule refs (single instance, Windows IP, Docker debug) 2026-06-18 12:24:15 +07:00
datadunia 83722abd32 chore: update server-core submodule ref 2026-06-18 11:24:32 +07:00
datadunia 359bd6f9c4 chore: update device-agent + server-core submodule refs (Windows support, DeviceID) 2026-06-18 11:15:22 +07:00
datadunia e81c1dd448 chore(gitignore): exclude test directories (.tests/, tests/) 2026-06-18 11:14:51 +07:00
datadunia 9433dc3547 fix: device-agent v1.0.0-dev.33 - Windows syscall fixes
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 1m54s
CI / build-device-agent (push) Successful in 9m21s
CI / build-dashboard (push) Successful in 3m15s
CI / build-docs (push) Has been skipped
CI / release (push) Successful in 29s
2026-06-18 10:16:10 +07:00
datadunia 4d58b01641 ci: fix pipeline flow - tests→build→docs→release with fail-fast
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 1m45s
CI / build-device-agent (push) Failing after 2m29s
CI / build-dashboard (push) Successful in 1m14s
CI / build-docs (push) Has been skipped
CI / release (push) Has been skipped
2026-06-18 09:59:04 +07:00
datadunia 4e19c371d0 fix: device-agent winres.json location for go-winres
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 1m43s
CI / build-dashboard (push) Has been cancelled
CI / build-docs (push) Has been cancelled
CI / release (push) Has been cancelled
CI / build-device-agent (push) Has been cancelled
2026-06-18 09:55:48 +07:00
datadunia aa620dec6a fix(ci): restore swagger docs step + fix release needs (remove build-docs dependency)
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 6m18s
CI / build-device-agent (push) Failing after 2m21s
CI / release (push) Has been skipped
CI / build-dashboard (push) Successful in 1m59s
CI / build-docs (push) Has been skipped
2026-06-18 09:44:29 +07:00
datadunia 48340aa7a3 ci: fix missing closing quote in release condition
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Failing after 2m12s
CI / build-dashboard (push) Has been cancelled
CI / build-docs (push) Has been cancelled
CI / release (push) Has been cancelled
CI / build-device-agent (push) Has been cancelled
2026-06-18 09:33:25 +07:00
datadunia 68ebfbaad3 ci: revert to 3-section structure (test/build/docs) with proper tag conditions 2026-06-18 09:23:56 +07:00
datadunia 46ff7ca737 fix(ci): add missing closing quote in release-stable if condition
CI / build-server-core-dev (push) Failing after 1m55s
CI / build-device-agent-dev (push) Failing after 2m20s
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / release-dev (push) Has been cancelled
CI / build-server-core-release (push) Has been cancelled
CI / build-device-agent-release (push) Has been cancelled
CI / build-dashboard-release (push) Has been cancelled
CI / build-docs-release (push) Has been cancelled
CI / release-stable (push) Has been cancelled
CI / build-dashboard-dev (push) Has been cancelled
2026-06-18 09:14:31 +07:00
datadunia 08b152a370 feat(ci): skip build if no source changes (cache build output by hash) 2026-06-18 09:09:51 +07:00
datadunia 6f22aad1ad ci: restructure pipeline - test only for test tags, build only for dev/beta, build+docs for release 2026-06-18 09:01:36 +07:00
datadunia d21863ce8f perf: remove slow swag init from CI (20min → 1min build) 2026-06-18 08:51:51 +07:00
datadunia eba741cd97 feat: device-agent v1.0.0-dev.30 - auto-elevate admin + key format fix
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-device-agent (push) Has been cancelled
CI / build-dashboard (push) Has been cancelled
CI / build-docs (push) Has been cancelled
CI / release (push) Has been cancelled
CI / build-server-core (push) Has been cancelled
2026-06-18 08:19:50 +07:00
datadunia fd9da1fd71 fix: device-agent v1.0.0-dev.29 - base64→hex key conversion for WireGuard IPC
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-device-agent (push) Has been cancelled
CI / build-dashboard (push) Has been cancelled
CI / build-docs (push) Has been cancelled
CI / release (push) Has been cancelled
CI / build-server-core (push) Has been cancelled
2026-06-18 08:17:39 +07:00
datadunia d46bc11485 fix: device-agent v1.0.0-dev.28 - remove config file persistence (security fix)
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-dashboard (push) Successful in 2m2s
CI / build-docs (push) Successful in 55s
CI / build-server-core (push) Successful in 7m56s
CI / build-device-agent (push) Successful in 3m5s
CI / release (push) Successful in 28s
2026-06-18 08:01:44 +07:00
datadunia f1fa0acfa7 fix: device-agent v1.0.0-dev.27 - fix winres.json (remove broken icon, UAC manifest only)
CI / server-core-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / build-docs (push) Successful in 55s
CI / build-server-core (push) Successful in 18m18s
CI / build-device-agent (push) Successful in 2m33s
CI / build-dashboard (push) Successful in 2m13s
CI / release (push) Successful in 27s
2026-06-18 07:07:57 +07:00
datadunia a5c1ea1c1b feat: device-agent v1.0.0-dev.26 - embedded UAC manifest (auto admin prompt)
CI / build-dashboard (push) Successful in 1m30s
CI / server-core-test (push) Has been skipped
CI / device-agent-test (push) Has been skipped
CI / dashboard-test (push) Has been skipped
CI / build-server-core (push) Successful in 5m38s
CI / build-device-agent (push) Successful in 2m28s
CI / build-docs (push) Successful in 52s
CI / release (push) Successful in 25s
2026-06-18 06:29:44 +07:00
10 changed files with 226 additions and 41 deletions
+14 -2
View File
@@ -23,22 +23,34 @@ jobs:
with: with:
node-version: '24' node-version: '24'
- name: Hash source files
id: hash-src
run: echo "hash=$(find apps/dashboard-ui -type f \( -name '*.vue' -o -name '*.ts' -o -name '*.js' -o -name '*.css' -o -name '*.json' -o -name '*.html' \) | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v3
id: cache-build
with:
path: apps/dashboard-ui/dist
key: build-dashboard-${{ steps.hash-src.outputs.hash }}
- name: Hash package-lock - name: Hash package-lock
id: hash-npm id: hash-npm
run: echo "hash=$(sha256sum apps/dashboard-ui/package-lock.json | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" run: echo "hash=$(sha256sum apps/dashboard-ui/package-lock.json | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v3 - uses: actions/cache@v3
id: cache-npm
with: with:
path: | path: ~/.npm
~/.npm
key: npm-dashboard-${{ steps.hash-npm.outputs.hash }} key: npm-dashboard-${{ steps.hash-npm.outputs.hash }}
restore-keys: npm-dashboard- restore-keys: npm-dashboard-
- name: Install - name: Install
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/dashboard-ui working-directory: apps/dashboard-ui
run: npm ci || npm install run: npm ci || npm install
- name: Build - name: Build
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/dashboard-ui working-directory: apps/dashboard-ui
run: npm run build run: npm run build
+23 -1
View File
@@ -24,7 +24,29 @@ jobs:
go-version: '1.26' go-version: '1.26'
cache: true cache: true
- uses: https://gitea.com/actions/go-hashfiles@v0.0.1
id: hash-src
with:
patterns: |
apps/device-agent/**/*.go
apps/device-agent/go.mod
apps/device-agent/go.sum
- uses: actions/cache@v3
id: cache-build
with:
path: apps/device-agent/bin
key: build-device-agent-${{ steps.hash-src.outputs.hash }}
- name: Generate Windows resources (UAC manifest + icon)
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/device-agent
run: |
go install github.com/tc-hib/go-winres@latest
go-winres make
- name: Build all platforms - name: Build all platforms
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/device-agent working-directory: apps/device-agent
shell: bash shell: bash
run: | run: |
@@ -37,7 +59,7 @@ jobs:
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
OUT="bin/nexus-device-agent-${GOOS}-${GOARCH}${EXT}" OUT="bin/nexus-device-agent-${GOOS}-${GOARCH}${EXT}"
echo "Building $OUT ..." echo "Building $OUT ..."
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build -o "$OUT" . CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build -ldflags="-s -w" -o "$OUT" .
done done
echo "=== Build output ===" echo "=== Build output ==="
ls -la bin/ ls -la bin/
+18
View File
@@ -24,13 +24,31 @@ jobs:
go-version: '1.26' go-version: '1.26'
cache: true cache: true
- uses: https://gitea.com/actions/go-hashfiles@v0.0.1
id: hash-src
with:
patterns: |
apps/server-core/**/*.go
apps/server-core/go.mod
apps/server-core/go.sum
- uses: actions/cache@v3
id: cache-build
with:
path: |
apps/server-core/bin
apps/server-core/docs
key: build-server-core-${{ steps.hash-src.outputs.hash }}
- name: Generate Swagger docs - name: Generate Swagger docs
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/server-core working-directory: apps/server-core
run: | run: |
go install github.com/swaggo/swag/cmd/swag@v1.16.6 go install github.com/swaggo/swag/cmd/swag@v1.16.6
swag init -g main.go --parseDependency --parseInternal swag init -g main.go --parseDependency --parseInternal
- name: Build - name: Build
if: steps.cache-build.outputs.cache-hit != 'true'
working-directory: apps/server-core working-directory: apps/server-core
run: go build -o bin/server-core . run: go build -o bin/server-core .
+23 -16
View File
@@ -10,9 +10,11 @@ on:
- 'v[0-9]*.[0-9]*.[0-9]' - 'v[0-9]*.[0-9]*.[0-9]'
jobs: jobs:
# --- TESTS (test tags + stable release tags) --- # ====================================================================
# TESTS — test tags only
# ====================================================================
server-core-test: server-core-test:
if: contains(gitea.ref_name, 'test') || (!contains(gitea.ref_name, 'dev') && !contains(gitea.ref_name, 'beta')) if: contains(gitea.ref_name, 'test')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -26,17 +28,13 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.26' go-version: '1.26'
- name: Generate swagger docs cache: true
working-directory: apps/server-core
run: |
go install github.com/swaggo/swag/cmd/swag@v1.16.6
swag init -g main.go --parseDependency --parseInternal
- name: Test (skip nftables - needs root) - name: Test (skip nftables - needs root)
working-directory: apps/server-core working-directory: apps/server-core
run: go test $(go list ./... | grep -v internal/firewall) -tags dev -cover -count=1 run: go test $(go list ./... | grep -v internal/firewall) -tags dev -cover -count=1
device-agent-test: device-agent-test:
if: contains(gitea.ref_name, 'test') || (!contains(gitea.ref_name, 'dev') && !contains(gitea.ref_name, 'beta')) if: contains(gitea.ref_name, 'test')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -50,12 +48,13 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.26' go-version: '1.26'
cache: true
- name: Test - name: Test
working-directory: apps/device-agent working-directory: apps/device-agent
run: go test ./... -cover run: go test ./... -cover
dashboard-test: dashboard-test:
if: contains(gitea.ref_name, 'test') || (!contains(gitea.ref_name, 'dev') && !contains(gitea.ref_name, 'beta')) if: contains(gitea.ref_name, 'test')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -76,37 +75,45 @@ jobs:
working-directory: apps/dashboard-ui working-directory: apps/dashboard-ui
run: npm run build run: npm run build
# --- BUILD + RELEASE (dev/beta/release tags) --- # ====================================================================
# BUILD — after tests pass (release) or directly (dev/beta)
# ====================================================================
build-server-core: build-server-core:
if: always() && !contains(gitea.ref_name, 'test') && !failure() if: always() && !contains(gitea.ref_name, 'test') && !failure() && !cancelled()
needs: [server-core-test, device-agent-test, dashboard-test] needs: [server-core-test, device-agent-test, dashboard-test]
runs-on: ubuntu-latest runs-on: ubuntu-latest
uses: ./.gitea/workflows/build_server_core.yaml uses: ./.gitea/workflows/build_server_core.yaml
secrets: inherit secrets: inherit
build-device-agent: build-device-agent:
if: always() && !contains(gitea.ref_name, 'test') && !failure() if: always() && !contains(gitea.ref_name, 'test') && !failure() && !cancelled()
needs: [server-core-test, device-agent-test, dashboard-test] needs: [server-core-test, device-agent-test, dashboard-test]
runs-on: ubuntu-latest runs-on: ubuntu-latest
uses: ./.gitea/workflows/build_device_agent.yaml uses: ./.gitea/workflows/build_device_agent.yaml
secrets: inherit secrets: inherit
build-dashboard: build-dashboard:
if: always() && !contains(gitea.ref_name, 'test') && !failure() if: always() && !contains(gitea.ref_name, 'test') && !failure() && !cancelled()
needs: [server-core-test, device-agent-test, dashboard-test] needs: [server-core-test, device-agent-test, dashboard-test]
runs-on: ubuntu-latest runs-on: ubuntu-latest
uses: ./.gitea/workflows/build_dashboard.yaml uses: ./.gitea/workflows/build_dashboard.yaml
secrets: inherit secrets: inherit
# ====================================================================
# DOCS — after builds pass, release tags only
# ====================================================================
build-docs: build-docs:
if: always() && !contains(gitea.ref_name, 'test') && !failure() if: always() && !contains(gitea.ref_name, 'dev') && !contains(gitea.ref_name, 'beta') && !contains(gitea.ref_name, 'test') && !failure() && !cancelled()
needs: [server-core-test, device-agent-test, dashboard-test] needs: [build-server-core, build-device-agent, build-dashboard]
runs-on: ubuntu-latest runs-on: ubuntu-latest
uses: ./.gitea/workflows/docs_call.yaml uses: ./.gitea/workflows/docs_call.yaml
secrets: inherit secrets: inherit
# ====================================================================
# RELEASE — after everything passes
# ====================================================================
release: release:
if: always() && !contains(gitea.ref_name, 'test') && !failure() if: always() && !contains(gitea.ref_name, 'test') && !failure() && !cancelled()
needs: [build-server-core, build-device-agent, build-dashboard, build-docs] needs: [build-server-core, build-device-agent, build-dashboard, build-docs]
uses: ./.gitea/workflows/release_call.yaml uses: ./.gitea/workflows/release_call.yaml
with: with:
+11
View File
@@ -21,6 +21,17 @@ jobs:
with: with:
node-version: '24' node-version: '24'
- name: Hash package-lock
id: hash-npm
run: echo "hash=$(sha256sum apps/docs/package-lock.json 2>/dev/null || echo 'none') | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v3
with:
path: |
~/.npm
key: npm-docs-${{ steps.hash-npm.outputs.hash }}
restore-keys: npm-docs-
- name: Install - name: Install
working-directory: apps/docs working-directory: apps/docs
run: npm ci || npm install run: npm ci || npm install
+2
View File
@@ -36,6 +36,8 @@ temp_*
# Build output # Build output
dist/ dist/
/dist/ /dist/
.tests/
tests/
# OpenCode # OpenCode
connect_remote.txt connect_remote.txt
+120 -7
View File
@@ -1,11 +1,22 @@
# PROJECT KNOWLEDGE BASE # PROJECT KNOWLEDGE BASE
**Generated:** 2026-05-22 **Generated:** 2026-06-20
**Commit:** `92051d5`
**Branch:** `main` **Branch:** `main`
## OVERVIEW ## OVERVIEW
NexusGuard SD-WAN Suite Enterprise Zero-Trust SD-WAN with WireGuard tunneling, centralized IPAM, and real-time nftables network isolation. Monorepo with 3 git submodules: Go backend (Gin), Vue 3 dashboard, Go device agent. NexusGuard SD-WAN Suite: Enterprise Zero-Trust SD-WAN with WireGuard tunneling, centralized IPAM, and real-time nftables network isolation. Monorepo with 3 git submodules: Go backend (Gin), Vue 3 dashboard, Go device agent.
## TOPOLOGY
| Host | SSH | Role |
|------|-----|------|
| Production server | `root@172.20.8.191` | Runs server-core, Postgres, Redis, nginx, nftables, WireGuard |
| Gitea server | `root@172.20.8.92` | Private Git hosting (`git.datadunia.com`) |
- Server project folder: `/root/Nexus-Guard-Suite`
- Deploy: `./update.sh` (don't build manually)
- Actual WireGuard wg0 IP: `10.172.21.1/24` (on server 172.20.8.191)
- Agent WG IPs: dynamic from pool `10.172.21.0/24`
## STRUCTURE ## STRUCTURE
``` ```
@@ -20,7 +31,7 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin
├── setup.sh # First-run: generate .env + random keys ├── setup.sh # First-run: generate .env + random keys
├── update.sh # Docker update: pull/build/migrate ├── update.sh # Docker update: pull/build/migrate
├── nexusguard-install.sh # Native install (systemd + nginx) ├── nexusguard-install.sh # Native install (systemd + nginx)
├── nexusguard-uninstall.sh # Native uninstall ├── nexusguard-uninstall.sh
├── .env.example # DB/JWT/SALT/VITE config template ├── .env.example # DB/JWT/SALT/VITE config template
├── .gitmodules # 3 submodules → git.datadunia.com ├── .gitmodules # 3 submodules → git.datadunia.com
└── .opencode/ # IDE agent config (tooling, not project code) └── .opencode/ # IDE agent config (tooling, not project code)
@@ -35,16 +46,78 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin
| Backend core | `apps/server-core/internal/` | auth, config, firewall, heartbeat, ipam, models, wgmanager | | Backend core | `apps/server-core/internal/` | auth, config, firewall, heartbeat, ipam, models, wgmanager |
| Dev migration | `apps/server-core/main_dev.go` | GORM AutoMigrate (build tag `dev`) | | Dev migration | `apps/server-core/main_dev.go` | GORM AutoMigrate (build tag `dev`) |
| Firewall rules | `apps/server-core/internal/firewall/` | nftables Linux rules | | Firewall rules | `apps/server-core/internal/firewall/` | nftables Linux rules |
| gRPC signaling | `apps/server-core/signaling/` | Manager + Server: gRPC session tracking, Connect handler, recv loop |
| Dashboard views | `apps/dashboard-ui/src/views/` | Vue SFC pages | | Dashboard views | `apps/dashboard-ui/src/views/` | Vue SFC pages |
| Dashboard API client | `apps/dashboard-ui/src/api/` | Axios API modules | | Dashboard API client | `apps/dashboard-ui/src/api/` | Axios API modules |
| Dashboard stores | `apps/dashboard-ui/src/stores/` | Pinia state stores | | Dashboard stores | `apps/dashboard-ui/src/stores/` | Pinia state stores |
| Agent client | `apps/device-agent/internal/client/` | Provisioning + heartbeat | | Agent client | `apps/device-agent/internal/client/` | Provisioning + heartbeat |
| Agent signaling | `apps/device-agent/internal/signaling/` | gRPC connect with fallback + reconnect |
| Agent tunnel | `apps/device-agent/internal/tunnel/` | Memory-injected WireGuard | | Agent tunnel | `apps/device-agent/internal/tunnel/` | Memory-injected WireGuard |
| Shared crypto | `apps/*/shared/crypto/encryptor.go` | AES-256-GCM (duplicated identical) | | Shared crypto | `apps/*/shared/crypto/encryptor.go` | AES-256-GCM (duplicated identical) |
| CI workflows | `apps/*/.gitea/workflows/build.yml` | Gitea Actions per submodule | | CI workflows | `apps/*/.gitea/workflows/build.yml` | Gitea Actions per submodule |
| Build config | `apps/dashboard-ui/vite.config.ts` | Vite 8 + Vue + TailwindCSS v4 | | Build config | `apps/dashboard-ui/vite.config.ts` | Vite 8 + Vue + TailwindCSS v4 |
| Source of truth | `apps/server-core/docs/` | API_SPEC, KEY_ROTATION, PEER_DISCOVERY | | Source of truth | `apps/server-core/docs/` | API_SPEC, KEY_ROTATION, PEER_DISCOVERY |
| Plan guardrails | `.sisyphus/plans/` | Anti-patterns, "Must NOT do" rules |
## SIGNALING ARCHITECTURE (CRITICAL)
### Topology
```
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Dashboard │──HTTP──▶│ Server Core │◀─WG────▶│ Device Agent │
│ (Vue 3) │ :8080 │ (Go/Gin) │ :51820 │ (Go) │
└──────────────┘ │ │ └──────────────┘
│ Port 8080: │ │
│ - HTTP API │ ┌────┴────┐
│ - gRPC Signal │ │ TUN (wg)│
│ (cmux) │ │ Memory │
└─────────────────┘ └─────────┘
```
### Transport Fallback Chain (Agent → Server)
1. gRPC via HTTPS domain (TLS) → `italy-twenty.gl.at.ply.gg:443`
2. gRPC via WireGuard IP (insecure, tunnel-encrypted) → `10.172.21.1:8080`
3. HTTP heartbeat (fallback) → `serverURL/api/v1/heartbeat`
### Heartbeat = PRIMARY Channel
Always runs. Handles:
- Health check (30s interval)
- Config sync (detects config changes → rebuild tunnel)
- Handshake monitoring (rebuilds tunnel if lastHandshake > 120s)
- Recovery after failure (wasFailing → OnRecovered → full rebuild)
### gRPC = BONUS Channel
Best-effort. Handles:
- Real-time commands: Suspend, Resume, ConfigUpdate, Reconnect, Disconnect
- StatusReport from agent (tunnel_up, lastHandshake, state)
- Ping/Pong keepalive (20s)
### gRPC Port Multiplexing
HTTP + gRPC share port 8080 via `cmux`:
- Server: `cmux.New(lis)` → match gRPC by `content-type` header, match HTTP by `Any()`
- Agent connects to same port for both HTTP API and gRPC
### Key Design Decisions
- Agent NEVER destroys tunnel on heartbeat failure — only rebuilds
- `OnFailure = log only`, `OnRecovered = full rebuild`
- gRPC OnDisconnect/OnGRPCFailed just log — heartbeat continues
- Heartbeat reads `last_handshake_time_sec` from WG IPC to detect stale tunnel
- gRPC StatusReport sends handshake age to server every 30s
- Server WG IP read from actual kernel interface (`net.InterfaceByName`), NOT from stale DB
### Agent Connection Lifecycle
1. Provision → register with server, get WireGuard config
2. Start tunnel (memory-injected, no disk files)
3. Start heartbeat (always, primary channel)
4. Start gRPC (if ServerWGIP available, bonus channel)
5. On heartbeat config change → rebuild tunnel
6. On heartbeat stale handshake → rebuild tunnel
7. On heartbeat failure+recovery → rebuild tunnel
8. On gRPC suspend → stop tunnel, heartbeat continues
9. On gRPC resume → rebuild tunnel from server config
### Protobuf Messages
- **Agent → Server**: HelloMessage, HeartbeatAck, StatusReport, PingMessage
- **Server → Agent**: ConfigUpdate, SuspendCommand, ResumeCommand, ReconnectCommand, DisconnectCommand, KeepAlive, PongMessage
## CODE MAP ## CODE MAP
| Symbol | Type | Location | Role | | Symbol | Type | Location | Role |
@@ -60,8 +133,15 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin
| `firewall.InitNetwork()` | func | `apps/server-core/internal/firewall/` | nftables table/set creation | | `firewall.InitNetwork()` | func | `apps/server-core/internal/firewall/` | nftables table/set creation |
| `ipam.AllocateIP()` | func | `apps/server-core/internal/ipam/` | IP pool allocation from CIDR | | `ipam.AllocateIP()` | func | `apps/server-core/internal/ipam/` | IP pool allocation from CIDR |
| `wgmanager.SetConfig()` | func | `apps/server-core/internal/wgmanager/` | WireGuard config push | | `wgmanager.SetConfig()` | func | `apps/server-core/internal/wgmanager/` | WireGuard config push |
| `wgmanager.GetInterfaceAddress()` | func | `apps/server-core/internal/wgmanager/` | Read actual WG interface IP from kernel |
| `models.AutoMigrate()` | func | `apps/server-core/internal/models/` | GORM schema migration | | `models.AutoMigrate()` | func | `apps/server-core/internal/models/` | GORM schema migration |
| `encrypt()` / `decrypt()` | func | `apps/*/shared/crypto/encryptor.go` | AES-256-GCM (identical) | | `encrypt()` / `decrypt()` | func | `apps/*/shared/crypto/encryptor.go` | AES-256-GCM (identical) |
| `StartHeartbeat()` | func | `apps/device-agent/internal/client/heartbeat.go` | Heartbeat loop + handshake monitoring |
| `checkHandshake()` | func | `apps/device-agent/internal/client/heartbeat.go` | Read WG IPC handshake time |
| `ConnectAndRun()` | func | `apps/device-agent/internal/signaling/client.go` | gRPC connect with fallback + reconnect |
| `statusLoop()` | func | `apps/device-agent/internal/signaling/client.go` | Sends StatusReport every 30s |
| `NewManager()` | func | `apps/server-core/signaling/manager.go` | gRPC session tracking |
| `NewServer()` | func | `apps/server-core/signaling/server.go` | gRPC Connect handler + recv loop |
## CONVENTIONS ## CONVENTIONS
- **Go**: Standard layout (`main.go` in root, `internal/`, `api/`) - **Go**: Standard layout (`main.go` in root, `internal/`, `api/`)
@@ -79,7 +159,7 @@ NexusGuard SD-WAN Suite — Enterprise Zero-Trust SD-WAN with WireGuard tunnelin
## ANTI-PATTERNS (THIS PROJECT) ## ANTI-PATTERNS (THIS PROJECT)
- **NEVER** `nft flush table` — only atomic add/remove - **NEVER** `nft flush table` — only atomic add/remove
- **NEVER** commit temp/debug/test files (`nft-fix.sh`, `temp_*.txt` etc) in project root use ./tests folder and dont commit - **NEVER** commit temp/debug/test files (`nft-fix.sh`, `temp_*.txt` etc) in project root; use `./tests` folder
- **NEVER** log plaintext or encryption keys - **NEVER** log plaintext or encryption keys
- **NEVER** reopen completed phases/commits — fix forward only - **NEVER** reopen completed phases/commits — fix forward only
- **NEVER** rebuild `shared/crypto/encryptor.go` — copy identical file - **NEVER** rebuild `shared/crypto/encryptor.go` — copy identical file
@@ -200,10 +280,43 @@ go run -tags dev ./apps/server-core -create-admin -user admin -pass "..."
sudo /usr/local/bin/nexusguard-server -create-admin -user admin -pass "..." sudo /usr/local/bin/nexusguard-server -create-admin -user admin -pass "..."
``` ```
## heartbeat server <-> device-agent
Konsep yang Benar
Heartbeat = satu konsep, tiga jalur transport:
0 Transport Protocol Endpoint Kapan Dipakai
1 gRPC via domain (HTTPS proxy) gRPC bidi stream api-nexus.datadunia.com:443 Pertama dicoba
2 gRPC via WG IP (direct) gRPC bidi stream 10.172.21.1:8080 Fallback jika proxy swallowed
3 HTTP API REST POST /api/v1/heartbeat Fallback terakhir / always running
Satu konsep yang sama: kirim config_hash + last_handshake + tunnel_up → server compare → respond dengan config jika berubah.
Yang Perlu Diperbaiki
client.go: Perlu ada gRPC heartbeat loop (kirim HeartbeatRequest via stream periodik) + handle HeartbeatResponse
heartbeat.go: HTTP heartbeat tetap ada sebagai fallback
Transport switching: Saat gRPC connected → heartbeat via gRPC. Saat gRPC disconnected → heartbeat via HTTP
handler.go: Perlu handleHeartbeatResponse untuk process config dari gRPC heartbeat
Server manager.go: Perlu sendMu untuk prevent concurrent stream.Send()
Alur yang Benar (setelah perbaikan)
Agent Start
→ Provision (HTTP) → config pertama dari HTTP API → build tunnel
→ Start HTTP heartbeat (always running, fallback transport)
→ Start gRPC (HTTPS → WG IP)
gRPC Connected:
→ establishStream: kirim HelloMessage → terima ConfigUpdate (verify only, jangan rebuild)
→ heartbeatLoop: kirim HeartbeatRequest via gRPC setiap 30s
→ Server respond: HeartbeatResponse (config_changed? → rebuild via handler)
→ dispatch: handle Suspend/Resume/ConfigUpdate/Reconnect/Disconnect
gRPC Disconnected:
→ HTTP heartbeat continues (unaffected)
→ gRPC reconnect loop
→ When reconnected → switch heartbeat back to gRPC
## NOTES ## NOTES
- Submodules → private Gitea (`git.datadunia.com`); CI via Gitea Actions per submodule - Submodules → private Gitea (`git.datadunia.com`); CI via Gitea Actions per submodule
- Go versions diverge: server-core `1.25.7`, device-agent `1.25.1` - Go versions diverge: server-core `1.25.7`, device-agent `1.25.1`
- No root linter configs (`.golangci.yml`, `.eslintrc`, `.editorconfig`) - No root linter configs (`.golangci.yml`, `.eslintrc`, `.editorconfig`)
- Shell scripts use deprecated `docker-compose` v1, Makefile uses `docker compose` v2 - Shell scripts use deprecated `docker-compose` v1, Makefile uses `docker compose` v2
- Root has stale artifacts: `connect_remote.txt`, `temp_section*.txt`
- `package.json` name is `"temp-ui"` (stale scaffold remnant) - `package.json` name is `"temp-ui"` (stale scaffold remnant)