Initial commit - Jetson Home Hub Setup (Face/Finger Auth, RustDesk, Authentik, Whisper)

This commit is contained in:
Ubuntu 2025-12-15 04:02:51 +00:00
commit cf46181e48
23 changed files with 2497 additions and 0 deletions

215
AI-SETUP-INSTRUCTIONS.md Normal file
View file

@ -0,0 +1,215 @@
# HOFMANNS.AI - SETUP INSTRUCTIONS FOR AI AGENTS
**Ziel:** Jetson Orin Nano als zentraler Hub mit SSO (Passkeys) + RustDesk
---
## WICHTIG: Architektur
```
JETSON (192.168.1.50) = MASTER
├── Authentik (SSO mit Face ID / Fingerprint)
├── RustDesk Server (hbbs/hbbr)
├── Alle Services (HA, Whisper, Jellyfin, etc.)
├── Private Daten (Kunden, Source, Docs)
└── Cloudflare Tunnel → *.hofmanns.ai
INFOMANIAK (185.143.102.153) = DEV/PILOT
├── Git Repos
├── Dev Environments
├── AI Agents arbeiten hier
└── Kann jederzeit resettet werden
```
---
## FILES IN THIS PACKAGE
```
jetson-setup/
├── FOR JETSON (192.168.1.50):
│ ├── setup.sh → Run first after JetPack flash
│ ├── docker-compose.yml → All services including Authentik
│ ├── homepage/ → Dashboard config
│ └── whisper-webui-jetson/ → GPU-accelerated Whisper
├── FOR INFOMANIAK (185.143.102.153):
│ └── infomaniak/
│ ├── setup-infomaniak.sh → Optional dev setup
│ └── docker-compose.yml → Dev services only
└── DOCUMENTATION:
├── CHAT-SUMMARY-COMPLETE.md → Full context
├── README.md → User guide
└── AI-SETUP-INSTRUCTIONS.md → This file
```
---
## JETSON SETUP STEPS
### 1. Flash JetPack (on Ubuntu PC with SDK Manager)
```bash
# Jetson in Recovery Mode
# SDK Manager → Jetson Orin Nano 8GB → JetPack 6.x → NVMe
```
### 2. First Boot Config
```bash
# After first boot, copy files to Jetson
scp -r jetson-setup/ d@192.168.1.50:~/
# SSH to Jetson
ssh d@192.168.1.50
# Run setup
cd ~/jetson-setup
chmod +x setup.sh
sudo ./setup.sh
sudo reboot
```
### 3. Start Services
```bash
cd ~/docker
docker-compose up -d
```
### 4. Configure Authentik
1. Open: `http://192.168.1.50:9000/if/flow/initial-setup/`
2. Create admin account
3. Enable WebAuthn/Passkeys:
- Admin → Flows → default-authentication-flow
- Add Stage: "authenticator_validate" with WebAuthn
4. Create Application for each service
5. Set up Forward Auth proxy
### 5. Configure Cloudflare Tunnel
```bash
# Install cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64 -o cloudflared
chmod +x cloudflared
sudo mv cloudflared /usr/local/bin/
# Login and create tunnel
cloudflared tunnel login
cloudflared tunnel create jetson-home
# Configure tunnel
cat > ~/.cloudflared/config.yml << EOF
tunnel: <TUNNEL_ID>
credentials-file: /home/d/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: auth.hofmanns.ai
service: http://localhost:9000
- hostname: portal.hofmanns.ai
service: http://localhost:3001
- hostname: home.hofmanns.ai
service: http://localhost:8123
- hostname: whisper.hofmanns.ai
service: http://localhost:7860
- hostname: files.hofmanns.ai
service: http://localhost:8081
- hostname: "*.hofmanns.ai"
service: http://localhost:80
- service: http_status:404
EOF
# Run as service
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
```
### 6. DNS Records (Cloudflare)
| Type | Name | Content |
|------|------|---------|
| CNAME | auth | <TUNNEL_ID>.cfargotunnel.com |
| CNAME | portal | <TUNNEL_ID>.cfargotunnel.com |
| CNAME | home | <TUNNEL_ID>.cfargotunnel.com |
| CNAME | whisper | <TUNNEL_ID>.cfargotunnel.com |
| CNAME | files | <TUNNEL_ID>.cfargotunnel.com |
### 7. Register Passkeys
1. Open auth.hofmanns.ai on each device
2. Login with initial password
3. Settings → MFA → Add WebAuthn Device
4. Use Face ID / Fingerprint to register
5. Done - no more passwords needed
---
## RUSTDESK SETUP
### Server runs on Jetson:
```bash
# After docker-compose up, get the key:
cat ~/docker/rustdesk/id_ed25519.pub
```
### Client Config (all devices):
| Setting | Value |
|---------|-------|
| ID Server | jetson.hofmanns.ai (or via tunnel) |
| Relay Server | jetson.hofmanns.ai |
| Key | (from id_ed25519.pub) |
---
## PORTS OVERVIEW
### Jetson (192.168.1.50):
| Port | Service |
|------|---------|
| 80 | Nginx Proxy Manager |
| 443 | Nginx Proxy Manager (SSL) |
| 81 | NPM Admin UI |
| 3001 | Homepage Dashboard |
| 8123 | Home Assistant |
| 9000 | Authentik |
| 9090 | Cockpit |
| 7860 | Whisper |
| 8096 | Jellyfin |
| 8081 | FileBrowser |
| 8384 | Syncthing |
| 11434 | Ollama |
| 21115-21119 | RustDesk |
---
## USER REQUIREMENTS
- **NO passwords** - only Face ID / Fingerprint
- **NO email codes** - Passkeys only
- **NO 2FA apps** - device IS the auth
- **ONE login** - session stays active
- **ALL devices** - Quest 3, Android, Laptop, TV
- **Browser-based** - no apps needed (except RustDesk client)
---
## RESULT
After setup, user experience:
1. Open browser → portal.hofmanns.ai
2. Touch finger OR show face
3. **Done. Everything accessible. All day.**
---
**Package created:** December 2025
**For:** Dee / hofmanns.ai / DOUANA®
**By:** Claude (Anthropic)

370
CHAT-SUMMARY-COMPLETE.md Normal file
View file

@ -0,0 +1,370 @@
# Hofmanns.AI - Complete Infrastructure Setup
**Chat Summary - Dezember 2025**
**Erstellt für:** Dee / DOUANA® / ZFEB GmbH
---
## 🎯 Das Problem
- 48 Jahre alt, Enterprise-Kunden (Banken, Konzerne)
- Jeden Tag 1-2 Stunden verloren mit: Logins, Passwörter, Email-Codes, Verify-Me, 2FA-Apps
- Jedes Device, jede App, jeder Service will eigenen Login
- Kein Bock mehr auf Console-Gefrickel
- Meta Quest 3 als primäres Arbeitsgerät → braucht Browser-UI
**Das Ziel:** Ein Finger/Gesicht → alles offen. Fertig.
---
## 🏗️ Die Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ AUTHENTIK SSO │
│ auth.hofmanns.ai (Passkeys/WebAuthn) │
│ Face ID / Fingerprint = einmal einloggen = fertig │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ INFOMANIAK │ │ JETSON │ │ DEVICES │
│ SERVER │ │ ORIN NANO │ │ │
│ (öffentlich) │◄──►│ (privat) │◄──►│ Quest 3 │
│ │ │ │ │ Android │
│ - Git Repos │ │ - Kundendaten │ │ Laptop │
│ - Dev/Pilot │ │ - Source Code │ │ TV │
│ - Auth Server │ │ - Dokumente │ │ │
│ - RustDesk ID │ │ - Home Hub │ │ │
└───────────────┘ └───────────────┘ └───────────────┘
```
---
## 📍 Server Overview
### Infomaniak Server (öffentlich)
| Info | Wert |
|------|------|
| IP | 185.143.102.153 |
| OS | Debian 12 (bookworm) |
| User | debian |
| SSH | Port 22 mit RSA Key |
| Projekt | /home/debian/hofmanns.ai |
| Command | `hof` → navigiert zum Projekt |
**Domains (alle auf diesem Server):**
- hofmanns.ai
- hofmanns.tech
- hofmanns.app
- hofmanns.shop
- hofmanns.ltd
- hofmann-s.com
**Was läuft dort:**
- Git Repository (git.hofmanns.ai)
- Dev/Pilot Umgebungen
- AI Agents arbeiten hier (Claude, Manus, Perplexity)
- Docker installiert
**Was NOCH drauf kommt:**
- Authentik (SSO mit Passkeys)
- RustDesk ID Server (hbbs/hbbr)
- Cloudflare Tunnel oder direkt
### Jetson Orin Nano (privat, zu Hause)
| Info | Wert |
|------|------|
| IP | 192.168.1.50 |
| OS | JetPack 6.x (Ubuntu-based) |
| User | d |
| Storage | 1TB NVMe |
| GPU | NVIDIA (für AI) |
| Standort | Am TV, zentral in Wohnung |
**Was läuft dort:**
- Kundendaten
- Source Code (produktiv)
- Wichtige Dokumente
- Home Assistant
- AdGuard DNS
- Whisper (Sprache → Text)
- Jellyfin (Media)
- Private Cloud (FileBrowser)
- Syncthing
- Ollama (LLMs)
---
## 🔐 SSO mit Passkeys (WebAuthn)
**Keine Passwörter. Keine Email-Codes. Kein 2FA-App Stress.**
### So funktioniert es:
1. **Einmalig einrichten:** Passkey auf jedem Gerät registrieren
2. **Danach:** Finger auf Sensor oder Gesicht zeigen → eingeloggt
3. **Session:** Bleibt aktiv, kein ständiges neu einloggen
### Unterstützte Geräte:
| Gerät | Auth-Methode |
|-------|--------------|
| Android Handy | Fingerprint / Face |
| Meta Quest 3 | Handy als Authenticator (zeigt in VR) |
| Laptop | Fingerprint / Windows Hello |
| iPad/iPhone | Face ID / Touch ID |
### Authentik Setup:
```
auth.hofmanns.ai
├── Passkey Registration
├── Session Management
├── Application Proxy
└── Geschützte Apps:
├── portal.hofmanns.ai (Dashboard)
├── git.hofmanns.ai
├── home.hofmanns.ai (Home Assistant)
├── files.hofmanns.ai (FileBrowser)
├── whisper.hofmanns.ai
└── ... alle anderen Services
```
---
## 🖥️ Das Dashboard
**Ein Tab. Alles drin. Vollbild. Kein OS-Scheiss.**
```
┌─────────────────────────────────────────────────────────────────┐
│ portal.hofmanns.ai │
│ (nach Passkey-Auth = sofort da) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Home │ │ Whisper │ │ Jellyfin │ │
│ │ Assistant │ │ Speech→Text│ │ Media │ │
│ │ │ │ (GPU) │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Portainer │ │ Files │ │ Syncthing │ │
│ │ Docker │ │ Cloud │ │ Sync │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Git │ │ RustDesk │ │ Ollama │ │
│ │ Repos │ │ Remote │ │ LLM │ │
│ │ │ │ Desktop │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 🔌 RustDesk (Self-Hosted TeamViewer)
**Kein WireGuard Console-Gefrickel. GUI everywhere.**
### Server (auf Infomaniak):
```yaml
rustdesk-hbbs: # ID/Rendezvous Server
ports: 21115, 21116, 21118
rustdesk-hbbr: # Relay Server
ports: 21117, 21119
```
### Client Setup:
1. RustDesk App installieren (alle Plattformen)
2. Settings → Network → ID/Relay Server
3. ID Server: `185.143.102.153` (oder rustdesk.hofmanns.ai)
4. Relay Server: `185.143.102.153`
5. Key: (wird beim ersten Start generiert)
### Zugriff:
- Von Quest 3 Browser → RustDesk Web Client
- Von Handy → RustDesk App
- Von Laptop → RustDesk App
- Auf alle Rechner: Jetson, Gaming PC, Server
---
## 📱 Jetson Orin Nano - Services
| Port | Domain | Service |
|------|--------|---------|
| 3001 | portal.hofmanns.ai | Homepage Dashboard |
| 81 | - | Nginx Proxy Manager |
| 9443 | docker.hofmanns.ai | Portainer |
| 9090 | system.hofmanns.ai | Cockpit |
| 8123 | home.hofmanns.ai | Home Assistant |
| 3000 | dns.hofmanns.ai | AdGuard Home |
| 7860 | whisper.hofmanns.ai | Whisper WebUI |
| 8096 | media.hofmanns.ai | Jellyfin |
| 8081 | files.hofmanns.ai | FileBrowser |
| 8384 | sync.hofmanns.ai | Syncthing |
| 11434 | llm.hofmanns.ai | Ollama |
---
## 🌐 Netzwerk Setup
### Zu Hause:
```
5G Antenne (500 CHF)
Netgear Nighthawk M5
(DHCP 192.168.1.x, DNS=192.168.1.50)
Netgear 8-Port Switch
├── Jetson Orin Nano (192.168.1.50)
│ └── USB WiFi → AP "Home.8"
└── Gaming PC (RTX 4080)
```
### Tunnel nach draussen:
```
Jetson ──► Cloudflare Tunnel ──► *.hofmanns.ai
└── RustDesk ──► Infomaniak Server ──► Clients überall
```
---
## 📋 Hardware Übersicht
| Gerät | Zweck | Status |
|-------|-------|--------|
| Jetson Orin Nano 8GB | Home Hub, AI, Private Cloud | Setup pending |
| Gaming PC (Ryzen 9, RTX 4080, 64GB) | Workstation, Gaming | Aktiv |
| Netgear Nighthawk M5 | 5G Router, 800 Mbit | Aktiv |
| TP-Link USB WiFi Antenne | AP für Jetson | Vorhanden |
| Meta Quest 3 | VR, primäres Arbeitsgerät | Aktiv |
| 5x Raspberry Pi 5 | Übrig, verkaufen oder Projekte | Unused |
| Infomaniak Root Server | Public Services, Auth, Git | Aktiv |
---
## 🚀 Installation - Reihenfolge
### Phase 1: Jetson Setup (zu Hause)
1. JetPack auf NVMe flashen (SDK Manager)
2. `setup.sh` ausführen
3. Docker Services starten
4. WiFi AP "Home.8" aktivieren
5. AdGuard DNS konfigurieren
### Phase 2: Infomaniak Server
1. SSH verbinden: `ssh hofmanns.ai`
2. Authentik installieren (Docker)
3. RustDesk Server installieren
4. Cloudflare Tunnel oder Nginx konfigurieren
5. DNS Records für alle Subdomains
### Phase 3: SSO Integration
1. Authentik Passkeys einrichten
2. Alle Services als Applications registrieren
3. Proxy-Provider für jeden Service
4. Passkey auf allen Geräten registrieren
### Phase 4: Dashboard
1. Homepage auf Jetson konfigurieren
2. Alle Services einbinden
3. Authentik Forward Auth
4. Testen von allen Devices
---
## 📁 Dateien in diesem Setup
```
jetson-setup/
├── setup.sh # Haupt-Setup für Jetson
├── docker-compose.yml # Alle lokalen Services
├── CHAT-SUMMARY.md # Diese Datei
├── adguard-dns-rewrites.txt # DNS Einträge
├── homepage/
│ ├── services.yaml
│ ├── settings.yaml
│ ├── widgets.yaml
│ ├── docker.yaml
│ └── bookmarks.yaml
└── whisper-webui-jetson/
├── Dockerfile
└── app.py
```
---
## 🔑 Wichtige Credentials (ÄNDERN!)
| Was | Default | Ändern auf |
|-----|---------|------------|
| WiFi "Home.8" | 123 | Sicheres Passwort |
| Nginx Proxy Manager | admin@example.com / changeme | Eigene |
| FileBrowser | admin / admin | Eigene |
| Portainer | (bei Setup) | Eigene |
| Authentik | (bei Setup) | Eigene |
---
## 🎯 Das Endergebnis
**Morgens aufstehen:**
1. Quest 3 aufsetzen
2. Browser öffnen → portal.hofmanns.ai
3. Handy kurz ans Gesicht halten (Face ID)
4. **Fertig. Alles offen. Den ganzen Tag.**
**Unterwegs:**
1. Laptop/Handy öffnen
2. portal.hofmanns.ai
3. Finger auf Sensor
4. **Fertig. Voller Zugriff auf alles.**
**Keine Passwörter. Keine Codes. Keine Apps. Keine Scheisse.**
---
## 📞 Nächste Schritte
1. [ ] Jetson JetPack flashen
2. [ ] `setup.sh` ausführen auf Jetson
3. [ ] Authentik auf Infomaniak installieren
4. [ ] RustDesk Server auf Infomaniak
5. [ ] DNS Records für Subdomains
6. [ ] Cloudflare Tunnel einrichten
7. [ ] Passkeys registrieren
8. [ ] Testen mit Quest 3
---
**Erstellt:** Dezember 2025
**Chat mit:** Claude (Anthropic)
**Für:** Dee / hofmanns.ai / DOUANA®

