CMS/Blog: WordPress, Ghost, Grav, Jekyll, Hugo, Hexo, Drupal, Joomla, TYPO3, OctoberCMS, Textpattern Analytics: Matomo, Plausible Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
✨ Features
- 🗂️ Manual inventory — add and manage devices (21 types) with IPs, VLANs, descriptions and optional web links
- 🗺️ Topology view — card-based layout per network (LAN / VLAN 802.1Q), with WAN and gateway sections
- 📡 ICMP ping sweep — check reachability of all known hosts in one click
- 🔍 Auto-discovery — ping sweep + PTR DNS lookup on a subnet to import new hosts
- 🏷️ Brand logos — automatic detection and display of vendor logos (Proxmox, Cisco, Synology, Docker, 30+ more)
- 🔐 Authentication — JWT-based login with forced password change on first use
- 🌙 Dark mode — light / dark theme toggle
- 🌍 i18n — French, English, Spanish
🛠️ Stack
| Layer | Technology |
|---|---|
| Backend | FastAPI + SQLAlchemy + SQLite (Python 3.11) |
| Frontend | Vue 3 + Vite, served by Nginx |
| Auth | JWT HS256, 24-hour expiry |
| Runtime | Docker Compose |
🚀 Quick start
# 1. Clone and enter the project
git clone https://git.raspot.in/olivier/stupid-simple-network-inventory.git
cd stupid-simple-network-inventory
# 2. Create the data directory owned by the current user
mkdir -p db_data
# 3. Configure environment (required for correct bind-mount ownership)
cp .env.example .env
# Edit .env:
# DOCKER_UID / DOCKER_GID → output of: id -u && id -g
# INITIAL_ADMIN_PASSWORD → set to avoid the default admin/admin bootstrap
# 4. Build and start
docker compose --env-file .env up --build -d
# 5. Open http://localhost:8080 in your browser
First login
| Case | Credentials | Behaviour |
|---|---|---|
INITIAL_ADMIN_PASSWORD set |
admin / <your password> |
Normal login |
INITIAL_ADMIN_PASSWORD unset |
admin / admin |
Forced password change before accessing the app |
🔄 Updating
Data is stored in ./db_data/ (bind-mount), which is never touched by container rebuilds. Updating is safe:
git pull
docker compose up --build -d
Database migrations run automatically on startup — no manual steps required.
⚙️ Configuration
All configuration is via environment variables. See .env.example for the full list with descriptions.
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
auto-generated | JWT signing key. Set explicitly in production. |
INITIAL_ADMIN_PASSWORD |
(empty) | Bootstrap admin password. If unset, admin/admin is used with forced change. |
ALLOWED_ORIGINS |
* |
CORS allowed origins (comma-separated). Set to your domain in production. |
BIND_ADDRESS |
0.0.0.0 |
IP address to listen on. Set to the interface facing the reverse proxy. |
DOCKER_UID / DOCKER_GID |
1000 |
UID/GID for the backend process. Must match the host user owning ./db_data/. |
cp .env.example .env
# Edit .env — at minimum set DOCKER_UID, DOCKER_GID, INITIAL_ADMIN_PASSWORD
docker compose --env-file .env up --build -d
🔒 Security
Secret management
Two options depending on your security requirements.
Option A — Auto-generated secret (recommended for single-node)
Leave SECRET_KEY unset (or empty) in .env. On first start the backend generates a random 64-character hex key, writes it to db_data/secret_key.txt with permissions 0600, and reuses it on every subsequent restart. The secret never appears in an environment variable, a compose file, or a log.
# .env — leave the line empty or remove it
SECRET_KEY=
The only requirement is that db_data/ is backed up (it already contains the database).
Option B — Docker Compose file secret
Stores the secret in a file on the host, outside version control, and mounts it into the container. The value never appears in an environment variable.
# Generate and store the secret outside the project directory
mkdir -p ~/.secrets
python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.secrets/topologie_secret_key
chmod 600 ~/.secrets/topologie_secret_key
Then uncomment the secrets: blocks in docker-compose.yml (see comments in that file) and remove SECRET_KEY from .env. Docker Compose merges the override automatically:
docker compose up -d
Key rotation
To rotate the JWT secret (invalidates all active sessions):
# Option A — environment variable (recommended)
# Set a new SECRET_KEY in your deployment config and restart
# Option B — file rotation
docker compose stop backend
rm db_data/secret_key.txt
docker compose start backend
# All users will need to log in again
HTTPS
This application does not terminate TLS. For production use, place it behind a reverse proxy that handles HTTPS.
Nginx
server {
listen 443 ssl;
server_name inventory.example.com;
ssl_certificate /etc/letsencrypt/live/inventory.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/inventory.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Traefik — Docker labels
Add these labels to the frontend service and connect it to the network shared with Traefik:
# docker-compose.override.yml
services:
frontend:
labels:
- "traefik.enable=true"
- "traefik.http.routers.inventory.rule=Host(`inventory.example.com`)"
- "traefik.http.routers.inventory.entrypoints=websecure"
- "traefik.http.routers.inventory.tls.certresolver=letsencrypt"
- "traefik.http.services.inventory.loadbalancer.server.port=8080"
networks:
- internal
- traefik_public # network shared with your Traefik instance
networks:
traefik_public:
external: true
Traefik — Dynamic configuration (file provider)
If Traefik is not running in Docker (or you prefer the file provider), drop a file in your dynamic config directory:
# /etc/traefik/dynamic/inventory.yml
http:
routers:
inventory:
rule: "Host(`inventory.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: inventory-svc
services:
inventory-svc:
loadBalancer:
servers:
- url: "http://127.0.0.1:8080"
Traefik picks up the file automatically — no restart required.
For local-only use, bind to loopback to prevent accidental LAN exposure:
# docker-compose.override.yml
services:
frontend:
ports:
- "127.0.0.1:8080:8080"
Container hardening
| Measure | Backend | Frontend |
|---|---|---|
| Non-root user | DOCKER_UID:DOCKER_GID (host user) |
nginx (UID 101) |
cap_drop: ALL |
✓ | ✓ |
cap_add: NET_RAW |
✓ (ping) | — |
no-new-privileges |
— ¹ | ✓ |
| Healthcheck | ✓ | ✓ |
¹ Omitted on the backend: ping uses file capabilities (cap_net_raw=ep); no-new-privileges suppresses the file effective bit and would prevent the subprocess from acquiring CAP_NET_RAW in its effective set even though the parent holds it in its permitted set.
💾 Data persistence
All data is stored in ./db_data/:
| File | Description |
|---|---|
topology.db |
SQLite database |
secret_key.txt |
Auto-generated JWT secret (0600 permissions) |
Backup: cp -r db_data/ db_data.bak/
Restore: stop the stack, replace db_data/, restart.
🏷️ Brand logo detection
Logo detection runs automatically against the device name and description fields (case-insensitive keyword matching). No manual configuration needed — just include a recognisable keyword in the name or description.
Logos are displayed in two places:
- Topology view — small inline SVGs next to the device name on each chip
- Device list — coloured badge(s) in the device card
Multiple logos can appear simultaneously if several keywords match.
| Category | Brand | Trigger keywords |
|---|---|---|
| Virtualisation | Proxmox | proxmox, pve |
| Virtualisation | Docker | docker |
| NAS | Synology | synology, dsm |
| NAS | TrueNAS | truenas, freenas |
| Network | Ubiquiti / UniFi | ubiquiti, unifi, usg, udm |
| Network | MikroTik | mikrotik, routeros |
| Network | Cisco | cisco |
| Network | TP-Link | tp-link, tplink |
| Network | ASUS | asus |
| Network | Netgear | netgear |
| Network | pfSense | pfsense |
| Network | OPNsense | opnsense |
| Network | OpenWrt | openwrt |
| Web / proxy | Apache | apache, apache2, httpd |
| Web / proxy | Traefik | traefik |
| Auth / SSO | Authelia | authelia |
| Auth / SSO | Keycloak | keycloak |
| Auth / SSO | Authentik | authentik |
| Auth / SSO | Okta | okta |
| Auth / SSO | Auth0 | auth0 |
| Password vault | Vaultwarden | vaultwarden |
| Password vault | Bitwarden | bitwarden |
| Password vault | 1Password | 1password, onepassword |
| Password vault | KeePassXC | keepass, keepassxc |
| Password vault | HashiCorp Vault | hashicorp vault, hashicorp |
| Database | MariaDB | mariadb |
| Orchestration | Kubernetes | kubernetes, k8s, kubectl, k3s |
| OS | Debian | debian |
| OS | Ubuntu | ubuntu |
| Automation | Ansible | ansible |
| Servers | Dell | dell, idrac, poweredge |
| Servers | HP | proliant, ilo, hewlett |
| SBC / DIY | Raspberry Pi | raspberry, rpi, raspi |
| SBC / DIY | Arduino | arduino |
| Desktop | KDE / Plasma | kde, plasma |
| Tools | Excalidraw | excalidraw |
| Self-hosted | Nextcloud | nextcloud |
| Self-hosted | Paperless-NGX | paperless, paperless-ngx |
| Self-hosted | Uptime Kuma | uptime-kuma, uptime kuma |
| Self-hosted | MkDocs | mkdocs |
| CMS / Blog | WordPress | wordpress |
| CMS / Blog | Ghost | ghost |
| CMS / Blog | Grav | grav |
| CMS / Blog | Jekyll | jekyll |
| CMS / Blog | Hugo | hugo |
| CMS / Blog | Hexo | hexo |
| CMS / Blog | Drupal | drupal |
| CMS / Blog | Joomla | joomla |
| CMS / Blog | TYPO3 | typo3 |
| CMS / Blog | OctoberCMS | octobercms, october cms |
| CMS / Blog | Textpattern | textpattern |
| Analytics | Matomo | matomo |
| Analytics | Plausible | plausible |
| Media / torrent | Radarr | radarr |
| Media / torrent | Sonarr | sonarr |
| Media / torrent | Transmission | transmission |
| Media / home automation | Jellyfin | jellyfin |
| Media / home automation | Home Assistant | homeassistant, home assistant, hassio |
| Media / home automation | Philips Hue | philips hue, hue bridge |
| Media / home automation | Xiaomi | xiaomi, mi home, yeelight |
🧑💻 Development
Backend tests
cd backend
pip install -r requirements.txt -r requirements-test.txt
pytest tests/ -v
Local dev (without Docker)
# Backend
cd backend
pip install -r requirements.txt
uvicorn main:app --reload
# Frontend (separate terminal)
cd frontend
npm install
npm run dev # Vite dev server on :5173, proxies /api/ to :8000
🏗️ Architecture
See docs/architecture.md for the detailed request flow, Docker setup, and authentication model.
🤖 Built with AI assistance
This application was developed entirely with the help of AI coding assistants:
- Claude Code (Anthropic) — used throughout the project for architecture design, backend API (FastAPI, SQLAlchemy, authentication, ICMP discovery), frontend components (Vue 3, CSS topology layout, i18n, dark mode), Docker & Nginx configuration, and documentation.
- Codex (OpenAI) — also used during development for code generation and suggestions across the entire codebase.
📄 License
This project is licensed under the GNU General Public License v3.0.
You are free to use, modify and distribute this software under the terms of the GPL v3. Any derivative work must be distributed under the same license.
