3.1 KiB
3.1 KiB
Development guide
Layout
cmd/pilens service + CLI (serve, init, setup, reset-password, rlimit-exec)
cmd/pilens-helper the only root component: typed network operations over a socket
internal/config configuration + validation (shared by core and helper)
internal/netgen renders NetworkManager / dnsmasq / nftables (pure, unit-tested)
internal/helper core↔helper protocol + parsers
internal/store SQLite open + migrations
internal/capture capture engine, pcap/pcapng I/O, tshark, analysis, findings
internal/pcapfile bounds-checked pcap/pcapng reader/writer
internal/sysinfo unprivileged host/interface/health probes
internal/auth Argon2id, TOTP, session tokens, login throttling
internal/api HTTP API + static UI serving + SSE
web/ React + TypeScript + Vite UI (embedded via go:embed)
migrations/ ordered SQL, embedded
systemd/ scripts/ packaging/ docs/ tests/
Prerequisites
Go ≥ 1.27 (the module pins toolchain 1.27.2), Node ≥ 20, Docker for the integration suite. On a dev box you do not need tshark/dnsmasq/nftables unless you run those tests directly.
Common tasks
make test # go vet + go test + tsc --noEmit (no external tools needed)
make test-docker # tshark/dumpcap tests + netns integration suite (needs Docker)
make package # cross-compile arm64, bundle UI + docs + scripts -> dist/*.tar.gz
cd web && npm run dev # UI against a running appliance: PILENS_API=https://<pi>:8443 npm run dev
The dev loop used to build this on real hardware: make package VERSION=x.y.z,
scp the tarball to the Pi, run install.sh --yes (it upgrades in place,
keeping data), then drive the API or UI. install.sh backs up and rolls back.
Design rules (that reviewers should enforce)
- The core never runs as root. Privileged actions go through the helper, which has a fixed set of typed operations and re-validates everything. No generic command execution, ever.
- Never interpolate user input into a shell or SQL. All execs use argv
lists; all queries use bound parameters. tshark children run via
rlimit-execwith resource limits andno_new_privs. - Original captures are immutable. Packet blocks are copied byte-for-byte; derived data (summaries, dissection) is regenerable and clearly separated.
- Be honest in the UI. No fabricated data, no controls that do nothing, no claims the evidence doesn't support (e.g. TLS version, interception status, drop vs. UI-drop). A feature that isn't built is absent, not faked.
- Validation lives in
internal/configand runs twice — in the core and again in the root helper, which does not trust the core.
Adding a migration
Drop migrations/NNN_name.sql; it is applied in order on next start and
recorded in schema_migrations. Migrations run inside a transaction.
Releasing
make test && make test-dockergreen.go run golang.org/x/vuln/cmd/govulncheck ./...andcd web && npm audit.make package VERSION=x.y.z; the tarball carries its own SHA-256.- Record hardware results in
docs/testing.mdonly after actually running them.