131
CHAT-SUMMARY.md Normal file
View file

@ -0,0 +1,131 @@
# Jetson Orin Nano - Home Hub Setup
## Ausgangsproblem
Raspberry Pi 5 als Access Point für "Home.8" WiFi hatte falsche Konfiguration:
- **Problem:** NetworkManager Hotspot Mode erstellt eigenes Subnetz (10.42.0.x) mit NAT
- **Gewollt:** Einfache Bridge die IPs vom Nighthawk Router (192.168.1.x) durchreicht
## Lösung: Jetson Orin Nano als zentraler Hub
Statt den RP5 weiter zu debuggen → komplettes Setup auf Jetson Orin Nano am TV.
### Hardware Setup
```
5G Antenne (500 CHF) ──► Netgear Nighthawk M5 (800 Mbit)
Netgear 8-Port Switch
┌─────────┴─────────┐
│ │
Jetson Orin Nano Gaming PC
(@ TV, NVMe 1TB) (RTX 4080)
┌──────────┼──────────┐
│ │ │
USB WiFi HDMI 4K Docker Services
(TP-Link) TV (alles lokal)
```
### Services (alle mit Web UI!)
| Port | Service | Beschreibung |
|------|---------|--------------|
| 3001 | Homepage | Zentrales Dashboard |
| 81 | Nginx Proxy Manager | Reverse Proxy mit GUI |
| 9443 | Portainer | Docker Management |
| 9090 | Cockpit | System Management |
| 8123 | Home Assistant | Smart Home |
| 3000 | AdGuard Home | DNS + Werbeblocker |
| 7860 | Whisper WebUI | Sprache → Text (GPU) |
| 8096 | Jellyfin | Media Server |
| 8081 | FileBrowser | Private Cloud |
| 8384 | Syncthing | File Sync |
| 11434 | Ollama | Lokale LLMs |
| 21115-21119 | RustDesk | Remote Desktop Server |
### Warum diese Entscheidungen
1. **RustDesk statt WireGuard** - Remote Desktop mit GUI, kein Console-Gefrickel
2. **Nginx Proxy Manager statt Caddy** - Proxy-Konfiguration per Web UI
3. **Homepage Dashboard** - Zentrales UI für alles, perfekt für Meta Quest 3 Browser
4. **Cockpit** - System-Management im Browser statt SSH
### Dateien im Setup
```
jetson-setup/
├── setup.sh # Hauptscript
├── docker-compose.yml # Alle Services
├── adguard-dns-rewrites.txt # DNS Einträge
├── homepage/
│ ├── services.yaml # Dashboard Services
│ ├── settings.yaml # Dashboard Settings
│ ├── widgets.yaml # System Widgets
│ ├── docker.yaml # Docker Integration
│ └── bookmarks.yaml # Links
└── whisper-webui-jetson/
├── Dockerfile
└── app.py # Gradio Web UI
```
### Installation
1. **JetPack auf NVMe flashen** (SDK Manager am Ubuntu PC)
2. **Setup-Dateien kopieren:**
```bash
scp -r jetson-setup/ d@192.168.1.50:~/
```
3. **Setup ausführen:**
```bash
cd ~/jetson-setup
chmod +x setup.sh
sudo ./setup.sh
sudo reboot
```
4. **Services starten:**
```bash
cd ~/docker
docker-compose up -d
```
### Nach dem Setup
1. **Homepage öffnen:** `http://192.168.1.50:3001`
2. **Nginx Proxy Manager:** `http://192.168.1.50:81` (admin@example.com / changeme)
3. **AdGuard DNS Rewrites** für .lan Domains einrichten
4. **RustDesk Key holen:** `cat ~/docker/rustdesk/id_ed25519.pub`
### RustDesk Client Setup
1. RustDesk App installieren (Windows/Mac/Linux/Android/iOS)
2. Settings → Network → ID/Relay Server
3. ID Server: `192.168.1.50`
4. Relay Server: `192.168.1.50`
5. Key: (aus `id_ed25519.pub`)
### Vorteile
- **100% lokal** - keine Cloud-Abhängigkeit
- **Alles Web UI** - nix Console im Alltag
- **GPU beschleunigt** - Whisper, Ollama, Jellyfin Transcoding
- **Meta Quest 3 kompatibel** - Dashboard im VR Browser
- **Remote Access** - RustDesk für Desktop, alle Services über Proxy
### Hardware die rumliegt
- 5x Raspberry Pi 5 → verkaufen oder andere Projekte
- Jetson Orin Nano → jetzt der zentrale Hub
- TP-Link USB WiFi Antenne → AP "Home.8" am Jetson
- Netgear Nighthawk M5 → 5G Internet + DHCP
- Gaming PC (Ryzen 9, RTX 4080, 64GB) → bleibt Workstation
### Noch zu tun
- [ ] WiFi Passwort ändern (aktuell: 123)
- [ ] Nginx Proxy Manager Domains einrichten
- [ ] AdGuard DNS Rewrites hinzufügen
- [ ] RustDesk auf allen Geräten installieren
- [ ] Home Assistant Geräte einbinden
- [ ] Whisper/Piper für HA Voice Assistant

269
README.md Normal file
View file

