Skip to content

Add a runnable local Linux demo (server + client + curl)#244

Merged
compscidr merged 7 commits intomainfrom
feature/linux-local-demo
Apr 29, 2026
Merged

Add a runnable local Linux demo (server + client + curl)#244
compscidr merged 7 commits intomainfrom
feature/linux-local-demo

Conversation

@compscidr
Copy link
Copy Markdown
Owner

Summary

  • Applies the Gradle `application` plugin to `server/` and `client/` with `mainClass` set, so `./gradlew :server:run` and `:client:run` work out of the box.
  • Changes `LinuxProxyClient.main`'s no-arg default server from `10.0.0.114` (a stray hardcoded LAN address) to `127.0.0.1`, so the no-arg path is useful for a local loopback demo.
  • Adds `client/scripts/demo.sh`: brings up the TUN device, starts the server + client via Gradle, adds a host route for a chosen target IP through the `kanon` device, and curls it.
  • Extends `client/scripts/cleanup.sh` to also kill `GradleWorkerMain` / `ProxyServer` / `LinuxProxyClient` and drop any host routes still bound to `kanon`.
  • Documents the demo in the top-level README with both a one-shot scripted path and step-by-step manual commands, plus a Wireshark attach hint.

Test plan

  • `./gradlew :server:assemble :client:assemble` succeeds (the new `application` plugin produces `startScripts` and `distTar`/`distZip`)
  • `./gradlew :client:lintKotlinMain` passes
  • `bash client/scripts/demo.sh` end-to-end on a real Linux host: TUN comes up, server + client start, curl reaches the target through the proxy
  • `bash client/scripts/cleanup.sh` tears everything down

🤖 Generated with Claude Code

- Apply the Gradle `application` plugin to `server/` and `client/` and
  set `mainClass` so `./gradlew :server:run` and `:client:run` work.
- Change LinuxProxyClient.main's no-arg default server from
  10.0.0.114 to 127.0.0.1 (the previous value was a stray hardcoded
  LAN IP that made the no-arg path useless for a local demo).
- Add `client/scripts/demo.sh` that brings up the kanon TUN device,
  starts the server and client via Gradle, adds a host route through
  the TUN, and runs a curl against the chosen target IP.
- Extend `client/scripts/cleanup.sh` to also kill GradleWorkerMain,
  ProxyServer, and LinuxProxyClient processes and to drop any host
  routes still pointed at the kanon device.
- Document the demo in the top-level README with both the scripted
  one-shot path and the step-by-step manual commands, plus a
  Wireshark attach hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 18:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a runnable local Linux “server + client + curl” demo path by making :server:run / :client:run work out of the box, providing demo/cleanup scripts, and documenting the workflow in the README.

Changes:

  • Apply Gradle application plugin to server/ and client/ with configured mainClass so :server:run and :client:run are runnable.
  • Update LinuxProxyClient default no-arg server address to 127.0.0.1 for local loopback demos.
  • Add client/scripts/demo.sh, extend client/scripts/cleanup.sh, and document the end-to-end demo in README.md.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/build.gradle.kts Adds application plugin + main class to make server runnable via Gradle.
client/build.gradle.kts Adds application plugin + main class to make client runnable via Gradle.
client/src/main/kotlin/com/jasonernst/kanonproxy/LinuxProxyClient.kt Switches default server address to localhost for no-arg runs.
client/scripts/demo.sh New end-to-end Linux demo script (TUN + server + client + route + curl).
client/scripts/cleanup.sh Expands cleanup to kill additional processes and remove kanon-bound routes.
README.md Documents scripted and manual demo steps plus Wireshark attachment info.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread client/scripts/demo.sh Outdated
Comment thread client/scripts/demo.sh Outdated
Comment thread client/scripts/cleanup.sh Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.86%. Comparing base (98a7818) to head (2377707).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #244      +/-   ##
============================================
+ Coverage     60.38%   60.86%   +0.48%     
- Complexity      226      230       +4     
============================================
  Files            15       15              
  Lines          2070     2070              
  Branches        315      315              
============================================
+ Hits           1250     1260      +10     
+ Misses          644      627      -17     
- Partials        176      183       +7     
Flag Coverage Δ
libunittests 60.86% <ø> (+0.48%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Previously the demo added a host route for the curl target through the
kanon device. That caused a loop: the proxy server's own outbound TCP
SYN to the same target also matched the kanon route and got pulled
back into the TUN, which the client then forwarded to the server
again, and around it went until curl timed out.

Switching to `sudo curl --interface kanon http://<target>/` avoids the
loop entirely. SO_BINDTODEVICE pins only curl's socket to the TUN, so
the proxy server's outbound TCP stays unbound and follows the normal
default route to the real internet. No changes to the kernel route
table needed.

Updates demo.sh and README to remove `ip route add/del` and use
`--interface kanon` (with sudo, since SO_BINDTODEVICE needs CAP_NET_RAW).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the whole cleanup body was wrapped in `{ ... } &> /dev/null`
which hid every error - so when the kanon TUN failed to delete you got
no feedback. Combined with `pkill` being asynchronous, the script
could race the kernel's fd release and leave the interface stuck.

This rewrite:
- Drops the blanket `&> /dev/null` and prints diagnostics; only the
  individual `ip` commands silence their own no-op errors via
  `2>/dev/null || true`.
- SIGTERM the JVM processes first, sleep 1s for them to release the
  TUN fd, then SIGKILL anything still around.
- Retry `ip tuntap del` up to 5 times with 1-second gaps to handle
  any remaining race with kernel cleanup.
- Print a clear warning and an `lsof /dev/net/tun` hint if the
  interface is still present afterwards, so the user can investigate
  who's still holding it.
- Remove `rm *.jar *.so`. It depended on cwd and was never relevant
  to clean teardown of the proxy.
- Skip everything if the kanon interface isn't there in the first
  place (idempotent rerun).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructures the Local Linux demo into Path A (scripted) and Path B
(manual, 4-terminal) to match what was actually verified end-to-end.
Adds:
- The expected log lines and verification commands per terminal
  (e.g. ss -lun | grep 8080) so users know what success looks like.
- Notes that the script leaves server/client running and shows how
  to fire additional curls (and tail the session log).
- A "Watching packets" section that calls out `sudo wireshark -i kanon`
  as the easy native-libpcap option on Linux, alongside the in-process
  pcap-ng TCP dumpers, with a one-line explanation of when each one
  is the right choice.
- A reminder about the cleanup.sh "kanon interface is still present"
  warning and the `sudo lsof /dev/net/tun` follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- demo.sh: track SERVER_READY in the wait loop, exit non-zero on
  timeout, and bail early if the server PID exits before binding the
  UDP port. Previously the loop would always fall through after 30
  seconds and the script would happily start the client and curl even
  if the server had crashed.
- cleanup.sh: scope all process kills to the current user via
  `pgrep -u $UID -f` instead of an unscoped `pkill -f`. Patterns like
  "ProxyServer" / "LinuxProxyClient" are otherwise broad enough to
  match unrelated JVMs (other Gradle builds, IDEs, etc.) on a shared
  host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Embeds a clickable YouTube poster thumbnail for the new Linux demo
(https://youtu.be/_ypo_3PYqTM) at the top of the Local Linux demo
section, matching the thumbnail-link pattern already used for the
Android demos at the top of the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@compscidr compscidr merged commit 0663eed into main Apr 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants