diff options
| -rw-r--r-- | AGENTS.md | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..434d657 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# AGENTS.md — jns + +## What this project is + +**jns** is a high-performance network port scanner (Java 25, virtual threads), +scanning IPv4/IPv6 ranges for open TCP (+ optional UDP) ports with service-type +detection from banners. It is a CLI library + app: `App`/`Main` parse args, +`Scan` holds config, `Scanner` orchestrates, `CliPrinter` renders results, +`ScannerLibrary` exposes a programmatic API for other apps. + +## Build / run / test + +- Build: `mvn -q package -DskipTests` → `target/jns-1.0-SNAPSHOT.jar` + (no unit tests exist; `mvn test` is a no-op). +- Run: `java -jar target/jns-1.0-SNAPSHOT.jar <ip-or-file> -p 1-1024 ...` + (see `--help`; `-i -` reads hosts from stdin, `-u`/`-uu` for UDP, `-q` quiet, + `-sf` show filtered ports, `-f` rate limit, `-b` banner recognition). +- Sanity checks used during development: + - open port: start `python3 -m http.server 8765`, scan `127.0.0.1 -p 22,80,8765` + → expect exactly `127.0.0.1` + `\t (1) open:\t8765`; + - offline host: 1024 ports → empty output, port count 1 (online check only); + - `scanTests/` holds recorded expected outputs (`scan-result-*`, `scan-error-*`); + compare new output against the matching file before declaring a change done. + +## Architecture notes (read before touching) + +- Concurrency: one virtual thread per port probe today + (`ProducerThread` → `executor.submit(new ScanPortTask(...))`), throttled by + semaphores: `socketLimit` (default 2048), `maxHostsLimit`, `maxWorkersPerHost`. + `CompletionConsumer` drains `PortScanResultAccumulator`; Ctrl-C cancels but + partial results are still printed — preserve that contract. +- Shared per-scan state flows through the record `ScanExecutionContext` + (semaphores, rate limiter, shared `PortRange`, ...). `ScanHostContext` is the + host-level record. **Multi-scan constraint: the scanner must support several + concurrent scans with different configs in one JVM — never store per-scan state + in statics; only true compile-time constants may be static.** +- Carrier-thread pinning is forbidden: never sleep/lock around blocking calls + inside virtual threads (see `PortScanRateLimiter` sleep-outside-synchronized + pattern and `Scan*PortTask` non-blocking wait loops). +- `LinuxUdpSocketAPI` (Foreign Memory API, per-probe `Arena.ofConfined`): + do NOT share its errno call-state segment across threads (data race). +- `CliPrinter.getColumnWidth()`: the `stty size` process spawn per call is + intentional (terminal resize) — do not cache the width. + +## Optimization state + +- Phase 2 (allocation/CPU hot-path cleanups) is **done and measured**: + see `TODO.md` (commits, benchmark evidence: −31 % CPU, −4 % wall, −8 % RSS on + a 20 k hosts × 65535-port run). The scan is network-latency-bound; remaining + headroom is in `TODO.md` Phase 2b / 3 / 4 (deferred, measure-first). +- Benchmark method that was used: `/usr/bin/time -v` around + `java -jar ... -i - -p all` (host list on stdin), keep the + `before-optimization.txt` / `after-optimization-N.txt` artifacts. + +## Working rules for this repo + +- Never change user-visible output format or scan behavior "while at it" — + only when it is the explicit purpose of the change. +- Small, logical steps per file edit; verify with `mvn -q compile` before + showing the diff. +- `/tmp` is not persistent between tool calls; re-read files before editing to + confirm their current state. |
