Files
KLALB/AGENTS.md
T

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.json from CWD
  • loads native libs from CWD: tuntap4j.dll/.so/.dylib, wintun.dll, fastcopy.dll (TUN device support)
  • classpath must include src as well as bin: i18n bundles (/klalb_*.properties) and images (/assets/*) are classpath resources that Eclipse copies to bin but manual javac does 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's FastLib reflects into jdk.internal.misc.Unsafe; without the two JVM flags above it throws InaccessibleObjectException at startup (app still runs).
  • Creating the SRv6 TUN adapter (WintunCreateAdapter) requires an elevated shell; without admin rights it logs "创建虚拟网卡失败" and continues with only the inLoopBack interface — links/bridges still work.
  • To disable TUN creation completely (e.g. for non-admin UI/routing testing), set "enableTUN": false in klalb-config.json or toggle off "启用 TUN 虚拟网卡" in GUI/Web settings.
  • Routing broadcast (RouterInfo) transmits deviceName, which topology and node overview panels display. deviceDescription is NOT broadcast — it only leaves the node in full node-info query responses (see srv6 API below); ExtraRoutes remain 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 → build KLALBProxySystem → open Swing GUI (KLALBStateGUI3) unless "nogui": true → start KLALBWebServer (if "webUI": true or web server enabled, default port 4665) → interactive console (help, links-state, route, kperf, ...).
  • org.kne.cloud.network.klalb.web.KLALBWebServer — built-in HTTP/SSE server (JDK HttpServer):
    • 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.
  • org.kne.cloud.network — generic socket framework: VirtualSocket* hierarchy, SocketBridge port-forwarding proxies, ProtocolDetector (multi-protocol mux on one port), MultiProtocolSocketAddress = URI-style addresses (tcp://, udp://, kltp://, ntp://) dispatched through the SocketType registry.
  • ...network.klalb — app core: KLALBController (the virtual SRv6 network), KLALBRemoteLink (WAN lines), *Packet wire-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 on KLALBRoutingProtocol.DEFAULT_PORT=1001): KLALBRoutingProtocolAPIServer/Client speak 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=true hides ONLY the endpoint list (data=null); name/description still answer.
    • GUI rule: opening NodeInformationPanel = Full query; use requestNodeInfoTiny for lightweight/background lookups. Legacy openlines* message types were removed — mixed-version meshes get silence, so upgrade the whole network together.
    • JsonDataPacket stores 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 on git.code.cq.cn). Commit frontend changes inside dashboard/ 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 Java KLALBWebServer.
  • Package Manager: pnpm (run all commands from dashboard/ 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 plain pnpm add.
  • Commands:
    • pnpm dev — Start Vite dev server (proxies /api to backend http://127.0.0.1:4665).
    • pnpm build — Typecheck and build SPA to dashboard/dist (which Java KLALBWebServer serves 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-table every 1s (use-routing-table.ts); cost is delay-derived and displayed in milliseconds.
    • Topology polls /api/nodes every 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-force headless simulation (recomputed only when node/edge structure changes) rendered by @xyflow/react with custom device-node / link-edge components in src/components/topology/.
    • React hooks lint rule forbids setState synchronously inside effects — initialize form state via component key remount + lazy useState(() => ...) initializers (see SettingsForm pattern).

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 from openConnections, which itself replaced legacy LineTable; the old name was a developer naming mistake — these addresses are this node's externally published endpoints, not "connections").
  • ntpServers: time server list (replaces ntpServerTable).
  • 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 from denyConnectionQuery / denyConnectionBroadcast, which replaced denyLineTableQuery / denyLineTableBroadcast).
  • enableTUN: boolean flag for TUN interface creation ("TUNName" configures device name).
  • webListen: Web API listen address, normally http://0.0.0.0:4665; legacy webPort is 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_ELEMENT adapter factory only matches exact JsonElement.class, NOT subclasses. Calling gson.toJson(Object) with a runtime JsonObject/JsonArray reflectively serializes the internal field as {"members": {...}}. KLALBWebServer.sendJsonResponse guards against this by using JsonElement.toString() for JsonElement instances — keep that guard when adding new response paths. SSE avoids the issue entirely via JsonObject.toString().
  • /api/config GET/POST is parsed field-by-field in KLALBWebServer.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, and KLALBConfigItem custom adapters are registered on the shared Gson in KLALBProxySystem; the web server reuses that instance via proxySystem.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 both src/klalb_zh_CN.properties and src/klalb_en_US.properties.
  • client.cfg, server.cfg, linetable.txt at the root are example line-table/port-rule files loaded via the GUI file picker — not hardwired paths.