@ -0,0 +1,269 @@
# Jetson Orin Nano 8GB - Complete Home Hub
## Übersicht
Alles lokal, keine Cloud, volle Kontrolle.
| Domain | Service | Beschreibung |
|--------|---------|--------------|
| `home.lan` | Home Assistant | Smart Home Zentrale |
| `adguard.lan` | AdGuard Home | DNS + Werbeblocker |
| `whisper.lan` | Whisper WebUI | Sprache → Text (GPU) |
| `jellyfin.lan` | Jellyfin | Media Server |
| `portainer.lan` | Portainer | Docker Management |
| `vpn.lan` | WireGuard Easy | VPN für unterwegs |
| `files.lan` | FileBrowser | Private Cloud |
| `sync.lan` | Syncthing | Dateisync |
| `ollama.lan` | Ollama | Lokale LLMs |
## Hardware Setup
```
5G Antenne ──► Nighthawk M5 (DHCP, DNS=192.168.1.50)
Netgear Switch
└── Jetson Orin Nano @ TV
├── eth0: 192.168.1.50
├── USB: TP-Link WiFi → AP "Home.8"
└── HDMI: 4K TV
```
## Installation
### 1. JetPack flashen (am PC mit Ubuntu)
```bash
# SDK Manager: https://developer.nvidia.com/sdk-manager
# Jetson in Recovery Mode (Power aus, Recovery halten, Power an)
sdkmanager
# → Jetson Orin Nano 8GB
# → JetPack 6.x
# → NVMe als Ziel
```
### 2. Nach erstem Boot
```bash
# Dateien auf Jetson kopieren
scp -r jetson-setup/ d@192.168.1.50:~/
# SSH zum Jetson
ssh d@192.168.1.50
# Setup ausführen
cd ~/jetson-setup
chmod +x setup.sh
sudo ./setup.sh
sudo reboot
```
### 3. Nach Reboot
```bash
cd ~/docker
docker-compose up -d
# Logs
docker-compose logs -f
```
### 4. AdGuard DNS Rewrites einrichten
1. Öffne `http://192.168.1.50:3000`
2. Setup durchführen
3. Settings → DNS rewrites
4. Alle Einträge aus `adguard-dns-rewrites.txt` hinzufügen
### 5. Nighthawk DNS ändern
Im Nighthawk Router: DNS auf `192.168.1.50` setzen
Jetzt funktionieren alle `.lan` Domains!
## Zugriff
### Im LAN
Einfach im Browser:
- `http://home.lan`
- `http://adguard.lan`
- `http://whisper.lan`
- `http://portainer.lan`
- etc.
### Unterwegs (VPN)
1. Öffne `http://vpn.lan` (Passwort: 123 - ÄNDERN!)
2. Neuen Client erstellen
3. QR-Code mit WireGuard App scannen
4. Verbinden - fertig, alle .lan Domains erreichbar
**Wichtig:** `WG_HOST` in docker-compose.yml auf deine externe IP/DynDNS setzen!
## Services im Detail
### Home Assistant (`home.lan`)
Voice Assistant einrichten:
1. Settings → Devices & Services → Add Integration
2. "Wyoming Protocol" hinzufügen
3. Whisper: `localhost:10300`
4. Piper TTS: `localhost:10200`
### Whisper WebUI (`whisper.lan`)
- Sprache aufnehmen im Browser
- Dateien hochladen
- Läuft 100% lokal auf GPU
- Deutsch, Englisch, etc.
### FileBrowser (`files.lan`)
Private Cloud für alle Dateien:
- Default Login: `admin` / `admin`
- Zugriff auf `/home/d/`
### Syncthing (`sync.lan`)
Dateien zwischen Geräten synchronisieren:
- Handy ↔ Jetson ↔ PC
- Verschlüsselt, kein Server in der Mitte
### Portainer (`portainer.lan`)
Docker Management mit GUI:
- Container starten/stoppen
- Logs anschauen
- Compose verwalten
## Dateistruktur
```
~/docker/
├── docker-compose.yml
├── caddy/
│ └── Caddyfile
├── homeassistant/config/
├── adguard/conf/
├── whisper/models/
├── jellyfin/config/
├── portainer/
├── wireguard/
├── filebrowser/
├── syncthing/
└── ollama/
```
## Passwörter ändern!
```bash
# WiFi AP
sudo nano /etc/hostapd/hostapd.conf
# wpa_passphrase=NEUES_PASSWORT
sudo systemctl restart hostapd
# WireGuard
nano ~/docker/docker-compose.yml
# PASSWORD=NEUES_PASSWORT
docker-compose up -d wg-easy
```
## Port Forwarding (für VPN von aussen)
Im Nighthawk Router:
- UDP 51820 → 192.168.1.50:51820
## Whisper Modelle
| Modell | VRAM | Speed | Qualität |
|--------|------|-------|----------|
| tiny | ~1GB | ⚡⚡⚡⚡ | ★★☆☆☆ |
| base | ~1GB | ⚡⚡⚡ | ★★★☆☆ |
| small | ~2GB | ⚡⚡ | ★★★★☆ |
| medium | ~5GB | ⚡ | ★★★★★ |
Ändern in `docker-compose.yml`:
```yaml
environment:
- WHISPER_MODEL=small
```
## Troubleshooting
### Domains funktionieren nicht
```bash
# DNS Test
nslookup home.lan 192.168.1.50
# AdGuard läuft?
docker logs adguard
```
### WiFi AP startet nicht
```bash
sudo systemctl status hostapd
sudo journalctl -u hostapd -f
iw dev
```
### GPU nicht erkannt
```bash
# Test
docker run --rm --runtime=nvidia nvidia/cuda:11.4-base nvidia-smi
# Jetson Stats
sudo pip3 install jetson-stats
jtop
```
### Container startet nicht
```bash
docker-compose logs <container-name>
docker-compose up <container-name> # ohne -d für live output
```
## Updates
```bash
cd ~/docker
docker-compose pull
docker-compose up -d
```
## Backup
```bash
# Alles
tar -czvf jetson-backup.tar.gz ~/docker/
# Nur Configs
tar -czvf ha-backup.tar.gz ~/docker/homeassistant/config
tar -czvf adguard-backup.tar.gz ~/docker/adguard/conf
```
## Nützliche Befehle
```bash
# Alle Container Status
docker ps -a
# Ressourcen
docker stats
# Jetson GPU/CPU Monitor
jtop
# Logs live
docker-compose logs -f
# Neustart einzelner Service
docker-compose restart homeassistant
# Shell in Container
docker exec -it homeassistant bash
```

48
adguard-dns-rewrites.txt Normal file
View file

@ -0,0 +1,48 @@
# =============================================================================
# ADGUARD DNS REWRITES
# Nach AdGuard Setup: Settings → DNS rewrites → Bulk Add
# Kopiere alles unten und füge es ein
# =============================================================================
# Home Assistant
home.lan 192.168.1.50
ha.lan 192.168.1.50
# AdGuard
adguard.lan 192.168.1.50
dns.lan 192.168.1.50
# Whisper
whisper.lan 192.168.1.50
stt.lan 192.168.1.50
# Jellyfin
jellyfin.lan 192.168.1.50
media.lan 192.168.1.50
tv.lan 192.168.1.50
# Portainer
portainer.lan 192.168.1.50
docker.lan 192.168.1.50
# VPN
vpn.lan 192.168.1.50
wg.lan 192.168.1.50
# Ollama
ollama.lan 192.168.1.50
llm.lan 192.168.1.50
# Cockpit
cockpit.lan 192.168.1.50
system.lan 192.168.1.50
# FileBrowser
files.lan 192.168.1.50
cloud.lan 192.168.1.50
# Syncthing
sync.lan 192.168.1.50
# Jetson direkt
jetson.lan 192.168.1.50

72
caddy/Caddyfile Normal file
View file

@ -0,0 +1,72 @@
# =============================================================================
# CADDY REVERSE PROXY
# Lokale Domains - kein HTTPS nötig im LAN
# =============================================================================
# Home Assistant
home.lan, ha.lan {
reverse_proxy localhost:8123
}
# AdGuard Home
adguard.lan, dns.lan {
reverse_proxy localhost:80
}
# Whisper Web UI
whisper.lan, stt.lan {
reverse_proxy localhost:7860
}
# Jellyfin
jellyfin.lan, media.lan, tv.lan {
reverse_proxy localhost:8096
}
# Portainer (Docker Management)
portainer.lan, docker.lan {
reverse_proxy localhost:9443 {
transport http {
tls_insecure_skip_verify
}
}
}
# Ollama API
ollama.lan, llm.lan {
reverse_proxy localhost:11434
}
# Whisper API
whisper-api.lan {
reverse_proxy localhost:9000
}
# WireGuard Easy (VPN Management)
vpn.lan, wg.lan {
reverse_proxy localhost:51821
}
# Cockpit (System Management) - falls installiert
cockpit.lan, system.lan {
reverse_proxy localhost:9090 {
transport http {
tls_insecure_skip_verify
}
}
}
# FileBrowser (Private Cloud)
files.lan, cloud.lan {
reverse_proxy localhost:8081
}
# Syncthing
sync.lan {
reverse_proxy localhost:8384
}
# Fallback - zeigt Status
:80 {
respond "Jetson Home Hub - Use: home.lan, adguard.lan, whisper.lan, jellyfin.lan, portainer.lan, vpn.lan, files.lan, sync.lan"
}

292
docker-compose.yml Normal file
View file

@ -0,0 +1,292 @@
version: '3.8'
# =============================================================================
# JETSON ORIN NANO - COMPLETE HOME HUB
# Alles mit Web UI - keine Console!
# =============================================================================
services:
# ===========================================
# NGINX PROXY MANAGER (Reverse Proxy mit GUI!)
# ===========================================
npm:
container_name: nginx-proxy-manager
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./npm/data:/data
- ./npm/letsencrypt:/etc/letsencrypt
# ===========================================
# HOMEPAGE - Zentrales Dashboard
# ===========================================
homepage:
container_name: homepage
image: ghcr.io/gethomepage/homepage:latest
restart: unless-stopped
ports:
- "3001:3000"
volumes:
- ./homepage:/app/config
- /var/run/docker.sock:/var/run/docker.sock:ro
# ===========================================
# PORTAINER - Docker Management UI
# ===========================================
portainer:
container_name: portainer
image: portainer/portainer-ce:latest
restart: unless-stopped
ports:
- "9443:9443"
- "8000:8000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer:/data
# ===========================================
# RUSTDESK SERVER (Self-hosted Remote Desktop)
# ===========================================
rustdesk-hbbs:
container_name: rustdesk-hbbs
image: rustdesk/rustdesk-server:latest
restart: unless-stopped
command: hbbs
ports:
- "21115:21115"
- "21116:21116"
- "21116:21116/udp"
- "21118:21118"
volumes:
- ./rustdesk:/root
depends_on:
- rustdesk-hbbr
rustdesk-hbbr:
container_name: rustdesk-hbbr
image: rustdesk/rustdesk-server:latest
restart: unless-stopped
command: hbbr
ports:
- "21117:21117"
- "21119:21119"
volumes:
- ./rustdesk:/root
# ===========================================
# HOME ASSISTANT
# ===========================================
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
restart: unless-stopped
privileged: true
network_mode: host
depends_on:
- adguard
volumes:
- ./homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
- /run/dbus:/run/dbus:ro
environment:
- TZ=Europe/Zurich
# ===========================================
# ADGUARD HOME (DNS + Ad Blocking + Local DNS)
# ===========================================
adguard:
container_name: adguard
image: adguard/adguardhome:latest
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp"
- "8080:80/tcp"
volumes:
- ./adguard/work:/opt/adguardhome/work
- ./adguard/conf:/opt/adguardhome/conf
# ===========================================
# WHISPER GRADIO WEB UI
# ===========================================
whisper-webui:
container_name: whisper-webui
build:
context: ./whisper-webui-jetson
dockerfile: Dockerfile
restart: unless-stopped
ports:
- "7860:7860"
volumes:
- ./whisper/models:/root/.cache/whisper
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
- WHISPER_MODEL=base
devices:
- /dev/snd:/dev/snd
# ===========================================
# WYOMING WHISPER (Home Assistant Voice)
# ===========================================
wyoming-whisper:
container_name: wyoming-whisper
image: rhasspy/wyoming-whisper:latest
restart: unless-stopped
ports:
- "10300:10300"
command: --model base --language de
volumes:
- ./whisper/wyoming:/data
runtime: nvidia
# ===========================================
# PIPER TTS (Text-to-Speech)
# ===========================================
piper:
container_name: piper
image: rhasspy/wyoming-piper:latest
restart: unless-stopped
ports:
- "10200:10200"
command: --voice de_DE-thorsten-high
volumes:
- ./piper:/data
# ===========================================
# JELLYFIN (Media Server)
# ===========================================
jellyfin:
container_name: jellyfin
image: jellyfin/jellyfin:latest
restart: unless-stopped
ports:
- "8096:8096"
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /media:/media:ro
environment:
- TZ=Europe/Zurich
- NVIDIA_VISIBLE_DEVICES=all
runtime: nvidia
devices:
- /dev/dri:/dev/dri
# ===========================================
# OLLAMA (Local LLM)
# ===========================================
ollama:
container_name: ollama
image: ollama/ollama:latest
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ./ollama:/root/.ollama
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
# ===========================================
# FILEBROWSER (Private Cloud)
# ===========================================
filebrowser:
container_name: filebrowser
image: filebrowser/filebrowser:latest
restart: unless-stopped
ports:
- "8081:80"
volumes:
- /home/d:/srv
- ./filebrowser/database.db:/database.db
environment:
- PUID=1000
- PGID=1000
# ===========================================
# AUTHENTIK - SSO mit Passkeys
# ===========================================
authentik-postgres:
container_name: authentik-postgres
image: postgres:16-alpine
restart: unless-stopped
volumes:
- ./authentik/database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS:-supersecret}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
healthcheck:
test: ["CMD-SHELL", "pg_isready -U authentik"]
interval: 30s
timeout: 5s
retries: 5
authentik-redis:
container_name: authentik-redis
image: redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
volumes:
- ./authentik/redis:/data
authentik:
container_name: authentik
image: ghcr.io/goauthentik/server:2024.2
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-supersecret}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-changeme-run-openssl-rand-60-base64}
ports:
- "9000:9000"
depends_on:
- authentik-postgres
- authentik-redis
authentik-worker:
container_name: authentik-worker
image: ghcr.io/goauthentik/server:2024.2
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-supersecret}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-changeme-run-openssl-rand-60-base64}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- authentik-postgres
- authentik-redis
# ===========================================
# SYNCTHING (File Sync)
# ===========================================
syncthing:
container_name: syncthing
image: syncthing/syncthing:latest
restart: unless-stopped
ports:
- "8384:8384"
- "22000:22000/tcp"
- "22000:22000/udp"
- "21027:21027/udp"
volumes:
- ./syncthing:/var/syncthing
- /home/d/sync:/data
environment:
- PUID=1000
- PGID=1000

21
first-boot.sh Normal file
View file

@ -0,0 +1,21 @@
#!/bin/bash
# =============================================================================
# FIRST BOOT - Run this from USB stick after JetPack flash
# =============================================================================
echo "Copying setup files to home directory..."
cp -r /media/$USER/*/jetson-setup ~/jetson-setup 2>/dev/null || \
cp -r /media/*/jetson-setup ~/jetson-setup 2>/dev/null || \
echo "Please copy jetson-setup folder manually to ~/"
if [ -d ~/jetson-setup ]; then
echo "Files copied. Now run:"
echo ""
echo " cd ~/jetson-setup"
echo " chmod +x setup.sh"
echo " sudo ./setup.sh"
echo ""
else
echo "ERROR: Could not find jetson-setup on USB"
echo "Copy manually and run setup.sh"
fi

19
homepage/bookmarks.yaml Normal file
View file

@ -0,0 +1,19 @@
# =============================================================================
# HOMEPAGE DASHBOARD - Bookmarks
# =============================================================================
- Management:
- Nighthawk Router:
- icon: router
href: http://192.168.1.1
- Proxy Manager:
- icon: nginx-proxy-manager.svg
href: http://192.168.1.50:81
- Dokumentation:
- Home Assistant:
- icon: home-assistant.svg
href: https://www.home-assistant.io/docs/
- RustDesk:
- icon: rustdesk.svg
href: https://rustdesk.com/docs/

6
homepage/docker.yaml Normal file
View file

@ -0,0 +1,6 @@
# =============================================================================
# HOMEPAGE DASHBOARD - Docker Configuration
# =============================================================================
my-docker:
socket: /var/run/docker.sock

105
homepage/services.yaml Normal file
View file

@ -0,0 +1,105 @@
# =============================================================================
# HOMEPAGE DASHBOARD - Services Configuration
# =============================================================================
- Smart Home:
- Home Assistant:
icon: home-assistant.svg
href: http://home.lan
description: Smart Home Zentrale
server: my-docker
container: homeassistant
widget:
type: homeassistant
url: http://192.168.1.50:8123
key: YOUR_HA_TOKEN
- Netzwerk:
- AdGuard Home:
icon: adguard-home.svg
href: http://adguard.lan
description: DNS & Werbeblocker
server: my-docker
container: adguard
widget:
type: adguard
url: http://192.168.1.50:8080
username: admin
password: YOUR_PASSWORD
- Nginx Proxy Manager:
icon: nginx-proxy-manager.svg
href: http://192.168.1.50:81
description: Reverse Proxy GUI
server: my-docker
container: nginx-proxy-manager
- Remote Access:
- RustDesk:
icon: rustdesk.svg
href: http://rustdesk.lan
description: Remote Desktop Server
server: my-docker
container: rustdesk-hbbs
- Portainer:
icon: portainer.svg
href: http://portainer.lan
description: Docker Management
server: my-docker
container: portainer
widget:
type: portainer
url: https://192.168.1.50:9443
env: 1
key: YOUR_PORTAINER_KEY
- AI & Voice:
- Whisper:
icon: openai.svg
href: http://whisper.lan
description: Sprache zu Text (GPU)
server: my-docker
container: whisper-webui
- Ollama:
icon: ollama.svg
href: http://ollama.lan
description: Lokale LLMs
server: my-docker
container: ollama
- Piper TTS:
icon: mdi-text-to-speech
href: "#"
description: Text zu Sprache
server: my-docker
container: piper
- Media:
- Jellyfin:
icon: jellyfin.svg
href: http://jellyfin.lan
description: Media Server
server: my-docker
container: jellyfin
widget:
type: jellyfin
url: http://192.168.1.50:8096
key: YOUR_JELLYFIN_KEY
enableBlocks: true
- Cloud & Sync:
- FileBrowser:
icon: filebrowser.svg
href: http://files.lan
description: Private Cloud
server: my-docker
container: filebrowser
- Syncthing:
icon: syncthing.svg
href: http://sync.lan
description: File Sync
server: my-docker
container: syncthing

