Aus der statischen Seite wird ein Go-Binary mit eingebetteter Oberfläche. Grund: Apps über .env zu pflegen wurde unübersichtlich, und kosmetische Einstellungen sollen auf jedem Gerät gleich sein. - Anmeldung über den eigenen OIDC-Anbieter (PocketID), mit PKCE. Ohne Anmeldung gibt es nichts zu sehen – auch keine App-Liste. - Sitzung als signiertes Cookie ohne Gegenstück im Server; Schreibzugriffe zusätzlich über ein lesbares CSRF-Cookie abgesichert - Apps werden in der Oberfläche angelegt und bearbeitet, nicht mehr in der .env. Der Server prüft jeden Eintrag und meldet zurück, was er behält. - Einstellungen liegen je Nutzer im Server; localStorage ist nur noch der Vorschuss, damit die Röhre beim Laden nicht in der falschen Farbe blitzt - die Leiste holt die App-Liste aus /api/apps – freigegeben sind allein die Ursprünge, die selbst als App eingetragen sind - Tests fahren einen echten OIDC-Anbieter hoch und gehen den Anmeldeweg einmal ganz durch Ohne HONEYOS_OIDC_ISSUER läuft alles offen – für die Entwicklung, mit deutlicher Warnung beim Start. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
181 lines
7.6 KiB
Markdown
181 lines
7.6 KiB
Markdown
# HoneyOS
|
|
|
|
Ein Retro-Schreibtisch vor den eigenen Apps. Amber-CRT, Boot-Log, zwei Betriebsarten,
|
|
Anmeldung über den eigenen OIDC-Anbieter — und dieselbe Leiste zum Einhängen in die Apps.
|
|
|
|
**Was es ist:** eine Oberfläche davor. Jede App bleibt ihre eigene PWA auf ihrer eigenen
|
|
Domain mit ihrer eigenen Anmeldung. HoneyOS ist der Startpunkt, der Umschalter und die
|
|
gemeinsame Leiste — **kein Rahmen, der die Apps einsperrt** (kein iframe).
|
|
|
|
**Wo der Zustand liegt:** im Server. Die App-Liste gilt für alle, die Einstellungen gehören
|
|
je Nutzer — deshalb sieht dein HoneyOS auf jedem Gerät gleich aus, an dem du angemeldet bist.
|
|
Der Browser hält dieselben Einstellungen nur als Vorschuss, damit die Röhre beim Laden nicht
|
|
in der falschen Farbe aufflackert.
|
|
|
|
## Betrieb
|
|
|
|
```bash
|
|
mkdir -p /srv/honeyos && chown 1000:1000 /srv/honeyos
|
|
cp .env.example .env # PUBLIC_URL, SESSION_SECRET, OIDC-Zugang
|
|
docker login gitea.honeyy.duckdns.org
|
|
docker compose pull && docker compose up -d
|
|
```
|
|
|
|
Zu sichern ist genau eine Datei: `${HONEYOS_DATA_DIR}/honeyos.json`. Darin stehen die Apps
|
|
und die Einstellungen je Nutzer. Sichere sie zusammen mit der `.env` — ohne
|
|
`HONEYOS_SESSION_SECRET` müssen sich alle neu anmelden, mehr passiert aber nicht.
|
|
|
|
`/healthz` antwortet mit `ok`. Hinter deinem Reverse Proxy braucht es nichts Besonderes,
|
|
außer dass `X-Forwarded-Proto: https` durchkommt — sonst setzt HoneyOS das Sitzungscookie
|
|
ohne `Secure`.
|
|
|
|
## Anmeldung
|
|
|
|
In PocketID einen Client anlegen und dort als Rückrufadresse **exakt** eintragen:
|
|
|
|
```
|
|
https://os.honeyy.duckdns.org/api/auth/callback
|
|
```
|
|
|
|
HoneyOS schickt PKCE mit, braucht aber trotzdem ein Client-Geheimnis — also **keinen
|
|
„public client"** anlegen. Danach in die `.env`:
|
|
|
|
```dotenv
|
|
HONEYOS_PUBLIC_URL=https://os.honeyy.duckdns.org
|
|
HONEYOS_SESSION_SECRET=… # openssl rand -hex 32
|
|
HONEYOS_OIDC_ISSUER=https://id.honeyy.duckdns.org
|
|
HONEYOS_OIDC_CLIENT_ID=honeyos
|
|
HONEYOS_OIDC_CLIENT_SECRET=…
|
|
HONEYOS_BUTTON_LABEL=Anmelden mit PocketID
|
|
# HONEYOS_ALLOWED_USERS=marc # zweiter Riegel neben den PocketID-Gruppen
|
|
```
|
|
|
|
Wer nicht angemeldet ist, sieht **nur** den Anmeldeschirm — keine App-Liste, keine Namen.
|
|
Die Sitzung ist ein signiertes Cookie ohne Gegenstück auf dem Server: ein Neustart wirft
|
|
niemanden hinaus, und es gibt nichts aufzuräumen. Sie hält 30 Tage.
|
|
|
|
**Ohne `HONEYOS_OIDC_ISSUER` läuft HoneyOS ganz ohne Anmeldung.** Das ist zum Entwickeln
|
|
gedacht und sagt beim Start deutlich, dass es so nicht ins offene Netz gehört.
|
|
|
|
## Apps anlegen
|
|
|
|
**In der Oberfläche**, nicht in der `.env`: *Einstellungen → Programme* oder die Kachel
|
|
„+ app hinzufügen". Pro App:
|
|
|
|
| Feld | wofür |
|
|
|---|---|
|
|
| Kennung | klein, ohne Leerzeichen — muss zu `data-app` der Leiste passen |
|
|
| Name, Untertitel | Beschriftung auf Kachel und Symbol |
|
|
| Adresse | wohin der Start springt |
|
|
| Symbol | cloud · note · box · chip · folder · gear · globe · disk |
|
|
| Taste | Zifferntaste im Terminal-Modus |
|
|
| Wer-bin-ich | optional, siehe unten |
|
|
| Token-Schlüssel | nur für Apps mit Bearer-Token im localStorage statt Cookie |
|
|
|
|
Gespeichert wird die Liste als Ganzes; der Server prüft sie noch einmal und antwortet mit
|
|
dem, was er wirklich behalten hat. Eine Adresse ohne `https://` kommt als Fehlermeldung
|
|
zurück, nicht als kaputter Eintrag.
|
|
|
|
## Zwei Betriebsarten, ein Datenstand
|
|
|
|
| | Terminal-Modus | Desktop-Modus |
|
|
|---|---|---|
|
|
| Apps | Kacheln nebeneinander | Symbole auf der Fläche, Doppelklick öffnet |
|
|
| Fenster | Dialog in der Mitte | verschiebbar, minimierbar |
|
|
| unten | Statuszeile mit Tastenhinweisen | Taskleiste mit Startmenü |
|
|
|
|
Umschalten mit **`M`**, über *Einstellungen → Betriebsart* oder im Startmenü. Gleich bleiben
|
|
in beiden: Menüleiste, Palette, Einstellungen, App-Liste.
|
|
|
|
**Tasten:** `1` `2` App starten · `M` Betriebsart · `S` Einstellungen · `?` Hilfe ·
|
|
`Esc` schließt Startmenü, oberstes Fenster oder Dialog — in dieser Reihenfolge.
|
|
|
|
## Die Leiste in Aether / InkNote
|
|
|
|
Eine Zeile ins `frontend/index.html` der App:
|
|
|
|
```html
|
|
<script src="https://os.honeyy.duckdns.org/honeybar/honeybar.js"
|
|
data-app="aether"></script>
|
|
```
|
|
|
|
`data-app` ist die Kennung aus der App-Liste. Die Liste holt sich die Leiste selbst von
|
|
`/api/apps` — mit deiner HoneyOS-Sitzung, weil alle Adressen unter derselben Domain liegen.
|
|
**Freigegeben sind nur die Ursprünge, die selbst als App eingetragen sind**: die App-Liste
|
|
ist zugleich ihre eigene CORS-Freigabe.
|
|
|
|
Sie rendert in einem **Shadow DOM**: Tailwind (Aether) und die InkNote-Palette kommen nicht
|
|
hinein, ihre Stile nicht hinaus. Sie schiebt den Seiteninhalt um 32 px nach unten
|
|
(`data-push="none"` legt sie stattdessen darüber). Rechnet die App mit voller Höhe:
|
|
|
|
```css
|
|
.app-shell { height: calc(100vh - var(--honeybar-h, 0px)); }
|
|
```
|
|
|
|
Vorschau ohne Einbau: `/honeybar/demo.html`.
|
|
|
|
## Wer ist wo angemeldet
|
|
|
|
Das Feld **Wer-bin-ich** ist der Pfad, unter dem eine App „wer bin ich" beantwortet
|
|
(relativ zur Adresse oder absolut). Aus der Antwort wird der Anzeigename gefischt —
|
|
`display_name`, `displayName`, `name`, `username`, auch verschachtelt unter `user`.
|
|
|
|
Gelesen wird das an zwei Stellen, und die verhalten sich **unterschiedlich**:
|
|
|
|
1. **Die Leiste in der App** fragt ihre eigene App. Gleicher Ursprung → funktioniert
|
|
sofort, ohne Änderung an Aether oder InkNote.
|
|
2. **Der Desktop auf `os.…`** fragt über die Domaingrenze. Dafür muss die App diesen
|
|
Ursprung erlauben:
|
|
- InkNote: `ALLOWED_ORIGINS=https://os.honeyy.duckdns.org`
|
|
- Aether: CORS mit `Access-Control-Allow-Credentials` — noch nicht vorhanden
|
|
|
|
Fehlt das, bleibt die Anzeige leer und sonst passiert nichts.
|
|
|
|
**Voraussetzung:** alle Adressen unter derselben Domain (`*.honeyy.duckdns.org`). Sonst
|
|
behandelt der Browser sie als fremde Site und schickt die Sitzung gar nicht erst mit.
|
|
|
|
## Entwickeln
|
|
|
|
```bash
|
|
docker compose -f docker-compose.dev.yml up -d --build # → http://localhost:8088
|
|
go test ./... # Anmeldeablauf, CSRF, Prüfungen
|
|
```
|
|
|
|
Der Dev-Betrieb läuft **ohne Anmeldung**. Die Oberfläche steckt per `go:embed` im Binary —
|
|
nach Änderungen an `web/` also neu bauen, ein Reload allein genügt nicht.
|
|
|
|
```
|
|
main.go Konfiguration, Routen, statische Auslieferung
|
|
auth.go OIDC mit PKCE, Sitzungscookie, CSRF
|
|
store.go die JSON-Datei: Apps, Nutzer, Einstellungen
|
|
auth_test.go falscher OIDC-Anbieter — der ganze Anmeldeweg im Test
|
|
web/ index.html · style.css · os.js · honeybar/ · looks/
|
|
```
|
|
|
|
Die Tests fahren einen echten OIDC-Anbieter mit eigenem Schlüssel hoch und gehen den Weg
|
|
einmal ganz durch. Wenn die grün sind, liegt ein Anmeldeproblem am Anbieter oder an der
|
|
Konfiguration, nicht am Code.
|
|
|
|
## Bauen und schieben
|
|
|
|
```bash
|
|
./build.sh # → gitea.honeyy.duckdns.org/docker/honeyos:latest
|
|
./build.sh 0.2 # mit Tag
|
|
```
|
|
|
|
## Grenzen, die man kennen sollte
|
|
|
|
- **Die Lampe an der Kachel ist kein Health-Check.** Bei einer fremden Domain darf der
|
|
Browser nur sehen, *dass* geantwortet wurde. Ein 500er sähe aus wie „online".
|
|
- **Die App-Liste ist für alle gleich.** Wer angemeldet ist, darf sie ändern — das ist für
|
|
eine Handvoll Konten gewollt und wäre für mehr Leute zu wenig.
|
|
- **Ein Angemeldeter kann jede Adresse eintragen.** Der Server prüft nur, dass es eine
|
|
http(s)-Adresse ist. Das ist zugleich die CORS-Freigabe der Leiste, also nicht
|
|
gedankenlos etwas Fremdes eintragen.
|
|
|
|
## Was als Nächstes ansteht
|
|
|
|
1. **CORS in Aether** für `os.…`, damit der Desktop den Anmeldestand sehen kann.
|
|
2. **Überall abmelden** in der Leiste — braucht pro App den CSRF-Weg zum Logout.
|
|
3. **Fenster mit Inhalt** statt nur Verweisen: „letzte Dateien", „letzte Notizen".
|