Move development runtime to isolated Athena Debian instance
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Ersteinrichtung
|
||||
|
||||
Beim ersten Öffnen der lokalen Oberfläche legt der Betreiber zwei getrennte
|
||||
Bei der ersten Einrichtung legt der Betreiber zwei getrennte
|
||||
Zugangsdaten fest:
|
||||
|
||||
1. **Oberflächenkennwort**: 16–256 Zeichen, ein Administratorzugang.
|
||||
@@ -17,19 +17,12 @@ Auf HTTP-Adressen kann die Browser-Zwischenablage gesperrt sein. Dann „Token a
|
||||
verwenden und manuell kopieren. Im ersten Schritt wird noch keine Sitzung erzeugt:
|
||||
nach dem Einrichten mit dem gewählten Kennwort anmelden.
|
||||
|
||||
Lokaler Zustand: `.state/auth.json`, Verzeichnis 0700, Datei 0600, in Git und
|
||||
Docker-Builds ausgeschlossen. Es gibt keine voreingestellten oder vom Agenten
|
||||
festgelegten Zugangsdaten. Auch die Mac-Verwaltungsoberfläche verlangt jetzt die
|
||||
Ersteinrichtung und Anmeldung. Status, Hardware und Steuerung sind davor gesperrt.
|
||||
Der Ersteinrichtungs-Endpunkt ist einmalig und nur in der Loopback-Instanz aktiv.
|
||||
|
||||
Die optionale Athena-Serverinstallation erhält in ihrem Installationsformular
|
||||
**eigene** Zugangsdaten (Kennwort und API-Token). Diese gelten für die Server-Instanz,
|
||||
nicht für die Mac-Instanz. Die Server-Anwendung wird bereits provisioniert gestartet;
|
||||
es gibt keinen ungeschützten Fernzugriff, mit dem ein beliebiger Besucher den
|
||||
Server beanspruchen könnte. Ein vorhandener 0.2-Kennwortstand bleibt lesbar;
|
||||
API-Zugriff ist dort zunächst deaktiviert, bis ein Token eingerichtet wird.
|
||||
Bestehende Server-Container werden durch diese Codeänderung nicht automatisch aktualisiert.
|
||||
Der Debian-Installer richtet die Zugangsdaten vor dem Start ein. Speicherung unter
|
||||
`state/auth.json` im privaten Installationsverzeichnis, Datei 0600. Es gibt keine
|
||||
voreingestellten Zugangsdaten. Status, Hardware und Steuerung sind anmeldepflichtig.
|
||||
Der Ersteinrichtungs-Endpunkt ist im provisionierten Serverbetrieb deaktiviert.
|
||||
Die isolierte Entwicklungsinstanz erlaubt eine neue einmalige Einrichtung über
|
||||
den SSH-Tunnel. Zugangsdaten der früheren Vorschau werden nicht übertragen.
|
||||
|
||||
## Einstellungen → Zugang & API
|
||||
|
||||
@@ -41,7 +34,7 @@ Bestehende Server-Container werden durch diese Codeänderung nicht automatisch a
|
||||
Browser-Sitzungen bleiben gültig. API-Clients müssen den neuen Token erhalten.
|
||||
- **Abmelden:** beendet die aktuelle Sitzung serverseitig.
|
||||
|
||||
Die Einstellungsseite benennt die gerade verwaltete Instanz (Mac oder Athena).
|
||||
Die Einstellungsseite benennt die gerade verwaltete Instanz auf dem Debian-Server.
|
||||
Es gibt zunächst **einen Administrator**, keine Benutzernamen, weiteren Benutzer,
|
||||
Rollen, persönlichen Tokens oder Passwort-zurücksetzen-per-E-Mail.
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Entwicklung auf Athena (Debian)
|
||||
|
||||
Zielsystem: Debian 13, RTX 5080 und RTX 3060. Der Arbeitsplatz dient nur als
|
||||
Editor/SSH-Client und Browser. Die Anwendung und ihre Tests laufen auf Athena.
|
||||
|
||||
- Quellcode: `/opt/athena-deck-dev/source`
|
||||
- Eigener Container: `athena-deck-dev`
|
||||
- Zustand und Installationsmanifest: `/opt/athena-deck-dev/runtime`
|
||||
- Serverbindung: ausschließlich `127.0.0.1:8108`
|
||||
- GPU-Zugriff: NVIDIA `utility` für Telemetrie; keine Modellstarts.
|
||||
|
||||
Vom Arbeitsplatz:
|
||||
|
||||
```sh
|
||||
ssh -i /Users/mike_i386/.ssh/athena_key -o BatchMode=yes -N -o ExitOnForwardFailure=yes -L 8108:127.0.0.1:8108 root@192.168.1.212
|
||||
```
|
||||
|
||||
Browser: http://127.0.0.1:8108 . Die Adresse führt durch den Tunnel zum Server.
|
||||
Beim ersten Öffnen neue Zugangsdaten einrichten. Vorhandene Zugangsdaten werden
|
||||
nicht vom Arbeitsplatz übertragen. Browser-Profilentwürfe bleiben bei gleicher
|
||||
Adresse erhalten. Nur diese isolierte Entwicklungsinstanz erlaubt die einmalige
|
||||
Browser-Einrichtung über den SSH-Tunnel (`development_setup` im Manifest).
|
||||
Der normale Debian-Installer provisioniert weiterhin vor dem Start.
|
||||
|
||||
Auf Athena:
|
||||
|
||||
```sh
|
||||
cd /opt/athena-deck-dev/source
|
||||
./install.sh --status --directory /opt/athena-deck-dev/runtime
|
||||
./install.sh --stop --directory /opt/athena-deck-dev/runtime
|
||||
./install.sh --start --directory /opt/athena-deck-dev/runtime
|
||||
# Nach gezielter Aktualisierung der Quelldateien:
|
||||
./install.sh --update --directory /opt/athena-deck-dev/runtime
|
||||
python3 -m unittest discover -s . -v
|
||||
```
|
||||
|
||||
Diese Befehle verwalten ausschließlich den eigenen, per Label geprüften
|
||||
Deck-Container. Produktiver Router, Modelle, WireGuard und Hostkonfiguration
|
||||
werden nicht verändert. Kein Docker-Socket im Webcontainer.
|
||||
|
||||
Modellkatalog, Downloads, Profile und llama.cpp-Build/Update sind weiterhin
|
||||
GUI-Entwürfe. Die Netzwerk-Erweiterung ist in dieser Installation deaktiviert.
|
||||
Das Verschieben der Entwicklung aktiviert keine dieser Funktionen automatisch.
|
||||
+2
-1
@@ -89,7 +89,8 @@ Danach [Athena Deck](http://127.0.0.1:8110) öffnen und mit dem bei der Installa
|
||||
gewählten Kennwort anmelden. Terminal offen halten; Ctrl+C beendet nur den Tunnel.
|
||||
Bei anderem Installationsport beide Portangaben entsprechend anpassen. Der lokale
|
||||
Browserport sollte dem gewählten Serverport entsprechen (Host-Allowlist).
|
||||
Die bestehende Mac-Vorschau auf Port 8108 bleibt eine separate Instanz.
|
||||
Der Arbeitsplatz führt nur den Browser und SSH-Tunnel aus. Auch die Entwicklung
|
||||
läuft auf Athena; siehe [DEVELOPMENT.md](DEVELOPMENT.md).
|
||||
|
||||
## Betrieb und Updates
|
||||
|
||||
|
||||
@@ -18,37 +18,24 @@ Downloads und Builds sind deaktiviert. Details: [GUI-Prototyp](STUDIO.md).
|
||||
## Zugang und API-Token
|
||||
|
||||
Beim ersten Öffnen werden Oberflächenkennwort und separater API-Token eingerichtet.
|
||||
Beide sind unter **Zugang & API** änderbar. Die lokale Vorschau ist jetzt ebenfalls
|
||||
anmeldepflichtig. Details und API-Rechte: [Zugangsverwaltung](ACCESS.md).
|
||||
Beide sind unter **Zugang & API** änderbar. Der Debian-Installer richtet den Zugang vor dem Serverstart ein. Details und API-Rechte: [Zugangsverwaltung](ACCESS.md).
|
||||
|
||||
## WireGuard-Einstellungen
|
||||
|
||||
Installation einer getrennten Server-Instanz, sicherer Conf-Import und LAN-/Tunnel-Zugriff
|
||||
sind jetzt unter Einstellungen verfügbar. Einrichtung, Sicherheitsgrenzen und Tests:
|
||||
[WireGuard-Modul](network/README.md). Ohne Installation bleibt die Mac-Vorschau
|
||||
rein lokal; erst der Installationsknopf erstellt den neuen Container auf Athena.
|
||||
Das separate [WireGuard-Modul](network/README.md) enthält Conf-Import und
|
||||
LAN-/Tunnel-Zugriff. In der aktuellen Debian-Entwicklungsinstanz ist es nicht
|
||||
angebunden; der Zugriff erfolgt über SSH-Tunnel. Der bestehende Gateway bleibt unverändert.
|
||||
|
||||
## Start und Stopp auf dem Mac
|
||||
## Zielsystem und Entwicklung
|
||||
|
||||
```sh
|
||||
cd /Users/mike_i386/Documents/ChatGPT/Athena/AI-Profile-Router/experiments/athena-control-ui
|
||||
python3 server.py --port 8108
|
||||
```
|
||||
Athena Deck läuft vollständig auf einem Debian-Server mit RTX 5080 und RTX 3060.
|
||||
Oberfläche, API, Demo-Prozess und Hardware-Erfassung laufen dort. Der Arbeitsplatz
|
||||
benötigt nur Browser und SSH; er ist kein Anwendungsserver. Auch Builds und
|
||||
Integrationstests werden auf Athena ausgeführt. Modelllaufzeiten sind noch nicht angebunden.
|
||||
|
||||
GUI: http://127.0.0.1:8108 · Beenden mit Ctrl+C im Startterminal.
|
||||
SIGTERM beendet ebenfalls den Server und seinen Demo-Kindprozess sauber.
|
||||
Ein belegter UI-Port führt zum Abbruch, nicht zur Übernahme eines Dienstes.
|
||||
Der Demo-Port wird vom Betriebssystem vergeben und als bereits gebundener
|
||||
Socket an den Kindprozess weitergereicht (keine Port-Prüfungs-Race).
|
||||
Kein SSH-Tunnel erforderlich. Die Anwendung bindet ausschließlich Loopback.
|
||||
Im separaten Git-Repository liegen diese Dateien im Wurzelverzeichnis;
|
||||
dort entsprechend `cd` in den Checkout und denselben Startbefehl verwenden.
|
||||
|
||||
Hardware-Abfragen verwenden den vorhandenen SSH-Zugang:
|
||||
`ssh -i ~/.ssh/athena_key -o BatchMode=yes root@192.168.1.212`.
|
||||
Der bekannte Hostschlüssel muss bereits hinterlegt sein. Schlüssel werden
|
||||
weder kopiert noch ausgegeben. Athena erhält nur den Collector über stdin;
|
||||
auf dem Host werden keine Dateien angelegt und keine Dienste gesteuert.
|
||||
Die isolierte Entwicklungsinstallation ist in [DEVELOPMENT.md](DEVELOPMENT.md)
|
||||
beschrieben. Hardware wird direkt über `/proc`, hwmon und nvidia-smi gelesen;
|
||||
Deck benötigt dafür keinen SSH-Schlüssel. Der Demo-Prozess lädt kein Modell.
|
||||
|
||||
## Interne API v1
|
||||
|
||||
@@ -75,8 +62,8 @@ curl -X POST -H 'Authorization: Bearer <API-TOKEN>' http://127.0.0.1:8108/api/v1
|
||||
curl -X POST -H 'Authorization: Bearer <API-TOKEN>' http://127.0.0.1:8108/api/v1/demo/stop
|
||||
```
|
||||
|
||||
Hardware: `available` bezeichnet Erfolg der SSH-Collector-Abfrage, einzelne
|
||||
Messwerte können trotzdem fehlen (`null`). SSH-Ausfall liefert available=false,
|
||||
Hardware: `available` bezeichnet Erfolg der Hardware-Abfrage, einzelne
|
||||
Messwerte können trotzdem fehlen (`null`). Ein Ausfall der Erfassung liefert available=false,
|
||||
leere Daten und eine Fehlermeldung, keine alten Werte als vermeintliches Livebild.
|
||||
CPU-Auslastung: 250-ms-Differenz aus `/proc/stat` ohne doppelte Guest-Zählung.
|
||||
RAM: MemTotal minus MemAvailable aus `/proc/meminfo`, Einheit Bytes.
|
||||
@@ -90,7 +77,7 @@ Keine Historie und kein Hintergrund-Collector bei geschlossener Hardware-Seite.
|
||||
## Struktur und Grenzen
|
||||
|
||||
- `server.py`: API, separater HardwareProvider, eigenständiger DemoService.
|
||||
- `demo.py`: Health-only HTTP-Kindprozess auf dem Mac, ohne Modellabhängigkeiten.
|
||||
- `demo.py`: Health-only HTTP-Kindprozess auf dem Debian-Server, ohne Modellabhängigkeiten.
|
||||
- `collect_hardware.py`: fest begrenzte lesende Linux-Hardware-Abfragen.
|
||||
- `index.html`, `app.js`, `style.css`: neue responsive Oberfläche.
|
||||
- `test_server.py`: Prozess-Lebenszyklus, Kontrollgrenzen, Hardware-Ausfall.
|
||||
@@ -102,8 +89,7 @@ mit derselben Status-/Start-/Stopp-Trennung erhalten. Noch kein Worker-Registry,
|
||||
Scheduler oder produktionsreifer Worker-Supervisor. Die optionale Server-Instanz
|
||||
hat eine eigene Passwortanmeldung; ihre Netzwerkgrenzen stehen in der Modul-Dokumentation.
|
||||
SIGKILL/Absturz-Cleanup ist nicht implementiert; regulär Ctrl+C/SIGTERM verwenden.
|
||||
SSH verwendet den vorgegebenen Root-Zugang; ein eingeschränktes Telemetrie-Konto
|
||||
wäre ein späterer eigener Auftrag. Der Hardware-Collector ändert keine Host-Konfiguration. Die optionale
|
||||
Der Hardware-Collector ändert keine Host-Konfiguration. Die optionale
|
||||
Netzwerkmodul-Installation erzeugt einen eigenen Docker-Container samt Portbindungen.
|
||||
|
||||
## Verifikation am 28.09.2026
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ def collect():
|
||||
gpus.append(dict(index=int(index), name=name, uuid=uuid, total_mib=number(total), used_mib=number(used), percent=number(usage), temperature_c=number(temp)))
|
||||
except (OSError, subprocess.SubprocessError, ValueError):
|
||||
errors.append('GPU-Messwerte nicht verfügbar')
|
||||
return dict(source='Athena · SSH /proc + hwmon + nvidia-smi', sampled_at=time.time(), cpu=cpu, ram=ram, gpus=gpus, errors=errors)
|
||||
return dict(source='Debian-Server · /proc + hwmon + nvidia-smi', sampled_at=time.time(), cpu=cpu, ram=ram, gpus=gpus, errors=errors)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ COPY index.html app.js studio.js style.css login.html login.js access-ui.js netw
|
||||
COPY network/__init__.py network/client.py network/config.py network/rpc.py /app/network/
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 HOME=/tmp \
|
||||
DECK_BIND_HOST=0.0.0.0 DECK_STATE_DIR=/var/lib/deck \
|
||||
DECK_REQUIRE_SETUP=1 DECK_LOCAL_HARDWARE=1 DECK_LOCATION=Server \
|
||||
DECK_REQUIRE_SETUP=1 DECK_LOCAL_HARDWARE=1 DECK_LOCATION=Athena-Debian \
|
||||
DECK_NETWORK_MODE=disabled
|
||||
USER 65534:65534
|
||||
CMD ["python3", "server.py", "--port", "8108"]
|
||||
|
||||
+7
-2
@@ -98,6 +98,10 @@ def launch(config):
|
||||
'-p','127.0.0.1:'+str(config['port'])+':8108',
|
||||
'--health-cmd', 'python3 -c "import urllib.request,json; assert json.load(urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3))[\'initialized\']"',
|
||||
'--health-interval','30s','--health-timeout','5s','--health-retries','3']
|
||||
if config.get('development_setup'):
|
||||
args[args.index('--health-cmd')+1] = 'python3 -c "import urllib.request; urllib.request.urlopen(\'http://127.0.0.1:8108/api/v1/auth/status\',timeout=3)"'
|
||||
# Explicit isolated development bootstrap, reachable only through host loopback/SSH.
|
||||
args += ['-e','DECK_REQUIRE_SETUP=0']
|
||||
if config['gpu_telemetry']:
|
||||
args += ['--gpus','all','-e','NVIDIA_DRIVER_CAPABILITIES=utility']
|
||||
args.append(config['image'])
|
||||
@@ -111,7 +115,7 @@ def ready(config):
|
||||
if item and item['State']['Running']:
|
||||
try:
|
||||
with urllib.request.urlopen('http://127.0.0.1:'+str(config['port'])+'/api/v1/auth/status',timeout=2) as r:
|
||||
if json.load(r).get('initialized') is True:
|
||||
if json.load(r).get('initialized') is True or config.get('development_setup'):
|
||||
return
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
@@ -226,8 +230,9 @@ def main():
|
||||
elif args.stop:
|
||||
if item:run('docker','stop','--time','15',config['name'])
|
||||
elif args.start:
|
||||
if not (args.directory/'state/auth.json').exists():
|
||||
if not (args.directory/'state/auth.json').exists() and not config.get('development_setup'):
|
||||
raise RuntimeError('Zugangseinrichtung unvollständig. Siehe INSTALL.md zur Wiederaufnahme.')
|
||||
if (args.directory/'state/auth.json').exists():
|
||||
os.chown(args.directory/'state/auth.json',65534,65534)
|
||||
if item:run('docker','start',config['name'])
|
||||
else:launch(config)
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ with patch('getpass.getpass',side_effect=lambda _:next(values)),patch('sys.stdin
|
||||
probe="""import json,sys,urllib.request
|
||||
v=json.load(sys.stdin)
|
||||
req=urllib.request.Request('http://127.0.0.1:8108/api/v1/status',headers={'Authorization':'Bearer '+v['token']})
|
||||
with urllib.request.urlopen(req,timeout=3) as r:assert json.load(r)['location']=='Server'
|
||||
with urllib.request.urlopen(req,timeout=3) as r:assert json.load(r)['location']=='Athena-Debian'
|
||||
"""
|
||||
for _ in range(20):
|
||||
try:run('docker','exec','-i',NAME,'python3','-c',probe,data=json.dumps({'token':token}));break
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ function networkButtons(){
|
||||
async function networkPage(){
|
||||
const view=document.querySelector('#view');
|
||||
if(!document.querySelector('#network-page')){
|
||||
view.innerHTML=`<div id="network-page"><div class="kicker">EINSTELLUNGEN / NETZWERK</div><h1>Dein Weg zu Athena.</h1><p>Ein eigener WireGuard-Zugang für Athena Deck und seine internen API-Endpunkte.</p><section class="card network-card"><div class="card-top"><h2>WireGuard-Modul</h2><span id="network-state" class="pill">Wird geprüft …</span></div><div id="network-details" class="network-status"></div><p id="network-notice"></p><div id="network-setup"><h2>Auf Athena einrichten</h2><p>Installiert WireGuard und Deck in einem eigenen Container. Der vorhandene Gateway bleibt unverändert. Die erste Einrichtung startet im LAN-Modus auf Port 8110.</p><label for="network-password">Neues Deck-Passwort · mindestens 16 Zeichen</label><input id="network-password" type="password" autocomplete="new-password" minlength="16" maxlength="256"><label for="network-api-token">Eigener API-Token für die Server-Instanz</label><input id="network-api-token" type="password" autocomplete="off" minlength="32" maxlength="256"><button id="network-generate-token" type="button" class="secondary">API-Token erzeugen</button><button id="network-show-token" type="button" class="secondary">API-Token anzeigen</button><button id="network-copy-token" type="button" class="secondary">API-Token kopieren</button><p>Vor der Installation kopieren und sicher speichern. Dieser Token ist vom lokalen Mac-Zugang getrennt.</p><button id="network-install">Modul auf Athena installieren</button><p class="note">Benötigt den vorhandenen SSH-Zugang und Docker auf Athena. LAN-Zugriff verwendet HTTP; für vertrauliche Eingaben WireGuard oder SSH-Tunnel verwenden.</p></div><div id="network-config" hidden><label for="network-file">Eigene WireGuard-Konfiguration (.conf)</label><input id="network-file" type="file" accept=".conf,text/plain"><p>Verwende einen eigenen Peer für Deck. Schlüssel werden nicht angezeigt. Ein Peer und IPv4 werden unterstützt; Shell-Hooks werden abgelehnt.</p><button id="network-import">Konfiguration importieren</button><button id="network-delete" class="secondary">Konfiguration löschen</button><div class="note"><button id="network-connect">WireGuard aktivieren</button><button id="network-disconnect" class="secondary">WireGuard deaktivieren</button><p>Aktivieren richtet den Tunnel ein. „Verbunden“ erscheint erst nach einem aktuellen Handshake.</p></div></div></section><section class="card network-card" id="network-access"><h2>Zugriff auf Deck und API</h2><label for="network-mode">Erreichbarkeit</label><select id="network-mode"><option value="lan">Nur lokales Netzwerk</option><option value="tunnel">Nur WireGuard-Tunnel</option><option value="both">Tunnel und lokales Netzwerk</option></select><button id="network-apply">Zugriffsmodus testen</button><p id="network-current"></p><div id="network-trial" hidden><p id="network-countdown" class="warning"></p><button id="network-confirm">Zugriff hier bestätigen</button><button id="network-cancel" class="secondary">Änderung zurücknehmen</button><p>Öffne die angezeigte Zieladresse und bestätige dort innerhalb von 120 Sekunden. Ohne Bestätigung wird der vorige Modus wiederhergestellt.</p></div><p class="note">Der lokale Mac-Zugang per SSH bleibt als Verwaltungskanal erhalten. Produktionsendpunkte werden nicht umgestellt. Im Modus „Nur Tunnel“ gibt es bei Tunnelausfall keinen LAN-Fallback.</p></section><p id="network-message" class="network-message" role="status" aria-live="polite"></p></div>`;
|
||||
view.innerHTML=`<div id="network-page"><div class="kicker">EINSTELLUNGEN / NETZWERK</div><h1>Dein Weg zu Athena.</h1><p>Ein eigener WireGuard-Zugang für Athena Deck und seine internen API-Endpunkte.</p><section class="card network-card"><div class="card-top"><h2>WireGuard-Modul</h2><span id="network-state" class="pill">Wird geprüft …</span></div><div id="network-details" class="network-status"></div><p id="network-notice"></p><div id="network-setup"><h2>Auf Athena einrichten</h2><p>Installiert WireGuard und Deck in einem eigenen Container. Der vorhandene Gateway bleibt unverändert. Die erste Einrichtung startet im LAN-Modus auf Port 8110.</p><label for="network-password">Neues Deck-Passwort · mindestens 16 Zeichen</label><input id="network-password" type="password" autocomplete="new-password" minlength="16" maxlength="256"><label for="network-api-token">Eigener API-Token für die Server-Instanz</label><input id="network-api-token" type="password" autocomplete="off" minlength="32" maxlength="256"><button id="network-generate-token" type="button" class="secondary">API-Token erzeugen</button><button id="network-show-token" type="button" class="secondary">API-Token anzeigen</button><button id="network-copy-token" type="button" class="secondary">API-Token kopieren</button><p>Vor der Installation kopieren und sicher speichern. Dieser Token gehört zur jeweiligen Server-Instanz.</p><button id="network-install">Modul auf Athena installieren</button><p class="note">Benötigt den vorhandenen SSH-Zugang und Docker auf Athena. LAN-Zugriff verwendet HTTP; für vertrauliche Eingaben WireGuard oder SSH-Tunnel verwenden.</p></div><div id="network-config" hidden><label for="network-file">Eigene WireGuard-Konfiguration (.conf)</label><input id="network-file" type="file" accept=".conf,text/plain"><p>Verwende einen eigenen Peer für Deck. Schlüssel werden nicht angezeigt. Ein Peer und IPv4 werden unterstützt; Shell-Hooks werden abgelehnt.</p><button id="network-import">Konfiguration importieren</button><button id="network-delete" class="secondary">Konfiguration löschen</button><div class="note"><button id="network-connect">WireGuard aktivieren</button><button id="network-disconnect" class="secondary">WireGuard deaktivieren</button><p>Aktivieren richtet den Tunnel ein. „Verbunden“ erscheint erst nach einem aktuellen Handshake.</p></div></div></section><section class="card network-card" id="network-access"><h2>Zugriff auf Deck und API</h2><label for="network-mode">Erreichbarkeit</label><select id="network-mode"><option value="lan">Nur lokales Netzwerk</option><option value="tunnel">Nur WireGuard-Tunnel</option><option value="both">Tunnel und lokales Netzwerk</option></select><button id="network-apply">Zugriffsmodus testen</button><p id="network-current"></p><div id="network-trial" hidden><p id="network-countdown" class="warning"></p><button id="network-confirm">Zugriff hier bestätigen</button><button id="network-cancel" class="secondary">Änderung zurücknehmen</button><p>Öffne die angezeigte Zieladresse und bestätige dort innerhalb von 120 Sekunden. Ohne Bestätigung wird der vorige Modus wiederhergestellt.</p></div><p class="note">Der SSH-Zugang zum Debian-Server bleibt als Verwaltungskanal erhalten. Produktionsendpunkte werden nicht umgestellt. Im Modus „Nur Tunnel“ gibt es bei Tunnelausfall keinen LAN-Fallback.</p></section><p id="network-message" class="network-message" role="status" aria-live="polite"></p></div>`;
|
||||
document.querySelector('#network-install').onclick=()=>{const input=document.querySelector('#network-password'),password=input.value,token=document.querySelector('#network-api-token'),api_token=token.value;input.value='';token.value='';networkAction('install',{password,api_token});};
|
||||
document.querySelector('#network-generate-token').onclick=()=>{document.querySelector('#network-api-token').value=accessToken();};
|
||||
document.querySelector('#network-show-token').onclick=()=>{const input=document.querySelector('#network-api-token');input.type=input.type==='password'?'text':'password';document.querySelector('#network-show-token').textContent=input.type==='password'?'API-Token anzeigen':'API-Token verbergen';};
|
||||
@@ -44,7 +44,7 @@ async function networkPage(){
|
||||
const labels={'disabled-module':'Nicht angebunden',unavailable:'Nicht bereit',installing:'Installation läuft',disabled:'Deaktiviert',connecting:'Warte auf Handshake',connected:'Verbunden'};
|
||||
document.querySelector('#network-state').textContent=labels[s.state]||'Nicht verfügbar';
|
||||
const details=document.querySelector('#network-details');details.replaceChildren();
|
||||
for(const [label,value] of [['Konfiguration',s.configured?'Gespeichert':'Keine'],['Letzter Handshake',s.latest_handshake?new Date(s.latest_handshake*1000).toLocaleString('de-DE'):'Noch keiner'],['Dieser Zugang',s.ingress==='management'?'Mac / SSH-Verwaltung':s.ingress==='tunnel'?'WireGuard-Tunnel':'LAN']]){const d=document.createElement('div'),small=document.createElement('small'),strong=document.createElement('strong');small.textContent=label;strong.textContent=value;d.append(small,strong);details.append(d);}
|
||||
for(const [label,value] of [['Konfiguration',s.configured?'Gespeichert':'Keine'],['Letzter Handshake',s.latest_handshake?new Date(s.latest_handshake*1000).toLocaleString('de-DE'):'Noch keiner'],['Dieser Zugang',s.ingress==='management'?'SSH-Verwaltung':s.ingress==='tunnel'?'WireGuard-Tunnel':'LAN']]){const d=document.createElement('div'),small=document.createElement('small'),strong=document.createElement('strong');small.textContent=label;strong.textContent=value;d.append(small,strong);details.append(d);}
|
||||
for(const [label,url] of [['LAN',s.lan_url],['Tunnel',s.tunnel_url]])if(url){const d=document.createElement('div'),a=document.createElement('a');a.href=url+'/#network';a.textContent=label+': '+url;a.target='_blank';a.rel='noopener';d.append(a);details.append(d);}
|
||||
document.querySelector('#network-setup').hidden=!!s.installed;
|
||||
document.querySelector('#network-config').hidden=false;
|
||||
|
||||
+9
-7
@@ -6,7 +6,8 @@ Einstellungen → Netzwerk bietet Installation, Conf-Import, Aktivieren/Deaktivi
|
||||
Handshake-Status und die Modi **LAN**, **Tunnel**, **beides**. Diese Modi gelten
|
||||
für die Server-Instanz von Athena Deck und alle ihre `/api/v1/*`-Endpunkte.
|
||||
Produktive Router-, Modell-, Audio- und Bild-Endpunkte werden nicht übernommen.
|
||||
Die lokale Mac-Instanz bleibt ein separater SSH-Verwaltungskanal.
|
||||
Die aktuelle Debian-Entwicklungsinstanz verwendet dieses Modul noch nicht.
|
||||
Die folgenden Angaben dokumentieren den separat getesteten Netzwerk-Prototyp.
|
||||
|
||||
Die Implementierung benutzt einen eigenen Container `athena-deck-network` mit
|
||||
eigenem Netzwerk-Namespace. Im Container laufen ein eingeschränkter Root-Helper
|
||||
@@ -23,7 +24,7 @@ Port-Veröffentlichung; eine Aussage „keinerlei Netzwerkänderung“ wäre daf
|
||||
|
||||
## Einrichtung in der Oberfläche
|
||||
|
||||
1. Auf dem Mac http://127.0.0.1:8108/#network öffnen.
|
||||
1. Die Verwaltungsoberfläche über den SSH-Tunnel öffnen (separater Netzwerk-Prototyp).
|
||||
2. Eigenes Deck-Passwort (mindestens 16 Zeichen) und separaten API-Token
|
||||
(mindestens 32 Zeichen) vergeben, Token sichern und **Modul auf Athena
|
||||
installieren** wählen. Installation läuft asynchron und meldet ihren Status.
|
||||
@@ -33,7 +34,7 @@ Port-Veröffentlichung; eine Aussage „keinerlei Netzwerkänderung“ wäre daf
|
||||
führt zum Status „Verbunden“. Ein geladenes Interface alleine genügt nicht.
|
||||
5. Gewünschten Zugriffsmodus testen, die Zieladresse öffnen, dort anmelden und
|
||||
innerhalb von 120 Sekunden bestätigen. Tunnel/beides muss über den Tunnel,
|
||||
LAN über den LAN-Zugang bestätigt werden. Der Mac-Verwaltungskanal kann diese
|
||||
LAN über den LAN-Zugang bestätigt werden. Der SSH-Verwaltungskanal kann diese
|
||||
Bestätigung nicht ersetzen.
|
||||
|
||||
Ohne Bestätigung wird der vorige Modus wiederhergestellt. Gespeichert wird nur der
|
||||
@@ -62,7 +63,7 @@ gezielten Wiederherstellung liegen. Automatische Updates/Reparatur bestehender
|
||||
Installationen sind noch nicht implementiert.
|
||||
|
||||
Der neue Server verwendet aktuell HTTP. WireGuard schützt den Tunneltransport.
|
||||
Für Einrichtung und Secrets aus entfernten Netzen den lokalen Mac-Verwaltungskanal
|
||||
Für Einrichtung und Secrets aus entfernten Netzen den lokalen SSH-Verwaltungskanal
|
||||
(SSH) verwenden. LAN-HTTP nur in einem vertrauenswürdigen Netz verwenden;
|
||||
HTTPS/Reverse-Proxy-Zertifikate sind noch nicht enthalten. Passwort wird mit
|
||||
PBKDF2-SHA256 (600.000 Iterationen, individuellem Salt) gespeichert. Sitzungen sind
|
||||
@@ -79,7 +80,8 @@ ssh -i /Users/mike_i386/.ssh/athena_key -o BatchMode=yes \
|
||||
|
||||
Dann http://127.0.0.1:8110 öffnen. Dieser Zugriff zählt als LAN, nicht als
|
||||
WireGuard-Bestätigung. Im Tunnel-only-Modus wird auch er gesperrt.
|
||||
Die Mac-Oberfläche auf Port 8108 kann weiterhin per SSH den Helper verwalten.
|
||||
Der frühere Remote-Verwaltungsadapter kann den Helper per SSH verwalten;
|
||||
dieser Adapter ist in der Debian-Entwicklungsinstanz deaktiviert.
|
||||
Zur Wiederherstellung dort Modus LAN testen, SSH-Tunnel öffnen und dort bestätigen.
|
||||
|
||||
Nur den neuen Container stoppen/starten:
|
||||
@@ -125,7 +127,7 @@ serverseitige Prüfung des tatsächlichen Zugangs gelten vor dem API-Aufruf.
|
||||
|---|---|
|
||||
| POST `/login` | `password` |
|
||||
| GET `/network` | Status ohne Secrets |
|
||||
| POST `/network/install` | `password`, `api_token` für neue Server-Instanz, nur angemeldete Mac-Verwaltung |
|
||||
| POST `/network/install` | `password`, `api_token` für neue Server-Instanz, nur angemeldete SSH-Verwaltung |
|
||||
| POST `/network/import` | `config` als Dateiinhalt |
|
||||
| POST `/network/connect` | `{}` |
|
||||
| POST `/network/disconnect` | `{}` |
|
||||
@@ -136,7 +138,7 @@ serverseitige Prüfung des tatsächlichen Zugangs gelten vor dem API-Aufruf.
|
||||
|
||||
`/network` liefert `installed`, `configured`, `enabled`, `connected`,
|
||||
`latest_handshake`, `state`, `mode`, `pending`, `ingress`, URLs und ggf. Fehler.
|
||||
Bei Nicht-Erreichbarkeit meldet die Mac-Ansicht ausdrücklich „noch nicht installiert
|
||||
Bei Nicht-Erreichbarkeit meldet die Verwaltungsansicht ausdrücklich „noch nicht installiert
|
||||
oder nicht erreichbar“; sie behauptet nicht, den Unterschied sicher zu kennen.
|
||||
Anwendungsfehler geben HTTP 400, fehlende Sitzung 401, unerlaubter Host/Origin oder
|
||||
Zugangsweg 403. Installation läuft als Hintergrundauftrag mit `job.state`.
|
||||
|
||||
@@ -71,7 +71,7 @@ class HardwareProvider:
|
||||
self.checked = 0
|
||||
|
||||
def snapshot(self):
|
||||
if os.environ.get('DECK_LOCAL_HARDWARE') == '1':
|
||||
if os.environ.get('DECK_LOCAL_HARDWARE', '1') == '1':
|
||||
from collect_hardware import collect
|
||||
with self.lock:
|
||||
if self.cached is None or time.monotonic()-self.checked >= 5:
|
||||
@@ -218,7 +218,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
signed_in = self.authenticated(session_only=True)
|
||||
value = dict(initialized=record is not None, authenticated=signed_in)
|
||||
if signed_in:
|
||||
value.update(api_token_configured=bool(record['api_token_hash']), password_changed_at=record['password_changed_at'], token_changed_at=record['token_changed_at'], account='Administrator', location=os.environ.get('DECK_LOCATION', 'Athena' if os.environ.get('DECK_AUTH_RPC') else 'Mac'))
|
||||
value.update(api_token_configured=bool(record['api_token_hash']), password_changed_at=record['password_changed_at'], token_changed_at=record['token_changed_at'], account='Administrator', location=os.environ.get('DECK_LOCATION', 'Athena · Debian-Server'))
|
||||
return self.respond(value)
|
||||
|
||||
def change_credentials(self, kind):
|
||||
@@ -262,7 +262,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
name, mime = routes[self.path]
|
||||
return self.respond((ROOT/name).read_bytes(), mime=mime)
|
||||
if self.path == '/api/v1/status':
|
||||
return self.respond(dict(name='Athena Deck', version='0.4.0', state='ready', uptime_seconds=round(time.time()-self.server.started), mode='isolated', location=os.environ.get('DECK_LOCATION', 'Athena' if os.environ.get('DECK_AUTH_RPC') else 'Mac'), demo=self.server.demo.status()))
|
||||
return self.respond(dict(name='Athena Deck', version='0.4.0', state='ready', uptime_seconds=round(time.time()-self.server.started), mode='isolated', location=os.environ.get('DECK_LOCATION', 'Athena · Debian-Server'), demo=self.server.demo.status()))
|
||||
if self.path == '/api/v1/hardware':
|
||||
return self.respond(self.server.hardware.snapshot())
|
||||
if self.path == '/api/v1/network':
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class Tests(unittest.TestCase):
|
||||
self.assertEqual(e.exception.code,404)
|
||||
e.exception.close()
|
||||
def test_unavailable_hardware(self):
|
||||
with patch('server.subprocess.run',side_effect=OSError()):
|
||||
with patch.dict('os.environ', {'DECK_LOCAL_HARDWARE':'0'}), patch('server.subprocess.run',side_effect=OSError()):
|
||||
result=HardwareProvider().snapshot()
|
||||
self.assertFalse(result['available'])
|
||||
self.assertEqual(result['gpus'],[])
|
||||
|
||||
Reference in New Issue
Block a user