feat: F5.2 sign-in with the dashboard's ticket and the service way

Server F checks the dashboard's entry ticket as itsdangerous does
(tools/ticket-oracle: tickets made by itsdangerous 2.2.0 itself), redeems
it at /__auth for a session cookie (only the key's SHA-256 is kept) and
decides at /sync by the session's grant: none, read or write; a frozen
project is read by everyone; every refusal names its reason (one list in
packages/protocol/src/sync.ts). A WebSocket from a page of another origin
is refused. /svc/* with the service key makes, deletes, freezes and
thaws projects, ends a person's grant or all their sessions (connections
let go) and lists the sessions. Snapshots, restoring and copying move to
F6 (they need the canonical doc.json and an epoch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 13:56:45 +00:00
co-authored by Claude Opus 5.5
parent 88db6eff7e
commit 5ded84cb33
24 changed files with 1794 additions and 151 deletions
+14 -1
View File
@@ -176,6 +176,7 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| `src/gnutella.ts` | Java `GUIApplicationPeerToPeerAnwendungWindow` (gemessen mit `GnutellaWindowRun`) | neu (F4.6g4): der Bildschirmstand von Gnutella — die bekannten Teilnehmer, die Ergebnisse wie die Tabelle sie zeigt, die Dateien von `/peer2peer`, die Zahl der Teilnehmer |
| `src/viewers.ts`, `src/sim.ts` (`sat`, `nat`, `drop`, `dropped`, `clock`) | Java `SatViewer`, `NatViewer`, `SimulationControl` (gemessen mit `ViewersRun`) | neu (F4.6g5): die Tabellen eines Switches und eines Heimrouters als Bildschirmstände; die Nachricht `drop` (Javas „Pakete verwerfen“ gedrückt oder losgelassen), das Thema `dropped` mit den verworfenen Frames; der Zustand trägt `clock`, die Uhrzeit, für die die Zeit 0 der Simulation steht |
| `src/sim.ts` (`clearCapture`) | Java `AggregatedMessageTable` („Tabellen löschen“, `Lauscher.reset`) | neu (F4.7): die Nachricht `clearCapture` |
| `src/sync.ts` | Plan §8.5 | neu (F5.2): was F einem Editor über seinen Platz im gemeinsamen Dokument sagt — warum eine Verbindung abgewiesen wird (`notAProject`, `notSignedIn`, `noAccess`, `unknownProject`), warum F sie schließt (`revoked`, `deleted`), und die Nachricht `access`, wenn sich der Zugang einer bleibenden Verbindung ändert (eingefroren, aufgetaut) |
## packages/sim
@@ -263,7 +264,19 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| Datei | Quelle | Art der Übertragung |
| --- | --- | --- |
| `src/sync.ts`, `src/persistence.ts`, `src/store.ts`, `src/server.ts`, `src/log.ts`, `src/main.ts`, `src/index.ts` | Plan §5.3, §5.4, §8.2; Hocuspocus 3.4.4 (MIT), `ws` (MIT), `pg` (MIT) | neu (F5.1): Server F mit den gemeinsamen Dokumenten — Hocuspocus auf `/sync`, ein Projekt je Dokument, nur Projekte, die der Speicher kennt (Absage mit Grund `unknownProject` bzw. `notAProject`); jede Aktualisierung sofort in Postgres (`filius_doc_update`, der Reihe nach), verdichtet nach 500 Aktualisierungen, eine Minute nach der letzten Änderung und wenn die letzte Person geht (`filius_doc`, `FOR UPDATE` auf die beim Anlegen geschriebene Zeile); ein Projekt, das während des Bearbeitens verschwindet, wird nicht still neu geschrieben, sondern im Protokoll gemeldet; der Hausmeister läuft nur hier, beim Laden und 250 ms nach jeder fremden Änderung; Lesestufe kann nichts ändern; `/healthz`; Protokoll als JSON-Zeilen. Beim Schließen nimmt der Dienst niemanden mehr an und schließt auch die WebSockets (Hocuspocus' eigenes Schließen ist eine Nachricht, nach der sich ein Client auf demselben Socket wieder anmeldet). Hocuspocus 4 verlangt Node 22; 3.4.4 läuft auf Node 20 und 22 |
| `test/sync.test.ts`, `test/database.ts` | — | neu (F5.1): echte Clients (Hocuspocus' Provider über WebSocket) gegen eine Wegwerf-Postgres in Docker: Wiederkehr nach Neustart, nie verdichtete Aktualisierungen nach hartem Halt, zwei Personen live, Verdichten (500, Ruhe, nichts ohne Änderung), Schließen mit Personen, die sofort wiederkommen, gelöschtes Projekt, doppeltes Anlegen, Hausmeister beim Laden und nach einer Zusammenführung, unbekanntes Projekt, Lesestufe, `/healthz`, Protokoll |
| `src/ticket.ts` | itsdangerous 2.2.0 (BSD-3-Clause) `URLSafeTimedSerializer`, `TimestampSigner`, `Signer` (django-concat, HMAC-SHA1); Dashboard `backend/modules/scratch/src/ticket.py` | neu (F5.2): die Eintrittskarte des Dashboards in Node geprüft, wie itsdangerous sie prüft (zlib-gepackte Nutzlast mit „.“, Zeitstempel als kürzeste Bytes, Signatur zuerst, dann das Alter: über 60 s oder unter 0 abgelehnt); die Ansprüche Person, Login, Name, Farbe, Sprache, Projekt, Stufe — ohne Person, Login, Projekt oder gültige Stufe keine Karte. Strenger als Python: eine Signatur mit fremden Zeichen, die Pythons Base64 überliest, wird abgelehnt (itsdangerous erzeugt sie nie) |
| `src/sessions.ts`, `src/auth.ts` | Scratch-Editor `dashboard_auth_routes.py`, `models/session.py` | neu (F5.2): `/__auth` tauscht die Karte gegen einen Keks (`filius_session`, HttpOnly, SameSite=Lax, über HTTPS `Secure`, 12 h) und schickt den Browser nach `next` — nur an einen Ort von F selbst, aufgelöst wie im Browser: `/\…` und `/<Tab>/…` führen nicht weg (Scratchs Prüfung lässt beide durch, Werkzeug reicht sie roh weiter, der Browser liest `//…` — gemessen), und weil F den aufgelösten Pfad weitergibt, auch nicht `/.//…`, das die Auflösung zu `//…` kürzt; F speichert nur den SHA-256 des Schlüssels; ein zweites Projekt im selben Browser hängt eine Berechtigung an die Sitzung, eine Karte einer anderen Person beendet die alte Sitzung; jede Karte verlängert, abgelaufene werden gelöscht |
| `src/sync.ts` (Anmeldung), `src/server.ts` (Wege, Herkunft) | Plan §8.5 | geändert (F5.2): `/sync` entscheidet nach der Berechtigung der Sitzung, die der Keks nennt — keine, Lesen, Schreiben; ein eingefrorenes Projekt lesen alle; jede Absage mit Grund und im Protokoll; ein WebSocket von einer Seite fremder Herkunft wird abgewiesen (eine Seite anderswo unter derselben Domain brächte den Keks mit, SameSite trennt nur Sites); unerwartete Fehler eines Wegs werden 500 und stehen im Protokoll |
| `src/service.ts`, `src/store.ts` (`freeze`, `remove`), `src/persistence.ts` (`discard`), `src/main.ts` | Dashboard `backend/modules/scratch/src/service.py`, Scratch-Editor `revoke`, `active_sessions` | neu (F5.2): `/svc/*` mit Dienstschlüssel (in konstanter Zeit verglichen): Projekt anlegen (leeres FILIUS-Projekt), löschen (erst nicht mehr gespeichert, dann weg, dann alle getrennt — wer wiederkommt, findet kein Projekt), einfrieren und auftauen (wer drin ist, liest bzw. schreibt wieder und erfährt es), Berechtigung einer Person an einem Projekt oder alle ihre Sitzungen beenden (wer verbunden ist, wird getrennt und beim Wiederkommen abgewiesen), die Sitzungen auflisten; ohne Geheimnisse startet der Dienst nicht |
| `test/ticket.test.ts`, `test/tickets.ts`, `test/tickets.python.json` | `tools/ticket-oracle` | neu (F5.2): jede Karte des Python-Orakels liest Node wie Python (Nutzlast oder derselbe Grund der Ablehnung); in Node signierte Karten tragen dasselbe JSON zum selben Zeitpunkt (Nodes zlib packt anders, ungepackt byte-gleich); die Ansprüche |
| `test/access.test.ts`, `test/harness.ts` | — | neu (F5.2): `/__auth` (Keks, `next`, schlechte Karten, unbekanntes Projekt, zweites Projekt, nächste Person, Verlängerung), Absagen an `/sync` mit Grund, Herkunft, Dienstweg (Schlüssel, Anlegen, Fehlerantworten, Löschen, Einfrieren, Widerruf einzeln und ganz, Sitzungsliste, 500) |
| `test/sync.test.ts`, `test/database.ts` | — | neu (F5.1), seit F5.2 mit echter Anmeldung (Karte, Keks): echte Clients (Hocuspocus' Provider über WebSocket) gegen eine Wegwerf-Postgres in Docker: Wiederkehr nach Neustart, nie verdichtete Aktualisierungen nach hartem Halt, zwei Personen live, Verdichten (500, Ruhe, nichts ohne Änderung), Schließen mit Personen, die sofort wiederkommen, gelöschtes Projekt, doppeltes Anlegen, Hausmeister beim Laden und nach einer Zusammenführung, unbekanntes Projekt, Lesestufe, `/healthz`, Protokoll |
## tools/ticket-oracle
Schreibt Eintrittskarten mit itsdangerous 2.2.0 selbst (der Version des
Dashboards) bei fester Uhr und hält fest, was itsdangerous beim Lesen
antwortet: `packages/server/test/tickets.python.json`.
## tools/swift-oracle