40
homepage/settings.yaml Normal file
View file

@ -0,0 +1,40 @@
# =============================================================================
# HOMEPAGE DASHBOARD - Settings
# =============================================================================
title: Jetson Home Hub
description: Dein lokales Smart Home & AI Center
theme: dark
color: slate
background:
image: https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=1920
blur: sm
opacity: 30
cardBlur: md
favicon: https://raw.githubusercontent.com/home-assistant/assets/master/favicon/favicon.ico
layout:
Smart Home:
style: row
columns: 1
Netzwerk:
style: row
columns: 2
Remote Access:
style: row
columns: 2
AI & Voice:
style: row
columns: 3
Media:
style: row
columns: 1
Cloud & Sync:
style: row
columns: 2
headerStyle: clean

29
homepage/widgets.yaml Normal file
View file

@ -0,0 +1,29 @@
# =============================================================================
# HOMEPAGE DASHBOARD - Widgets (System Info)
# =============================================================================
- logo:
icon: https://developer.nvidia.com/sites/default/files/akamai/embedded/images/jetson-orin-nano/jetson-orin-nano-front.png
- greeting:
text_size: xl
text: Jetson Home Hub
- datetime:
text_size: l
format:
dateStyle: long
timeStyle: short
hour12: false
- resources:
cpu: true
memory: true
disk: /
cputemp: true
uptime: true
label: System
- search:
provider: google
target: _blank

18
infomaniak/.env.example Normal file
View file

@ -0,0 +1,18 @@
# =============================================================================
# AUTHENTIK ENVIRONMENT
# Für Infomaniak Server (185.143.102.153)
# =============================================================================
# PostgreSQL
PG_PASS=CHANGE_ME_SECURE_PASSWORD_HERE
# Authentik Secret Key (generieren mit: openssl rand 60 | base64 -w 0)
AUTHENTIK_SECRET_KEY=CHANGE_ME_RUN_openssl_rand_60_base64
# Optional: Email
# AUTHENTIK_EMAIL__HOST=smtp.example.com
# AUTHENTIK_EMAIL__PORT=587
# AUTHENTIK_EMAIL__USERNAME=
# AUTHENTIK_EMAIL__PASSWORD=
# AUTHENTIK_EMAIL__USE_TLS=true
# AUTHENTIK_EMAIL__FROM=authentik@hofmanns.ai

View file

@ -0,0 +1,128 @@
version: "3.8"
# =============================================================================
# INFOMANIAK SERVER - AUTH + RUSTDESK
# Für: 185.143.102.153 / hofmanns.ai
# =============================================================================
services:
# ===========================================
# AUTHENTIK - SSO mit Passkeys/WebAuthn
# ===========================================
postgresql:
container_name: authentik-postgres
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
start_period: 20s
interval: 30s
retries: 5
timeout: 5s
volumes:
- ./authentik/database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
redis:
container_name: authentik-redis
image: docker.io/library/redis:alpine
command: --save 60 1 --loglevel warning
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s
retries: 5
timeout: 3s
volumes:
- ./authentik/redis:/data
authentik-server:
container_name: authentik-server
image: ghcr.io/goauthentik/server:2024.2
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
volumes:
- ./authentik/media:/media
- ./authentik/custom-templates:/templates
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
authentik-worker:
container_name: authentik-worker
image: ghcr.io/goauthentik/server:2024.2
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./authentik/media:/media
- ./authentik/certs:/certs
- ./authentik/custom-templates:/templates
depends_on:
- postgresql
- redis
# ===========================================
# RUSTDESK SERVER - Remote Desktop
# ===========================================
rustdesk-hbbs:
container_name: rustdesk-hbbs
image: rustdesk/rustdesk-server:latest
restart: unless-stopped
command: hbbs
ports:
- "21115:21115"
- "21116:21116"
- "21116:21116/udp"
- "21118:21118"
volumes:
- ./rustdesk:/root
rustdesk-hbbr:
container_name: rustdesk-hbbr
image: rustdesk/rustdesk-server:latest
restart: unless-stopped
command: hbbr
ports:
- "21117:21117"
- "21119:21119"
volumes:
- ./rustdesk:/root
# ===========================================
# NGINX - Reverse Proxy mit SSL
# ===========================================
nginx:
container_name: nginx
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./nginx/html:/usr/share/nginx/html:ro
depends_on:
- authentik-server

View file

