mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Restructure README into a getting-started-first layout
Reorganize into What is OOTT?, Getting started (Docker + mobile apps), Deeper dive (Nix flake, config options, HTTPS), and Things to keep in mind (storage, privileges/ports). Inline a minimal oott.toml and the compose file in the Docker quickstart, and document the missing web_server.* options. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8df21e41b1
commit
5872fcf8db
@@ -3,70 +3,97 @@ Easy to setup and use network device discovery and alert system
|
||||
|
||||
Licensed under AGPL v3. See [LICENSE](LICENSE).
|
||||
|
||||
* [What is it?](#what-is-it)
|
||||
* [Installation & configuration](#installation-configuration)
|
||||
* [Install OOTT using Docker](#install-oott-using-docker)
|
||||
* [Install OOTT using NixOS flakes](#install-oott-using-nixos-flakes)
|
||||
* [Configuration options](#configuration-options)
|
||||
* [Network ports](#network-ports)
|
||||
* [Connecting the app to the backend](#connecting-the-app-to-the-backend)
|
||||
* [Storage considerations](#storage-considerations)
|
||||
* [What is OOTT?](#what-is-oott)
|
||||
* [Getting started](#getting-started)
|
||||
* [Simple installation with Docker](#simple-installation-with-docker)
|
||||
* [Using the mobile apps](#using-the-mobile-apps)
|
||||
* [Deeper dive](#deeper-dive)
|
||||
* [Installing OOTT as a Nix flake](#installing-oott-as-a-nix-flake)
|
||||
* [Full list of configuration options](#full-list-of-configuration-options)
|
||||
* [Using HTTPS and domain names with the mobile apps](#using-https-and-domain-names-with-the-mobile-apps)
|
||||
* [Things to keep in mind](#things-to-keep-in-mind)
|
||||
* [Storage considerations](#storage-considerations)
|
||||
* [Privileges and network ports](#privileges-and-network-ports)
|
||||
|
||||
## What is it?
|
||||
OOTT provides a service that runs behind the scenes and monitors your local network. It's most relevant features are:
|
||||
* Scan network regularly using ARP probes
|
||||
* Notify when a new device is found
|
||||
* Notify when a device changed it's IP address or network interface vendor (based on its MAC address)
|
||||
* Notify when a device came back online after a configurable period of being offline
|
||||
## What is OOTT?
|
||||
OOTT runs behind the scenes and monitors your local network, notifying you when something changes. Its most relevant features are:
|
||||
* Scans the network regularly using ARP probes, with additional mDNS, SSDP, DHCP and (optionally) SNMP discovery
|
||||
* Notifies you when a new device is found
|
||||
* Notifies you when a device changes its IP address or network interface vendor (based on its MAC address)
|
||||
* Notifies you when a device comes back online after a configurable period of being offline
|
||||
|
||||
OOTT can be installed as a NixOS module (using flakes) or as a docker image, see below for instructions on how to deploy and configure it.
|
||||
You configure and browse the data it collects through a companion app available on the web, desktop, iOS and Android.
|
||||
|
||||
# Installation & configuration
|
||||
## Install OOTT using Docker
|
||||
OOTT is available on Docker Hub as a pre-built image [here](https://hub.docker.com/repository/docker/rzuasti/oott/general).
|
||||
To use it I recommend using docker compose, you can find a sample compose file [here](https://github.com/rzuasti/oott/blob/main/examples/docker-compose.yml).
|
||||
## Getting started
|
||||
|
||||
If you use our Docker Hub image and docker compose the steps you have to follow are:
|
||||
1. Create a folder structure in your host
|
||||
2. Create the configuration file
|
||||
3. Create the `docker-compose.yml` file
|
||||
4. Start the service
|
||||
### Simple installation with Docker
|
||||
This is the quickest way to get OOTT running. OOTT is published on Docker Hub as a pre-built image, [`rzuasti/oott`](https://hub.docker.com/repository/docker/rzuasti/oott/general), and the steps below get you to a running service with the smallest possible configuration.
|
||||
|
||||
#### 1. Create a folder structure in your host
|
||||
You need a place to store the docker-compose.yml file, the OOTT configuration file and the database that will store the application state:
|
||||
You need somewhere on the host to store the configuration file and the database (OOTT uses SQLite, so the database folder should be on local — not remote — storage):
|
||||
```bash
|
||||
mkdir -p /docker/oott/config
|
||||
mkdir -p /docker/oott/db
|
||||
```
|
||||
The user that runs Docker needs read access to the config folder and read/write access to the db folder. You can use any paths you like as long as you keep the volume mappings below in sync.
|
||||
|
||||
You can choose whatever structure or locations you choose. Note that:
|
||||
* The user that runs the docker process must have read access to the config folder and read/write access to the db folder
|
||||
* OOTT uses SQLite and it doesn't really like remote access so the `db/` folder should be local to your docker host
|
||||
Create a minimal `oott.toml` at `/docker/oott/config/oott.toml`:
|
||||
```toml
|
||||
[database]
|
||||
path = "/db/oott.db" # Must be "/db/oott.db" for the Docker image (the mounted volume)
|
||||
|
||||
#### 2. Create the configuration file
|
||||
Create the `oott.toml` file (for example at `/docker/oott/config/oott.toml` in your docker host) and populate it with your preferences. You can use the [provided example](https://github.com/rzuasti/oott/blob/main/examples/sample_oott.toml) as baseline.
|
||||
[web_server]
|
||||
api_key = "CHANGE_ME" # API key the app uses to talk to the backend — change this!
|
||||
|
||||
[notifications]
|
||||
method = "pushover" # Use "none" to only log notifications instead of sending them
|
||||
|
||||
[notifications.pushover]
|
||||
token = "" # Your Pushover application token
|
||||
user_key = "" # Your Pushover user key
|
||||
```
|
||||
This is enough to start the service; every other option falls back to a sensible default. See [Full list of configuration options](#full-list-of-configuration-options) for everything you can tune, and the [full sample config](https://github.com/rzuasti/oott/blob/main/examples/sample_oott.toml) for a commented, all-options file.
|
||||
|
||||
Create a `docker-compose.yml` at `/docker/oott/docker-compose.yml`:
|
||||
```yaml
|
||||
name: oott
|
||||
|
||||
services:
|
||||
oott:
|
||||
image: rzuasti/oott:latest
|
||||
volumes:
|
||||
- /docker/oott/config:/config
|
||||
- /docker/oott/db:/db
|
||||
network_mode: host
|
||||
```
|
||||
Host networking is required so OOTT can see the multicast and broadcast traffic its scanners rely on (see [Privileges and network ports](#privileges-and-network-ports)).
|
||||
|
||||
Start the service from where you placed the compose file:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
That's it. Check that everything is running with `docker ps`, and follow the logs with `docker logs CONTAINER_ID -f`.
|
||||
|
||||
### Using the mobile apps
|
||||
The OOTT app (web, desktop, iOS and Android) talks to the backend exclusively over its REST API on port `3000`. Open the app's settings and point it at your backend's address on the local network over HTTP, for example `http://192.168.1.50:3000`, using the API key you set in `web_server.api_key`.
|
||||
|
||||
This is the simplest setup and needs no extra infrastructure, but it only works while your phone is on the same local network as the backend.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `database.path` option must always be set to `"/db/oott.db"` when using our docker image.
|
||||
> On iOS, the direct HTTP connection only works when you use the backend's **private-range IP address** (`192.168.x.x`, `10.x.x.x`, `172.16–31.x.x`) or a `*.local` (mDNS/Bonjour) name. A custom internal domain (e.g. `oott.mylan.com`) served over plain HTTP is blocked by iOS App Transport Security, even when it resolves to a private IP. Use the IP address directly, or set up HTTPS.
|
||||
|
||||
#### 3. Create the `docker-compose.yml` file
|
||||
Create a docker compose file to run the container (for example at `/docker/oott/docker-compose.yml`), you can use the [provided example](https://github.com/rzuasti/oott/blob/main/examples/docker-compose.yml) as is or adjust it to match your environment.
|
||||
> [!NOTE]
|
||||
> The first time the iOS app reaches your backend on the local network, iOS shows a one-time "find devices on your local network" prompt. This is expected — tap Allow to continue.
|
||||
|
||||
#### 4. Start the service
|
||||
Run `docker compose up -d` from where you placed your `docker-compose.yml` file and verify everything is running smoothly.
|
||||
To use a domain name, or to reach the backend from outside the local network, set up TLS as described in [Using HTTPS and domain names with the mobile apps](#using-https-and-domain-names-with-the-mobile-apps).
|
||||
|
||||
You can check the applications log using `docker logs CONTAINER_ID -f`, to see the active containers you can use `docker ps`.
|
||||
## Deeper dive
|
||||
|
||||
## Install OOTT using NixOS flakes
|
||||
OOTT comes with a pre-built NixOS flake that you can integrate in your configuration. If you are not using flakes, well you should. If you still won't I guess you can use the [flake code](https://github.com/rzuasti/oott/tree/main/nix) as a baseline and write your own derivation.
|
||||
### Installing OOTT as a Nix flake
|
||||
OOTT comes with a pre-built NixOS flake that you can integrate into your configuration. If you are not using flakes you can use the [flake code](https://github.com/rzuasti/oott/tree/main/nix) as a baseline and write your own derivation.
|
||||
|
||||
To integrate the OOTT flake into your config in most cases you should do the following:
|
||||
1. Add OOTT to your inputs
|
||||
2. Add the OOTT module and overlay
|
||||
3. Enable and setup OOTT in your system configuration
|
||||
To integrate the OOTT flake into your config you generally do three things: add OOTT to your inputs, add its module and overlay, then enable and configure the service.
|
||||
|
||||
#### 1. Add OOTT to your inputs
|
||||
In your `flake.nix` inputs section add OOTT:
|
||||
**1. Add OOTT to your inputs** — in your `flake.nix` inputs section:
|
||||
```nix
|
||||
inputs = {
|
||||
...
|
||||
@@ -78,8 +105,7 @@ inputs = {
|
||||
};
|
||||
```
|
||||
|
||||
#### 2. Add the OOTT module and overlay
|
||||
In your `flake.nix` modules section add the OOTT module and overlay:
|
||||
**2. Add the OOTT module and overlay** — in your `flake.nix` modules section:
|
||||
```nix
|
||||
...
|
||||
modules = [
|
||||
@@ -94,8 +120,8 @@ modules = [
|
||||
];
|
||||
...
|
||||
```
|
||||
#### 3. Enable and setup OOTT in your system configuration
|
||||
Finally, in your `configuration.nix` (or in an import file) enable and configure OOTT (note that you should embed the following sections in your file appropriately):
|
||||
|
||||
**3. Enable and configure OOTT** — in your `configuration.nix` (or an imported file). Set all options through the service definition; the keys mirror the config file options listed [below](#full-list-of-configuration-options):
|
||||
```nix
|
||||
{
|
||||
pkgs,
|
||||
@@ -124,19 +150,17 @@ Finally, in your `configuration.nix` (or in an import file) enable and configure
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration options
|
||||
The system configuration is centralized in a single config file, you can use TOML, JSON or YAML to write it.
|
||||
### Full list of configuration options
|
||||
The system configuration lives in a single config file, which you can write in TOML, JSON or YAML. With Docker, TOML is recommended — the [full sample config](https://github.com/rzuasti/oott/blob/main/examples/sample_oott.toml) lists every supported option with comments. With the Nix flake, set the same options through the service definition (see above).
|
||||
|
||||
If you are using the provided NixOS flake you should set all the options via nix in the service definition (see above).
|
||||
|
||||
If you are using Docker I recommend writing the config using TOML, [here](https://github.com/rzuasti/oott/blob/main/examples/sample_oott.toml) is a sample with all the supported options.
|
||||
|
||||
### Options list
|
||||
|Option|Sample value|Description|
|
||||
|------|-------------|-----------|
|
||||
|`database.path`|`/var/lib/oott.db`|Location of the system database|
|
||||
|`database.path`|`/var/lib/oott.db`|Location of the system database. Must be `/db/oott.db` when using the Docker image.|
|
||||
|`networking.interface`|`eno1`|Network interface to use for scans. Optional — if not set, the first non-loopback connected interface is used automatically.|
|
||||
|`log.level`|`info`|Log level to use (trace, debug, info, warn, error)|
|
||||
|`web_server.ip_address`|`0.0.0.0`|Address the API and web UI bind to. Use `0.0.0.0` to bind all interfaces.|
|
||||
|`web_server.port`|`3000`|Port the API and web UI listen on.|
|
||||
|`web_server.api_key`|`CHANGE_ME`|API key the app must present to use the backend. Always change this from the default.|
|
||||
|`arp_scanner.enabled`|`true`|Whether to run the ARP scanner. The whole `[arp_scanner]` section is optional; omit it to use the defaults below. Defaults to enabled; set to `false` to turn it off.|
|
||||
|`arp_scanner.wait_between_scans`|`30m`|Time to wait between each network scan (you can express it in seconds, minutes, hours, etc. as a suffix - for example: 30s, 10m, 1h)|
|
||||
|`arp_scanner.sender_timeout`|`1m`|If the ARP sender process takes longer than this it will be stopped (for a class C network - 254 IPs - it should take less than a minute)|
|
||||
@@ -158,47 +182,17 @@ If you are using Docker I recommend writing the config using TOML, [here](https:
|
||||
|`retention.window`|`365d`|How long to retain device events and notifications. Records older than this are purged daily. Accepts duration strings (e.g. `90d`, `1y`, `6m`). Defaults to one year.|
|
||||
|`device_events.deduplication_window`|`1m`|Suppress duplicate device events: if the same scanner sees the same device (same MAC and IPv4) again within this window, only one event is recorded. Accepts duration strings (e.g. `30s`, `1m`, `5m`). Defaults to one minute.|
|
||||
|
||||
## Network ports
|
||||
OOTT binds to the following ports on the host where it runs:
|
||||
### Using HTTPS and domain names with the mobile apps
|
||||
To reach the backend through a domain name, or from outside the local network, put it behind a reverse proxy (nginx, Caddy, Traefik, …) that terminates TLS with a **valid certificate from a trusted CA** (for example [Let's Encrypt](https://letsencrypt.org/)), then point the app at the HTTPS URL (e.g. `https://oott.example.com`). This works on every platform — including iOS — with no further configuration.
|
||||
|
||||
|Port|Protocol|Configurable|Used by|
|
||||
|----|--------|------------|-------|
|
||||
|`3000`|TCP|Yes (`web_server.port`)|REST API and web UI|
|
||||
|`5353`|UDP (multicast)|**No — fixed**|mDNS/Bonjour scanner|
|
||||
|`1900`|UDP (multicast)|**No — fixed**|SSDP/UPnP scanner|
|
||||
|`67`|UDP (broadcast)|**No — fixed**|DHCP scanner|
|
||||
Connect using the **domain name the certificate is issued for**, not an IP address. Self-signed certificates are not supported unless they are manually trusted on the device.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The mDNS (UDP `5353`), SSDP (UDP `1900`) and DHCP (UDP `67`) ports are fixed by their respective protocols and **cannot be changed**. They must be available on the server where OOTT is installed.
|
||||
>
|
||||
> OOTT binds these sockets with address/port reuse, so it can run alongside other responders already listening on them (for example `avahi` on `5353`, `minidlna` on `1900`, or a DHCP server/relay on `67`). However, the ports must not be blocked by a host firewall, and the corresponding multicast/broadcast traffic must be allowed to reach the host — otherwise the scanners will not discover any devices.
|
||||
>
|
||||
> Port `67` is a privileged port, so OOTT must run with sufficient privileges to bind it (it already requires raw-socket access for the ARP scanner).
|
||||
>
|
||||
> When running under Docker these scanners require host networking (or an equivalent setup that exposes the host's multicast and broadcast traffic to the container); see the [sample compose file](https://github.com/rzuasti/oott/blob/main/examples/docker-compose.yml).
|
||||
## Things to keep in mind
|
||||
|
||||
## Connecting the app to the backend
|
||||
The OOTT app (web, desktop, iOS and Android) talks to the backend exclusively over its REST API (port `3000` by default). You point the app at your backend from the app's settings. There are two supported ways to expose the backend to the app:
|
||||
|
||||
### Direct connection over HTTP (local network only)
|
||||
Point the app at the backend's address on your local network, for example `http://192.168.1.50:3000`. This is the simplest setup and needs no extra infrastructure, but only works while the device is on the same local network as the backend.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> On iOS this only works when you use the backend's **private-range IP address** (`192.168.x.x`, `10.x.x.x`, `172.16–31.x.x`) or a `*.local` (mDNS/Bonjour) name. A custom internal domain (e.g. `oott.mylan.com`) served over plain HTTP will be blocked by iOS App Transport Security, even when it resolves to a private IP — use the IP address directly, or HTTPS (see below), instead.
|
||||
|
||||
### Behind a reverse proxy with HTTPS (recommended)
|
||||
Put the backend behind a reverse proxy (nginx, Caddy, Traefik, …) that terminates TLS with a **valid certificate from a trusted CA** (for example [Let's Encrypt](https://letsencrypt.org/)), and point the app at the HTTPS URL (e.g. `https://oott.example.com`). This works on all platforms — including iOS — with no further configuration, and is required for access from outside the local network.
|
||||
|
||||
Connect using the **domain name the certificate is issued for** (not an IP address). Self-signed certificates are not supported unless they are manually trusted on the device.
|
||||
|
||||
> [!NOTE]
|
||||
> The first time the iOS app reaches your backend on the local network, iOS shows a one-time "find devices on your local network" prompt. This is expected — tap Allow to continue.
|
||||
|
||||
# Storage considerations
|
||||
### Storage considerations
|
||||
OOTT stores a timestamped event in the database for every device detected on every scan. Storage therefore scales with three factors: number of active devices, scan frequency, and the retention window.
|
||||
|
||||
## Estimates by network size
|
||||
|
||||
#### Estimates by network size
|
||||
The figures below assume a 1-minute scan duration (`arp_scanner.scan_duration = 1m`) with a 1-minute wait between scans (`arp_scanner.wait_between_scans = 1m`), giving 720 scans per day, and a 365-day retention window. Assume all devices are continuously online (worst case).
|
||||
|
||||
| Network size | Active devices | Storage / year |
|
||||
@@ -216,8 +210,7 @@ storage ≈ devices × scans_per_day × 145 bytes × retention_days
|
||||
|
||||
where `145 bytes` covers the database row and its index entry, and `scans_per_day = 86400 / (scan_duration_seconds + wait_between_scans_seconds)`.
|
||||
|
||||
## Tuning scan timings and retention to control storage
|
||||
|
||||
#### Tuning scan timings and retention to control storage
|
||||
Storage is directly proportional to scan frequency and retention window. The two main levers are:
|
||||
|
||||
**Scan interval** — `arp_scanner.wait_between_scans` is the most effective knob. Increasing it reduces scans per day linearly:
|
||||
@@ -250,3 +243,22 @@ A 15-minute wait (the default) cuts storage to about one eighth of the worst-cas
|
||||
| Homelab | `5m` | `180d` | ~350–700 MB |
|
||||
| Small office | `5m` | `90d` | ~175–350 MB |
|
||||
| Medium office | `15m` | `90d` | ~175–450 MB |
|
||||
|
||||
### Privileges and network ports
|
||||
OOTT binds to the following ports on the host where it runs:
|
||||
|
||||
|Port|Protocol|Configurable|Used by|
|
||||
|----|--------|------------|-------|
|
||||
|`3000`|TCP|Yes (`web_server.port`)|REST API and web UI|
|
||||
|`5353`|UDP (multicast)|**No — fixed**|mDNS/Bonjour scanner|
|
||||
|`1900`|UDP (multicast)|**No — fixed**|SSDP/UPnP scanner|
|
||||
|`67`|UDP (broadcast)|**No — fixed**|DHCP scanner|
|
||||
|
||||
The mDNS (UDP `5353`), SSDP (UDP `1900`) and DHCP (UDP `67`) ports are fixed by their respective protocols and **cannot be changed**. They must be available on the server where OOTT runs.
|
||||
|
||||
OOTT binds these sockets with address/port reuse, so it can run alongside other responders already listening on them (for example `avahi` on `5353`, `minidlna` on `1900`, or a DHCP server/relay on `67`). However, the ports must not be blocked by a host firewall, and the corresponding multicast/broadcast traffic must be allowed to reach the host — otherwise the scanners will not discover any devices.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> OOTT needs elevated privileges: the ARP scanner requires raw-socket access, and port `67` is a privileged port. The pre-built Docker image and NixOS module already run with what they need.
|
||||
>
|
||||
> Under Docker, the scanners require **host networking** (or an equivalent setup that exposes the host's multicast and broadcast traffic to the container), as shown in the [Docker installation](#simple-installation-with-docker) above.
|
||||
|
||||
Reference in New Issue
Block a user