11 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) transmitsdeviceName, which topology and node overview panels display.deviceDescriptionis NOT broadcast — it only leaves the node in full node-info query responses (see srv6 API below);ExtraRoutesremain 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/routing-table,/api/nodes(topology graph),/api/node-info?address=<ipv6>(on-demand full node info),/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.- Node-info query API (
...network.srv6, JSON datagrams onKLALBRoutingProtocol.DEFAULT_PORT=1001):KLALBRoutingProtocolAPIServer/Clientspeak two request types —nodeinfotinyreq/resp→ device name ONLY; never gated by any flag (name is public via broadcast anyway).nodeinfofullreq/resp→ externalEndpoints + deviceName + deviceDescription.denyExternalEndpointQuery=truehides ONLY the endpoint list (data=null); name/description still answer.- GUI rule: opening
NodeInformationPanel= Full query; userequestNodeInfoTinyfor lightweight/background lookups. Legacyopenlines*message types were removed — mixed-version meshes get silence, so upgrade the whole network together. JsonDataPacketstores its UTF-8 payload length in a 2-byte header field: keep every JSON message under 64 KiB.
...network.frpc— frp client integration....klalb.ui— all Swing UI code.
Frontend (Dashboard)
Located in dashboard/:
- Git layout:
dashboard/is a separate git repo wired in as a submodule (own origin ongit.code.cq.cn). Commit frontend changes insidedashboard/first, then bump the submodule pointer in the parent repo — parent-repo commits alone do not capture them. - 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,#/routing-table,#/topology,#/settings) 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. - Routing table polls
/api/routing-tableevery 1s (use-routing-table.ts);costis delay-derived and displayed in milliseconds. - Topology polls
/api/nodesevery 1s (use-topology.ts) — SSE does NOT carry topology. - Selecting a topology node queries
/api/node-info; remote node descriptions require a full SRv6 node-info request and can time out after 3s. - 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:
externalEndpoints/autoConnections: published vs auto-connect endpoint lists (renamed fromopenConnections, which itself replaced legacyLineTable; the old name was a developer naming mistake — these addresses are this node's externally published endpoints, not "connections").ntpServers: time server list (replacesntpServerTable).denyExternalEndpointQuery/denyExternalEndpointBroadcast: safety flags — the query flag hides ONLY the external-endpoint list in full node-info responses (device name/description still answer; Tiny queries are never gated), the broadcast flag disables LAN multicast discovery (renamed fromdenyConnectionQuery/denyConnectionBroadcast, which replaceddenyLineTableQuery/denyLineTableBroadcast).enableTUN: boolean flag for TUN interface creation ("TUNName"configures device name).webListen: Web API listen address, normallyhttp://0.0.0.0:4665; legacywebPortis accepted on load/API input.
Legacy JSON keys are still accepted on load: KLALBConfigItem.getDefaultJsonDeserializer() normalizes old key names (openConnections/LineTable, denyConnectionQuery, denyLineTable*, ...) before reflective deserialization (manual rewrite because gson-2.1 has no @SerializedName(alternate=...)), and handleConfig in the web server accepts them too. New saves always write canonical names.
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.