@ -0,0 +1,82 @@
# =============================================================================
# NGINX CONFIG - hofmanns.ai
# =============================================================================
# Authentik SSO
server {
listen 80;
server_name auth.hofmanns.ai;
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
server_name auth.hofmanns.ai;
ssl_certificate /etc/nginx/ssl/hofmanns.ai.crt;
ssl_certificate_key /etc/nginx/ssl/hofmanns.ai.key;
location / {
proxy_pass http://authentik-server:9000;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# RustDesk Web (optional)
server {
listen 80;
server_name rustdesk.hofmanns.ai;
location / {
proxy_pass http://rustdesk-hbbs:21118;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# Forward Auth für alle geschützten Services
# Beispiel für einen Service hinter Authentik
server {
listen 443 ssl http2;
server_name portal.hofmanns.ai;
ssl_certificate /etc/nginx/ssl/hofmanns.ai.crt;
ssl_certificate_key /etc/nginx/ssl/hofmanns.ai.key;
# Authentik Forward Auth
location /outpost.goauthentik.io {
proxy_pass http://authentik-server:9000/outpost.goauthentik.io;
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;
}
location / {
auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;
# Nach erfolgreicher Auth → weiterleiten zum Jetson
proxy_pass http://JETSON_TUNNEL_OR_IP:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location @goauthentik_proxy_signin {
internal;
add_header Set-Cookie $auth_cookie;
return 302 /outpost.goauthentik.io/start?rd=$request_uri;
}
}

View file

@ -0,0 +1,80 @@
#!/bin/bash
# =============================================================================
# INFOMANIAK SERVER SETUP
# Server: 185.143.102.153 (hofmanns.ai)
# Run as: debian user
# =============================================================================
set -e
echo "=========================================="
echo " HOFMANNS.AI - INFOMANIAK SERVER SETUP"
echo "=========================================="
# 1. Update System
echo "[1/6] System Update..."
sudo apt update && sudo apt upgrade -y
# 2. Install Docker (falls noch nicht)
echo "[2/6] Docker Check..."
if ! command -v docker &> /dev/null; then
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker debian
echo "Docker installed. Please logout and login again, then re-run this script."
exit 0
fi
# 3. Install Docker Compose
echo "[3/6] Docker Compose Check..."
if ! command -v docker-compose &> /dev/null; then
sudo apt install -y docker-compose-plugin
fi
# 4. Create directories
echo "[4/6] Creating directories..."
mkdir -p ~/hofmanns-auth/{authentik,rustdesk,nginx/conf.d,nginx/ssl,nginx/html}
# 5. Copy config files
echo "[5/6] Copying config files..."
cp docker-compose.yml ~/hofmanns-auth/
cp .env.example ~/hofmanns-auth/.env
cp -r nginx/* ~/hofmanns-auth/nginx/
# 6. Generate secrets
echo "[6/6] Generating secrets..."
cd ~/hofmanns-auth
# Generate Authentik secret
AUTHENTIK_SECRET=$(openssl rand 60 | base64 -w 0)
sed -i "s/CHANGE_ME_RUN_openssl_rand_60_base64/$AUTHENTIK_SECRET/" .env
# Generate PostgreSQL password
PG_PASSWORD=$(openssl rand -base64 32)
sed -i "s/CHANGE_ME_SECURE_PASSWORD_HERE/$PG_PASSWORD/" .env
echo ""
echo "=========================================="
echo " SETUP COMPLETE!"
echo "=========================================="
echo ""
echo " NEXT STEPS:"
echo ""
echo " 1. SSL Zertifikate erstellen (Let's Encrypt):"
echo " sudo certbot certonly --standalone -d auth.hofmanns.ai"
echo " sudo cp /etc/letsencrypt/live/auth.hofmanns.ai/* ~/hofmanns-auth/nginx/ssl/"
echo ""
echo " 2. Services starten:"
echo " cd ~/hofmanns-auth"
echo " docker compose up -d"
echo ""
echo " 3. Authentik Initial Setup:"
echo " https://auth.hofmanns.ai/if/flow/initial-setup/"
echo ""
echo " 4. RustDesk Key anzeigen:"
echo " cat ~/hofmanns-auth/rustdesk/id_ed25519.pub"
echo ""
echo " PORTS:"
echo " - 80/443 → Nginx (Authentik Proxy)"
echo " - 9000 → Authentik (intern)"
echo " - 21115-21119 → RustDesk"
echo ""

172
setup.sh Normal file
View file

@ -0,0 +1,172 @@
#!/bin/bash
# =============================================================================
# JETSON ORIN NANO 8GB - COMPLETE SETUP SCRIPT
# Run this ONCE after JetPack flash
# =============================================================================
set -e
USER="d"
WIFI_SSID="Home.8"
WIFI_PASS="123"
STATIC_IP="192.168.1.50"
GATEWAY="192.168.1.1"
echo "=========================================="
echo " JETSON ORIN NANO - HOME HUB SETUP"
echo "=========================================="
# 1. System Update
echo "[1/10] System Update..."
sudo apt update && sudo apt upgrade -y
# 2. Install required packages
echo "[2/10] Installing packages..."
sudo apt install -y \
docker.io docker-compose \
hostapd bridge-utils \
git curl wget htop \
python3-pip \
alsa-utils pulseaudio \
ffmpeg \
nvtop
# 3. Add user to docker group
echo "[3/10] Docker permissions..."
sudo usermod -aG docker $USER
# 4. Create swap (8GB for Whisper models)
echo "[4/10] Creating 8GB swap..."
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 5. Set static IP for eth0
echo "[5/10] Network configuration..."
sudo tee /etc/netplan/01-netcfg.yaml << EOF
network:
version: 2
renderer: NetworkManager
ethernets:
eth0:
addresses:
- ${STATIC_IP}/24
routes:
- to: default
via: ${GATEWAY}
nameservers:
addresses:
- 127.0.0.1
- 1.1.1.1
EOF
# 6. Disable NetworkManager for wlan (we use hostapd)
echo "[6/10] Disable NetworkManager for wlan..."
sudo tee /etc/NetworkManager/conf.d/99-unmanaged-wlan.conf << EOF
[keyfile]
unmanaged-devices=interface-name:wlan*
EOF
# 7. Setup Bridge for AP
echo "[7/10] Bridge configuration..."
sudo tee /etc/network/interfaces.d/br0 << EOF
auto br0
iface br0 inet dhcp
bridge_ports eth0
bridge_stp off
bridge_fd 0
EOF
# 8. hostapd configuration (Bridge Mode - NO NAT!)
echo "[8/10] hostapd configuration..."
sudo tee /etc/hostapd/hostapd.conf << EOF
interface=wlan0
bridge=br0
driver=nl80211
ssid=${WIFI_SSID}
hw_mode=a
channel=36
wmm_enabled=1
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=${WIFI_PASS}
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
country_code=CH
# 802.11n/ac settings
ieee80211n=1
ieee80211ac=1
ht_capab=[HT40+][SHORT-GI-20][SHORT-GI-40]
vht_capab=[SHORT-GI-80][MAX-MPDU-11454]
vht_oper_chwidth=1
vht_oper_centr_freq_seg0_idx=42
EOF
sudo sed -i 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd
# 9. Enable services
echo "[9/10] Enable services..."
sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl enable docker
# 10. Create docker directory structure
echo "[10/10] Creating directory structure..."
mkdir -p /home/$USER/docker/{homeassistant,adguard,whisper,jellyfin,portainer,filebrowser,syncthing,ollama,piper,rustdesk,homepage,npm}
mkdir -p /home/$USER/docker/homeassistant/config
mkdir -p /home/$USER/docker/adguard/{work,conf}
mkdir -p /home/$USER/docker/whisper/{models,wyoming}
mkdir -p /home/$USER/docker/jellyfin/{config,cache}
mkdir -p /home/$USER/docker/npm/{data,letsencrypt}
mkdir -p /home/$USER/docker/syncthing
mkdir -p /home/$USER/sync
# Copy all config files
cp /home/$USER/jetson-setup/docker-compose.yml /home/$USER/docker/
cp -r /home/$USER/jetson-setup/homepage/* /home/$USER/docker/homepage/
cp -r /home/$USER/jetson-setup/whisper-webui-jetson /home/$USER/docker/
# Create filebrowser database
touch /home/$USER/docker/filebrowser/database.db
# Install Cockpit for system management (Web UI)
sudo apt install -y cockpit
# Set permissions
chown -R $USER:$USER /home/$USER/docker
chown -R $USER:$USER /home/$USER/sync
echo ""
echo "=========================================="
echo " SETUP COMPLETE!"
echo "=========================================="
echo ""
echo " NEXT STEPS:"
echo " 1. REBOOT: sudo reboot"
echo " 2. After reboot, start services:"
echo " cd ~/docker && docker-compose up -d"
echo ""
echo " WEB UIs (alle im Browser!):"
echo " ─────────────────────────────────────────"
echo " http://192.168.1.50:3001 → Homepage Dashboard"
echo " http://192.168.1.50:81 → Nginx Proxy Manager"
echo " http://192.168.1.50:9443 → Portainer (Docker)"
echo " http://192.168.1.50:9090 → Cockpit (System)"
echo " http://192.168.1.50:8123 → Home Assistant"
echo " http://192.168.1.50:3000 → AdGuard Setup"
echo " http://192.168.1.50:7860 → Whisper (Speech→Text)"
echo " http://192.168.1.50:8096 → Jellyfin (Media)"
echo " http://192.168.1.50:8081 → FileBrowser (Cloud)"
echo " http://192.168.1.50:8384 → Syncthing"
echo ""
echo " RUSTDESK Server: 192.168.1.50"
echo " → Ports: 21115-21119"
echo " → Key: cat ~/docker/rustdesk/id_ed25519.pub"
echo ""
echo " WiFi AP: ${WIFI_SSID} (CHANGE PASSWORD!)"
echo ""

View file

@ -0,0 +1,16 @@
# Jetson Orin Nano optimized Whisper WebUI
FROM dustynv/whisper:r36.2.0
WORKDIR /app
# Install Gradio
RUN pip3 install --no-cache-dir gradio scipy
# Copy app
COPY app.py /app/app.py
EXPOSE 7860
ENV WHISPER_MODEL=base
CMD ["python3", "/app/app.py"]

177
whisper-webui-jetson/app.py Normal file
View file

@ -0,0 +1,177 @@
#!/usr/bin/env python3
"""
Whisper Web UI - Browser-based Speech-to-Text
Runs locally on Jetson Orin Nano with GPU acceleration
"""
import gradio as gr
import whisper
import torch
import os
import tempfile
# Load model (use GPU if available)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using device: {device}")
MODEL_SIZE = os.getenv("WHISPER_MODEL", "base")
print(f"Loading Whisper model: {MODEL_SIZE}")
model = whisper.load_model(MODEL_SIZE, device=device)
print("Model loaded!")
def transcribe_audio(audio_path, language="auto", task="transcribe"):
"""Transcribe audio file using Whisper"""
if audio_path is None:
return "No audio provided"
try:
# Transcribe
options = {
"task": task,
"fp16": device == "cuda"
}
if language != "auto":
options["language"] = language
result = model.transcribe(audio_path, **options)
# Format output
text = result["text"]
detected_lang = result.get("language", "unknown")
output = f"**Detected Language:** {detected_lang}\n\n"
output += f"**Transcription:**\n{text}\n\n"
# Add segments with timestamps
output += "**Segments:**\n"
for segment in result["segments"]:
start = segment["start"]
end = segment["end"]
segment_text = segment["text"]
output += f"[{start:.2f}s - {end:.2f}s] {segment_text}\n"
return output
except Exception as e:
return f"Error: {str(e)}"
def transcribe_microphone(audio, language="auto", task="transcribe"):
"""Transcribe from microphone input"""
if audio is None:
return "No audio recorded"
# audio is a tuple (sample_rate, numpy_array) from gradio
sr, audio_data = audio
# Save to temp file
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
import scipy.io.wavfile as wav
wav.write(f.name, sr, audio_data)
return transcribe_audio(f.name, language, task)
# Language options
LANGUAGES = [
("Auto Detect", "auto"),
("German", "de"),
("English", "en"),
("French", "fr"),
("Italian", "it"),
("Spanish", "es"),
("Portuguese", "pt"),
("Dutch", "nl"),
("Russian", "ru"),
("Chinese", "zh"),
("Japanese", "ja"),
("Korean", "ko"),
]
# Create Gradio interface
with gr.Blocks(title="Whisper Local - Speech to Text", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🎤 Whisper Local - Speech to Text
### Runs 100% locally on Jetson Orin Nano with GPU acceleration
No cloud, no internet required, your audio stays private.
""")
with gr.Tabs():
# Tab 1: Microphone
with gr.TabItem("🎙️ Microphone"):
gr.Markdown("Record audio directly from your microphone")
with gr.Row():
mic_input = gr.Audio(
sources=["microphone"],
type="numpy",
label="Record Audio"
)
with gr.Row():
mic_language = gr.Dropdown(
choices=LANGUAGES,
value="auto",
label="Language"
)
mic_task = gr.Radio(
choices=["transcribe", "translate"],
value="transcribe",
label="Task (translate = to English)"
)
mic_button = gr.Button("🚀 Transcribe", variant="primary")
mic_output = gr.Markdown(label="Result")
mic_button.click(
fn=transcribe_microphone,
inputs=[mic_input, mic_language, mic_task],
outputs=mic_output
)
# Tab 2: File Upload
with gr.TabItem("📁 File Upload"):
gr.Markdown("Upload an audio or video file")
with gr.Row():
file_input = gr.Audio(
sources=["upload"],
type="filepath",
label="Upload Audio/Video"
)
with gr.Row():
file_language = gr.Dropdown(
choices=LANGUAGES,
value="auto",
label="Language"
)
file_task = gr.Radio(
choices=["transcribe", "translate"],
value="transcribe",
label="Task (translate = to English)"
)
file_button = gr.Button("🚀 Transcribe", variant="primary")
file_output = gr.Markdown(label="Result")
file_button.click(
fn=transcribe_audio,
inputs=[file_input, file_language, file_task],
outputs=file_output
)
gr.Markdown(f"""
---
**Model:** {MODEL_SIZE} | **Device:** {device} | **GPU:** {'✅ CUDA' if device == 'cuda' else '❌ CPU'}
""")
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
ssl_verify=False
)

24
whisper-webui/Dockerfile Normal file
View file

@ -0,0 +1,24 @@
FROM nvcr.io/nvidia/l4t-pytorch:r35.2.1-pth2.0-py3
WORKDIR /app
# Install dependencies
RUN apt-get update && apt-get install -y \
ffmpeg \
git \
&& rm -rf /var/lib/apt/lists/*
# Install Python packages
RUN pip3 install --no-cache-dir \
openai-whisper \
gradio \
numpy \
torch \
torchaudio
# Create the web interface
COPY app.py /app/app.py
EXPOSE 7860
CMD ["python3", "app.py"]

177
whisper-webui/app.py Normal file
View file

@ -0,0 +1,177 @@
#!/usr/bin/env python3
"""
Whisper Web UI - Browser-based Speech-to-Text
Runs locally on Jetson Orin Nano with GPU acceleration
"""
import gradio as gr
import whisper
import torch
import os
import tempfile
# Load model (use GPU if available)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using device: {device}")
MODEL_SIZE = os.getenv("WHISPER_MODEL", "base")
print(f"Loading Whisper model: {MODEL_SIZE}")
model = whisper.load_model(MODEL_SIZE, device=device)
print("Model loaded!")
def transcribe_audio(audio_path, language="auto", task="transcribe"):
"""Transcribe audio file using Whisper"""
if audio_path is None:
return "No audio provided"
try:
# Transcribe
options = {
"task": task,
"fp16": device == "cuda"
}
if language != "auto":
options["language"] = language
result = model.transcribe(audio_path, **options)
# Format output
text = result["text"]
detected_lang = result.get("language", "unknown")
output = f"**Detected Language:** {detected_lang}\n\n"
output += f"**Transcription:**\n{text}\n\n"
# Add segments with timestamps
output += "**Segments:**\n"
for segment in result["segments"]:
start = segment["start"]
end = segment["end"]
segment_text = segment["text"]
output += f"[{start:.2f}s - {end:.2f}s] {segment_text}\n"
return output
except Exception as e:
return f"Error: {str(e)}"
def transcribe_microphone(audio, language="auto", task="transcribe"):
"""Transcribe from microphone input"""
if audio is None:
return "No audio recorded"
# audio is a tuple (sample_rate, numpy_array) from gradio
sr, audio_data = audio
# Save to temp file
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
import scipy.io.wavfile as wav
wav.write(f.name, sr, audio_data)
return transcribe_audio(f.name, language, task)
# Language options
LANGUAGES = [
("Auto Detect", "auto"),
("German", "de"),
("English", "en"),
("French", "fr"),
("Italian", "it"),
("Spanish", "es"),
("Portuguese", "pt"),
("Dutch", "nl"),
("Russian", "ru"),
("Chinese", "zh"),
("Japanese", "ja"),
("Korean", "ko"),
]
# Create Gradio interface
with gr.Blocks(title="Whisper Local - Speech to Text", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🎤 Whisper Local - Speech to Text
### Runs 100% locally on Jetson Orin Nano with GPU acceleration
No cloud, no internet required, your audio stays private.
""")
with gr.Tabs():
# Tab 1: Microphone
with gr.TabItem("🎙️ Microphone"):
gr.Markdown("Record audio directly from your microphone")
with gr.Row():
mic_input = gr.Audio(
sources=["microphone"],
type="numpy",
label="Record Audio"
)
with gr.Row():
mic_language = gr.Dropdown(
choices=LANGUAGES,
value="auto",
label="Language"
)
mic_task = gr.Radio(
choices=["transcribe", "translate"],
value="transcribe",
label="Task (translate = to English)"
)
mic_button = gr.Button("🚀 Transcribe", variant="primary")
mic_output = gr.Markdown(label="Result")
mic_button.click(
fn=transcribe_microphone,
inputs=[mic_input, mic_language, mic_task],
outputs=mic_output
)
# Tab 2: File Upload
with gr.TabItem("📁 File Upload"):
gr.Markdown("Upload an audio or video file")
with gr.Row():
file_input = gr.Audio(
sources=["upload"],
type="filepath",
label="Upload Audio/Video"
)
with gr.Row():
file_language = gr.Dropdown(
choices=LANGUAGES,
value="auto",
label="Language"
)
file_task = gr.Radio(
choices=["transcribe", "translate"],
value="transcribe",
label="Task (translate = to English)"
)
file_button = gr.Button("🚀 Transcribe", variant="primary")
file_output = gr.Markdown(label="Result")
file_button.click(
fn=transcribe_audio,
inputs=[file_input, file_language, file_task],
outputs=file_output
)
gr.Markdown(f"""
---
**Model:** {MODEL_SIZE} | **Device:** {device} | **GPU:** {'✅ CUDA' if device == 'cuda' else '❌ CPU'}
""")
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
ssl_verify=False
)

View file

@ -0,0 +1,6 @@
openai-whisper
gradio>=4.0.0
torch
torchaudio
numpy
scipy