- sendJsonResponse now serializes JsonElement via toString(), since gson-2.1's JSON_ELEMENT factory misses JsonObject/JsonArray subclasses and falls back to reflection (leaking the internal "members" field into every REST response) - Document javac full-path requirement, pre-existing compile warnings, frontend page data flow, and gson quirks in AGENTS.md
8.8 KiB
AGENTS.md
KLALB ("KLALB Decentralized SRv6 Network") — Java load-balancing/tunnel system that merges multiple WAN links into one virtual IPv6/SRv6 network. Version constant lives in src/org/kne/cloud/network/klalb/CONST.java. Protocol specs and manuals are the Chinese .docx files in the repo root.
Build & run
No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in lib/, output goes to bin/ (gitignored). When adding a jar, update both .classpath and KLALB.iml.
Compile (javac is NOT on PATH — use the full JDK path; -encoding UTF-8 is mandatory because sources contain Chinese text):
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
Warnings about ThreadTool varargs / deprecated finalize are pre-existing and expected — success = exit code 0. After recompiling, restart the running app (IDE-debugged JVMs keep old classes).
Run from the repo root — CWD matters:
- reads
klalb-config.jsonfrom CWD - loads native libs from CWD:
tuntap4j.dll/.so/.dylib,wintun.dll,fastcopy.dll(TUN device support) - classpath must include
srcas well asbin: i18n bundles (/klalb_*.properties) and images (/assets/*) are classpath resources that Eclipse copies tobinbut manualjavacdoes not
java --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
IDE metadata targets JDK 26 (jdk-26.0.1); the tree also compiles cleanly on JDK 25. .classpath now references the standard container JavaSE-25 — an execution-environment spec that any JDK ≥25 satisfies, so it works unchanged on JDK 26 machines too (the original named jdk-26.0.1 VM broke VS Code import on machines without it). .vscode/settings.json maps JavaSE-25 to the locally installed Adoptium JDK; register every installed JDK there when adding another one. Keep compiler compliance ≤25 (.settings pins 19) so both JDKs stay usable.
Runtime gotchas (all verified):
- On JDK 25,
KNEOptimize.jar'sFastLibreflects intojdk.internal.misc.Unsafe; without the two JVM flags above it throwsInaccessibleObjectExceptionat startup (app still runs). - Creating the SRv6 TUN adapter (
WintunCreateAdapter) requires an elevated shell; without admin rights it logs "创建虚拟网卡失败" and continues with only theinLoopBackinterface — links/bridges still work. - To disable TUN creation completely (e.g. for non-admin UI/routing testing), set
"enableTUN": falseinklalb-config.jsonor toggle off "启用 TUN 虚拟网卡" in GUI/Web settings. - Routing broadcast (
RouterInfo) transmitsdeviceNameacross the network, which topology and node overview panels display.deviceDescriptionandExtraRoutesremain local controller configs.
Verification
No test suite, no CI. Classes named *Test* (nathole/, ntp/) are manual main() harnesses requiring real network peers. Practical check = compile succeeds + app launches.
Architecture
- Entrypoint
org.kne.cloud.network.klalb.KLALBMain: load config → buildKLALBProxySystem→ open Swing GUI (KLALBStateGUI3) unless"nogui": true→ startKLALBWebServer(if"webUI": trueor web server enabled, default port4665) → interactive console (help,links-state,route,kperf, ...). org.kne.cloud.network.klalb.web.KLALBWebServer— built-in HTTP/SSE server (JDKHttpServer):- API endpoints:
/api/status,/api/events(SSE stream, 200ms intervals),/api/links,/api/links/action,/api/links/reconnect,/api/routes,/api/nodes(topology graph),/api/interfaces,/api/config. - Static file hosting / SPA fallback: serves
dashboard/dist/assets directly.
- API endpoints:
org.kne.cloud.network— generic socket framework:VirtualSocket*hierarchy,SocketBridgeport-forwarding proxies,ProtocolDetector(multi-protocol mux on one port),MultiProtocolSocketAddress= URI-style addresses (tcp://,udp://,kltp://,ntp://) dispatched through theSocketTyperegistry....network.klalb— app core:KLALBController(the virtual SRv6 network),KLALBRemoteLink(WAN lines),*Packetwire-format classes, virtual socket implementations....network.congestion— pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via"congestionAlgorithm"in config....network.kltp— custom reliable transport protocol (packets/streams)....network.ipv6,...network.srv6— packet codecs, route table, Dijkstra path computation....network.frpc— frp client integration....klalb.ui— all Swing UI code.
Frontend (Dashboard)
Located in dashboard/:
- Stack: Vite + React 19 + TypeScript + Tailwind CSS v4 +
@base-ui/react(style:base-nova, icons:lucide-react, toasts:@base-ui/react/toast). - Routing: Hash-based routing (
#/overview,#/connections,#/topology,#/settings, etc.) for seamless SPA hosting under JavaKLALBWebServer. - Package Manager:
pnpm(run all commands fromdashboard/directory). - Component installation: Must use CLI via
pnpm dlx shadcn@latest add <component>(e.g.pnpm dlx shadcn@latest add alert card badge toast). Never create or fake shadcn components manually. Non-shadcn libs (@xyflow/react,d3-force) are installed via plainpnpm add. - Commands:
pnpm dev— Start Vite dev server (proxies/apito backendhttp://127.0.0.1:4665).pnpm build— Typecheck and build SPA todashboard/dist(which JavaKLALBWebServerserves directly).pnpm lint/pnpm typecheck— Verification.
- Pages & data flow:
- Overview / Connections read the SSE stream (
use-klalb-sse.ts, 200ms pushes of status + links). - Settings loads/saves
/api/config(use-klalb-config.ts); save payload must keep legacy field aliases alongside new names for compatibility. - Topology polls
/api/nodesevery 1s (use-topology.ts) — SSE does NOT carry topology. - Topology layout:
d3-forceheadless simulation (recomputed only when node/edge structure changes) rendered by@xyflow/reactwith customdevice-node/link-edgecomponents insrc/components/topology/. - React hooks lint rule forbids
setStatesynchronously inside effects — initialize form state via componentkeyremount + lazyuseState(() => ...)initializers (seeSettingsFormpattern).
- Overview / Connections read the SSE stream (
Config
klalb-config.json is an array of items discriminated by their "Type" field. Adding a new item type requires a KLALBConfigItem subclass plus new cases in both KLALBConfigItem.getDefaultJsonDeserializer() and getDefaultJsonSerializer(); unknown types are preserved as UnknownKLALBConfigItem. Any Gson instance handling config must register these adapters via registerToGsonBuilder (see KLALBProxySystem).
Key controller config fields:
openConnections/autoConnections: published vs auto-connect endpoint lists (replaces legacyLineTable/ConnectLineTable).ntpServers: time server list (replacesntpServerTable).denyConnectionQuery/denyConnectionBroadcast: query & discovery broadcast safety flags (replacesdenyLineTableQuery/denyLineTableBroadcast).enableTUN: boolean flag for TUN interface creation ("TUNName"configures device name).webPort: default4665.
Gson quirks:
- gson-2.1 (vendored) is ancient: its
JSON_ELEMENTadapter factory only matches exactJsonElement.class, NOT subclasses. Callinggson.toJson(Object)with a runtimeJsonObject/JsonArrayreflectively serializes the internal field as{"members": {...}}.KLALBWebServer.sendJsonResponseguards against this by usingJsonElement.toString()for JsonElement instances — keep that guard when adding new response paths. SSE avoids the issue entirely viaJsonObject.toString(). /api/configGET/POST is parsed field-by-field inKLALBWebServer.handleConfig(NOT whole-object Gson reflection) because polymorphic fields (List<InetAddress>,List<MultiProtocolSocketAddress>) break reflective mapping. Keep new config fields in sync there, accepting both legacy and new JSON key names.InetAddress,MultiProtocolSocketAddress, andKLALBConfigItemcustom adapters are registered on the shared Gson inKLALBProxySystem; the web server reuses that instance viaproxySystem.getGson().- Saving via web API persists through
KLALBProxySystem.saveConfigToFile()(GUI save consumer takes precedence when present).
Conventions
- Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese.
- UI strings go through
UIEnv.getRsb().getString(...); add keys to bothsrc/klalb_zh_CN.propertiesandsrc/klalb_en_US.properties. client.cfg,server.cfg,linetable.txtat the root are example line-table/port-rule files loaded via the GUI file picker — not hardwired paths.