feat(studio): registry credentials panel in Settings (v0.44.0)

The hub install path now reads `~/.fai/registry-token` as a
fallback when `FAI_REGISTRY_TOKEN` is unset (platform v0.10.92).
Studio now writes that file directly: a fresh operator pastes
the PAT into Settings → Registry credentials, hits Save, and
the next install attempt resolves the auth wall without any
shell or env-var setup.

The token never round-trips back into Studio after save —
status is shown only as "Configured (40 chars)" / "Not set"
with no display of the secret itself. The Clear action deletes
the file. On Unix the file is chmod-ed to 0600 (owner-only
read/write); Windows leaves the default user ACL in place.

Storage is strictly local: `~/.fai/registry-token`, never sent
to a remote service. The hint text under the field says so to
make the data flow explicit.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-09 13:38:47 +02:00
parent 01f3f773cf
commit 878f0a4e28
8 changed files with 464 additions and 6 deletions

View file

@ -433,6 +433,22 @@
"@flowsOpenInEditorFailed": {
"placeholders": { "error": { "type": "String" } }
},
"registryCredentialsHeader": "REGISTRY-ZUGANGSDATEN",
"registryCredentialsBlurb": "Token zum Herunterladen von .fai-Modulen aus einer Registry mit Anmeldepflicht (Forgejo, private GitHub-Repos). Gespeichert in ~/.fai/registry-token, Modus 0600. Die Umgebungsvariable FAI_REGISTRY_TOKEN hat weiterhin Vorrang, wenn gesetzt.",
"registryTokenStatusConfigured": "Konfiguriert ({chars} Zeichen)",
"@registryTokenStatusConfigured": { "placeholders": { "chars": { "type": "int" } } },
"registryTokenStatusNotSet": "Nicht gesetzt",
"registryTokenFieldLabel": "Token",
"registryTokenFieldHint": "Forgejo- oder GitHub-PAT einfügen",
"registryTokenSaveButton": "Speichern",
"registryTokenClearButton": "Entfernen",
"registryTokenStorageHint": "Lokal gespeichert in ~/.fai/registry-token. Wird nie an einen Remote-Dienst übertragen.",
"registryTokenSavedToast": "Registry-Token gespeichert.",
"registryTokenClearedToast": "Registry-Token entfernt.",
"registryTokenSaveFailedToast": "Speichern fehlgeschlagen: {error}",
"@registryTokenSaveFailedToast": {
"placeholders": { "error": { "type": "String" } }
},
"welcomeChecklistAllSetTitle": "Du bist eingerichtet.",
"welcomeChecklistAllSetBody": "Drei Stränge, an denen du als nächstes ziehen kannst:",
"welcomeChecklistNextAuditTitle": "Audit-Log lesen",

View file

@ -434,6 +434,22 @@
"@flowsOpenInEditorFailed": {
"placeholders": { "error": { "type": "String" } }
},
"registryCredentialsHeader": "REGISTRY CREDENTIALS",
"registryCredentialsBlurb": "Token used to download .fai modules from a registry behind a signin wall (Forgejo, GitHub-private). Stored at ~/.fai/registry-token, mode 0600. The FAI_REGISTRY_TOKEN env var still wins when set.",
"registryTokenStatusConfigured": "Configured ({chars} chars)",
"@registryTokenStatusConfigured": { "placeholders": { "chars": { "type": "int" } } },
"registryTokenStatusNotSet": "Not set",
"registryTokenFieldLabel": "Token",
"registryTokenFieldHint": "Paste your Forgejo or GitHub personal access token",
"registryTokenSaveButton": "Save",
"registryTokenClearButton": "Clear",
"registryTokenStorageHint": "Saved locally to ~/.fai/registry-token. Never sent to a remote service.",
"registryTokenSavedToast": "Registry token saved.",
"registryTokenClearedToast": "Registry token cleared.",
"registryTokenSaveFailedToast": "Could not save: {error}",
"@registryTokenSaveFailedToast": {
"placeholders": { "error": { "type": "String" } }
},
"welcomeChecklistAllSetTitle": "You're set up.",
"welcomeChecklistAllSetBody": "Three threads to pull on next:",
"welcomeChecklistNextAuditTitle": "Read the audit log",

View file

