B3.Blicks.Admin.UI 2026.8.6.1

B3.Blicks.Admin.UI

The in-app Blicks Admin page — an operator view a host Shell mounts at /_blicks/admin. Read-only by default (slice 1); a host that registers a write path (slice 2) gets browse/assign/save with pre-flight gating too.

Slice 1 (read-only)

  • Region 1 — current selection. Each pinned Blick's id, version, status, and where the pin came from (relay vs. appsettings fallback). Wired from a host-implemented ICurrentBlickSelection; a config-backed default reads Blicks:SelectedVersions.
  • Region 2 — registry catalog. Every Blick in the registry (not just pinned ones — see slice 2), with pin-target versions read per Blick (GET /api/v1/blicks/{id}). A registry that can't be reached renders "unverifiable" — it never throws.
  • Region 3 — pre-flight. Picking a version link runs SharedManifestPreflight.Validate against the Shell's shared-platform manifest and reports OK / would-be-refused / unverifiable.

With no write path registered, the page is exactly this — no save, no restart button.

Slice 2 (write path)

Registering an IBlickSelectionCommands before calling AddBlicksAdmin turns on assign/change/remove/save:

builder.Services.AddSingleton<IBlickSelectionCommands>(sp => new YourSaveImplementation(...));
builder.Services.AddBlicksAdmin(builder.Configuration);
  • Region 2's catalog rows get an Assign action per pin-target version; an already-pinned Blick shows an Assigned badge. Region 1's running-selection rows get a Remove action.
  • Edits round-trip through the page's own pending query/form value (no server-side session) and show up in Region 1 as an unsaved changes panel — a diff table (Blick / running / pending / live pre-flight outcome) plus Save and Discard.
  • Save re-runs pre-flight for every CHANGED entry: WouldBeRefused blocks the save and shows the reasons; Unverifiable (registry unreachable) saves anyway with a prominent warning; OK saves. It calls IBlickSelectionCommands.Save with the FULL desired selection (not a delta).
  • After a successful save, Region 1 shows the pending-vs-running difference honestly — the write path only persists a desired selection; nothing here restarts the process. A host's Save implementation decides how (or whether) to apply it — see Tester.Shell.Blicks.BlickUpdater for a reference implementation that just writes a file and leaves the restart to the operator.

No default is registered for IBlickSelectionCommands — its absence IS the read-only fallback, matching slice 1 exactly.

Wiring it into a Shell

builder.Services.AddBlicksAdmin(builder.Configuration);
// ...
app.MapBlicksAdmin();

Off by default — set BlicksAdmin:Enabled to true to mount the page. The route (and, when a write path is registered, its /working, /discard, /save POST endpoints) is gated by the BlicksAdminAuthorization.PolicyName authorization policy, which the Shell MUST define via AddAuthorization(o => o.AddPolicy(BlicksAdminAuthorization.PolicyName, ...)). If the Shell never defines it, ASP.NET Core's authorization middleware refuses every request for the missing policy — fail-closed, never fall open. Every POST binds its form data via [FromForm], which ASP.NET Core automatically requires a valid antiforgery token for once app.UseAntiforgery() is in the pipeline — the page's forms carry <AntiforgeryToken />.

Configuration (BlicksAdmin: section): Enabled, RegistryBaseURL, RegistryAPIKey.

To get a real current-selection view and a meaningful pre-flight check, register your own ICurrentBlickSelection and a populated SharedPlatformManifest singleton before calling AddBlicksAdmin — otherwise the page falls back to the config-backed defaults (an empty manifest is a safe no-op, matching ResolverOptions.SharedPlatform's own default).

No packages depend on B3.Blicks.Admin.UI.

.NET 10.0

Version Downloads Last updated
2026.8.6.1 6 8/6/2026