intercept: a privileged container, intercept/intercept database credentials, and a receiver with no scope check
iNTERCEPT, a free and open-source platform that unites the best signal intelligence tools into a single, accessible interface.
At a glance
- What is it?
- A web interface that bundles software-defined radio tooling for pager, ADS-B, AIS, satellite and drone detection. Its install profiles include an offensive RF toolchain and its Docker path runs privileged with the USB bus mapped in, neither of which the documentation frames as a decision you are making.
- Who is it for?
- Judgment, and the restrictions come first: do not point this at a network, a spectrum or a device that is not yours or that you have written permission to observe. There is no authorization check, no dry-run mode and no scope limit anywhere in this project, because it is a receiver rather than a targeter, and a receiver in range collects from everyone in range.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Install profile 4 pulls in an offensive RF toolchain with no scope check
Six install profiles are offered, and one of them is worth reading before you type anything. Profile 4, called RF Security, installs aircrack-ng, BlueZ, hcxtools, Ubertooth and SoapySDR. Those are the standard components of a wireless assessment kit, and they are offered here beside three profiles of passive reception with no commentary on who is authorised to receive what.
Two features consume them. WiFi Scanning is described as monitor mode reconnaissance via aircrack-ng, and Bluetooth scanning covers device discovery and tracker detection with Ubertooth support, with a separate mode that locates a Bluetooth device from its signal trail and raises proximity alerts.
What the project does not ship is any of the controls the operational-security literature treats as standard: no per-target authorisation, no dry run, no kill switch, no record of what was collected. To be fair about the other side of it, the dependency annotations show scapy pulled in for deauthentication attack detection as a counter-surveillance input, not for launching one, and TSCM is defined as baseline comparison and threat detection. Reception is also what most of the other 25 features do. None of that makes a kit that enumerates every device in radio range a tool to point at a shared building, and the documentation offers no reminder of that.
The container runs privileged with the entire USB bus mapped in
The Docker note says privileged mode is required for USB SDR access, and devices are passed through from `/dev/bus/usb`. The Compose file does both, unconditionally:
# Privileged mode required for USB SDR device access
privileged: true
# USB device mapping for all USB devices
devices:
- /dev/bus/usb:/dev/bus/usbPrivileged mode plus a whole-bus device mapping is close to giving the container the host's authority. It is the standard way to reach an RTL-SDR without writing udev rules, and it is also a container that can address every USB device attached to the machine rather than the dongle you meant.
Two smaller details in the same file. `pull_policy: never` means Compose will never fetch a newer image, so the image you run is whatever you last built, and the documented ADS-B command omits the `--build` flag the basic command carries. The volume list mounts four subdirectories individually rather than the parent, with a comment explaining that a wider mount would shadow Python modules living under `/app/data`.
The documented database credentials are intercept and intercept
The ADS-B history feature is genuinely optional, and it is the only part of the system that needs a server. The variables given to enable it are:
INTERCEPT_ADSB_HISTORY_ENABLED=true
INTERCEPT_ADSB_DB_HOST=adsb_db
INTERCEPT_ADSB_DB_PORT=5432
INTERCEPT_ADSB_DB_NAME=intercept_adsb
INTERCEPT_ADSB_DB_USER=intercept
INTERCEPT_ADSB_DB_PASSWORD=interceptA username and a password that are the same word, published in the project documentation as the settings to copy. The local path is safer in one respect and worse in another: running the setup flag installs PostgreSQL if needed and creates the database, user and tables itself, writing the connection settings into your `.env`.
The other two defaults in that file are quieter. `INTERCEPT_HOST` is set to all interfaces inside the container, and the map's default centre is given as a latitude of 51.5074 and a longitude of -0.1278, which is central London, so a fresh install draws its satellite pass prediction and aircraft view over a city you are probably not in.
Setting those coordinates before you generate any passes is a two-line change and the difference between a useful map and a confusing one.
setup.sh then sudo ./start.sh, and the dev path asks for root too
The quick start is four lines:
git clone https://github.com/smittix/intercept.git
cd intercept
./setup.sh # Interactive menu (first run launches setup wizard)
sudo ./start.shThe server runs as root, and so does the alternative offered for development, which invokes the interpreter directly from the virtual environment under sudo. Neither line explains why root is needed; the SDR device access is the obvious reason and the obvious alternative is a udev rule.
There is a good piece of engineering hiding in the note beside those commands. `start.sh` detects gunicorn and gevent and runs a production server with cooperative greenlets, which is what lets several SSE and WebSocket clients be served without blocking, and it falls back to the Flask development server if gunicorn is not installed. The fallback is silent, and the health check, which otherwise covers installed tools, SDR devices, port availability, permissions, the virtual environment, `.env` configuration and PostgreSQL connectivity, does not list which server it found. So a deployment can be running the development server and pass its own check.
There is a dedicated health check flag, `--health-check`, and a matching menu entry, which is more than most projects of this size offer.
Every radio tool in the image is compiled from the tip of its default branch
The Dockerfile is a two-stage build: a builder on `python:3.11-slim` that installs around twenty-eight development libraries and compiles the decoders from source, and a runtime that keeps only what is needed. The dump1090 step reads:
RUN cd /tmp \
&& git clone --depth 1 https://github.com/flightaware/dump1090.git \
&& cd dump1090 \
&& make BLADERF=no RTLSDR=yesThree things follow from those five lines. The clone is shallow with no tag and no commit reference, so the code compiled today is the tip of the default branch and the code compiled next month is whatever that branch has become. The build strips the warnings-as-errors flag from the upstream Makefile before compiling, which means the ADS-B decoder in the image is not stock dump1090 but a locally patched build, installed under the name `dump1090-fa` with a symlink called `dump1090` to keep it distinguishable from a system install. And every other tool is cloned the same way, AIS-catcher among them.
That is fine for a field kit you rebuild often and wrong for anything whose output has to be reproducible or auditable later. Nothing in the tree pins a version for any of them.
The manifest says 2.32.0, the tags say 2.33.77, and two releases landed four hours apart
The project metadata and the release history disagree by a full minor version. The manifest declares version 2.32.0, while the newest tag is v2.33.77.
There is also a `semver.py` at the repository root, so semantic version handling is implemented in the tree while the version number itself is a static string in the manifest. Whichever the code prefers, the number a package index would read is the one that has fallen behind.
The release naming is unusual and worth knowing about. Tag and name are the same number, and the name carries a feature description rather than a changelog: v2.33.77 is AIS on HackRF opening the selected device, v2.33.76 is the updater finding new versions alongside the same feature, and v2.33.47 is one-click WiFi capture. The two releases on 30 September 2026 are four hours apart, which says something about how this ships.
For an operator, the practical consequence is that the tag is a better record of what is running than the manifest is, so record the tag at deployment time.
Two dependency manifests disagree about the Python floor and what is core
Dependencies are declared twice. The manifest gives a tidy list with a Python floor of 3.9 and classifiers stopping at 3.12. `requirements.txt` gives a second list where nearly every entry carries a comment explaining what it is for, and those comments are the more useful document.
Two of them contradict the manifest. One line reads that meshcore 2.3.0 and later are required for a specific event type and that it needs Python 3.10 or newer, against a declared floor of 3.9. Another shows psycopg2 as optional, which is correct, but that package does not appear in the manifest at all, so the declared package dependencies are not sufficient for the ADS-B history feature.
Two things are declared core that are worth crediting. `flask-limiter` and `flask-wtf` are in the main dependency list, so rate limiting and CSRF protection are built in rather than left to a reverse proxy, which matters for something that will be exposed to a network. And there are two assistant instruction files at the root, `CLAUDE.md` and `AGENTS.md`, alongside a `plans/` directory, so the project publishes its working notes as well as its code.
Editorial conclusion
Judgment, and the restrictions come first: do not point this at a network, a spectrum or a device that is not yours or that you have written permission to observe. There is no authorization check, no dry-run mode and no scope limit anywhere in this project, because it is a receiver rather than a targeter, and a receiver in range collects from everyone in range. That includes the Bluetooth and WiFi discovery features and the drone detection mode, whose 433 and 868 MHz and 2.4 and 5.8 GHz passes will log neighbouring devices by default. For what it is legitimately good at, read it as a front end: it saves you wiring twenty command-line decoders to a web map, the Postgres history store is genuinely optional, the health check is unusually broad, and rate limiting and CSRF are core dependencies rather than afterthoughts. Before you run it, know four things. The container needs privileged mode and the whole USB bus. The documented database user and password are both `intercept`. The server starts as root and silently falls back to the Flask development server when gunicorn is missing. And every SDR tool in the image is compiled from the tip of its default branch, so the image is not reproducible and the ADS-B decoder inside it is a patched build.
Frequently asked questions
What is intercept and what does it run on?
intercept is a web interface for software-defined radio tools, covering pager decoding, 433MHz sensors, ADS-B aircraft, AIS vessels, ACARS, satellite imagery, APRS, utility meters, WiFi and Bluetooth scanning, and drone detection. It is a Flask application serving server-rendered templates, running on Linux and macOS with Python 3.9 and later.
How do I install intercept?
Clone the repository, run `./setup.sh` for a first-run wizard or an interactive menu, then `sudo ./start.sh`. The wizard offers six install profiles and the script also takes flags such as `--non-interactive`, `--profile=core,weather`, `--health-check` and `--postgres-setup`. For containers, `docker compose --profile basic up -d --build` is the documented route, and `build-multiarch.sh` covers amd64 and arm64.
Does intercept need a database, and is it safe to expose?
A database is only needed for the optional ADS-B history feature, which persists to PostgreSQL and is configured with the `INTERCEPT_ADSB_DB_*` variables, the documented values being user `intercept` and password `intercept`. The web stack includes rate limiting and CSRF dependencies as core, but the Docker path runs the container in privileged mode with `/dev/bus/usb` mapped in, which is the part to weigh before putting it on a network.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/smittix-intercept)