@ -1970,6 +1970,78 @@ abstract class AppLocalizations {
/// **'Could not open file: {error}'**
String flowsOpenInEditorFailed(String error);
/// No description provided for @registryCredentialsHeader.
///
/// In en, this message translates to:
/// **'REGISTRY CREDENTIALS'**
String get registryCredentialsHeader;
/// No description provided for @registryCredentialsBlurb.
///
/// In en, this message translates to:
/// **'Token used to download .fai modules from a registry behind a signin wall (Forgejo, GitHub-private). Stored at ~/.fai/registry-token, mode 0600. The FAI_REGISTRY_TOKEN env var still wins when set.'**
String get registryCredentialsBlurb;
/// No description provided for @registryTokenStatusConfigured.
///
/// In en, this message translates to:
/// **'Configured ({chars} chars)'**
String registryTokenStatusConfigured(int chars);
/// No description provided for @registryTokenStatusNotSet.
///
/// In en, this message translates to:
/// **'Not set'**
String get registryTokenStatusNotSet;
/// No description provided for @registryTokenFieldLabel.
///
/// In en, this message translates to:
/// **'Token'**
String get registryTokenFieldLabel;
/// No description provided for @registryTokenFieldHint.
///
/// In en, this message translates to:
/// **'Paste your Forgejo or GitHub personal access token'**
String get registryTokenFieldHint;
/// No description provided for @registryTokenSaveButton.
///
/// In en, this message translates to:
/// **'Save'**
String get registryTokenSaveButton;
/// No description provided for @registryTokenClearButton.
///
/// In en, this message translates to:
/// **'Clear'**
String get registryTokenClearButton;
/// No description provided for @registryTokenStorageHint.
///
/// In en, this message translates to:
/// **'Saved locally to ~/.fai/registry-token. Never sent to a remote service.'**
String get registryTokenStorageHint;
/// No description provided for @registryTokenSavedToast.
///
/// In en, this message translates to:
/// **'Registry token saved.'**
String get registryTokenSavedToast;
/// No description provided for @registryTokenClearedToast.
///
/// In en, this message translates to:
/// **'Registry token cleared.'**
String get registryTokenClearedToast;
/// No description provided for @registryTokenSaveFailedToast.
///
/// In en, this message translates to:
/// **'Could not save: {error}'**
String registryTokenSaveFailedToast(String error);
/// No description provided for @welcomeChecklistAllSetTitle.
///
/// In en, this message translates to:

View file

@ -1117,6 +1117,48 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Konnte Datei nicht öffnen: $error';
}
@override
String get registryCredentialsHeader => 'REGISTRY-ZUGANGSDATEN';
@override
String get registryCredentialsBlurb =>
'Token zum Herunterladen von .fai-Modulen aus einer Registry mit Anmeldepflicht (Forgejo, private GitHub-Repos). Gespeichert in ~/.fai/registry-token, Modus 0600. Die Umgebungsvariable FAI_REGISTRY_TOKEN hat weiterhin Vorrang, wenn gesetzt.';
@override
String registryTokenStatusConfigured(int chars) {
return 'Konfiguriert ($chars Zeichen)';
}
@override
String get registryTokenStatusNotSet => 'Nicht gesetzt';
@override
String get registryTokenFieldLabel => 'Token';
@override
String get registryTokenFieldHint => 'Forgejo- oder GitHub-PAT einfügen';
@override
String get registryTokenSaveButton => 'Speichern';
@override
String get registryTokenClearButton => 'Entfernen';
@override
String get registryTokenStorageHint =>
'Lokal gespeichert in ~/.fai/registry-token. Wird nie an einen Remote-Dienst übertragen.';
@override
String get registryTokenSavedToast => 'Registry-Token gespeichert.';
@override
String get registryTokenClearedToast => 'Registry-Token entfernt.';
@override
String registryTokenSaveFailedToast(String error) {
return 'Speichern fehlgeschlagen: $error';
}
@override
String get welcomeChecklistAllSetTitle => 'Du bist eingerichtet.';

View file

@ -1131,6 +1131,49 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Could not open file: $error';
}
@override
String get registryCredentialsHeader => 'REGISTRY CREDENTIALS';
@override
String get registryCredentialsBlurb =>
'Token used to download .fai modules from a registry behind a signin wall (Forgejo, GitHub-private). Stored at ~/.fai/registry-token, mode 0600. The FAI_REGISTRY_TOKEN env var still wins when set.';
@override
String registryTokenStatusConfigured(int chars) {
return 'Configured ($chars chars)';
}
@override
String get registryTokenStatusNotSet => 'Not set';
@override
String get registryTokenFieldLabel => 'Token';
@override
String get registryTokenFieldHint =>
'Paste your Forgejo or GitHub personal access token';
@override
String get registryTokenSaveButton => 'Save';
@override
String get registryTokenClearButton => 'Clear';
@override
String get registryTokenStorageHint =>
'Saved locally to ~/.fai/registry-token. Never sent to a remote service.';
@override
String get registryTokenSavedToast => 'Registry token saved.';
@override
String get registryTokenClearedToast => 'Registry token cleared.';
@override
String registryTokenSaveFailedToast(String error) {
return 'Could not save: $error';
}
@override
String get welcomeChecklistAllSetTitle => 'You\'re set up.';