# HoneyOS Ein Retro-Schreibtisch vor den eigenen Apps. Amber-CRT, Boot-Log, zwei Betriebsarten — und dieselbe Leiste zum Einhängen in Aether und InkNote. **Was es ist:** eine Oberfläche davor. Jede App bleibt ihre eigene PWA auf ihrer eigenen Domain mit ihrer eigenen Arbeitsumgebung und ihrer eigenen Anmeldung. HoneyOS ist der Startpunkt, der Umschalter und die gemeinsame Leiste — **kein Rahmen, der die Apps einsperrt** (kein iframe). **Was es nicht ist:** eine Anmeldung. HoneyOS ist eine statische Seite ohne Backend; ein OIDC-Client bräuchte ein Client-Geheimnis und eine Sitzung, und beides gehört nicht in etwas, das der Browser vollständig lesen kann. Angemeldet wird bei Aether und InkNote, die beide an PocketID hängen. HoneyOS kann den Stand nur **anzeigen** — siehe unten. ## Betrieb ```bash cp .env.example .env # Adressen und Namen eintragen docker login gitea.honeyy.duckdns.org docker compose pull && docker compose up -d ``` Es gibt nichts zu sichern: keine Datenbank, kein Volume. Der ganze Zustand ist die `.env` — und was sich der Browser pro Gerät merkt (Betriebsart, Röhrenfarbe). Hinter deinem Reverse Proxy erwartet HoneyOS nichts Besonderes: `proxy_pass` auf den Port, fertig. `/healthz` antwortet mit `ok`. ## Konfiguration Alles kommt aus der `.env`. Beim Containerstart schreibt [`docker-entrypoint.d/20-honeyos-config.sh`](docker-entrypoint.d/20-honeyos-config.sh) daraus `config.json`, die der Browser liest. Nach jeder Änderung `docker compose up -d`. **Alles darin ist öffentlich.** Adressen und Namen gehören hinein, Geheimnisse niemals. Eine App entsteht durch ihre `_URL`; fehlt die, wird die Nummer übersprungen (bis 12): ```dotenv HONEYOS_APP_1_ID=aether HONEYOS_APP_1_NAME=AETHER HONEYOS_APP_1_SUBTITLE=Cloud & Dateien HONEYOS_APP_1_URL=https://cloud.honeyy.duckdns.org HONEYOS_APP_1_GLYPH=cloud # cloud | note | box | chip | folder | gear HONEYOS_APP_1_KEY=1 # Zifferntaste im Terminal-Modus HONEYOS_APP_1_ME=/api/v1/auth/me # optional, siehe „Wer ist angemeldet" ``` Ohne `HONEYOS_APP_1_URL` bleibt die eingebackene `web/config.json` gültig — ein `docker run` ohne `.env` zeigt also die Standard-Apps. Adressen lassen sich zusätzlich im Browser unter *Einstellungen* überschreiben; das gilt dann nur für dieses Gerät. ## 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: die Menüleiste oben, die Palette, die Einstellungen, `config.json`. `renderTiles()` und `renderDeskIcons()` in `os.js` zeichnen aus denselben Daten — eine neue App erscheint ohne Zutun in beiden. **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 ``` `data-app` ist die `id` aus der Konfiguration — danach markiert sich die Leiste selbst als aktiv. Die App-Liste holt sie von allein neben ihrem eigenen Skript. 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 angemeldet `_ME` ist der Pfad, unter dem eine App „wer bin ich" beantwortet (relativ zur `_URL` 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. Die Lampe „online" funktioniert unabhängig davon weiter. `_TOKEN_KEY` brauchen nur Apps, die einen Bearer-Token im localStorage halten statt eines Cookies (InkNote: `inknote_token`). Der ist pro Domain sichtbar — von `os.…` aus also nicht lesbar, für die Leiste in der App dagegen schon. **Voraussetzung für all das:** alle Adressen liegen 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 ``` `web/` ist gemountet: Datei ändern, Seite neu laden. Weil der Mount schreibgeschützt ist, greift dort die `.env`-Konfiguration **nicht** — es gilt die eingebackene `web/config.json`. Zum Prüfen der `.env` das echte Image nehmen (`docker compose up -d --build`). ``` web/ index.html Desktop style.css alle Farben in den --p*-Variablen ganz oben os.js Boot, beide Ansichten, Fenster, Einstellungen config.json Standardkonfiguration (wird zur Laufzeit aus der .env ersetzt) honeybar/ die einbettbare Leiste + Vorschauseite looks/ zwei verworfene Entwürfe (System 7, Win95) — nur Ansicht ``` Neue Symbole sind 12×12-Pixelraster in `os.js` (`GLYPHS`). ## Bauen und schieben ```bash ./build.sh # → gitea.honeyy.duckdns.org/docker/honeyos:latest ./build.sh 0.2 # mit Tag ``` ## Grenzen, die man kennen sollte - **localStorage ist pro Domain.** Betriebsart und Röhrenfarbe auf `os.…` kennt die Leiste auf `notes.…` nicht. Der Umschalter ist ein Schalter, kein geteilter Zustand. - **Die Lampe ist kein Health-Check.** Bei einer fremden Domain darf der Browser nur sehen, *dass* geantwortet wurde, nicht *was*. Ein 500er sähe aus wie „online". - **`config.json` und `honeybar.js` gehen mit `Access-Control-Allow-Origin: *` raus**, weil die anderen Domains sie holen. Öffentliche Konfiguration — nichts Vertrauliches hinein. ## Was als Nächstes ansteht 1. **CORS in Aether** für `os.…`, damit der Desktop den Anmeldestand sehen kann. InkNote hat mit `ALLOWED_ORIGINS` schon den Haken dafür. 2. **Überall abmelden** in der Leiste. Braucht pro App den CSRF-Weg zum Logout — bei Aether ein `POST` mit Token, das geht nicht nebenbei über die Domaingrenze. 3. **Fenster mit Inhalt** statt nur Verweisen: „letzte Dateien", „letzte Notizen". Fenster zeigen dann Daten aus den APIs — die Apps selbst bleiben in ihrem eigenen Tab.