Initial commit - Jetson Home Hub Setup (Face/Finger Auth, RustDesk, Authentik, Whisper)
This commit is contained in:
commit
cf46181e48
215
AI-SETUP-INSTRUCTIONS.md
Normal file
215
AI-SETUP-INSTRUCTIONS.md
Normal 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
370
CHAT-SUMMARY-COMPLETE.md
Normal 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
131
CHAT-SUMMARY.md
Normal 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
269
README.md
Normal 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
48
adguard-dns-rewrites.txt
Normal 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
72
caddy/Caddyfile
Normal 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
292
docker-compose.yml
Normal 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
21
first-boot.sh
Normal 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
19
homepage/bookmarks.yaml
Normal 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
6
homepage/docker.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# =============================================================================
|
||||||
|
# HOMEPAGE DASHBOARD - Docker Configuration
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
my-docker:
|
||||||
|
socket: /var/run/docker.sock
|
||||||
105
homepage/services.yaml
Normal file
105
homepage/services.yaml
Normal 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
40
homepage/settings.yaml
Normal 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
29
homepage/widgets.yaml
Normal 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
18
infomaniak/.env.example
Normal 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
|
||||||
128
infomaniak/docker-compose.yml
Normal file
128
infomaniak/docker-compose.yml
Normal 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
|
||||||
82
infomaniak/nginx/conf.d/hofmanns.conf
Normal file
82
infomaniak/nginx/conf.d/hofmanns.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
80
infomaniak/setup-infomaniak.sh
Normal file
80
infomaniak/setup-infomaniak.sh
Normal 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
172
setup.sh
Normal 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 ""
|
||||||
16
whisper-webui-jetson/Dockerfile
Normal file
16
whisper-webui-jetson/Dockerfile
Normal 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
177
whisper-webui-jetson/app.py
Normal 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
24
whisper-webui/Dockerfile
Normal 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
177
whisper-webui/app.py
Normal 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
|
||||||
|
)
|
||||||
6
whisper-webui/requirements.txt
Normal file
6
whisper-webui/requirements.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
openai-whisper
|
||||||
|
gradio>=4.0.0
|
||||||
|
torch
|
||||||
|
torchaudio
|
||||||
|
numpy
|
||||||
|
scipy
|
||||||
Loading…
Reference in a new issue