Skip to content

BetaHuhn/LanLord

Repository files navigation

LanLord icon

LanLord

A self-hosted network monitoring service for your home server. Continuously scans the local network for connected devices, tracks metadata and connection history, and sends configurable notifications when devices connect or disconnect.

Features

  • Automatic network scanning — discovers devices via arp-scan or /proc/net/arp on a configurable interval
  • FRITZ!Box integration — queries your AVM FRITZ!Box router via the TR-064 API for richer metadata: online/offline status, interface type (WiFi vs Ethernet), and signal strength
  • Device metadata — tracks IP address, hostname, MAC address, manufacturer (from IEEE OUI database), inferred device type, and connection history
  • Event log — records every device discovered, connected, disconnected, or IP/hostname change
  • Notification rules — create configurable rules that fire on events, with support for per-device filters, tag filters, and cooldowns
  • Notification channels — Telegram bot, SMTP email, or any HTTP webhook (ntfy.sh, Home Assistant, etc.)
  • Web dashboard — live device table with search, filter, and per-device detail pages
  • Docker deployment — single docker compose up with persistent storage

Screenshots

Dashboard

The main device table with live online/offline status, statistics, activity chart, and search/filter controls.

Dashboard

Device detail

Per-device metadata, 24 h presence timeline, and full event history.

Device detail

Notification rules

Rule management — configure triggers, channels (Telegram, email, webhook), tag/MAC filters, and cooldowns.

Notification rules

Architecture

