summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md62
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.