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

View file

@ -859,6 +859,39 @@ class HubService {
Future<void> reject(String id, String reviewer, String reason) =>
_client.reject(approvalId: id, reviewer: reviewer, reason: reason);
/// Federation satellites currently connected to this hub
/// (primary side). Empty when none are connected.
Future<List<Satellite>> listSatellites() async {
final entries = await _client.listSatellites();
return entries
.map(
(e) => Satellite(
hubId: e.hubId,
displayName: e.displayName,
hubVersion: e.hubVersion,
wireVersion: e.wireVersion,
region: e.region,
capabilities: e.capabilities.toList(),
),
)
.toList();
}
/// Issue a single-use federation bootstrap token enrolling
/// `name`. The result carries the primary CA so the operator can
/// bootstrap the satellite in one step (secure first connect).
Future<SatelliteEnrollment> issueSatelliteToken(
String name, {
String region = '',
}) async {
final r = await _client.issueBootstrapToken(name, region: region);
return SatelliteEnrollment(
token: r.token,
expiresAt: r.expiresAt,
primaryCaPem: r.primaryCaPem,
);
}
/// Composite "doctor" snapshot. One round-trip per piece, run
/// in parallel so the page populates fast.
Future<DoctorSnapshot> doctor() async {
@ -1208,6 +1241,51 @@ class PendingApproval {
/// Full approval record including decided-side fields. Used by
/// the History tab so operators can review what they (or the
/// A federation satellite currently connected to this hub.
class Satellite {
final String hubId;
final String displayName;
final String hubVersion;
final int wireVersion;
final String region;
final List<String> capabilities;
const Satellite({
required this.hubId,
required this.displayName,
required this.hubVersion,
required this.wireVersion,
required this.region,
required this.capabilities,
});
}
/// The artifact a satellite operator needs to enrol: the single-use
/// bootstrap token plus the primary's CA cert (for a secure first
/// connect). Both are pasted into the satellite's config.
class SatelliteEnrollment {
final String token;
final String expiresAt;
final String primaryCaPem;
const SatelliteEnrollment({
required this.token,
required this.expiresAt,
required this.primaryCaPem,
});
/// A ready-to-paste `federation:` config block for the satellite.
String toConfigYaml(String name) {
final ca = primaryCaPem.trim().isEmpty
? ''
: '\n primary_ca: |\n${primaryCaPem.trimRight().split('\n').map((l) => ' $l').join('\n')}';
return 'federation:\n'
' upstream: https://<this-primary-host>:<federation-port>\n'
' hub_name: $name\n'
' bootstrap_token_env: FAI_BOOTSTRAP_TOKEN$ca';
}
}
/// system) decided.
class ApprovalRecord {
final String id;