UI design spec — Unified Secret Provider (Angular)

All new UI gates on the existing scope helpers in src/app/core/auth/scopes.ts (CRYPTO_READ, CRYPTO_ADMIN, CRYPTO_PROFILE_ADMIN, CRYPTO_KEK_READ, CRYPTO_KEK_ROTATE). Never hardcode mock data; drive all state from backend clients (seed via DB/backend).


1. Master-key setup step (first-run wizard)

2. Crypto / secret settings page (extend crypto-control)

3. Registry provider-selection UI

4. Deployment provider / setup UI

5. Shared components introduced (reused across §1–§4)

ComponentPurposeHome
SecretMaterialInputComponentToggle generate/import, symmetric/asymmetric, masked pastesrc/app/shared/secret-provider/
ScopeGatingBannerComponent“You need scope X” actionable bannersrc/app/shared/secret-provider/
SecretBackendSelectorComponentbuiltin/vault/openbao picker (registry + deployment reuse)src/app/shared/secret-provider/

6. DI / services


8. SecretReferenceInputComponent — URL references everywhere (ADR-032)

8.1 What it is

The everyday secret-reference control. It edits a canonical URL (the non-secret pointer) and provides:

  1. URL field — free-text canonical reference (<scheme>://[id/]path[#version]), parsed + validated client-side (mirror SecretReference rules); shows the resolved scheme/provider/path breakdown as a hint.
  2. Select… affordance — a two-step picker: (a) choose provider from the /secret-providers list (kind + id + reachable badge), then (b) choose path.
  3. Debounced autocomplete (~250 ms) — as the operator types a path prefix, calls GET /secret-providers/{id}/paths?prefix=&pageToken= and lists path names only (never values), crypto:read-gated, paged (“load more”).
  4. Reference-existing / Seal-new toggle:
    • Reference existing (default) — pick a path that already holds a secret; the form value is just the URL.
    • Seal new here — type a new path under the chosen provider + supply the secret value once (reuse the masked input from SecretMaterialInput); on save the host calls the backend seal and stores the returned URL.
  5. Scope gating — wrap in ScopeGatingBannerComponent; autocomplete + Select require crypto:read; seal-new requires the surface’s existing write scope.

8.2 Distinction from SecretMaterialInputComponent

SecretMaterialInput stays for offline master-key material (raw key paste / PEM / generate at setup). SecretReferenceInput is for everyday secret references (URL + provider/path picker + autocomplete). A surface migrating from a plain password input adopts SecretReferenceInput; it does NOT use SecretMaterialInput.

8.3 Service additions (secret-provider.service.ts)

listProviders(): Observable<SecretProviderSummary[]>;          // GET /secret-providers
listPaths(providerId, prefix, pageToken?): Observable<PathPage>; // GET /secret-providers/{id}/paths
parseReference(url): ParsedSecretReference;                     // client-side mirror of SecretReference

PathPage = { paths: string[]; nextPageToken: string | null }. Never carries secret values.

8.4 Per-surface migration table (every Tier 1/2/3 surface)

TierSurface (path under src/Web/StellaOps.Web/src/app/)Field(s) to migrateTargetNotes
1features/integrations/integration-wizard.component.{ts,html}basic password, token, OAuth2 clientSecret, secret-authority bundle entry valuesSecretReferenceInput (picker + autocomplete + seal-new)registry-credential-backend selector already migrated; these auth fields are STILL plain password inputs
1features/setup-wizard/ master-key-stepmaster-key materialKEEP SecretMaterialInput (already WF3)offline material, not a reference — no change
1features/notify/notify-panel.*channel secretRefSecretReferenceInputupgrade existing secretRef text field to URL picker/autocomplete
1features/admin-notifications/components/channel-management.component.tschannel secretRefSecretReferenceInputalready a secretRef text field — add picker/autocomplete
2features/console-admin/connector-credentials/connector-credentials-list.component.tsapi_key/PAT, basic password, oauth2 clientSecret, cert PEM + passphraseSecretReferenceInput (reference-existing + seal-new)plain inputs today; cert PEM uses seal-new (large value)
2features/sbom-sources/components/source-wizard/source-wizard.component.tsbasic/token/oauth2 secrets + authref fieldSecretReferenceInputauthref:// parses as vault alias — existing values keep working
2features/vault-bundles/vault-bundle-author-page.component.tsbundle entry secret valuesSecretReferenceInputvalues may seal-new into a chosen provider
3features/release-orchestrator/.../create-release.component.tsdeployment backend selectoralready migratedverify it adopts URL form; no plain-input fields remain
3features/console-admin/crypto-control/crypto-rotate-wizard.component.tsKEK materialKEEP SecretMaterialInputreferences-only constraint already satisfied; no change
3features/console-admin/tenants/tenant-deployment-secret-mode.component.tsdeployment secret modeSecretReferenceInput where it stores a reference; mode toggle unchanged

Migration discipline: each surface keeps its existing form-control name + emitted value shape as a URL string; carry the surface’s .spec.ts (TESTING_PRACTICES Rule A) and add a spec asserting the emitted value is a canonical URL and that autocomplete never surfaces secret values.