feat(settings): hub bearer-token panel + auto-attach on every RPC
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
Studio gains a new "HUB AUTHENTICATION" panel in Settings
mirroring the existing registry-credentials panel:
- `lib/data/hub_auth_token.dart` — `~/.fai/hub-auth-token`
helper (read / write / clear, mode 0600 on Unix). Sister
of `RegistryToken` with the same on-disk hygiene.
- `HubService.loadPersistedEndpoint` now reads the token at
startup and reconnects with it. `reconnect()` grew an
`authToken:` parameter with a sentinel that distinguishes
"keep current" from "drop". `reloadAuthToken()` is the
one-liner Settings calls after save / clear.
- `_HubAuthTokenPanel` in `fai_settings_dialog.dart` — paste
with show/hide toggle, save button, clear button, status
pill ("Configured (40 chars)" / "Not set (anonymous)"),
storage-location hint. Trimmed token length only — the
secret never round-trips back into the UI after save.
- EN + DE ARB entries (`hubAuthToken*`) + regenerated
`app_localizations.dart` keep the bilingual surface
consistent.
The hub side (auth.tokens config + tower middleware) shipped
on the platform side 2026-05-28 (43a54a2). Until now an
operator had no GUI path to consume it: they had to find the
file in their home dir, paste the token by hand, and bounce
Studio. This panel closes that loop.
Bumped pubspec to 0.47.0. dart analyze clean (No issues
found!); flutter test green (11 tests pass).
Block E item 1 of 4 done. Capability-picker badges,
default_scope editor, multi-version uninstall picker follow.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
3237c4415a
commit
c5a96bc8d4
9 changed files with 538 additions and 12 deletions
|
|
@ -750,6 +750,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"hubAuthTokenHeader": "HUB-AUTHENTIFIZIERUNG",
|
||||
"hubAuthTokenBlurb": "Bearer-Token für die Studio-Anbindung an einen Hub mit aktivierter auth.tokens-Konfiguration (RBAC Level 2). Gespeichert in ~/.fai/hub-auth-token, Modus 0600. Studio sendet ihn als Authorization: Bearer bei jedem gRPC-Aufruf.",
|
||||
"hubAuthTokenStatusConfigured": "Eingerichtet ({chars} Zeichen)",
|
||||
"@hubAuthTokenStatusConfigured": {
|
||||
"placeholders": {
|
||||
"chars": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hubAuthTokenStatusNotSet": "Nicht gesetzt (anonym)",
|
||||
"hubAuthTokenFieldLabel": "Token",
|
||||
"hubAuthTokenFieldHint": "Token aus dem auth.tokens-Eintrag des Hub-Operators einfügen",
|
||||
"hubAuthTokenSaveButton": "Speichern",
|
||||
"hubAuthTokenClearButton": "Entfernen",
|
||||
"hubAuthTokenStorageHint": "Lokal gespeichert in ~/.fai/hub-auth-token. Studio verbindet nach dem Speichern automatisch neu, der Token wirkt sofort.",
|
||||
"hubAuthTokenSavedToast": "Hub-Token gespeichert.",
|
||||
"hubAuthTokenClearedToast": "Hub-Token entfernt.",
|
||||
"hubAuthTokenSaveFailedToast": "Speichern fehlgeschlagen: {error}",
|
||||
"@hubAuthTokenSaveFailedToast": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"maintenanceHeader": "HUB-WARTUNG",
|
||||
"maintenanceResetBlurb": "Operator-Zustand zurücksetzen und mit einem sauberen Hub neu starten. Stoppt jeden laufenden Daemon, sichert ~/.fai/ atomar in ein Backup, legt es dann neu an — Binary, Channel-Pointer, MCP-/n8n-/System-AI-Konfiguration und Registry-Token bleiben erhalten. Wiederherstellen durch Zurückverschieben des Backup-Ordners.",
|
||||
"maintenanceKeepModulesTitle": "Installierte Module behalten",
|
||||
|
|
|
|||
|
|
@ -753,6 +753,32 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"hubAuthTokenHeader": "HUB AUTHENTICATION",
|
||||
"hubAuthTokenBlurb": "Bearer token used to authenticate Studio against a hub that has auth.tokens: configured (RBAC Level 2). Stored at ~/.fai/hub-auth-token, mode 0600. Studio sends it as Authorization: Bearer on every gRPC call.",
|
||||
"hubAuthTokenStatusConfigured": "Configured ({chars} chars)",
|
||||
"@hubAuthTokenStatusConfigured": {
|
||||
"placeholders": {
|
||||
"chars": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hubAuthTokenStatusNotSet": "Not set (anonymous)",
|
||||
"hubAuthTokenFieldLabel": "Token",
|
||||
"hubAuthTokenFieldHint": "Paste a token from the hub operator's auth.tokens entry",
|
||||
"hubAuthTokenSaveButton": "Save",
|
||||
"hubAuthTokenClearButton": "Clear",
|
||||
"hubAuthTokenStorageHint": "Saved locally to ~/.fai/hub-auth-token. Studio reconnects after save so the new token takes effect immediately.",
|
||||
"hubAuthTokenSavedToast": "Hub auth token saved.",
|
||||
"hubAuthTokenClearedToast": "Hub auth token cleared.",
|
||||
"hubAuthTokenSaveFailedToast": "Could not save: {error}",
|
||||
"@hubAuthTokenSaveFailedToast": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"maintenanceHeader": "HUB MAINTENANCE",
|
||||
"maintenanceResetBlurb": "Wipe operator state and start over with a clean hub. Stops every running daemon, atomically backs ~/.fai/ up, then recreates it with the binary, channel state, MCP / n8n / system-AI config, and registry token preserved. Restore by moving the backup directory back.",
|
||||
"maintenanceKeepModulesTitle": "Keep installed modules",
|
||||
|
|
|
|||
|
|
@ -2174,6 +2174,78 @@ abstract class AppLocalizations {
|
|||
/// **'Could not save: {error}'**
|
||||
String registryTokenSaveFailedToast(String error);
|
||||
|
||||
/// No description provided for @hubAuthTokenHeader.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'HUB AUTHENTICATION'**
|
||||
String get hubAuthTokenHeader;
|
||||
|
||||
/// No description provided for @hubAuthTokenBlurb.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Bearer token used to authenticate Studio against a hub that has auth.tokens: configured (RBAC Level 2). Stored at ~/.fai/hub-auth-token, mode 0600. Studio sends it as Authorization: Bearer on every gRPC call.'**
|
||||
String get hubAuthTokenBlurb;
|
||||
|
||||
/// No description provided for @hubAuthTokenStatusConfigured.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Configured ({chars} chars)'**
|
||||
String hubAuthTokenStatusConfigured(int chars);
|
||||
|
||||
/// No description provided for @hubAuthTokenStatusNotSet.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Not set (anonymous)'**
|
||||
String get hubAuthTokenStatusNotSet;
|
||||
|
||||
/// No description provided for @hubAuthTokenFieldLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Token'**
|
||||
String get hubAuthTokenFieldLabel;
|
||||
|
||||
/// No description provided for @hubAuthTokenFieldHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Paste a token from the hub operator\'s auth.tokens entry'**
|
||||
String get hubAuthTokenFieldHint;
|
||||
|
||||
/// No description provided for @hubAuthTokenSaveButton.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Save'**
|
||||
String get hubAuthTokenSaveButton;
|
||||
|
||||
/// No description provided for @hubAuthTokenClearButton.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Clear'**
|
||||
String get hubAuthTokenClearButton;
|
||||
|
||||
/// No description provided for @hubAuthTokenStorageHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Saved locally to ~/.fai/hub-auth-token. Studio reconnects after save so the new token takes effect immediately.'**
|
||||
String get hubAuthTokenStorageHint;
|
||||
|
||||
/// No description provided for @hubAuthTokenSavedToast.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hub auth token saved.'**
|
||||
String get hubAuthTokenSavedToast;
|
||||
|
||||
/// No description provided for @hubAuthTokenClearedToast.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hub auth token cleared.'**
|
||||
String get hubAuthTokenClearedToast;
|
||||
|
||||
/// No description provided for @hubAuthTokenSaveFailedToast.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Could not save: {error}'**
|
||||
String hubAuthTokenSaveFailedToast(String error);
|
||||
|
||||
/// No description provided for @maintenanceHeader.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -1241,6 +1241,49 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
return 'Speichern fehlgeschlagen: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get hubAuthTokenHeader => 'HUB-AUTHENTIFIZIERUNG';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenBlurb =>
|
||||
'Bearer-Token für die Studio-Anbindung an einen Hub mit aktivierter auth.tokens-Konfiguration (RBAC Level 2). Gespeichert in ~/.fai/hub-auth-token, Modus 0600. Studio sendet ihn als Authorization: Bearer bei jedem gRPC-Aufruf.';
|
||||
|
||||
@override
|
||||
String hubAuthTokenStatusConfigured(int chars) {
|
||||
return 'Eingerichtet ($chars Zeichen)';
|
||||
}
|
||||
|
||||
@override
|
||||
String get hubAuthTokenStatusNotSet => 'Nicht gesetzt (anonym)';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenFieldLabel => 'Token';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenFieldHint =>
|
||||
'Token aus dem auth.tokens-Eintrag des Hub-Operators einfügen';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenSaveButton => 'Speichern';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenClearButton => 'Entfernen';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenStorageHint =>
|
||||
'Lokal gespeichert in ~/.fai/hub-auth-token. Studio verbindet nach dem Speichern automatisch neu, der Token wirkt sofort.';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenSavedToast => 'Hub-Token gespeichert.';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenClearedToast => 'Hub-Token entfernt.';
|
||||
|
||||
@override
|
||||
String hubAuthTokenSaveFailedToast(String error) {
|
||||
return 'Speichern fehlgeschlagen: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get maintenanceHeader => 'HUB-WARTUNG';
|
||||
|
||||
|
|
|
|||
|
|
@ -1256,6 +1256,49 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
return 'Could not save: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get hubAuthTokenHeader => 'HUB AUTHENTICATION';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenBlurb =>
|
||||
'Bearer token used to authenticate Studio against a hub that has auth.tokens: configured (RBAC Level 2). Stored at ~/.fai/hub-auth-token, mode 0600. Studio sends it as Authorization: Bearer on every gRPC call.';
|
||||
|
||||
@override
|
||||
String hubAuthTokenStatusConfigured(int chars) {
|
||||
return 'Configured ($chars chars)';
|
||||
}
|
||||
|
||||
@override
|
||||
String get hubAuthTokenStatusNotSet => 'Not set (anonymous)';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenFieldLabel => 'Token';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenFieldHint =>
|
||||
'Paste a token from the hub operator\'s auth.tokens entry';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenSaveButton => 'Save';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenClearButton => 'Clear';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenStorageHint =>
|
||||
'Saved locally to ~/.fai/hub-auth-token. Studio reconnects after save so the new token takes effect immediately.';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenSavedToast => 'Hub auth token saved.';
|
||||
|
||||
@override
|
||||
String get hubAuthTokenClearedToast => 'Hub auth token cleared.';
|
||||
|
||||
@override
|
||||
String hubAuthTokenSaveFailedToast(String error) {
|
||||
return 'Could not save: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get maintenanceHeader => 'HUB MAINTENANCE';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue