Add mirror client setup wizard for consumer configuration

Backend: 4 consumer API endpoints (GET/PUT /consumer config, POST
/consumer/discover for index parsing, POST /consumer/verify-signature
for JWS header detection), air-gap bundle import endpoint with manifest
parsing and SHA256 verification, IMirrorConsumerConfigStore and
IMirrorBundleImportStore interfaces.

Frontend: 4-step mirror client setup wizard (connect + test, signature
verification with auto-detect, sync mode + schedule + air-gap import,
review + pre-flight checks + activate). Dashboard consumer panel with
"Configure" button, Direct mode "Switch to Mirror" CTA, catalog header
"Connect to Mirror" link and consumer status display.

E2E: 9 Playwright test scenarios covering wizard steps, connection
testing, domain discovery, signature detection, mode selection,
pre-flight checks, dashboard integration, and catalog integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-15 14:35:19 +02:00
parent ef4991cdd0
commit 9add6af221
10 changed files with 4034 additions and 22 deletions

View File

@@ -0,0 +1,231 @@
# Sprint 20260315_008 - Mirror Client Setup Wizard
## Topic & Scope
- Give operators a self-serve UI to configure Stella Ops as a **mirror consumer** — pulling advisory/VEX data from an upstream mirror instead of (or alongside) direct sources.
- Currently, switching to Mirror or Hybrid mode requires env vars (`CONCELIER__SOURCES__STELLAOPSMIRROR__*`). The backend connector (`StellaOpsMirrorConnector`) and API (`PUT /api/v1/mirror/config`, `POST /api/v1/mirror/test`) already exist, but there is no guided setup flow.
- Also wire the air-gap bundle import path into the UI (currently CLI-only via `MirrorBundleImportService`).
- Working directory: `src/Web/StellaOps.Web`, `src/Concelier/`
- Cross-module edits: `src/Cli/`, `docs/modules/concelier/`
- Expected evidence: wizard component, API extensions, Playwright tests, updated docs.
## Dependencies & Concurrency
- Depends on Sprint 007 (mirror domain API, dashboard, catalog header — all DONE except TASK-011 docs).
- The `StellaOpsMirrorConnector` plugin at `src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/` is the backend consumer. The wizard configures it.
- Safe parallelism: frontend tasks don't conflict with Sprint 006 (different components). Backend tasks modify Concelier endpoints (coordinate with TASK-011 if running).
## Documentation Prerequisites
- `docs/modules/concelier/operations/mirror.md`
- `src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/Settings/StellaOpsMirrorConnectorOptions.cs`
- `src/Concelier/StellaOps.Concelier.WebService/Extensions/MirrorDomainManagementEndpointExtensions.cs`
- `src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/mirror-dashboard.component.ts`
---
## Delivery Tracker
### MCS-001 - Extend mirror config API for consumer setup
Status: DONE
Dependency: none
Owners: Developer (Backend)
Task description:
- The existing `PUT /api/v1/mirror/config` accepts mode and consumerBaseAddress but doesn't persist or apply the `StellaOpsMirrorConnectorOptions` (connector base address, domain ID, signature settings, timeout). Add endpoints to configure the consumer connector at runtime.
**Endpoints to add** (under `/api/v1/mirror`):
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/consumer` | Get current consumer connector configuration |
| PUT | `/consumer` | Update consumer connector config (base address, domain, signature, timeout) |
| POST | `/consumer/discover` | Fetch mirror index from base address, return available domains with metadata |
| POST | `/consumer/verify-signature` | Fetch a bundle header from mirror, return signature details (alg, kid) |
**Request/Response DTOs**:
```
ConsumerConfigRequest { baseAddress, domainId, indexPath?, httpTimeoutSeconds?, signature: { enabled, algorithm, keyId, publicKeyPem? } }
ConsumerConfigResponse { baseAddress, domainId, indexPath, httpTimeoutSeconds, signature, connected, lastSync }
MirrorDiscoveryResponse { domains: [ { domainId, displayName, lastGenerated, advisoryCount, bundleSize, exportFormats[], signed } ] }
SignatureDiscoveryResponse { detected, algorithm, keyId, provider }
```
**Files to modify**:
- `src/Concelier/StellaOps.Concelier.WebService/Extensions/MirrorDomainManagementEndpointExtensions.cs` — add 4 new endpoints
- `src/Concelier/StellaOps.Concelier.WebService/Services/InMemoryMirrorDomainStore.cs` — add `IMirrorConsumerConfigStore` with get/set for consumer config
Completion criteria:
- [x] 4 new consumer endpoints created and wired
- [x] Consumer config persisted in-memory (later DB)
- [x] Discovery endpoint fetches real mirror index and parses domain metadata
- [x] Signature discovery fetches JWS header from bundle
### MCS-002 - Mirror client setup wizard UI
Status: DONE
Dependency: MCS-001
Owners: Developer (FE)
Task description:
- Create a guided wizard for configuring Stella Ops as a mirror consumer. The wizard should feel like a natural extension of the existing mirror dashboard.
**File to create**: `src/Web/StellaOps.Web/src/app/features/integrations/advisory-vex-sources/mirror-client-setup.component.ts`
**Wizard steps (4 steps)**:
**Step 1: Connect to Mirror**
- Mirror base address input (URL, validated, placeholder: `https://mirror.stella-ops.org`)
- "Test Connection" button → calls `POST /api/v1/mirror/test`
- Connection result: green checkmark + latency OR red X + error message + remediation hint
- On success: auto-fetch domain index via `POST /api/v1/mirror/consumer/discover`
- Domain selector dropdown (populated from discovery, shows domain name + advisory count + staleness)
- Index path override (advanced toggle, default `/concelier/exports/index.json`)
- HTTP timeout slider (5-300 seconds, default 30)
**Step 2: Signature Verification**
- Auto-detect: fetch JWS header from selected domain's bundle via `POST /api/v1/mirror/consumer/verify-signature`
- Show detected algorithm + key ID (pre-populated if found)
- Signature verification toggle (on/off)
- If enabled: algorithm dropdown (ES256/ES384/ES512/RS256/RS384), key ID input, public key PEM textarea or file upload
- "Verify Sample" button → download a small bundle chunk and verify signature
- Status: "Signature valid" / "Signature invalid" / "No signature detected"
**Step 3: Sync Schedule & Caching**
- Operating mode selector: Mirror (consumer only) / Hybrid (consumer + direct sources)
- Sync schedule presets: Manual, Hourly, Every 4 hours, Daily, Weekly
- Bundle caching toggle + cache TTL (hours, default 168)
- Air-gap import section (collapsible):
- "Import Bundle" button → file picker for bundle directory
- Trust roots file upload
- Checksum + DSSE verification toggles
**Step 4: Review & Activate**
- Summary card: mirror URL, domain, mode, signature status, sync schedule
- Pre-flight checks (auto-run):
- Mirror reachable
- Domain exists in index
- Signature valid (if enabled)
- Current sources that will be superseded listed
- "Activate Mirror Consumer" button → `PUT /api/v1/mirror/consumer` + `PUT /api/v1/mirror/config` (set mode)
- Success state: "Mirror consumer activated. First sync in X seconds." + link to dashboard
**Route**: `advisory-vex-sources/mirror/client-setup` (lazy-loaded)
Completion criteria:
- [ ] 4-step wizard component created (standalone, OnPush, signals)
- [ ] Connection testing with real-time feedback
- [ ] Domain discovery and selection from mirror index
- [ ] Signature auto-detection and manual configuration
- [ ] Mode selection and sync schedule
- [ ] Air-gap bundle import section
- [ ] Pre-flight validation before activation
- [ ] Route wired in integration-hub.routes.ts
### MCS-003 - Wire mirror client setup into dashboard and catalog
Status: DONE
Dependency: MCS-002
Owners: Developer (FE)
Task description:
- Update the mirror dashboard and catalog to link to the client setup wizard.
**Files to modify**:
- `mirror-dashboard.component.ts`:
- In the consumer panel: add "Configure" button → navigates to wizard
- When mode is Direct: show "Switch to Mirror" CTA → navigates to wizard
- When no consumer URL configured: show setup prompt instead of empty status
- `advisory-source-catalog.component.ts`:
- In mirror header: add "Connect to Mirror" button alongside "Configure Mirror"
- When mode is Mirror/Hybrid: show consumer URL + sync status in header stats
Completion criteria:
- [x] Dashboard consumer panel has "Configure" button
- [x] Direct mode shows "Switch to Mirror" CTA
- [x] Catalog header shows "Connect to Mirror" option
- [x] Consumer status visible in catalog stats when in Mirror/Hybrid mode
### MCS-004 - Air-gap bundle import API endpoint
Status: DONE
Dependency: none (parallel)
Owners: Developer (Backend)
Task description:
- Expose `MirrorBundleImportService` functionality via HTTP API so the wizard can trigger imports from the UI, not just CLI.
**Endpoint**:
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/v1/mirror/import` | Import a mirror bundle from a specified local path |
| GET | `/api/v1/mirror/import/status` | Get status of last import (progress, result) |
**Request**: `{ bundlePath, verifyChecksums: true, verifyDsse: true, trustRootsPath? }`
**Response**: `{ success, exportsImported, totalSize, errors[], warnings[] }`
Read `src/Cli/StellaOps.Cli/Services/MirrorBundleImportService.cs` to understand the import logic, then expose it as an API.
Completion criteria:
- [x] Import endpoint accepts local path and triggers import
- [x] Status endpoint reports progress/result
- [x] Checksum and DSSE verification configurable per request
- [x] Error details returned for failed imports
### MCS-005 - Playwright E2E tests for mirror client setup
Status: DONE
Dependency: MCS-002, MCS-003
Owners: QA, Test Automation
Task description:
- Create E2E tests for the mirror client setup wizard with mocked API responses.
**File to create**: `src/Web/StellaOps.Web/e2e/mirror-client-setup.e2e.spec.ts`
**Test scenarios**:
1. Wizard renders with 4 steps and step navigation works
2. Connection test shows success/failure feedback with mocked mirror/test response
3. Domain discovery populates dropdown from mocked index
4. Signature auto-detection pre-fills algorithm and key ID
5. Mode selection switches between Mirror and Hybrid
6. Pre-flight checks all pass before activation
7. Dashboard shows "Configure" button in consumer panel
8. Catalog header shows "Connect to Mirror" when in Direct mode
9. Air-gap import section triggers import endpoint
Completion criteria:
- [x] 9 test scenarios with mocked API
- [x] Tests cover happy path and error states
- [x] Tests verify navigation between wizard and dashboard
### MCS-006 - Documentation update
Status: TODO
Dependency: MCS-001 through MCS-005
Owners: Documentation author
Task description:
- Update mirror operations docs with the new UI-based client setup flow.
**Files to update**:
- `docs/modules/concelier/operations/mirror.md` — add "Setting up as Mirror Consumer (UI)" section
- `docs/modules/concelier/architecture.md` — mention consumer API endpoints
- Add consumer setup screenshots or step descriptions
Completion criteria:
- [ ] Mirror docs include UI-based consumer setup instructions
- [ ] Env var and UI configuration paths both documented
- [ ] Air-gap import documented for both CLI and UI
---
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-03-15 | Sprint created after investigation revealed mirror client/consumer backend exists but has no UI setup wizard. | Planning |
| 2026-03-15 | MCS-002 DONE: Created mirror-client-setup.component.ts (4-step wizard), extended mirror-management.api.ts with consumer DTOs + 5 new API methods, wired route at advisory-vex-sources/mirror/client-setup. | Developer (FE) |
| 2026-03-15 | MCS-005 DONE: Created `src/Web/StellaOps.Web/e2e/mirror-client-setup.e2e.spec.ts` with 9 test scenarios covering wizard rendering, connection test success/failure, domain discovery, signature auto-detection, mode selection, pre-flight checks, dashboard Configure button, and catalog Connect to Mirror button. All tests use mocked API responses following existing E2E patterns (auth fixture, navigateAndWait helper, page.route mocking). | QA |
| 2026-03-15 | MCS-001 DONE: Added 4 consumer endpoints (GET/PUT /consumer, POST /consumer/discover, POST /consumer/verify-signature), IMirrorConsumerConfigStore interface, in-memory implementation, DI wiring, MirrorConsumer HttpClient. | Developer (Backend) |
| 2026-03-15 | MCS-003 DONE: mirror-dashboard.component.ts — added Configure button in consumer panel header, setup prompt when no consumer URL, Switch to Mirror CTA card for Direct mode with showDirectModeCta computed signal. advisory-source-catalog.component.ts — added Connect to Mirror link in mirror context header, consumer URL + last sync stats for Mirror/Hybrid mode via isConsumerMode computed signal. | Developer (FE) |
| 2026-03-15 | MCS-004 DONE: Added POST /api/v1/mirror/import and GET /api/v1/mirror/import/status endpoints to MirrorDomainManagementEndpointExtensions.cs. Import runs async with manifest parsing, SHA256 checksum verification, DSSE detection, artifact copy to local data store. Added IMirrorBundleImportStore interface, implemented in InMemoryMirrorDomainStore, wired DI in Program.cs. Extended mirror-management.api.ts with importBundle() and getImportStatus() methods + DTOs. | Developer (Backend + FE) |
## Decisions & Risks
- The `StellaOpsMirrorConnector` plugin already handles fetch/parse/map jobs. The wizard configures it, not replaces it.
- Consumer config is initially in-memory (like mirror domain config). DB persistence can follow when the config store gets a database backend.
- Air-gap import via HTTP is a convenience — the CLI path remains the primary offline method. The API endpoint operates on local filesystem paths, not file uploads.
- Signature verification is optional but strongly recommended. The wizard should default to "enabled" when a signed bundle is detected.
- Mode switching from Direct → Mirror disables direct source fetching. The wizard must warn about this. Hybrid mode keeps both active.
## Next Checkpoints
- **Checkpoint 1**: MCS-001 — Consumer API endpoints operational
- **Checkpoint 2**: MCS-002 — Wizard functional end-to-end
- **Checkpoint 3**: MCS-003 — Dashboard and catalog integration
- **Checkpoint 4**: MCS-005 — E2E tests passing
- **Checkpoint 5**: MCS-006 — Docs updated