diff --git a/README.md b/README.md index 49bddc4..1e57d1a 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ make test-docker # tshark/dumpcap tests + network-namespace integration s - [Benchmarks](docs/benchmarks.md) - [Testing and hardware acceptance checklist](docs/testing.md) - [Known limitations](docs/limitations.md) +- [Illustrated user guide (PDF)](docs/guide/pilens-guide.pdf) ## Licence diff --git a/docs/guide/README.md b/docs/guide/README.md new file mode 100644 index 0000000..4d88347 --- /dev/null +++ b/docs/guide/README.md @@ -0,0 +1,15 @@ +# PiLens user guide (LaTeX) + +A deliberately skimmable, callout-heavy user guide. British English. + +- `pilens-guide.tex` — source +- `fig/` — UI screenshots (captured headless from a running appliance) +- `pilens-guide.pdf` — built, 12 pages A4 + +Rebuild: + +```sh +cd docs/guide && pdflatex pilens-guide.tex && pdflatex pilens-guide.tex +``` + +Needs a TeX Live with `tcolorbox`, `titlesec`, `enumitem`, `fancyhdr`, `tikz`. diff --git a/docs/guide/fig/analyser.png b/docs/guide/fig/analyser.png new file mode 100644 index 0000000..c4e2c48 Binary files /dev/null and b/docs/guide/fig/analyser.png differ diff --git a/docs/guide/fig/conversations.png b/docs/guide/fig/conversations.png new file mode 100644 index 0000000..00c8b95 Binary files /dev/null and b/docs/guide/fig/conversations.png differ diff --git a/docs/guide/fig/devices.png b/docs/guide/fig/devices.png new file mode 100644 index 0000000..f2e213e Binary files /dev/null and b/docs/guide/fig/devices.png differ diff --git a/docs/guide/fig/dns.png b/docs/guide/fig/dns.png new file mode 100644 index 0000000..d3f5ec7 Binary files /dev/null and b/docs/guide/fig/dns.png differ diff --git a/docs/guide/fig/findings.png b/docs/guide/fig/findings.png new file mode 100644 index 0000000..bbddcae Binary files /dev/null and b/docs/guide/fig/findings.png differ diff --git a/docs/guide/fig/health.png b/docs/guide/fig/health.png new file mode 100644 index 0000000..6b9e943 Binary files /dev/null and b/docs/guide/fig/health.png differ diff --git a/docs/guide/fig/library.png b/docs/guide/fig/library.png new file mode 100644 index 0000000..0240b14 Binary files /dev/null and b/docs/guide/fig/library.png differ diff --git a/docs/guide/fig/network.png b/docs/guide/fig/network.png new file mode 100644 index 0000000..89dcc24 Binary files /dev/null and b/docs/guide/fig/network.png differ diff --git a/docs/guide/fig/overview.png b/docs/guide/fig/overview.png new file mode 100644 index 0000000..74fa712 Binary files /dev/null and b/docs/guide/fig/overview.png differ diff --git a/docs/guide/fig/statistics.png b/docs/guide/fig/statistics.png new file mode 100644 index 0000000..9f9e33a Binary files /dev/null and b/docs/guide/fig/statistics.png differ diff --git a/docs/guide/fig/tls.png b/docs/guide/fig/tls.png new file mode 100644 index 0000000..0d4e997 Binary files /dev/null and b/docs/guide/fig/tls.png differ diff --git a/docs/guide/pilens-guide.pdf b/docs/guide/pilens-guide.pdf new file mode 100644 index 0000000..62ca3f0 Binary files /dev/null and b/docs/guide/pilens-guide.pdf differ diff --git a/docs/guide/pilens-guide.tex b/docs/guide/pilens-guide.tex new file mode 100644 index 0000000..84d98c3 --- /dev/null +++ b/docs/guide/pilens-guide.tex @@ -0,0 +1,435 @@ +\documentclass[11pt]{article} + +% ---- PiLens User Guide. British English. Designed to be skimmed. ---- +\usepackage[a4paper,margin=20mm,marginparwidth=22mm,marginparsep=5mm]{geometry} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage{helvet} +\renewcommand{\familydefault}{\sfdefault} +\usepackage{microtype} +\usepackage{graphicx} +\usepackage{xcolor} +\usepackage[most]{tcolorbox} +\usepackage{enumitem} +\usepackage{titlesec} +\usepackage{fancyhdr} +\usepackage{marginnote} +\usepackage{pifont} +\usepackage[hidelinks]{hyperref} +\usepackage{tikz} +\usetikzlibrary{calc} + +% ---- palette (matches the appliance UI) ---- +\definecolor{ink}{HTML}{11161D} +\definecolor{panel}{HTML}{1B222C} +\definecolor{line}{HTML}{2A323D} +\definecolor{amber}{HTML}{E0922F} +\definecolor{amberlt}{HTML}{F2A93B} +\definecolor{cyan}{HTML}{2C92A4} +\definecolor{cyanlt}{HTML}{5CCFE0} +\definecolor{good}{HTML}{2E9E63} +\definecolor{bad}{HTML}{D4504E} +\definecolor{muted}{HTML}{6A7482} +\definecolor{paper}{HTML}{FBFBF9} + +\hypersetup{colorlinks=true, linkcolor=cyan, urlcolor=cyan} +\pagecolor{white} +\color{ink} + +% ---- headings ---- +\titleformat{\section}{\Large\bfseries\color{ink}}{\thesection}{0.6em}{}[{\color{line}\titlerule[1pt]}] +\titleformat{\subsection}{\large\bfseries\color{amber}}{}{0em}{} +\titleformat{\subsubsection}{\bfseries\color{ink}}{}{0em}{} +\titlespacing*{\section}{0pt}{2.4ex}{1.2ex} +\titlespacing*{\subsection}{0pt}{2ex}{0.6ex} +\setlength{\parindent}{0pt} +\setlength{\parskip}{0.6ex} +\renewcommand{\arraystretch}{1.25} + +% ---- callout boxes ---- +\newtcolorbox{tldr}{enhanced, colback=amberlt!10, colframe=amberlt, boxrule=0pt, + leftrule=3pt, arc=2pt, left=8pt, right=8pt, top=5pt, bottom=5pt, breakable, + fonttitle=\bfseries, title={\ding{220}\ \ TL;DR}} +\newtcolorbox{heads}{enhanced, colback=bad!8, colframe=bad, boxrule=0pt, + leftrule=3pt, arc=2pt, left=8pt, right=8pt, top=5pt, bottom=5pt, breakable, + fonttitle=\bfseries, title={\ding{43}\ \ Heads-up}} +\newtcolorbox{tip}{enhanced, colback=cyanlt!10, colframe=cyan, boxrule=0pt, + leftrule=3pt, arc=2pt, left=8pt, right=8pt, top=5pt, bottom=5pt, breakable, + fonttitle=\bfseries, title={\ding{42}\ \ Pro tip}} +\newtcolorbox{truth}{enhanced, colback=ink!5, colframe=ink, boxrule=0pt, + leftrule=3pt, arc=2pt, left=8pt, right=8pt, top=5pt, bottom=5pt, breakable, + fonttitle=\bfseries, title={\ding{45}\ \ The honest truth}} +\newtcolorbox{shotbox}{enhanced, colback=white, colframe=line, boxrule=0.6pt, + arc=3pt, left=3pt, right=3pt, top=3pt, bottom=3pt, drop fuzzy shadow=line} + +\newcommand{\screen}[2]{\begin{shotbox}\includegraphics[width=\linewidth]{fig/#1}\end{shotbox}\vspace{-0.3ex}{\small\color{muted} Figure: #2}\par\vspace{1ex}} +\newcommand{\key}[1]{\tikz[baseline=(k.base)]{\node[fill=panel,text=white,rounded corners=2pt,inner xsep=4pt,inner ysep=1.5pt,font=\footnotesize\ttfamily](k){#1};}} +\newcommand{\yes}{\textcolor{good}{\ding{51}}} +\newcommand{\no}{\textcolor{bad}{\ding{55}}} +\newcommand{\term}[1]{\textbf{#1}} +\newcommand{\mn}[1]{\marginnote{\footnotesize\color{muted}#1}} + +\pagestyle{fancy}\fancyhf{} +\renewcommand{\headrulewidth}{0.4pt} +\lhead{\footnotesize\color{muted}\textbf{PiLens} user guide} +\rhead{\footnotesize\color{muted}\leftmark} +\cfoot{\footnotesize\color{muted}\thepage} +\renewcommand{\sectionmark}[1]{\markboth{#1}{}} + +\begin{document} + +% ======================= COVER ======================= +\begin{titlepage} +\color{ink} +\begin{tikzpicture}[remember picture, overlay] + \fill[ink] (current page.north west) rectangle ([yshift=-95mm]current page.north east); + \node[anchor=north west, text=white] at ([xshift=20mm,yshift=-22mm]current page.north west) + {\fontsize{46}{50}\selectfont\bfseries PiLens}; + \node[anchor=north west, text=amberlt] at ([xshift=21mm,yshift=-38mm]current page.north west) + {\Large network forensics for the Raspberry Pi}; + % lens mark + \begin{scope}[shift={([xshift=-42mm,yshift=-42mm]current page.north east)}] + \draw[amberlt, line width=2.2pt] (0,0) circle (10mm); + \draw[amberlt, line width=3pt, line cap=round] (7mm,-7mm) -- (15mm,-15mm); + \draw[cyanlt, line width=1.4pt, line join=round] (-5mm,0) -- (-3mm,0) -- (-1.5mm,4mm) -- (1.5mm,-4mm) -- (3mm,0) -- (5mm,0); + \end{scope} +\end{tikzpicture} +\vspace{100mm} + +{\LARGE\bfseries The friendly field guide}\\[2mm] +{\large\color{muted} Turn a Raspberry Pi into a Wi-Fi test network you can actually \emph{see} inside.}\\[10mm] + +\begin{tldr} +This guide is built for skimming. Every section opens with a \textbf{TL;DR}. Boxes +in the margins and colour-coded callouts carry the important bits. \textbf{Read the +box, skip the prose, come back if you need it.} You will not miss anything vital +by jumping around --- that is the point. +\end{tldr} + +\vfill +{\footnotesize\color{muted} Version 0.3 \quad\textbullet\quad AGPL-3.0-or-later \quad\textbullet\quad For inspecting devices and networks you are authorised to inspect. Nothing else.} +\end{titlepage} + +% ======================= HOW TO READ ======================= +\section*{How to read this (30 seconds)} +\addcontentsline{toc}{section}{How to read this} + +\begin{tabular}{@{}p{3.2cm}p{12.5cm}@{}} +\ding{220}\ \textbf{TL;DR} & The one-line version. If you read nothing else, read these.\\ +\ding{43}\ \textbf{Heads-up} & Something that will bite you if you ignore it.\\ +\ding{42}\ \textbf{Pro tip} & A shortcut or nicety. Safe to skip.\\ +\ding{45}\ \textbf{Honest truth} & Where PiLens tells you what it \emph{cannot} do. Read these --- they save hours of confusion.\\ +\end{tabular} + +\vspace{1ex} +\begin{tip} +In a hurry? Jump straight to \textbf{\S1 The ten-minute start}, connect a phone, +and open \textbf{Live Capture}. Come back for the rest when you are curious. +\end{tip} + +{\small\color{muted}\tableofcontents} +\newpage + +% ======================= 1. QUICK START ======================= +\section{The ten-minute start} +\begin{tldr} +Flash the SD card \textgreater\ plug in \textbf{Ethernet} \textgreater\ run one installer +\textgreater\ open the web page \textgreater\ finish the wizard \textgreater\ connect a +test device to the new Wi-Fi \textgreater\ watch its traffic. That is the whole dance. +\end{tldr} + +\subsection{What you need} +\begin{itemize}[leftmargin=1.4em, itemsep=1pt] + \item A Raspberry Pi \textbf{4 or 5} running Raspberry Pi OS Lite (64-bit). + \item An \textbf{Ethernet} cable to your normal network. \mn{You manage PiLens over the wire, never over its own Wi-Fi.} + \item The Pi's built-in Wi-Fi (this becomes the test network). + \item A phone or laptop you own, to be the ``device under test''. +\end{itemize} + +\subsection{The steps} +\begin{enumerate}[leftmargin=1.6em, itemsep=2pt] + \item Copy the release onto the Pi and run \key{sudo ./install.sh}. It explains + everything before it touches anything, backs up your config, and \textbf{rolls + back if anything fails}. + \item It prints three things: a \textbf{web address}, a \textbf{certificate + fingerprint}, and a one-time \textbf{setup token}. Keep them. + \item Open the web address from a computer on your wired network. Your browser + will warn about the certificate --- that is expected (see \S2). Check the + fingerprint matches, then continue. + \item Work through the \textbf{setup wizard}: create your login, choose the + management network, name the Wi-Fi, pick a password, done. + \item Connect your test device to the new Wi-Fi network. + \item Open \textbf{Live Capture}, press \textbf{Start capture} on the Wi-Fi + interface, and watch the packets roll in. +\end{enumerate} + +\begin{heads} +The moment you create the access point, the Pi's own Wi-Fi \emph{becomes} that +access point. If you were connected to the Pi over Wi-Fi you will be kicked off. +\textbf{Always manage it over Ethernet.} +\end{heads} + +\screen{overview}{The Overview dashboard --- appliance status, forwarding state, clients, and a live glance at everything at once.} + +% ======================= 2. WHAT IT IS ======================= +\section{What PiLens is (and isn't)} +\begin{tldr} +PiLens is a \textbf{router with X-ray vision}. Devices on its Wi-Fi go through it +to reach the internet, and it records every packet. It shows you \emph{metadata and +packets} --- it does \textbf{not} decrypt HTTPS. +\end{tldr} + +PiLens sits between a test device and the internet. The device joins its Wi-Fi, +gets an address, and routes out through the Pi's Ethernet. Because \emph{every} +packet crosses the Pi, PiLens can capture and analyse all of it --- addresses, +ports, DNS, TLS handshakes, timing, the lot. + +\begin{truth} +\textbf{Encrypted means encrypted.} HTTPS, QUIC, VPNs and encrypted DNS stay +scrambled. PiLens shows you \emph{who} talked to \emph{whom}, \emph{when}, over +\emph{what} --- the server name, the TLS version, the certificate when it is sent +in the clear --- but \textbf{not the contents}. That is TLS doing its job, not +PiLens failing. Decryption needs a separate, explicit step on each device (a +future feature), and \textbf{a trusted certificate can never be pushed down the +network} --- the device's owner must install it by hand. Anything that claimed +otherwise would be lying to you. +\end{truth} + +\subsection{The two padlocks} +\begin{itemize}[leftmargin=1.4em, itemsep=1pt] + \item \textbf{The management padlock} is the HTTPS on the web interface. It is + self-signed, so your browser warns once. Check the fingerprint the installer + printed, trust it, move on. + \item \textbf{The interception padlock} (decrypting a device's HTTPS) is a + \emph{separate}, opt-in, per-device thing that is not part of this version. +\end{itemize} + +% ======================= 3. THE SCREENS ======================= +\section{A tour of the screens} +\begin{tldr} +Left-hand menu, top to bottom: \textbf{Overview} (glance), \textbf{Live Capture} +(the main event), \textbf{Conversations / DNS / TLS / Statistics / Findings} +(ways to slice what you caught), then \textbf{Library, Devices, Network, Health, +Settings} (housekeeping). +\end{tldr} + +\subsection{Live Capture --- the main event} +\begin{tldr} +Three stacked panes, exactly like Wireshark: the \textbf{packet list} up top, the +\textbf{protocol tree} in the middle, the \textbf{raw bytes} at the bottom. Click a +packet; the other two follow. +\end{tldr} + +\screen{analyser}{Live Capture. Packet list, protocol tree and a hex/ASCII inspector. The status bar along the bottom keeps you honest.} + +\textbf{The packet list} streams live and is colour-coded by protocol. It stays +smooth even on a busy network because only the visible rows are drawn. + +\textbf{The protocol tree} shows the selected packet decoded field by field, by +TShark (the Wireshark engine). Click a field and the matching bytes light up +below --- but \emph{only} when PiLens can prove those bytes really are that field. +It will never make up a highlight. + +\textbf{The byte pane} is a proper hex inspector: offsets, hex, ASCII, search, +copy-as-hex or text. + +\begin{tip} +The bottom status bar separates things people usually confuse: \textbf{Captured} +(packets stored), \textbf{Ingest lag} (stored but not yet summarised), +\textbf{UI updates dropped} (your browser fell behind --- harmless), and +\textbf{Capture drops} (the appliance genuinely missed packets --- not harmless). +A dropped \emph{screen update} is not a dropped \emph{packet}. +\end{tip} + +\begin{heads} +\textbf{Pause view} and \textbf{Pause capture} are different buttons. ``Pause view'' +just freezes the scrolling so you can read; capture keeps running. ``Pause +capture'' actually stops storing packets. ``Stop'' ends the session for good. +\end{heads} + +\subsection{Filters --- find the needle} +\begin{tldr} +Type a \textbf{Wireshark display filter} in the bar (e.g. \key{dns}, +\key{tcp.port == 443}, \key{ip.addr == 192.168.50.20}) and press Enter. It is +evaluated by TShark, so real Wireshark syntax works. +\end{tldr} + +The box turns green when the filter is valid, red when it isn't, with the reason. +Press \key{/} anywhere to jump to it. Right-click a packet or a field to build a +filter without typing. + +\subsection{Conversations \& Follow TCP Stream} +\begin{tldr} +\textbf{Conversations} groups traffic by endpoint pair with bytes each way. Hit +\textbf{Follow} on a TCP row to reassemble the whole back-and-forth. +\end{tldr} + +\screen{conversations}{Conversations --- who spoke to whom, how much each way, sortable, with one-click Follow and filtering.} + +\begin{truth} +``Follow'' shows the \emph{transport stream} --- the bytes as they crossed the wire. +For an HTTPS connection that is \textbf{ciphertext}, and PiLens labels it as such. +It is honest about the difference between reassembled packets and decrypted +content. +\end{truth} + +\subsection{DNS Explorer} +\begin{tldr} +Every cleartext name lookup: who asked, for what, what came back, and whether it +failed. Group by name, or filter to errors only. +\end{tldr} + +\screen{dns}{DNS Explorer, grouped by name --- queries, answers, record types and result codes.} + +\begin{heads} +If a device uses \textbf{encrypted DNS} (DoH/DoT), its lookups are inside TLS and +\emph{won't} appear here --- they look like HTTPS to a resolver. PiLens never +pretends encrypted DNS is visible. +\end{heads} + +\subsection{TLS Explorer} +\begin{tldr} +Every TLS/QUIC handshake: server name (SNI), negotiated version, cipher, ALPN, +and any certificate sent in the clear. Metadata only --- marked ``encrypted +pass-through'' on every row. +\end{tldr} + +\screen{tls}{TLS Explorer. Real server names and versions, honestly labelled. The certificate tab is usually empty on modern servers --- TLS 1.3 encrypts the certificate.} + +\begin{tip} +The version column reports the \emph{true negotiated} version, not the +compatibility value on the wire. QUIC shows as ``TLS 1.3 (QUIC)''. If you ever see +everything claiming ``TLS 1.2'', that's a bug in a lesser tool --- here it's read +from the handshake properly. +\end{tip} + +\subsection{Statistics} +\begin{tldr} +The shape of the capture: traffic over time, protocol mix, top talkers, packet +sizes. Toggle bytes vs packets. Every number is a real count, defined in the +panel on the right. +\end{tldr} + +\screen{statistics}{Statistics --- a traffic histogram, protocol hierarchy, top talkers and size distribution, with a definitions panel so nothing is a mystery.} + +\subsection{Findings} +\begin{tldr} +Observable hiccups --- retransmissions, resets, SERVFAILs, NXDOMAINs, dropped +packets --- each with evidence you can click straight to. +\end{tldr} + +\screen{findings}{Findings. Each card is a fact with example packets, a reproducing filter, and a note on benign causes.} + +\begin{truth} +Findings is a \textbf{magnifying glass, not a smoke alarm}. A finding means a +pattern was \emph{observed}, not that something is wrong or malicious. Each card +spells out the innocent explanations. PiLens will never tell you ``you've been +hacked'' from a retransmission. +\end{truth} + +\subsection{The housekeeping screens} +\begin{description}[leftmargin=0pt, style=nextline, itemsep=3pt] + \item[\textcolor{amber}{Capture Library}] Every saved session. Rename, tag, + add notes, mark \emph{preserved} (never auto-deleted), export, or check + integrity (SHA-256 per file + a manifest). + \item[\textcolor{amber}{Devices}] Who has been on the Wi-Fi. Label them, add + notes, jump to their packets. \mn{Randomised MACs show a badge; PiLens never pretends they identify hardware.} + \item[\textcolor{amber}{Network Settings}] Change the access point, DHCP, DNS, + forwarding and management access. Applied as one transaction with automatic + rollback. + \item[\textcolor{amber}{System Health}] CPU, memory, temperature, services, + storage, interfaces --- each with what was actually measured. + \item[\textcolor{amber}{Settings}] Theme, density, password, two-factor, + storage limits, colour rules, and the audit log. +\end{description} + +\screen{health}{System Health --- every service reports what it actually did, not just whether a process exists.} + +% ======================= 4. RECIPES ======================= +\section{Common tasks, as recipes} +\begin{tldr} +Short how-tos for the things you'll actually do. Find the one you need, ignore +the rest. +\end{tldr} + +\subsection{``See everything one device does''} +\begin{enumerate}[leftmargin=1.6em, itemsep=1pt] + \item \textbf{Devices} \textgreater\ find it \textgreater\ \textbf{Packets}. (Or + label it first so you recognise it.) + \item You land in Live Capture filtered to that device. Add \key{\&\& dns} or + \key{\&\& tls} to narrow further. +\end{enumerate} + +\subsection{``Save this capture as evidence''} +\begin{enumerate}[leftmargin=1.6em, itemsep=1pt] + \item Stop the capture. \textbf{Library} \textgreater\ the session \textgreater\ + \textbf{Edit} \textgreater\ tick \textbf{Preserved}, add notes. + \item \textbf{Integrity} \textgreater\ \textbf{Verify hashes}, then \textbf{Download + manifest}. The original \texttt{.pcapng} opens in desktop Wireshark too. +\end{enumerate} + +\subsection{``Analyse a capture from somewhere else''} +\begin{enumerate}[leftmargin=1.6em, itemsep=1pt] + \item \textbf{Library} \textgreater\ \textbf{Import pcap/pcapng}. It indexes in + the background; the file is kept exactly as you uploaded it. +\end{enumerate} + +\subsection{``Stop a device reaching the internet but still watch it''} +\begin{enumerate}[leftmargin=1.6em, itemsep=1pt] + \item \textbf{Network Settings} \textgreater\ \textbf{Forwarding} \textgreater\ + \textbf{No forwarding} \textgreater\ apply. Clients still get an address and + DNS, so you can see what they \emph{try} to reach. +\end{enumerate} + +% ======================= 5. TROUBLE ======================= +\section{When something's off} +\begin{tldr} +Run \key{sudo /opt/pilens/diagnose.sh} first (it redacts secrets). Then the table. +\end{tldr} + +\begin{tabular}{@{}p{5.2cm}p{10.5cm}@{}} +\textbf{Symptom} & \textbf{Try this}\\[-0.5ex] +\multicolumn{2}{@{}l}{\textcolor{line}{\rule{\linewidth}{0.4pt}}}\\ +Browser cert warning & Expected. Match the fingerprint, trust it.\\ +Can't log in & \key{sudo -u pilens /opt/pilens/bin/pilens reset-password -user admin}\\ +Locked out by the subnet & On the console: \key{sudo nft delete table inet pilens}, fix it in the UI, re-apply.\\ +No internet for clients & Check the Overview \textbf{Forwarding} card and your Ethernet uplink.\\ +AP won't start & Try WPA2 / 2.4\,GHz / a legal channel. Failed applies roll back by themselves.\\ +Capture shows drops & Use a capture filter; record to an SSD, not the SD card.\\ +Everything's wonky & \key{sudo /opt/pilens/repair.sh} fixes permissions and restarts services.\\ +\end{tabular} + +\begin{heads} +If a network change ever strands you, every apply keeps a backup and rolls back +on failure, and the last-good firewall is restored at boot before the Wi-Fi comes +up. You are rarely more than a reboot from a working appliance. +\end{heads} + +% ======================= 6. CHEAT SHEET ======================= +\section{Keyboard cheat sheet} +\begin{center} +\begin{tabular}{@{}ll@{\hspace{10mm}}ll@{}} +\key{/} & focus the filter & \key{Space} & pause / resume the view\\ +\key{$\uparrow$}\,\key{$\downarrow$} & move through packets & \key{f} & follow newest packet\\ +\key{PgUp}\,\key{PgDn} & jump a page & \key{Shift+F} & focus mode (hide chrome)\\ +\key{$\leftarrow$}\,\key{$\rightarrow$} & collapse / expand tree & \key{Alt+1..7} & jump between pages\\ +\key{Home}\,\key{End} & first / last packet & right-click & build a filter\\ +\end{tabular} +\end{center} + +\vspace{2ex} +\begin{truth} +\textbf{The one rule that matters:} only point PiLens at devices and networks you +own or are explicitly allowed to inspect. It has no hidden, covert or +impersonation features, on purpose. Captured traffic can hold passwords and +private messages --- treat your captures like the sensitive data they are, and +delete what you don't need. Access and exports are logged. +\end{truth} + +\vfill +\begin{center}\small\color{muted} +PiLens --- built to be fast, honest, and genuinely useful.\\ +Full docs live in the repository's \texttt{docs/} folder. +\end{center} + +\end{document}