Files
HoneyOS/README.md
T
marcandClaude Opus 5 84cc5d619e feat: HoneyOS – Retro-Schreibtisch und Leiste für die eigenen Apps
Statische Oberfläche vor Aether und InkNote: kein Backend, kein Build,
nginx liefert web/ aus. Jede App bleibt ihre eigene PWA auf ihrer eigenen
Domain – HoneyOS ist Startpunkt und Umschalter, kein iframe-Rahmen.

- zwei Betriebsarten aus einem Datenstand: Terminal (Kacheln, Tastatur)
  und Desktop (Symbole, verschiebbare Fenster, Taskleiste)
- honeybar.js als Drop-in für die Apps, rendert im Shadow DOM, damit
  Tailwind und die InkNote-Palette einander nicht in die Quere kommen
- Konfiguration vollständig aus der .env: das Skript in
  /docker-entrypoint.d/ schreibt config.json beim Containerstart
- optionaler _ME-Pfad pro App zeigt an, wer angemeldet ist; ohne CORS
  bleibt die Anzeige leer, ohne dass etwas kaputtgeht

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 14:45:55 +02:00

7.0 KiB
Raw Blame History

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

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 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):

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:

<script src="https://os.honeyy.duckdns.org/honeybar/honeybar.js"
        data-app="aether"></script>

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:

.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

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

./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.