Document mirror client setup wizard and consumer API endpoints

mirror.md: added section 8 covering the 4-step UI wizard flow, wizard
vs env var comparison table, and air-gap bundle import via UI and CLI.

architecture.md: added 6 consumer API endpoints (GET/PUT /consumer,
discover, verify-signature, import, import/status) to REST API section.

airgap-operations-runbook.md: cross-reference to UI import alternative.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-15 14:49:43 +02:00
parent 9add6af221
commit b2cc26b161
4 changed files with 174 additions and 5 deletions

View File

@@ -526,6 +526,26 @@ GET /domains/{domainId}/status → domain sync status (last gener
POST /test → test mirror endpoint connectivity
```
**Mirror consumer configuration** (under `/api/v1/mirror`)
```
GET /consumer → current consumer connector configuration (base address, domain, signature, timeout, connection status, last sync)
PUT /consumer → update consumer connector config (base address, domain ID, index path, HTTP timeout, signature settings)
POST /consumer/discover → fetch mirror index from base address, return available domains with metadata (domain ID, display name, advisory count, bundle size, export formats, signed status, last generated)
POST /consumer/verify-signature → fetch JWS header from selected domain's bundle, return detected algorithm, key ID, and provider
```
The consumer endpoints configure the `StellaOpsMirrorConnector` at runtime without requiring service restarts. Configuration is persisted via `IMirrorConsumerConfigStore` (in-memory, with planned DB backend). The `/consumer/discover` endpoint enables the UI setup wizard to present operators with a list of available domains before committing to a configuration.
**Air-gap bundle import** (under `/api/v1/mirror`)
```
POST /import → import a mirror bundle from a local filesystem path { bundlePath, verifyChecksums, verifyDsse, trustRootsPath? }
GET /import/status → import progress and result (exports imported, total size, errors, warnings)
```
The import endpoint triggers an async import of a mirror bundle directory accessible to the Concelier container. It parses the bundle manifest, verifies SHA-256 checksums (when `verifyChecksums` is true), detects DSSE envelopes (when `verifyDsse` is true), and copies artifacts into the local data store. Import state is tracked by `IMirrorBundleImportStore`. This exposes the same functionality as the CLI `MirrorBundleImportService` via HTTP.
Mirror domains group export plans with shared rate limits and authentication rules. Exports support multi-value filter shorthands: `sourceCategory` (e.g., `"Distribution"` resolves to all distro sources), `sourceTag` (e.g., `"linux"`), and comma-separated `sourceVendor` values. Domain configuration is persisted in `excititor.mirror_domains` / `excititor.mirror_exports` tables, with env-var config as fallback. The `MirrorExportScheduler` background service periodically regenerates stale bundles (configurable via `RefreshIntervalMinutes`, default 60 minutes).
**AuthN/Z:** Authority tokens (OpTok) with roles: `concelier.read`, `concelier.admin`, `concelier.export`.

View File

@@ -229,7 +229,153 @@ should show `X-Cache-Status: HIT/MISS`.
- **Quota tuning** adjust per-domain `MAXDOWNLOADREQUESTSPERHOUR` in `.env` or values file.
Align CDN rate limits and inform downstreams.
## 8. References
## 8. Setting up as Mirror Consumer (UI)
Stella Ops provides a guided wizard for configuring an instance as a mirror consumer. The wizard replaces the manual env-var approach for most operators, while the environment variable path (section 1.2) remains available for headless, scripted, and air-gap-first deployments.
### 8.1 Accessing the wizard
The mirror client setup wizard is available at two entry points:
- **Mirror Dashboard** -- Navigate to **Integrations > Advisory & VEX Sources > Mirror Dashboard**. In the consumer panel header, click **Configure**. If the instance is in Direct mode with no consumer configured, the dashboard shows a "Switch to Mirror" call-to-action card that links directly to the wizard.
- **Advisory Source Catalog** -- Navigate to **Integrations > Advisory & VEX Sources > Catalog**. In the mirror context header, click **Connect to Mirror** (visible when no consumer connection is active).
The wizard route is `advisory-vex-sources/mirror/client-setup` (lazy-loaded under the Integration Hub).
### 8.2 Wizard flow (4 steps)
#### Step 1: Connect to Mirror
Enter the base address of the upstream mirror server (e.g., `https://mirror-primary.stella-ops.org`).
1. Click **Test Connection** -- the wizard calls `POST /api/v1/mirror/test` and reports success (with latency) or failure (with error message and remediation hint).
2. On successful connection, the wizard automatically calls `POST /api/v1/mirror/consumer/discover` to fetch the mirror index. A domain selector dropdown is populated with all available domains, showing each domain's display name, advisory count, bundle size, export formats, signature status, and last-generated timestamp.
3. Select the target domain from the dropdown.
4. Optionally expand **Advanced Settings** to override the index path (default: `/concelier/exports/index.json`) or adjust the HTTP timeout slider (5--300 seconds, default 30s).
#### Step 2: Signature Verification
On entering this step, the wizard automatically calls `POST /api/v1/mirror/consumer/verify-signature` to detect whether the selected domain's bundle is signed.
- If a signature is detected, the algorithm and key ID are pre-populated and a green "Signature detected" banner is shown.
- If no signature is detected, a warning banner is shown. You may proceed without verification.
- If the signature is invalid, a red banner is shown.
To configure signature verification manually:
1. Toggle **Enable signature verification** on.
2. Select the algorithm from the dropdown: ES256, ES384, ES512, RS256, or RS384.
3. Enter the Key ID (e.g., `mirror-signing-key-01`).
4. Paste the public key in PEM format or leave empty if the mirror key registry will resolve it.
5. Click **Verify Sample** to download a small bundle chunk and verify the signature against your configuration.
> Signature verification is optional but strongly recommended. The wizard defaults to "enabled" when a signed bundle is detected.
#### Step 3: Sync Schedule & Caching
**Operating mode** -- choose one:
| Mode | Behavior |
|------|----------|
| **Mirror** | Consumer only. All advisory data comes from the upstream mirror. Direct source connectors are suspended. |
| **Hybrid** | Consumer + direct sources. Mirror data augments direct fetching. Both remain active. |
A warning banner is shown when selecting Mirror mode, noting that direct source fetching will be disabled. Connectors remain configured and can be reactivated by switching back to Hybrid or Direct mode.
**Sync schedule** -- choose a preset:
- Manual, Hourly, Every 4 hours, Daily, Weekly
**Bundle caching** -- toggle on and set a TTL in hours (default: 168 hours / 7 days). Caching reduces bandwidth and improves resilience against transient mirror outages.
**Air-gap import** -- expand the collapsible "Air-Gap Import" section for offline bundle loading (see section 8.4).
#### Step 4: Review & Activate
The wizard shows a configuration summary card with all selected settings (mirror URL, domain, index path, timeout, mode, signature configuration, sync schedule, caching).
Pre-flight checks run automatically:
1. **Mirror reachable** -- re-tests connectivity.
2. **Domain exists in index** -- confirms the selected domain is still available.
3. **Signature valid** -- if verification is enabled, confirms the bundle signature verifies.
4. **Sources superseded** -- if in Mirror mode, lists the direct source connections that will be suspended.
All pre-flight checks must pass before the **Activate Mirror Consumer** button becomes enabled. On activation, the wizard calls `PUT /api/v1/mirror/consumer` (to persist the consumer connector config) and `PUT /api/v1/mirror/config` (to set the operating mode).
On success, a confirmation screen shows the configured mirror URL, domain, mode, and sync schedule with a link to the Mirror Dashboard.
### 8.3 UI wizard vs. environment variables
Both the UI wizard and environment variables configure the same `StellaOpsMirrorConnector`. The following table maps wizard fields to env vars:
| Wizard field | Environment variable | Notes |
|---|---|---|
| Mirror Base Address | `CONCELIER__SOURCES__STELLAOPSMIRROR__BASEADDRESS` | Required |
| Domain | `CONCELIER__SOURCES__STELLAOPSMIRROR__DOMAINID` | Required |
| Index Path | `CONCELIER__SOURCES__STELLAOPSMIRROR__INDEXPATH` | Default: `/concelier/exports/index.json` |
| HTTP Timeout | `CONCELIER__SOURCES__STELLAOPSMIRROR__HTTPTIMEOUT` | Default: 30s |
| Signature Enabled | `CONCELIER__SOURCES__STELLAOPSMIRROR__SIGNATURE__ENABLED` | `true` or `false` |
| Algorithm | `CONCELIER__SOURCES__STELLAOPSMIRROR__SIGNATURE__KEYID` | Inferred from JWS header |
| Key ID | `CONCELIER__SOURCES__STELLAOPSMIRROR__SIGNATURE__PROVIDER` | e.g., `mirror-signing-key-01` |
| Public Key (PEM) | `CONCELIER__SOURCES__STELLAOPSMIRROR__SIGNATURE__PUBLICKEYPATH` | Path to PEM file on disk |
**When to use environment variables instead of the wizard:**
- Automated provisioning (Terraform, Ansible, Helm values)
- Headless or CLI-only deployments
- Air-gap environments where the UI may not be accessible during initial setup
- GitOps workflows where configuration is declarative
**When to use the wizard:**
- First-time consumer setup with an unknown mirror
- Interactive domain discovery (the wizard fetches and displays the mirror index)
- Signature auto-detection (the wizard probes the bundle JWS header)
- Quick mode switching (Direct to Mirror/Hybrid) without container restarts
> Configuration set via the wizard is persisted in-memory by the `IMirrorConsumerConfigStore` and takes effect immediately. Environment variables require a service restart to take effect. When both are set, the runtime (wizard) configuration takes precedence.
### 8.4 Air-gap bundle import
Air-gap environments can import mirror bundles via two paths:
#### CLI import (`MirrorBundleImportService`)
The `stellaops-cli mirror import` command is the primary offline method. It reads a bundle directory from the local filesystem, verifies checksums and DSSE envelopes, and imports the artifacts directly:
```bash
stellaops-cli mirror import \
--bundle-path /data/mirror-bundles/export-2026-03-15 \
--verify-checksums \
--verify-dsse \
--trust-roots /data/trust-roots/roots.pem
```
#### UI import (wizard Step 3)
The mirror client setup wizard provides an air-gap import section on Step 3 (Sync Schedule & Caching). Expand the **Air-Gap Import** panel:
1. Enter the **Bundle Path** -- a local filesystem path on the server where the bundle directory resides (e.g., `/data/mirror-bundles/export-2026-03-15`).
2. Optionally enter a **Trust Roots Path** for a PEM-encoded trust roots file.
3. Toggle **Verify checksums** (enabled by default) -- validates SHA-256 checksums for all artifacts in the bundle manifest.
4. Toggle **Verify DSSE envelopes** (enabled by default) -- validates Dead Simple Signing Envelope signatures.
5. Click **Import Bundle** -- calls `POST /api/v1/mirror/import` with the specified path and verification options.
> The API endpoint operates on server-local filesystem paths, not file uploads. The bundle directory must be accessible to the Concelier service container. Mount the bundle directory into the container (e.g., via Docker volume) before triggering the import.
Import progress and results can be monitored via `GET /api/v1/mirror/import/status`, which returns the number of exports imported, total size, and any errors or warnings.
| Feature | CLI | UI |
|---|---|---|
| Offline/air-gap primary path | Yes | Yes (requires server-local path) |
| Checksum verification | `--verify-checksums` | Toggle in wizard |
| DSSE verification | `--verify-dsse` | Toggle in wizard |
| Trust roots | `--trust-roots <path>` | Trust Roots Path field |
| Progress monitoring | Stdout | `GET /api/v1/mirror/import/status` |
| Automation-friendly | Yes | No (interactive) |
## 9. References
- Deployment profiles: `devops/compose/docker-compose.mirror.yaml`,
`devops/helm/stellaops/values-mirror.yaml`