Zum Hauptinhalt springen

Verknüpfung

Andere Rechner, andere Dienste, andere Notebooks

Veröffentlichungsdatum

23. September 2026

TippIn einem Satz

Eine Verknüpfung gibt einem anderen Famulus-Dienst einen Namen und eine Liste von Adressen; ein Block nennt diesen Namen, um dort eine Sammlung zu lesen, sich rechnen zu lassen oder ein Artefakt zu holen.

Die PDAI-Sammlungen liegen auf PDAI-1 am mac-studio. Ein Notebook auf einem anderen Rechner kommt an sie nur über den Dienst heran, der auf dem mac-studio läuft. Dieser Dienst heißt aus Sicht des Notebooks Peer. Ein Peer kann ein anderer Famulus-Notebook-Dienst sein, aber auch ai-query, der Lektor, ein Editions-Viewer oder ein MCP-Server.

1 Eine Verknüpfung eintragen

Die Verknüpfungen stehen in ~/.config/famulus/peers.yaml. Jeder Eintrag hat einen Namen, eine Art und die Adressen in der Reihenfolge, in der sie probiert werden.

peers:
  mac-studio:
    kind: notebook
    endpoints:
      - http://127.0.0.1:5118
      - http://192.168.178.20:5118
      - https://mac-studio.tail147011.ts.net/famulus-notebook
    token_env: FAMULUS_TOKEN_MAC_STUDIO

Die Kommandozeile schreibt und prüft dieselbe Datei:

famulus-notebook peers add mac-studio --kind notebook \
  --endpoint http://192.168.178.20:5118 \
  --endpoint https://mac-studio.tail147011.ts.net/famulus-notebook \
  --token-env FAMULUS_TOKEN_MAC_STUDIO
famulus-notebook peers probe mac-studio
famulus-notebook peers list

token_env nennt die Umgebungsvariable, in der das Token des Peers steht. Das Token selbst gehört nicht in peers.yaml, nicht in ein Notebook und nicht in ein Log.

2 Welcher Weg genommen wird

Der Rechner probiert die Adressen der Reihe nach. Die Probe ist eine Anfrage an eine feste Adresse des Peers, für einen Notebook-Dienst /api/peer, für ai-query /api/health, für den Lektor /health. Sie wartet höchstens 1,5 Sekunden. Die erste Adresse, die antwortet, gilt 60 Sekunden lang; danach wird neu probiert.

flowchart LR
  probe["Probe in Reihenfolge"] --> lo["127.0.0.1"]
  probe --> lan["Adresse im lokalen Netz"]
  probe --> vpn["Adresse im VPN"]
  probe --> ts["Tailscale-Name"]
  lo --> first["erste Antwort gilt 60 s"]
  lan --> first
  vpn --> first
  ts --> first

So arbeitet ein Laptop mit einer einzigen peers.yaml zu Hause im lokalen Netz, unterwegs über das VPN und sonst über das Tailnet. Welcher Weg gewählt wurde, steht in jedem Ergebnis, das über den Peer kam.

3 Was ein Block über einen Peer tut

Ein Eintrag in duckdb nennt den Peer. Die Abfrage läuft dann auf dem Peer, nicht am eigenen Rechner. Der Pfad muss mit $PDAI_COLLECTIONS/<sammlung>/ beginnen; die Sammlung bleibt, wo sie ist.

```{famulus}
---
id: zaehlung
duckdb:
  - alias: main
    path: $PDAI_COLLECTIONS/pirckheimer/pirckheimer.duckdb
    peer: mac-studio
partiturs:
  - id: duckdb-read
    input:
      sql: SELECT direction, count(*) AS n FROM letters_resolved GROUP BY direction
artefacts:
  - name: tabelle
    kind: table
    file: tabelle.json
---
Zähle die Briefe nach Richtung.
```

