Credentials
Moduł credentials pozwala operatorowi zdefiniować wielokrotnego
użytku konfigurację poświadczeń raz i referencjonować ją z wielu miejsc. Zamiast
wpisywać klucz API ponownie w ustawieniach asystenta AI, embeddera wyszukiwania
i newslettera, tworzysz jedną konfigurację Primary LLM i wskazujesz nią każde
ustawienie. Zmień klucz raz — każdy konsument pobiera nową wartość bez dalszych
edycji.
Pola tajne (klucze API, hasła) są szyfrowane w spoczynku, write-only na granicy (nigdy nie zwracane w plaintext), maskowane przy każdym odczycie i redagowane w logu audytu. Każde utworzenie / edycja / usunięcie jest audytowane przez Command Bus.
Dla Product Ownerów i operatorów
Czym jest konfiguracja poświadczeń
Konfiguracja to nazwana, zakodowana instancja typu konfiguracji. Dwa typy są dostarczane out of the box:
- LLM — dostawcy GPT (OpenAI), Gemini (Google), Claude (Anthropic) i DeepSeek. Pola: API Key (tajne, wymagane), Model (wymagany), Base URL (opcjonalny).
- Email adapter — dostawcy SMTP, Amazon SES i SendGrid.
Każdy dostawca ma własny zestaw pól z dokładnie jednym polem tajnym
(SMTP
password, SESsecretAccessKey, SendGridapiKey).
Wybrany typ i dostawca decydują, które pola pokazuje formularz.
Tworzenie konfiguracji
- Otwórz Credentials w sidebarze admina (widoczne z uprawnieniem
credentials:read). - Kliknij New configuration.
- Wybierz Type (np. LLM) i Provider (np. Claude). Formularz przeładuje się na pola tego dostawcy.
- Wypełnij Name (np.
Primary LLM) i Code (stabilny identyfikator jakprimary-llm— to referencjonują ustawienia), potem pola (API Key, Model, …). - Save. Konfiguracja pojawia się na liście; każde pole tajne pokazuje się jako set — nigdy wartość.
Edycja działa tak samo. Przy edycji typ i dostawca są zablokowane (nie można ich zmienić — utwórz nową konfigurację). Pozostawienie pola tajnego pustego zachowuje zapisane sekret; wpisanie nowej wartości je zastępuje.
Użycie konfiguracji z ustawienia
Niektóre ustawienia są typu "credential reference", ograniczone do jednego typu konfiguracji. Takie ustawienie renderuje się jako picker pasujących konfiguracji plus przycisk Preview:
- Otwórz Settings i znajdź ustawienie credential-reference (np. LLM credentials asystenta AI).
- Wybierz konfigurację z dropdownu — oferowane są tylko konfiguracje właściwego typu.
- Kliknij Preview, aby zobaczyć referencjonowaną konfigurację read-only (sekrety zamaskowane) bez opuszczania strony.
Przypisz tę samą konfigurację do kilku ustawień, aby ją wielokrotnie użyć. Zaktualizuj klucz konfiguracji raz, a wszystkie rozwiążą nową wartość.
Bezpieczeństwo
- Usunięcie jest blokowane, dopóki istnieje referencja. Próba usunięcia konfiguracji, na którą nadal wskazuje ustawienie, kończy się jasnym komunikatem wymieniającym dokładnie, które ustawienia trzeba najpierw odłączyć.
- Osierocone konfiguracje (których typ nie jest już dostępny) są pokazywane read-only („unavailable”) i nigdy nie crashują ekranu.
- Plaintext nigdy nie opuszcza serwera — lista, szczegóły i podgląd maskują sekrety; tylko ścieżka konsumenta po stronie serwera deszyfruje, w pamięci, do użycia.
Dla developerów
Model
Typ konfiguracji to deskryptor rejestrowany kodem (nie wiersze bazy):
interface ConfigurationTypeDescriptor {
code: string; // 'llm' | 'email_adapter' | …
label: string;
ownerModule: string;
providers: ProviderVariant[];
}
interface ProviderVariant { code: string; label: string; fields: FieldDefinition[]; }
interface FieldDefinition {
key: string;
label: string;
kind: 'string' | 'number' | 'boolean' | 'select';
required: boolean;
secret: boolean; // secret ⇒ encrypted at rest, masked on read
options?: { value: string; label: string }[];
placeholder?: string;
}
Zapisana konfiguracja przechowuje typeCode + providerCode + worek values
(pola tajne trzymają koperty AES-256-GCM, pola nietajne trzymają zwykłe
skalary) w tabeli credential_configurations (@GlobalEntity,
platform-global).
Rdzeń credentials jest agnostyczny typowo: czyta
deskryptor tylko po to, by renderować pola, wyprowadzić walidator zapisu i
dowiedzieć się, które pola są tajne. Nigdy nie rozgałęzia się po konkretnym
typeCode / providerCode — znaczenie dostawcy żyje u konsumenta.
Rejestracja nowego typu (punkt rozszerzenia)
Rejestruj ze ścieżki install dowolnego modułu przez process-wide singleton — bez zmiany rdzenia credentials (overlay-safe):
import { configurationTypeRegistry } from '@core/modules/credentials/services/registry-singleton.js';
configurationTypeRegistry.register({
code: 'sms_gateway',
label: 'SMS Gateway',
ownerModule: 'my_module',
providers: [
{
code: 'twilio',
label: 'Twilio',
fields: [
{ key: 'accountSid', label: 'Account SID', kind: 'string', required: true, secret: false },
{ key: 'authToken', label: 'Auth Token', kind: 'string', required: true, secret: true },
],
},
],
});
Typ natychmiast pojawia się w pickerze typów admina (GET /api/v1/admin/credentials/types) i można go utworzyć.
Typ wartości ustawienia credential_ref
Aby ustawienie referencjonowało konfigurację, zadeklaruj je w manifeście ustawień modułu:
{
code: 'prompt_actions.llm_credentials',
name: 'LLM credentials',
valueType: 'credential_ref',
configurationType: 'llm', // only configurations of this type are selectable
defaultValue: '', // empty ⇒ not configured
}
configurationType jest wymagane dla ustawień credential_ref i zabronione
inaczej. Zapisana wartość to po prostu code konfiguracji.
Rozwiązywanie referencji (strona konsumenta)
Rozwiązywanie to wywołanie serwisu po stronie serwera (nigdy odpowiedź HTTP) — jedyna ścieżka zwracająca odszyfrowane sekrety, tylko do użycia w pamięci:
const code = await settings.get('prompt_actions.llm_credentials'); // → 'primary-llm'
const cred = code ? await credentials.resolve(code) : { status: 'not_configured' };
if (cred.status !== 'ok') throw new Error('LLM not configured'); // fail closed
callProvider(cred.providerCode, cred.values.apiKey, cred.values.model);
resolve zwraca wynik dyskryminowany:
{ status: 'ok', typeCode, providerCode, values }— sekrety odszyfrowane;{ status: 'not_configured' }— referencja jest nieustawiona/pusta;{ status: 'unavailable', reason: 'missing' | 'inert_type' }— usunięty code lub niezarejestrowany typ.
Konsument nigdy nie dostaje obcej konfiguracji ani plaintext sekretu przez wire.
Integralność usuwania
CredentialsService.delete najpierw woła
SettingsService.listReferencesToConfiguration(code) (jedyny kanał, przez który
credentials dociera do settings). Niepusty wynik blokuje
usunięcie z 409 CREDENTIAL_IN_USE, niosąc { referencedBy: [{ settingCode, salesChannelCode? }] }.
Sekrety i konfiguracja
Pola tajne używają platformowego kodeka koperty AES-256-GCM kluczowanego istniejącą
zmienną env SETTINGS_SECRET_ENCRYPTION_KEY — bez nowego sekretu do
provisioning. Zapis sekretu bez klucza kończy się fail closed
(SETTING_SECRET_KEY_MISSING); start ostrzega, jeśli klucz jest nieustawiony.
Uprawnienia
credentials:read— podgląd konfiguracji i katalogu typów (sekrety zamaskowane).credentials:write— tworzenie / edycja / usuwanie konfiguracji, w tym zapis pól tajnych.