┌─────────────────────────────────────────────────┐
│                  SvelteKit Server               │
│                                                 │
│  hooks.server.ts ──► Scanner loop (setInterval) │
│         │                    │                  │
│         │           ┌────────┴────────┐         │
│         │           │                 │         │
│         │        arp-scan         FritzBox      │
│         │        proc-arp         TR-064 API    │
│         │           │                 │         │
│         │           └────────┬────────┘         │
│         │              merge by MAC             │
│         │                    │                  │
│         ▼                    ▼                  │
│   KV Storage (Deno KV) ◄── Reconcile state      │
│         │                    │                  │
│         │             emit DeviceEvents         │
│         │                    │                  │
│         │          Notification Dispatcher      │
│         │         Telegram / Email / Webhook    │
│         │                                       │
│      REST API  ◄──────────────────────────────  │
│      /api/*                                     │
│         │                                       │
│     Svelte UI (dashboard, device detail, rules) │
└─────────────────────────────────────────────────┘

Tech stack:

Concern Choice
Runtime Deno 2.x
Frontend SvelteKit 2 + Svelte 5
Storage unstorage + Deno KV driver (by default)
Build output @sveltejs/adapter-node (run with Deno's Node.js compat)
Containerisation Docker + Docker Compose

Source layout:

src/
├── hooks.server.ts          # Server init — starts the scanner background loop
├── lib/
│   ├── types.ts             # Shared TypeScript interfaces
│   └── server/
│       ├── config.ts        # Config loading (YAML file + env var overrides)
│       ├── logger.ts        # Structured logger (pretty / JSON)
│       ├── storage/         # Deno KV CRUD — devices, events, rules, meta
│       ├── oui/             # IEEE OUI manufacturer lookup + device type heuristics
│       ├── scanner/         # arp-scan, proc-arp, FritzBox TR-064 adapters + poll loop
│       └── notifications/   # Telegram, email, webhook adapters + rule evaluator
└── routes/
    ├── +page.svelte         # Dashboard
    ├── devices/[mac]/       # Device detail page
    ├── rules/               # Notification rules management
    └── api/                 # REST API endpoints

Quick start (Docker)

1. Create the config directory and copy the example config

mkdir -p config data
cp config.example.yaml config/config.yaml

2. Edit config/config.yaml

At minimum set your network interface:

scanner:
  interface: eth0        # replace with your LAN interface (ip link to find it)

Enable FRITZ!Box integration if you have one:

fritzbox:
  enabled: true
  host: fritz.box        # or 192.168.178.1
  password: ""           # set via FRITZBOX_PASSWORD env var instead

3. Configure secrets via environment variables

cp .env.example .env
# Edit .env — add TELEGRAM_BOT_TOKEN, FRITZBOX_PASSWORD, etc.

4. Start the service

docker compose up -d

The dashboard is available at http://your-server:3000.

Note: The service uses network_mode: host in Docker Compose. This is required for ARP scanning to work — it gives the container direct access to the host's network interfaces.

Configuration reference

All settings can be provided via config/config.yaml or environment variables. Env vars take precedence and are useful for secrets.

config.yaml

scanner:
  interval_seconds: 60         # scan frequency
  tool: auto                   # arp-scan | proc-arp | auto
  interface: eth0              # network interface for arp-scan
  offline_threshold_seconds: 180  # seconds without a response before marking offline

fritzbox:
  enabled: false
  host: fritz.box
  port: 49000                  # 49000 (HTTP) or 49443 (HTTPS)
  username: ""
  password: ""                 # prefer FRITZBOX_PASSWORD env var

server:
  port: 3000

notifications:
  telegram:
    enabled: false
    bot_token: ""              # prefer TELEGRAM_BOT_TOKEN env var
    chat_id: ""                # prefer TELEGRAM_CHAT_ID env var

  email:
    enabled: false
    smtp_host: ""
    smtp_port: 587
    smtp_user: ""
    smtp_pass: ""              # prefer SMTP_PASS env var
    from: "scanner@example.com"
    to: ["you@example.com"]

  webhook:
    enabled: false
    url: ""                    # prefer WEBHOOK_URL env var
    method: POST
    headers: {}                # e.g. { Authorization: "Bearer xxx" }

oui:
  db_path: /data/oui.txt       # IEEE OUI database (downloaded at Docker build time)

logging:
  level: info                  # debug | info | warn | error
  format: pretty               # pretty | json

Environment variables

Variable Description
CONFIG_PATH Path to config.yaml (default: /config/config.yaml)
KV_PATH Path to Deno KV database file (default: ./data/scanner.kv)
FRITZBOX_PASSWORD FRITZ!Box password
FRITZBOX_USERNAME FRITZ!Box username (often not needed)
FRITZBOX_ENABLED Set to true to enable (overrides config)
TELEGRAM_BOT_TOKEN Telegram bot token from @BotFather
TELEGRAM_CHAT_ID Telegram chat or channel ID
SMTP_HOST / SMTP_PORT SMTP server settings
SMTP_USER / SMTP_PASS SMTP credentials
WEBHOOK_URL Webhook target URL
PORT HTTP port (default: 3000)
LOG_LEVEL Log level override

Notification rules

Rules are created from the web dashboard at /rules or via the API.

Each rule defines:

  • Trigger — which event to react to
  • Channel — where to send the notification (Telegram, email, or webhook)
  • Filters (optional) — match only a specific MAC address or device tag
  • Cooldown — minimum seconds between repeat notifications for the same rule

Available triggers:

Trigger Description
any_new_device A MAC address is seen for the first time
any_device_online Any device transitions from offline → online
any_device_offline Any device transitions from online → offline
specific_device_online A particular device (by MAC or tag) comes online
specific_device_offline A particular device goes offline
ip_changed A device's IP address changed

Example: Telegram alert for all new devices

{
  "name": "New device alert",
  "trigger": "any_new_device",
  "channel": "telegram",
  "cooldown_seconds": 0
}

Example: Alert when a specific device goes offline

{
  "name": "NAS offline",
  "trigger": "specific_device_offline",
  "filter_mac": "AA:BB:CC:DD:EE:FF",
  "channel": "telegram",
  "cooldown_seconds": 300
}

REST API

All endpoints return { data: ... } JSON. Errors return { message: "..." }.

Method Endpoint Description
GET /api/devices List all devices. Query: ?online=true, ?tag=iot
PATCH /api/devices/:mac Update custom_name, tags, or device_type
GET /api/events Recent events. Query: ?limit=50, ?type=device_offline
GET /api/rules List notification rules
POST /api/rules Create a rule
PATCH /api/rules/:id Update a rule (e.g. toggle enabled)
DELETE /api/rules/:id Delete a rule
POST /api/scan Trigger an immediate scan
GET /api/scan Scanner status, last scan time, device counts

FRITZ!Box setup

The integration uses the TR-064 protocol (SOAP over HTTP) to query the router's host table.

  1. In your FRITZ!Box web interface go to Home Network → Network → Network Settings
  2. Enable "Allow access for applications" and "Transmit status information over UPnP"
  3. Set fritzbox.enabled: true and provide your FRITZ!Box password

When enabled, the FRITZ!Box becomes the authoritative source for online/offline status (it knows all DHCP-assigned devices). ARP scanning still runs alongside to catch any devices not using DHCP.

Running without Docker (development)

Requirements: Deno 2.x, arp-scan installed

# Install dependencies
deno install

# Start dev server (hot reload)
deno task dev

# Build for production
deno task build

# Run production build
deno run --allow-all --unstable-kv build/index.js

The --unstable-kv flag is required for Deno KV storage.

Set OUI_DB_PATH to a local copy of the IEEE OUI database for manufacturer lookup, or skip it (the service will warn but continue).

Data storage

Storage is configured in storage.config.ts via unstorage. Default backend is Deno KV, stored at ./data/lanlord.kv (or remotely via KV_PATH). You can swap drivers (filesystem, memory, redis, …) in that file.

Key schema:

devices:{MAC}                       Device record
device_events:{MAC}:{timestamp}     Per-device event log
events:{timestamp}:{MAC}            Global event log (sorted by time)
rules:{id}                          Notification rule
meta:last_scan                      Last scan metadata
meta:scanner_status                 "idle" | "running"

License

MIT

About

A self-hosted network monitoring service for your home server

Topics

Resources

Stars

Watchers

Forks

Contributors