Der Peer führt nur lesende Abfragen aus: SELECT oder WITH, ohne ATTACH, COPY, INSTALL, LOAD, PRAGMA und ohne schreibende Befehle. Die Datenbank wird nur lesend geöffnet, externer Zugriff ist abgeschaltet. Eine Antwort umfasst höchstens 5000 Zeilen, eine Abfrage läuft höchstens 30 Sekunden. Alle Datenbanken eines Blocks müssen zu demselben Peer und derselben Sammlung gehören.

engine nennt den Peer, der den ganzen Block ausführt. Der eigene Rechner braucht dann weder Sammlung noch Query-Pipeline.

engine: mac-studio

Der Rechner schickt Präambel und Instruktion. Der Peer führt den Block in einem eigenen, leeren Paket aus und liefert den Verlauf fortlaufend zurück. Am Ende schickt er das ganze Log. Es landet im Log des eigenen Notebooks, genau wie bei einem lokalen Lauf, mit dem Namen des Peers im Manifest.

Ein Block mit engine darf keinen Querverweis {{ref:…}} und keine Setting-Datei enthalten: beides liegt im eigenen Paket, das der Peer nicht sieht. Der Rechner lehnt einen solchen Block vor dem Senden ab und nennt den Grund.

Ein Querverweis kann auf ein Notebook eines Peers zeigen. Die Form ist peer:<name>/fam:<projekt>/<notebook>/<block>.<artefakt>; die ältere Form peer:<name>/<notebook>/<block>.<artefakt> gilt weiter.

Nimm {{ref:peer:mac-studio/pirckheimer-briefpartner/briefpartner-roster.roster}} als Grundlage.

Der Rechner holt die Datei nach _famulus/cache/extern/<peer>/…/<artefakt>.json und legt daneben <artefakt>.source.json mit Verweis, Adresse und Zeitpunkt ab. Jeder Lauf holt die Datei neu; die abgelegte Kopie belegt, welche Fassung der Lauf benutzt hat.

4 Zugang

Ein Dienst entscheidet bei jeder Anfrage, ob er sie annimmt:

Anfrage kommt Zugang
unmittelbar von 127.0.0.1 ohne Token; der Rechner gehört dem Besitzer
über den Plattform-Proxy oder Tailscale Serve nur mit Token
aus dem Tailnet 100.64.0.0/10 mit Token, oder ohne, wenn FAMULUS_TRUST_TAILNET=1
von sonst woher nur mit Token

Ohne gültiges Token antwortet der Dienst mit 401. Frei zugänglich sind nur die Seite /notebook, ihre Dateien unter /assets/ und diese Dokumentation unter /docs; alle Daten liegen hinter /api/.

Das Token steht in ~/.config/famulus/token (Modus 0600). Ein Programm schickt es im Kopf Authorization: Bearer …. Ein Browser auf einem anderen Rechner öffnet die Seite einmal mit dem Token hinter dem Zeichen #:

https://mac-studio.tail147011.ts.net/famulus-notebook/notebook#token=…

Der Teil hinter # geht nicht an den Server und erscheint daher in keinem Zugriffsprotokoll. Die Seite liest ihn, tauscht ihn gegen ein Cookie, das kein Skript lesen kann, und entfernt ihn aus der Adresszeile.

Zwei Wege bleiben dem eigenen Rechner vorbehalten, auch mit Token: Anmerkungen an den Lernzyklus (/api/annotations) und die Besitzersitzung im Lektor (/lektorat). Ein fremder Aufrufer bekommt beim Lektor dessen eigene Anmeldeseite.

5 Auskunft eines Peers

GET /api/peer sagt, was ein Notebook-Dienst anbietet: Name und Version, Rechner, ob Blöcke ausgeführt werden können, die Namen seiner Notebooks, die Wurzel der Sammlungen und die Fähigkeiten notebooks, artefacts, collections-read, run-block und partitur. GET /api/collections listet die Sammlungen mit ihrem Stand aus status.json.