Move development runtime to isolated Athena Debian instance

This commit is contained in:
Mikei386
2026-09-28 15:42:53 +02:00
parent 2ad844ed20
commit 6d5671f75b
12 changed files with 95 additions and 65 deletions
+8 -15
View File
@@ -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.
+43
View File
@@ -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
View File
@@ -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
+16 -30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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`.
+3 -3
View File
@@ -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
View File
@@ -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'],[])