feat: federation panel + Dart SDK federation methods (0.70.0)
Some checks failed
Security / Security check (push) Failing after 2s

A new 'Föderation' destination (primary side) lists connected
satellites — name, region, version, wire version, advertised
capabilities — and adds them in one step: 'Add satellite' issues a
single-use bootstrap token bundled with the primary CA as a
ready-to-paste satellite config (the bundled CA makes the first
connect tamper-proof). Localized EN + DE, in-app help doc. Uses the
new HubService.listSatellites / issueSatelliteToken wrapping the
SDK's federation methods.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-06-13 14:16:07 +02:00
parent b35bbc1b12
commit d0cfa5df05
12 changed files with 815 additions and 4 deletions

43
assets/docs/federation.md Normal file
View file

@ -0,0 +1,43 @@
# Federation
Federation lets one hub (the **primary**) run capabilities on
another hub (a **satellite**) — for example to reach a GPU box or to
keep data inside a specific location. A flow step opts in with an
`on:` selector; everything else stays exactly like a local step.
This panel is the **primary** side: it shows the satellites currently
connected and lets you enrol new ones.
## Add a satellite
1. Press **Add satellite** and give it a name (e.g. `satellite-a`).
2. The hub issues a single-use **bootstrap token** and bundles its
**CA certificate**. Copy the shown config into the satellite's
`config.yaml` (one paste) and export the token as
`FAI_BOOTSTRAP_TOKEN`.
3. Start the satellite. It dials the primary, the CA pin makes the
first connect tamper-proof, and the primary signs it a short-lived
certificate. It then appears in this list.
## How it stays secure
- The satellite **dials outbound** — no inbound firewall change.
- Every connection is **mTLS**. The first connect is verified against
the bundled CA; later connects authenticate with the satellite's
own certificate (no token needed).
- The satellite resolves and runs the capability in **its own
sandbox** — only inputs cross the wire, never code.
- Each cross-hub call is recorded on both sides (`dispatched_to`), so
the audit trail and replay determinism survive federation.
## Using a satellite in a flow
Add an `on:` selector to a step:
```yaml
- id: extract
use: text.extract@^1
on: region=berlin # or pin=satellite-a, auto:healthiest, auto:lowest_rtt
with:
input: $inputs.doc
```

View file

@ -0,0 +1,48 @@
# Föderation
Föderation lässt einen Hub (den **Primary**) Capabilities auf einem
anderen Hub (einem **Satelliten**) ausführen — etwa um eine GPU-Maschine
zu erreichen oder Daten an einem bestimmten Ort zu halten. Ein
Flow-Schritt entscheidet sich per `on:`-Selektor dafür; alles andere
bleibt wie bei einem lokalen Schritt.
Dieses Panel ist die **Primary**-Seite: Es zeigt die aktuell
verbundenen Satelliten und richtet neue ein.
## Satellit hinzufügen
1. **Satellit hinzufügen** drücken und einen Namen vergeben
(z.B. `satellite-a`).
2. Der Hub stellt ein einmaliges **Bootstrap-Token** aus und legt sein
**CA-Zertifikat** bei. Kopiere die gezeigte Konfiguration in die
`config.yaml` des Satelliten (ein Einfügen) und exportiere das
Token als `FAI_BOOTSTRAP_TOKEN`.
3. Starte den Satelliten. Er wählt sich beim Primary ein, der CA-Pin
macht den ersten Connect manipulationssicher, und der Primary
signiert ihm ein kurzlebiges Zertifikat. Danach erscheint er in
dieser Liste.
## Warum es sicher bleibt
- Der Satellit **wählt sich nach außen ein** — keine eingehende
Firewall-Änderung nötig.
- Jede Verbindung ist **mTLS**. Der erste Connect wird gegen die
beigelegte CA verifiziert; spätere Connects authentifizieren sich
mit dem eigenen Zertifikat des Satelliten (kein Token nötig).
- Der Satellit löst auf und führt die Capability in **seiner eigenen
Sandbox** aus — nur Eingaben gehen über die Leitung, niemals Code.
- Jeder hub-übergreifende Aufruf wird auf beiden Seiten protokolliert
(`dispatched_to`), sodass Audit-Trail und Replay-Determinismus die
Föderation überstehen.
## Satellit in einem Flow nutzen
Ergänze einen `on:`-Selektor an einem Schritt:
```yaml
- id: extract
use: text.extract@^1
on: region=berlin # oder pin=satellite-a, auto:healthiest, auto:lowest_rtt
with:
input: $inputs.doc
```